Evaluate tabulated subcontractor bids against specs and drawings — scope gap analysis, exclusion risk scoring, award recommendation. Triggers: 'evaluate bids', 'bid evaluation', 'lowest responsible bidder'.
日本語の概要は準備中です。原文の説明を表示しています。
Count-based quantity takeoff and tag completeness auditing for construction drawings. Vision + OCR reconciliation, sheet markup, Excel QTO output. Triggers: 'tag audit', 'quantity takeoff', 'QTO', 'count fixtures'.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Count-based QTO for tagged construction elements — fixtures, devices, doors, equipment, and any element identified by a tag/symbol on drawings. Does NOT do area (sqft) or linear (lf) takeoffs — only discrete counts.
The QTO is a byproduct of the audit. The real value: the completeness engine that finds what's tagged, what's missing, and produces a defensible count with full provenance for every number.
Does NOT: do area/linear takeoffs, create outbound documents autonomously, override user-approved groupings, or assume quantities without provenance.
Check for AgentCM: .construction/project.yaml at the project root.
AgentCM mode (.construction/project.yaml exists):
.construction/CLAUDE.md for project context.construction/database.yaml for project_id and api_url; run each query with sh .construction/query.sh "SQL".construction/rasters/{sheet_number}.pngVerify raster images exist: Check .construction/rasters/ for PNG files.
If empty, tell the user: "Raster images not found. Open this project in
AgentCM to trigger the export, or run:
curl -s -X POST '{api_url}/projects/{project_id}/graph/export' -H 'Content-Type: application/json' -d '{\"rootPath\": \"'$(pwd)'\"}'"
You can also rasterize individual sheets on demand using the rasterize_page.py script.
extracted_items table in PostgreSQLPOST /api/projects/{id}/tag-detections/ingestFlat File mode (no .construction/project.yaml):
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_PLUGIN_ROOT}/scripts/pdf/rasterize_page.py" "<pdf>" <page> --dpi 200 --output "<png>".construction/skills/tag-audit-and-takeoff/qto/ (Step 8)User provides: (1) tag type — e.g., "plumbing fixtures", "doors",
"light fixtures" (see references/tag-types.md for full list), and
(2) sheet scope — specific sheets, a discipline, or "all sheets".
Confirm scope before proceeding.
Custom tag types: If the user requests a tag type not in
references/tag-types.md (e.g., "keynote legends", "fire dampers",
"seismic bracing"), treat it as a custom type. Use it in the tag_type
field at ingest — the server auto-registers unknown types in the
tag_types table with default styling. The normalized name will be
qto_{type} (e.g., qto_keynote_legend). Custom types appear in the
Group Review gallery and support the full accept/reject/promote workflow.
REQUIRED: Tag type resolution. Before using any tag_type value,
you MUST resolve it against existing types:
GET /api/projects/{id}/group-review/available-types to see
all active tag types for this projectdetail_callout, section_callout, interior_elevation,
callout_single_ref, single_ref_callout → simple_calloutmulti_callout, callout_multi_ref, multi_ref_callout
→ multi_detail_calloutNEVER invent a new type name for concepts that already have canonical types. When in doubt, ask.
HARD GATE — you MUST complete this step before proceeding to Step 2. Do not skip this step. If the API is unreachable, stop and tell the user.
Before scanning sheets, query what's already tagged per sheet:
curl -s "{api_url}/projects/{project_id}/sheets/{sheet_id}/claimed-elements"
Response:
{
"claimed_element_ids": ["el_001", "el_002", ...],
"suggestion_count": 12,
"by_type": { "qto_room_tag": 8, "room_tag": 4 },
"existing_tags": [
{ "id": "...", "proposedType": "qto_room_tag", "proposedText": "KITCHEN",
"status": "accepted", "constituentIds": [...], "combinedBbox": {...}, "confidence": 0.92 }
]
}
Report to user: "Sheet A-1.4: 12 tags already detected (8 accepted, 4 pending)."
Reconciliation rules:
AND id NOT IN ('{id1}', '{id2}', ...) to SQL WHERE clausesexisting_tags in the final QTO output — the QTO captures
ALL items (existing + newly detected), not just new onesproposedText
values — these are "confirmed" not "new"existing_tags (from this endpoint) +
newly detected (from ingest response)The QTO is always a complete count. Reconciliation avoids redundant detection work, not redundant output.
Before proceeding to Step 2, report coverage and get confirmation: "Existing coverage: {N} sheets have {M} accepted, {P} pending detections for this tag type. {Q} sheets have zero coverage. Proceed with vision scan on {Q} uncovered sheets?" Wait for explicit user confirmation before continuing.
CRITICAL — Vision is MANDATORY for this step.
Examine each sheet image. Identify all visible tags of the requested type. Return tag texts and view type classification — no coordinates.
Vision must classify each tag's view: FLOOR PLAN, ENLARGED PLAN,
DETAIL, SECTION, or ELEVATION. This determines record type in Step 5.
Classification signals are in references/tag-types.md.
In Flat File mode: This is the primary (and only) identification step. Provide rough locations ("upper-left quadrant") for markup purposes.
For each vision-identified tag, find its spatial anchor in the DB using the most distinctive word in the tag text. "WALK-IN FREEZER" → search "FREEZER". "WOMEN'S RESTROOM" → search "WOMEN".
sh .construction/query.sh "SELECT id, text, x_min, y_min, x_max, y_max FROM extracted_items WHERE sheet_id = '{sheet_id}' AND is_deleted = false AND text ILIKE '%FREEZER%'"
Multiple hits expected — each is a potential tag location. Zero hits → try alternate words. Still zero → flag "vision-only, no OCR anchor".
For each anchor, pull nearby extracted items. Padding values by tag type
in references/spatial-params.md. Target: 8-25 candidates per tag.
sh .construction/query.sh "SELECT id, text, x_min, y_min, x_max, y_max FROM extracted_items WHERE sheet_id = '{sheet_id}' AND is_deleted = false AND x_min BETWEEN {anchor_x - pad} AND {anchor_x + pad} AND y_min BETWEEN {anchor_y - pad} AND {anchor_y + pad}"
Adaptive: double padding if < 3 items returned, reduce 30% if > 40.
Give Claude the vision tag text and the small candidate set (~15-20
items). Group constituent items following references/grouping-rules.md
— text reconstruction, spatial coherence, include/exclude criteria,
confidence scoring.
Batching: Process ALL tags per sheet in one call. 12 tags × ~200 tokens each = ~2400 tokens. Never load the full extracted items set.
Flat File mode: Skip grouping. Tags come from vision only. Mark
all detections as confidence: "vision_only" in output.
Validation checkpoint (after first 3 sheets): After grouping tags on the first 3 sheets, STOP and present 5 sample grouped detections to the user. Show for each:
Get explicit user confirmation: "Grouping quality acceptable? Proceed with remaining {N} sheets?" Do not continue until confirmed.
REQUIRED for all modes. Mark up each sheet with visual highlights so the user can see exactly what was identified.
QTO markup must show ALL tagged items per sheet — not just newly detected.
Pull existing items from the Step 1.5 claimed-elements response (existing_tags)
and include them in the markup alongside new detections. Use color to distinguish:
--color "green" for accepted items (from previous detection/review)--color "blue" for newly detected items (this session)--color "orange" for pending review itemsRun the markup script once per color group per sheet, layering onto the same
output image (use the previous output as --base for the next pass).
# Build items JSON with one entry per detected tag
# Then call the shared markup script:
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" \
"${CLAUDE_SKILL_DIR}/scripts/markup_tags.py" \
--base "{sheet_image_path}" \
--items "{items_json_path}" \
--output "{output_path}" \
--color "blue" \
--label-style "numbered"
Items JSON format (pixel coordinates):
[
{"x": 2500, "y": 1800, "shape": "circle", "radius": 20, "label": "WALK-IN FREEZER"},
{"x": 3100, "y": 2200, "shape": "circle", "radius": 20, "label": "DRY STORAGE"}
]
Convert normalized 0-1 coordinates to pixels using sheet image dimensions.
If PDF is available, also create native PDF annotations:
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" \
"${CLAUDE_PLUGIN_ROOT}/scripts/pdf/annotate_pdf.py" \
--pdf "{pdf_path}" --items "{items_json}" --output "{output_pdf}" \
--author "Claude Code QTO"
Record type depends on view type. Full data model in
references/quantity-model.md.
Floor plan tags → Element Instance. One tag = one installed item. Detail/section/elevation tags → Element Type Definition. Template.
Pre-ingest confirmation (REQUIRED before POSTing): Before making ANY ingest API calls, present this summary to the user:
"Ready to ingest {N} new detections as '{type_name}'? (Y/N)"
Do NOT POST to the ingest endpoint until the user explicitly confirms. Ingesting without confirmation is a blocking violation of this skill. The server enforces a maximum of 100 detections per request — batch your ingest calls per sheet.
AgentCM mode: POST detection results to the API:
curl -X POST "{api_url}/projects/{project_id}/tag-detections/ingest" \
-H "Content-Type: application/json" \
-d '{"sheet_id": "{sheet_id}", "detections": [...]}'
Each detection: { tag_text, tag_type, element_ids, bounding_box, confidence, view_type, room_id }.
bounding_box must be { x, y, width, height } in normalized 0-1 coordinates.
Results appear as highlighted overlays in AgentCM's canvas UI.
In AgentCM mode, also write the finding to .construction/agent_findings/qto_{tag_type}_{timestamp}.json.
CRITICAL — use the EXACT schema from references/qto-output-format.md.
The Excel script is rigid and parses specific key names. Required keys:
line_items[] (NOT qto_lines) — one entry per unique element/designationtotals{} with sheet_instances, derived_instances, deduplicated, building_quantityinstance_details[] with detection_id, sheet, room, status, confidencescope{}, completeness{}, issues[]Include ALL items — existing pipeline detections + newly detected this session.
The QTO is a complete count. Query the claimed-elements endpoint (Step 1.5) for
existing items and merge them into line_items[].instance_details[].
Both modes: Save the QTO JSON (working data) to
.construction/skills/tag-audit-and-takeoff/qto/{tag_type}_{timestamp}.json and the
marked-up PNGs (deliverables) to the project directory.
Excel export (both modes): Pass that QTO JSON as {qto_json_path}
and invoke the export script to produce a styled multi-sheet workbook:
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" \
"${CLAUDE_SKILL_DIR}/scripts/qto_to_xlsx.py" \
--data "{qto_json_path}" \
--project "{project_name}" \
--scope "{tag_type_display}" \
--output "QTO_{tag_type}_{date}.xlsx"
The workbook has 4 sheets: QTO Summary (line items with counts), Instance Detail (every detection with provenance), Type Definitions (templates applied to rooms), and Completeness (coverage metrics).
After type definitions are identified from details/sections/enlarged
plans, resolve where they apply. Full logic: references/quantity-model.md.
Three methods (in order of reliability):
Deduplication: direct instances always take precedence over derived.
Compare detected counts against schedules (door, fixture, equipment) when available. Report: "[tag type]: X detected, Y expected, Z% coverage. Gaps on sheets: [list]." No schedule → report raw counts.
Allowed scripts — exhaustive list. Only execute these scripts during this skill:
${CLAUDE_PLUGIN_ROOT}/scripts/pdf/rasterize_page.py — rasterize a sheet PDF page to PNG (flat file mode, or on demand)scripts/markup_tags.py — sheet markup with tag highlights${CLAUDE_PLUGIN_ROOT}/scripts/pdf/annotate_pdf.py — native PDF annotationsscripts/qto_to_xlsx.py — QTO Excel export (4-sheet workbook)${CLAUDE_PLUGIN_ROOT}/scripts/issue_manager.py — log schedule/drawing count mismatches to the Issue Registry (command and severity mapping in construction-guide)まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Evaluate tabulated subcontractor bids against specs and drawings — scope gap analysis, exclusion risk scoring, award recommendation. Triggers: 'evaluate bids', 'bid evaluation', 'lowest responsible bidder'.
日本語の概要は準備中です。原文の説明を表示しています。
Extract data from subcontractor bid PDFs and produce a comparison spreadsheet. Feeds into /bid-evaluator. Triggers: 'tabulate bids', 'bid comparison', 'compare bids', 'buyout analysis', 'bid tab'.
日本語の概要は準備中です。原文の説明を表示しています。
Scope-specific code gap analysis — extracts referenced codes from project docs, researches what should apply, surfaces the delta. Triggers: 'code research', 'what codes apply', 'code check', 'ADA requirements', 'egress'.
日本語の概要は準備中です。原文の説明を表示しています。
Operating guide for construction project documents — load before reading drawings, specs, schedules, RFIs, submittals or bids. Data-access rules (never read PDFs directly; rasterize first), AgentCM graph-guided vision, drawing and cross-reference conventions, document precedence. Triggers: 'construction project', 'drawings', 'specs', 'sheet', 'RFI', 'submittal'.
日本語の概要は準備中です。原文の説明を表示しています。
Construction document review with PE judgment — RFI research, submittal analysis, coordination checking, scope gap detection. Use when reviewing drawings, specs, submittals, or RFIs. Triggers: 'review', 'coordination', 'what's missing'.
日本語の概要は準備中です。原文の説明を表示しています。
Set up a construction project — inventories files, classifies drawings/specs/schedules/registers, detects AgentCM mode, appends construction context to CLAUDE.md. Triggers: 'set up project', 'construction setup', 'classify documents'.
日本語の概要は準備中です。原文の説明を表示しています。