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 tabular schedule data from construction drawings — door, window, finish, fixture, panel schedules — and output to Excel. Triggers: 'door schedule', 'extract schedule', 'schedule to Excel', 'panel schedule'.
中文简介正在准备中。当前显示原文说明。
安装前可以查看交给 AI 代理的指令内容。
Extracts tabular schedule data embedded in drawing sheets or spec pages and outputs structured Excel files. Schedules are typically one element among many on a sheet — or the entire sheet may be a schedule.
Before extracting or reconciling, read .construction/project.yaml. Present and readable means AgentCM mode: note its contract_version and follow ${CLAUDE_PLUGIN_ROOT}/reference/agentcm-mode.md. Absent means flat-file mode. A listing that shows the file, or .construction/database.yaml alone, does not decide it: read the file.
Extraction Progress:
- [ ] Step 1: Discover schedule locations
- [ ] Step 2: Isolate (crop) the schedule region
- [ ] Step 3: Extract structured data (pdfplumber + vision)
- [ ] Step 4: Validate and clean data
- [ ] Step 5: Output to Excel
- [ ] Step 6: Ingest to AgentCM (AgentCM mode)
- [ ] Step 7: Write a finding
Use a multi-source discovery approach, checking all available sources:
Search .construction/index/sheet_index.yaml (or the sheet index you've built) for sheets with "SCHEDULE" in the title. Many important schedules occupy an entire sheet — the sheet title tells you exactly what it is:
For dedicated schedule sheets, the entire page is the extraction target — no need to crop.
In AgentCM mode (.construction/project.yaml exists; follow ${CLAUDE_PLUGIN_ROOT}/reference/agentcm-mode.md), .construction/INDEX.md's Schedules and legends table lists every table AgentCM found, each in its own .construction/schedules/<sheet>-<n>.tsv. The file's comment line gives the table's box on the sheet (x y w h, 0 to 1 from the top left) and, for a schedule already ingested, its schedule_id (none for a table read from the sheet's text alone). For embedded schedules on non-schedule sheets, that box is where to look: sh .construction/crop.sh <sheet> <x> <y> <w> <h>.
Note: A table read from the text is AgentCM's reading, and its box an estimate: treat both as leads, not definitive boundaries. Always verify with vision.
Schedules appear on specific sheet types:
If no index or graph is available:
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_PLUGIN_ROOT}/scripts/pdf/rasterize_page.py" "{pdf_path}" {page} --dpi 150 --output ".construction/skills/schedule-extractor/pages/full_sheet.png"
Use vision on the full sheet image: "Identify any tabular schedules on this drawing sheet. Report the approximate bounding box (top-left x,y and bottom-right x,y) as fractions of the image width and height between 0 and 1, the schedule type, and the column headers visible."
For dedicated schedule sheets (entire page is a schedule): Skip cropping — use the full page.
For embedded schedules (schedule is one element on a larger sheet):
Crop the identified region with padding. The box is the 0–1 fractions from
Step 1, so --normalized is required; without it the script reads the values
as pixels and returns an empty crop:
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_PLUGIN_ROOT}/scripts/pdf/crop_region.py" ".construction/skills/schedule-extractor/pages/full_sheet.png" \
--box {x1},{y1},{x2},{y2} --normalized \
--padding 20 \
--output ".construction/skills/schedule-extractor/pages/schedule_crop.png"
Re-rasterize at higher DPI (300) for the cropped region to improve text clarity.
rasterize_page.py --crop takes percentages, so multiply each fraction by 100
(0.62,0.08,0.98,0.45 becomes 62,8,98,45):
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_PLUGIN_ROOT}/scripts/pdf/rasterize_page.py" "{pdf_path}" {page} \
--dpi 300 \
--crop {x1_pct},{y1_pct},{x2_pct},{y2_pct} \
--output ".construction/skills/schedule-extractor/pages/schedule_hires.png"
A table INDEX.md lists under Schedules and legends is already read. Turn its file into the exporter's data with the reader, never with a converter of your own:
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_SKILL_DIR}/scripts/agentcm_schedule.py" ".construction/schedules/<sheet>-<n>.tsv" \
--type door_schedule --data ".construction/skills/schedule-extractor/<sheet>-<n>.json"
It prints the header row it took and what it left out. A table read from the sheet's text has its title and header rows at the top: when it took the wrong row, give --header-row <y> (the line's position, as the file prints it) and --header-rows <n> for a header on several lines. Then verify the table against the drawing with crop.sh and the box on the file's comment line (Source B). Where the drawing shows a different value, correct that cell in the data JSON with the Edit tool, and record the correction as ${CLAUDE_PLUGIN_ROOT}/reference/agentcm-mode.md says. Methods A to C below are for a table AgentCM did not find.
Otherwise use a try → validate → fallback approach:
Extract the page's tables with the shared script (its JSON holds text and tables; add --bbox x1,y1,x2,y2 in PDF points to limit it to the schedule region):
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_PLUGIN_ROOT}/scripts/pdf/extract_text_region.py" "{pdf_path}" {page} --output ".construction/skills/schedule-extractor/tables.json"
If multiple tables are found, select the largest one (most rows with the most columns — schedules are wide). The first row of the selected table contains headers; subsequent rows are data. If no tables are found or results look garbled, fall back to vision (Method B). Do not write pdfplumber code of your own.
Check these criteria before proceeding:
Decision:
QUALITY GATE:
extracted_rows >= 10? YES → check next | NO → use Method B
column_consistency >= 80%? YES → check next | NO → use Method B
non_empty_cells >= 50%? YES → check next | NO → use Method B
first_col_has_valid_ids? YES → use Method A | NO → use Method B
When pdfplumber fails (common with complex layouts, merged cells, non-standard table lines):
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_PLUGIN_ROOT}/scripts/pdf/rasterize_page.py" "{pdf_path}" {page} --dpi 150 --output ".construction/skills/schedule-extractor/pages/full_sheet.png"
"This is a construction drawing sheet containing a schedule (tabular data). Extract ALL rows from the schedule as a JSON array of objects. The first row of the table contains column headers — use those as object keys. Each subsequent row is one entry. Preserve exact values including dimensions, abbreviations, and codes. If a cell is empty, use null."
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_PLUGIN_ROOT}/scripts/pdf/rasterize_page.py" "{pdf_path}" {page} --dpi 300 --output ".construction/skills/schedule-extractor/pages/schedule_hires.png"
Run both methods and cross-validate:
After extraction (by either method):
3' - 0" → 3'-0" (remove spaces around dashes).construction/tags.tsv (kind room or door; the lines starting with the number and a tab). Not finding one there proves nothing (INDEX.md says when its room list is incomplete): report it as "not in AgentCM's index" and check the plan sheets' .construction/sheets/<sheet>.tsv."${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_SKILL_DIR}/scripts/schedule_to_xlsx.py" \
--data ".construction/skills/schedule-extractor/<sheet>-<n>.json" \
--type door_schedule \
--project "Project Name" \
--sheet "A-0.01" \
--output "Door_Schedule_A-0.01.xlsx"
The data JSON is working data: write it (with the Write tool, or the reader in AgentCM mode) under .construction/skills/schedule-extractor/, never in the project root or a folder of your own. The workbook is the deliverable: write it in the project folder as the command above shows, never under .construction/. For a table AgentCM has already ingested (schedule_id=<id> on its file's comment line), add --schedule-id <id> --sheet-id <the sheet's id> so the workbook can come back through the round trip.
The script creates a formatted Excel workbook with:
After Excel output, send the extracted data to AgentCM's schedule ingest, which stores it and links its rows to rooms. The payload (below) goes in .construction/skills/schedule-extractor/outbox/schedules-ingest-<sheet>-<YYYYMMDD-HHMMSS>.json:
schedules/ not yet ingested (schedule_id=none): the reader writes it. Run it again with --payload <that path> --sheet-id <id> --row-entity-type <type> --source-method hybrid --confidence <0 to 1> (hybrid: AgentCM's reading, checked by eye with crop.sh), and make any correction from Step 3 in the payload's cell too.schedule_id=<id>): nothing to ingest. The reader refuses, and the PE edits come back through the round trip below.sourceMethod is one of pdfplumber, vision, hybrid or manual, and rowEntityType one of the types listed below.Send it as ${CLAUDE_PLUGIN_ROOT}/reference/agentcm-mode.md says under Writing back; if AgentCM is closed, the payload waits there and the PE is told:
# api_url and project_id from .construction/database.yaml
curl -sS -X POST "{api_url}/projects/{project_id}/schedules/ingest" \
-H "Content-Type: application/json" \
-d @".construction/skills/schedule-extractor/outbox/schedules-ingest-<sheet>-<YYYYMMDD-HHMMSS>.json" \
-w "\nHTTP %{http_code}\n"
The sheet's id. sheetId is the id of the entry in .construction/index/sheet_index.yaml whose sheet_number is the schedule's sheet. Never guess it from the number. If that file is missing, or its entries have no id (an older sheet-splitter wrote a pages: list with filename and page_index), say so and stop: do not ingest. The Excel workbook from Step 5 stands either way.
The ingest payload JSON follows this structure:
{
"sheetId": "<uuid of the source sheet>",
"scheduleType": "door",
"title": "DOOR SCHEDULE",
"rowEntityType": "door",
"columns": [
{"key": "MARK", "header": "MARK"},
{"key": "WIDTH", "header": "WIDTH"},
{"key": "HEIGHT", "header": "HEIGHT"},
{"key": "TYPE", "header": "TYPE"},
{"key": "FRAME", "header": "FRAME"},
{"key": "HARDWARE_SET", "header": "HARDWARE SET"},
{"key": "FIRE_RATING", "header": "FIRE RATING"}
],
"rows": [
{
"entityIdentifier": "2.101A",
"cells": {"MARK": "2.101A", "WIDTH": "3'-0\"", "HEIGHT": "7'-0\"", "TYPE": "A", "FRAME": "HM", "HARDWARE_SET": "HW-3", "FIRE_RATING": "1 HR"}
}
],
"sourceMethod": "pdfplumber",
"confidence": 0.95
}
Schedule types and their rowEntityType:
"door" (room derived from door number: "2.101A" → Room 2.101)"window" (room derived from window mark)"room" (entity identifier IS the room number)"equipment""fixture" (room linked spatially from plan tags)The ingest endpoint automatically:
In both modes, record the extraction in .construction/skills/schedule-extractor/findings/:
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_PLUGIN_ROOT}/scripts/graph/write_finding.py" \
--skill "schedule-extractor" --type "schedule_extracted" \
--title "Door schedule extracted from A-0.01" \
--source-sheet "A-0.01" \
--output-file "Door_Schedule_A-0.01.xlsx" \
--data '{"schedule_type": "door", "row_count": 45, "columns": ["MARK","SIZE","TYPE","FRAME","HARDWARE SET"]}'
When a PE edits an exported Excel schedule and wants changes applied back to AgentCM:
Reconciliation Progress:
- [ ] Step R1: Read metadata from edited Excel
- [ ] Step R2: Compute changeset (diff vs DB)
- [ ] Step R3: POST changeset to reconcile endpoint
- [ ] Step R4: Report summary to user
Exported Excel files contain reconciliation anchors:
_agentcm_meta sheet with schedule_id, schedule_type, sheet_id_row_key column A with entity identifiers (door numbers, room numbers, etc.)If the Excel lacks _agentcm_meta, it was not exported by AgentCM — treat as a fresh import (re-run the extraction workflow above).
Run the diff engine to compare the edited Excel against the rows AgentCM holds:
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_SKILL_DIR}/scripts/xlsx_to_changeset.py" \
--excel "edited_schedule.xlsx" \
--output ".construction/skills/schedule-extractor/outbox/schedules-reconcile-<sheet>-<YYYYMMDD-HHMMSS>.json"
The script:
_agentcm_meta → extracts schedule_id. A table never ingested (schedule_id=none) has nothing to reconcile and is refused: ingest it first (Step 6)_row_key column + all data columnsGET {api_url}/projects/{project_id}/schedules/{schedule_id}/rows, with project_id and api_url from .construction/database.yaml (nothing else in it)sourceFile the edited Excel's nameIt needs AgentCM running. If AgentCM does not answer, the script exits 3 and computes nothing: tell the PE to start AgentCM and rerun this step (the edited Excel is all it needs).
Send the changeset R2 wrote to the outbox, as ${CLAUDE_PLUGIN_ROOT}/reference/agentcm-mode.md says under Writing back:
# api_url and project_id from .construction/database.yaml
curl -sS -X POST "{api_url}/projects/{project_id}/schedules/reconcile" \
-H "Content-Type: application/json" \
-d @".construction/skills/schedule-extractor/outbox/schedules-reconcile-<sheet>-<YYYYMMDD-HHMMSS>.json" \
-w "\nHTTP %{http_code}\n"
The reconcile endpoint applies changes with:
isUserOverride=TRUE — future re-extractions will not overwrite themschedule_change_logThe endpoint returns an ExcelReconcileResult:
{
"rowsUpdated": 3,
"rowsAdded": 1,
"rowsSoftDeleted": 0,
"cellsChanged": 7,
"conflictsCreated": 0,
"columnsAdded": 0,
"columnsHidden": 0,
"errors": []
}
Report the summary to the user. If errors is non-empty, surface those for review.
When exporting schedules for PE review, always include reconciliation anchors:
"${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_SKILL_DIR}/scripts/schedule_to_xlsx.py" \
--data schedule_data.json \
--type door_schedule \
--project "Project Name" \
--sheet "A-0.01" \
--schedule-id "{schedule_uuid}" \
--sheet-id "{sheet_uuid}" \
--output "Door_Schedule_A-0.01.xlsx"
The --schedule-id and --sheet-id flags embed metadata in the hidden _agentcm_meta sheet, enabling the round-trip reconciliation workflow.
| Type | Common columns | Notes |
|---|---|---|
| Door | Mark, Width, Height, Type, Frame, Hardware Set, Fire Rating | Cross-ref hardware sets with spec 08 71 00 |
| Window | Mark, Width, Height, Type, Glazing, Frame Material | Check for energy code compliance |
| Room Finish | Room #, Name, Floor, Base, North/South/East/West Walls, Ceiling | Finish codes defined in legend |
| Panel | Circuit #, Description, Load (VA), Breaker Size, Phase | Verify total load vs panel capacity |
| Fixture (Plumbing) | Mark, Description, Manufacturer, Model, Connection | Cross-ref with spec Division 22 |
| Equipment (HVAC) | Tag, Description, CFM/BTU, Voltage, Weight | Cross-ref with spec Division 23 |
Never overwrite an existing schedule extraction. The export script uses safe_output_path() which appends _v2, _v3, etc. automatically.
Allowed scripts — exhaustive list. Only execute these scripts during this skill:
${CLAUDE_PLUGIN_ROOT}/scripts/pdf/extract_text_region.py — Method A table extraction (pdfplumber)${CLAUDE_PLUGIN_ROOT}/scripts/pdf/rasterize_page.py — rasterize PDF pages for vision extraction${CLAUDE_PLUGIN_ROOT}/scripts/pdf/crop_region.py — crop schedule region from full sheet imagescripts/agentcm_schedule.py — AgentCM mode: a table from .construction/schedules/ to the exporter's data and, for one not yet ingested, the ingest payload (Steps 3 and 6)scripts/schedule_to_xlsx.py — Excel export with reconciliation anchorsscripts/xlsx_to_changeset.py — diff the edited Excel against AgentCM's stored rows (its rows endpoint) for reconciliation${CLAUDE_PLUGIN_ROOT}/scripts/graph/write_finding.py — record the extraction as a finding (Step 7)${CLAUDE_PLUGIN_ROOT}/scripts/corrections_append.py — AgentCM mode: record a correction of AgentCM's data (reference/agentcm-mode.md)还没有评价。欢迎分享你的使用感受。
无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。
还没有评论。来写第一条吧。
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'.
中文简介正在准备中。当前显示原文说明。
Researches which building codes, accessibility standards and authority requirements (health department, fire marshal, state agency) apply to a scope or spec section, and which the project documents miss. Use for 'what codes apply', 'code check', 'ADA requirements', 'egress', 'health department'. Not for reading what the drawings or specs say (construction-guide).
中文简介正在准备中。当前显示原文说明。
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'.
中文简介正在准备中。当前显示原文说明。
Checks construction drawings and specs for what's missing, cross-trade coordination conflicts and scope gaps, with PE judgment. Use for 'what's missing', 'check coordination', 'scope gap', or researching an RFI before answering it. Not for drafting RFIs (rfi-drafter), code research (code-researcher) or reviewing a submittal package (the user runs /construction:submittal-review).
中文简介正在准备中。当前显示原文说明。
Set up a construction project — inventories files, classifies drawings/specs/schedules/registers and appends construction context to CLAUDE.md; in an AgentCM project, prints AgentCM's orientation instead. Triggers: 'set up project', 'construction setup', 'classify documents'.
中文简介正在准备中。当前显示原文说明。