Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build no-code/low-code automation workflows for construction using n8n. Automate data extraction, cost estimation, report generation, and system integrations without writing code.
.claude/skills/datadrivenconstruction-n8n-workflow-automation/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 107% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 553% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 190% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 594% | 0% |
| case-20 | ✗→✓ | ▲ Improved | 152% | 0% |
This skill implements visual workflow automation for construction processes using n8n. Automate repetitive tasks, integrate systems, and build PROJECT TO BUDGET pipelines without extensive programming.
Inspired by DDC Methodology - Automating the bridge between BIM models and cost estimation.
> "Автоматизация процесса 'от проекта к смете' позволяет сократить время на подготовку бюджета с недель до часов." > — DDC LinkedIn Post
bash# Using Docker (recommended) docker run -it --rm \ --name n8n \ -p 5678:5678 \ -v ~/.n8n:/home/node/.n8n \ n8nio/n8n # Using npm npm install n8n -g n8n start # Access at: http://localhost:5678
json{ "name": "Revit to Budget Automation", "nodes": [ { "name": "Watch Revit Export Folder", "type": "n8n-nodes-base.localFileTrigger", "parameters": { "path": "/data/revit_exports", "events": ["add"], "fileExtension": ".xlsx" } }, { "name": "Read Excel Data", "type": "n8n-nodes-base.readWriteFile", "parameters": { "operation": "read", "filePath": "={{ $json.fileName }}" } }, { "name": "Parse BIM Elements", "type": "n8n-nodes-base.code", "parameters": { "language": "python", "code": "import pandas as pd\nimport json\n\ndf = pd.read_excel(items[0].binary.data)\n\nelements = df.to_dict('records')\n\nreturn [{'json': {'elements': elements, 'count': len(elements)}}]" } }, { "name": "Match to Unit Prices", "type": "n8n-nodes-base.httpRequest", "parameters": { "url": "http://api.construction-prices.com/match", "method": "POST", "body": "={{ JSON.stringify($json.elements) }}" } }, { "name": "Calculate Costs", "type": "n8n-nodes-base.code", "parameters": { "language": "javascript", "code": "const elements = items[0].json.elements;\n\nlet totalCost = 0;\nconst costBreakdown = [];\n\nfor (const elem of elements) {\n const cost = elem.quantity * elem.unit_price;\n totalCost += cost;\n costBreakdown.push({\n category: elem.category,\n quantity: elem.quantity,\n unit_price: elem.unit_price,\n total: cost\n });\n}\n\nreturn [{\n json: {\n total_cost: totalCost,\n breakdown: costBreakdown\n }\n}];" } }, { "name": "Generate Report", "type": "n8n-nodes-base.spreadsheetFile", "parameters": { "operation": "create", "fileName": "cost_estimate_{{ $now.format('yyyy-MM-dd') }}.xlsx" } }, { "name": "Send Email Notification", "type": "n8n-nodes-base.emailSend", "parameters": { "to": "project-team@company.com", "subject": "New Cost Estimate Generated", "text": "Total estimate: ${{ $json.total_cost }}" } } ] }
json{ "name": "Daily Project Report", "nodes": [ { "name": "Schedule Trigger", "type": "n8n-nodes-base.cron", "parameters": { "cronExpression": "0 6 * * 1-5" } }, { "name": "Fetch Project Data", "type": "n8n-nodes-base.httpRequest", "parameters": { "url": "{{ $env.PROJECT_API }}/status", "method": "GET" } }, { "name": "Fetch Weather Data", "type": "n8n-nodes-base.httpRequest", "parameters": { "url": "https://api.openweathermap.org/data/2.5/weather", "qs": { "q": "{{ $json.project_location }}", "appid": "{{ $env.WEATHER_API_KEY }}" } } }, { "name": "Generate Report", "type": "n8n-nodes-base.code", "parameters": { "language": "javascript", "code": "const project = items[0].json;\nconst weather = items[1].json;\n\nconst report = {\n date: new Date().toISOString().split('T')[0],\n project_name: project.name,\n progress: project.progress_pct,\n weather: {\n condition: weather.weather[0].main,\n temp: Math.round(weather.main.temp - 273.15)\n },\n tasks_today: project.scheduled_tasks,\n blockers: project.blockers || []\n};\n\nreturn [{ json: report }];" } }, { "name": "Post to Slack", "type": "n8n-nodes-base.slack", "parameters": { "channel": "#project-updates", "text": "📊 Daily Report - {{ $json.project_name }}\n\nProgress: {{ $json.progress }}%\n🌡️ Weather: {{ $json.weather.condition }} ({{ $json.weather.temp }}°C)\n\nToday's Tasks:\n{{ $json.tasks_today.join('\\n') }}" } } ] }
json{ "name": "BIM Change Detection", "nodes": [ { "name": "Watch IFC Folder", "type": "n8n-nodes-base.localFileTrigger", "parameters": { "path": "/models", "events": ["change"], "fileExtension": ".ifc" } }, { "name": "Extract Model Data", "type": "n8n-nodes-base.executeCommand", "parameters": { "command": "python /scripts/extract_ifc.py {{ $json.fileName }}" } }, { "name": "Compare with Previous", "type": "n8n-nodes-base.code", "parameters": { "language": "python", "code": "import json\n\ncurrent = json.loads(items[0].json.output)\nprevious = load_previous_version()\n\nchanges = {\n 'added': [],\n 'modified': [],\n 'deleted': []\n}\n\n# Compare logic\nfor elem in current:\n if elem['id'] not in previous:\n changes['added'].append(elem)\n elif elem != previous[elem['id']]:\n changes['modified'].append(elem)\n\nfor elem_id in previous:\n if elem_id not in [e['id'] for e in current]:\n changes['deleted'].append(previous[elem_id])\n\nreturn [{'json': changes}]" } }, { "name": "Update Database", "type": "n8n-nodes-base.postgres", "parameters": { "operation": "executeQuery", "query": "INSERT INTO model_changes (timestamp, changes) VALUES (NOW(), '{{ JSON.stringify($json) }}')" } }, { "name": "Notify Team", "type": "n8n-nodes-base.microsoftTeams", "parameters": { "message": "🔔 Model Updated\n\n+{{ $json.added.length }} elements added\n📝 {{ $json.modified.length }} elements modified\n-{{ $json.deleted.length }} elements deleted" } } ] }
javascript// n8n Code Node - Extract BIM Quantities const xlsx = require('xlsx'); // Read uploaded file const workbook = xlsx.read(items[0].binary.data, { type: 'buffer' }); const sheetName = workbook.SheetNames[0]; const data = xlsx.utils.sheet_to_json(workbook.Sheets[sheetName]); // Process BIM elements const quantities = {}; for (const row of data) { const category = row['Category'] || 'Unknown'; const volume = parseFloat(row['Volume']) || 0; if (!quantities[category]) { quantities[category] = { count: 0, volume: 0 }; } quantities[category].count++; quantities[category].volume += volume; } return [{ json: { quantities, total_elements: data.length } }];
javascript// n8n Code Node - Match elements to unit prices const elements = items[0].json.elements; const priceDatabase = $env.PRICE_DATABASE; const matched = []; for (const elem of elements) { // Fuzzy match description to price items const match = await $http.post(`${priceDatabase}/search`, { query: elem.description, category: elem.category }); matched.push({ ...elem, matched_item: match.data.best_match, unit_price: match.data.unit_price, confidence: match.data.confidence }); } return [{ json: { matched_elements: matched } }];
javascript// n8n Code Node - Generate PDF Report const PDFDocument = require('pdfkit'); const doc = new PDFDocument(); const buffers = []; doc.on('data', buffers.push.bind(buffers)); // Header doc.fontSize(20).text('Cost Estimate Report', { align: 'center' }); doc.moveDown(); // Project Info doc.fontSize(12).text(`Project: ${items[0].json.project_name}`); doc.text(`Date: ${new Date().toLocaleDateString()}`); doc.moveDown(); // Cost Summary doc.fontSize(14).text('Cost Summary', { underline: true }); for (const [category, cost] of Object.entries(items[0].json.costs)) { doc.fontSize(10).text(`${category}: $${cost.toLocaleString()}`); } doc.end(); return new Promise(resolve => { doc.on('end', () => { resolve([{ json: { success: true }, binary: { data: Buffer.concat(buffers).toString('base64'), fileName: 'cost_report.pdf', mimeType: 'application/pdf' } }]); }); });
yamlData Sources: - Google Sheets: Project tracking, cost databases - Airtable: Element databases, issue tracking - PostgreSQL: BIM databases, project data - HTTP Request: API integrations File Processing: - Read/Write File: Excel, CSV, JSON - Execute Command: Python scripts, CLI tools - Code: Custom processing logic Communication: - Slack: Team notifications - Microsoft Teams: Project updates - Email: Reports, alerts - Telegram: Mobile notifications Cloud Storage: - AWS S3: Model storage - Google Drive: Document sharing - Dropbox: File sync
json{ "workflow": "QTO Extraction", "trigger": "Manual/Webhook", "steps": [ "Receive IFC file", "Extract quantities (Python/IfcOpenShell)", "Group by category", "Add unit prices", "Calculate totals", "Generate Excel report", "Upload to cloud storage", "Send notification" ] }
json{ "workflow": "Daily Status", "trigger": "Cron (6:00 AM)", "steps": [ "Fetch project status from API", "Get weather forecast", "Check scheduled tasks", "Compile daily report", "Post to Slack/Teams", "Email to stakeholders" ] }
markdown1. **Error Handling** - Always add error branches - Log failures to database - Send alerts on critical failures 2. **Data Validation** - Validate input data format - Check for required fields - Handle missing values gracefully 3. **Performance** - Use batch processing for large datasets - Implement pagination for API calls - Cache frequently used data 4. **Security** - Store credentials in environment variables - Use encryption for sensitive data - Implement access controls
| Workflow Type | Trigger | Common Nodes | |--------------|---------|--------------| | File Processing | File Trigger | Code, HTTP, Spreadsheet | | Scheduled Reports | Cron | HTTP, Code, Email | | Data Sync | Webhook | Database, API, Code | | Notifications | Various | Slack, Teams, Email |
etl-pipeline for code-based data pipelinesllm-data-automation for AI-powered automationvector-search for intelligent document search| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-15 | fail→fail | 18,941 | 20,125 | +6% | 1 | 1 | 0% | 2,913 | 6,665 | +129% | 0 | 0 | — |
case-01 | fail→fail | 27,057 | 23,153 | -14% | 1 | 1 | 0% | 5,667 | 8,266 | +46% | 0 | 0 | — |
case-02 | fail→fail | 29,416 | 19,465 | -34% | 1 | 1 | 0% | 6,225 | 7,453 | +20% | 0 | 0 | — |
case-03 | pass→pass | 20,089 | 16,517 | -18% | 1 | 1 | 0% | 3,598 | 6,736 | +87% | 0 | 0 | — |
case-04 | fail→pass | 21,031 | 21,630 | +3% | 1 | 1 | 0% | 3,551 | 7,361 | +107% | 0 | 0 | — |
case-05 | pass→pass | 16,430 | 13,933 | -15% | 1 | 1 | 0% | 3,190 | 6,037 | +89% | 0 | 0 | — |
case-06 | fail→fail | 17,190 | 17,319 | +1% | 1 | 1 | 0% | 3,442 | 6,953 | +102% | 0 | 0 | — |
case-07 | pass→pass | 10,608 | 11,157 | +5% | 1 | 1 | 0% | 2,133 | 5,671 | +166% | 0 | 0 | — |
case-08 | fail→fail | 12,519 | 10,596 | -15% | 1 | 1 | 0% | 2,280 | 5,343 | +134% | 0 | 0 | — |
case-09 | pass→pass | 26,421 | 22,802 | -14% | 1 | 1 | 0% | 5,698 | 8,438 | +48% | 0 | 0 | — |
case-10 | pass→pass | 4,808 | 2,486 | -48% | 1 | 1 | 0% | 995 | 3,890 | +291% | 0 | 0 | — |
case-11 | fail→fail | 14,845 | 23,056 | +55% | 1 | 1 | 0% | 2,451 | 7,985 | +226% | 0 | 0 | — |
case-12 | pass→pass | 18,372 | 10,280 | -44% | 1 | 1 | 0% | 3,080 | 5,266 | +71% | 0 | 0 | — |
case-13 | pass→pass | 21,265 | 25,954 | +22% | 1 | 1 | 0% | 3,147 | 7,320 | +133% | 0 | 0 | — |
case-14 | pass→pass | 19,682 | 17,355 | -12% | 1 | 1 | 0% | 3,021 | 6,281 | +108% | 0 | 0 | — |
case-16 | pass→fail | 17,386 | 15,878 | -9% | 1 | 1 | 0% | 2,580 | 5,855 | +127% | 0 | 0 | — |
case-17 | fail→pass | 4,352 | 3,959 | -9% | 1 | 1 | 0% | 633 | 4,133 | +553% | 0 | 0 | — |
case-18 | fail→pass | 15,479 | 5,788 | -63% | 1 | 1 | 0% | 1,521 | 4,410 | +190% | 0 | 0 | — |
case-19 | fail→pass | 3,818 | 2,965 | -22% | 1 | 1 | 0% | 565 | 3,923 | +594% | 0 | 0 | — |
case-20 | fail→pass | 13,650 | 12,404 | -9% | 1 | 1 | 0% | 2,196 | 5,543 | +152% | 0 | 0 | — |
case-21 | fail→pass | 3,399 | 3,663 | +8% | 1 | 1 | 0% | 449 | 3,967 | +784% | 0 | 0 | — |
case-22 | pass→pass | 10,112 | 5,798 | -43% | 1 | 1 | 0% | 1,703 | 4,470 | +162% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.