Skill: datadrivenconstruction/estimate-builder (version 2.1.0)

--- name: "estimate-builder" description: "Build construction project estimates. Generate detailed cost breakdowns with labor, materials, equipment, and overhead." homepage: "https://datadrivenconstruction.io" metadata: {"openclaw":{"emoji":"📊","os":["darwin","linux","win32"],"homepage":"https://datadrivenconstruction.io","requires":{"bins":["python3"]}}} --- # Estimate Builder ## Business Case ### Problem Statement Estimate creation challenges: - Complex cost structures - Multiple cost categories - Markup calculations - Format requirements vary ### Solution Structured estimate builder that creates professional construction estimates with proper cost categorization, markups, and export capabilities. ## Technical Implementation ```python import pandas as pd from typing import Dict, Any, List, Optional from dataclasses import dataclass, field from datetime import date from enum import Enum class CostCategory(Enum): LABOR = "labor" MATERIAL = "material" EQUIPMENT = "equipment" SUBCONTRACTOR = "subcontractor" OTHER = "other" @dataclass class EstimateLineItem: line_number: int wbs_code: str description: str quantity: float unit: str unit_cost: float category: CostCategory notes: str = "" @property def total_cost(self) -> float: return round(self.quantity * self.unit_cost, 2) @dataclass class CostSummary: labor: float = 0 material: float = 0 equipment: float = 0 subcontractor: float = 0 other: float = 0 @property def direct_cost(self) -> float: return self.labor + self.material + self.equipment + self.subcontractor + self.other @dataclass class Markup: name: str rate: float # As decimal (0.10 = 10%) base: str = "direct" # "direct" or "subtotal" class EstimateBuilder: """Build construction project estimates.""" def __init__(self, project_name: str, project_number: str = ""): self.project_name = project_name self.project_number = project_number self.estimate_date = date.today() self.items: List[EstimateLineItem] = [] self.markups: List[Markup] = [] self._next_line = 1 def add_item(self, wbs_code: str, description: str, quantity: float, unit: str, unit_cost: float, category: CostCategory = CostCategory.OTHER, notes: str = "") -> EstimateLineItem: """Add line item to estimate.""" item = EstimateLineItem( line_number=self._next_line, wbs_code=wbs_code, description=description, quantity=quantity, unit=unit, unit_cost=unit_cost, category=category, notes=notes ) self.items.append(item) self._next_line += 1 return item def add_markup(self, name: str, rate: float, base: str = "direct"): """Add markup (overhead, profit, contingency, etc.).""" self.markups.append(Markup(name=name, rate=rate, base=base)) def set_standard_markups(self, overhead: float = 0.15, profit: float = 0.10, contingency: float = 0.05): """Set standard construction markups.""" self.markups = [ Markup("General Conditions / Overhead", overhead, "direct"), Markup("Profit", profit, "subtotal"), Markup("Contingency", contingency, "subtotal") ] def get_cost_summary(self) -> CostSummary: """Get cost summary by category.""" summary = CostSummary() for item in self.items: cost = item.total_cost if item.category == CostCategory.LABOR: summary.labor += cost elif item.category == CostCategory.MATERIAL: summary.material += cost elif item.category == CostCategory.EQUIPMENT: summary.equipment += cost elif item.category == CostCategory.SUBCONTRACTOR: summary.subcontractor += cost else: summary.other += cost return summary def calculate_total(self) -> Dict[str, Any]: """Calculate total estimate with markups.""" summary = self.get_cost_summary() direct_cost = summary.direct_cost markups_detail = [] subtotal = direct_cost for markup in self.markups: if markup.base == "direct": amount = direct_cost * markup.rate else: amount = subtotal * markup.rate markups_detail.append({ 'name': markup.name, 'rate': f"{markup.rate * 100:.1f}%", 'amount': round(amount, 2) }) subtotal += amount return { 'cost_summary': { 'labor': round(summary.labor, 2), 'material': round(summary.material, 2), 'equipment': round(summary.equipment, 2), 'subcontractor': round(summary.subcontractor, 2), 'other': round(summary.other, 2), 'direct_cost': round(direct_cost, 2) }, 'markups': markups_detail, 'total_markups': round(subtotal - direct_cost, 2), 'grand_total': round(subtotal, 2) } def get_items_by_wbs(self) -> Dict[str, List[EstimateLineItem]]: """Group items by WBS code prefix.""" by_wbs = {} for item in self.items: prefix = item.wbs_code.split('.')[0] if '.' in item.wbs_code else item.wbs_code if prefix not in by_wbs: by_wbs[prefix] = [] by_wbs[prefix].append(item) return by_wbs def import_from_df(self, df: pd.DataFrame): """Import line items from DataFrame.""" for _, row in df.iterrows(): self.add_item( wbs_code=str(row.get('wbs_code', '')), description=row['description'], quantity=float(row['quantity']), unit=row['unit'], unit_cost=float(row['unit_cost']), category=CostCategory(row.get('category', 'other').lower()), notes=row.get('notes', '') ) def export_to_df(self) -> pd.DataFrame: """Export estimate to DataFrame.""" data = [] for item in self.items: data.append({ 'Line': item.line_number, 'WBS': item.wbs_code, 'Description': item.description, 'Qty': item.quantity, 'Unit': item.unit, 'Unit Cost': item.unit_cost, 'Total': item.total_cost, 'Category': item.category.value, 'Notes': item.notes }) return pd.DataFrame(data) def export_to_excel(self, output_path: str) -> str: """Export estimate to Excel.""" totals = self.calculate_total() with pd.ExcelWriter(output_path, engine='openpyxl') as writer: # Cover sheet cover_df = pd.DataFrame([{ 'Project Name': self.project_name, 'Project Number': self.project_number, 'Estimate Date': self.estimate_date, 'Total Items': len(self.items), 'Direct Cost': totals['cost_summary']['direct_cost'], 'Grand Total': totals['grand_total'] }]) cover_df.to_excel(writer, sheet_name='Summary', index=False) # Line items items_df = self.export_to_df() items_df.to_excel(writer, sheet_name='Line Items', index=False) # Cost breakdown breakdown_df = pd.DataFrame([totals['cost_summary']]) breakdown_df.to_excel(writer, sheet_name='Cost Breakdown', index=False) # Markups if totals['markups']: markups_df = pd.DataFrame(totals['markups']) markups_df.to_excel(writer, sheet_name='Markups', index=False) return output_path def validate(self) -> List[str]: """Validate estimate for common issues.""" issues = [] if not self.items: issues.append("Estimate has no line items") for item in self.items: if item.quantity <= 0: issues.append(f"Line {item.line_number}: Invalid quantity") if item.unit_cost < 0: issues.append(f"Line {item.line_number}: Negative unit cost") if not item.description: issues.append(f"Line {item.line_number}: Missing description") if not self.markups: issues.append("No markups defined (overhead, profit)") return issues ``` ## Quick Start ```python # Create estimate estimate = EstimateBuilder("Office Building A", "PRJ-2024-001") # Add line items estimate.add_item("01.01", "Site Preparation", 5000, "SF", 2.50, CostCategory.OTHER) estimate.add_item("03.01", "Concrete Foundation", 200, "CY", 350, CostCategory.MATERIAL) estimate.add_item("03.02", "Foundation Formwork", 1500, "SF", 8.50, CostCategory.LABOR) estimate.add_item("05.01", "Structural Steel", 50, "TON", 4500, CostCategory.SUBCONTRACTOR) # Set markups estimate.set_standard_markups(overhead=0.15, profit=0.10, contingency=0.05) # Calculate total result = estimate.calculate_total() print(f"Direct Cost: ${result['cost_summary']['direct_cost']:,.2f}") print(f"Grand Total: ${result['grand_total']:,.2f}") ``` ## Common Use Cases ### 1. Cost by Category ```python summary = estimate.get_cost_summary() print(f"Labor: ${summary.labor:,.2f}") print(f"Material: ${summary.material:,.2f}") ``` ### 2. Export to Excel ```python estimate.export_to_excel("estimate_output.xlsx") ``` ### 3. Validate Estimate ```python issues = estimate.validate() for issue in issues: print(f"Warning: {issue}") ``` ## Resources - **DDC Book**: Chapter 3.1 - Cost Calculations and Estimates - **Website**: https://datadrivenconstruction.io

path: claw.json
content: {
  "name": "estimate-builder",
  "version": "2.0.0",
  "description": "Build construction project estimates. Generate detailed cost breakdowns with labor, materials, equipment, and overhead.",
  "author": "datadrivenconstruction",
  "license": "MIT",
  "permissions": ["filesystem"],
  "entry": "instructions.md",
  "tags": ["construction", "estimation", "cost-management", "budgeting", "takeoff"],
  "models": ["claude-*", "gpt-*"],
  "minOpenClawVersion": "0.8.0"
}


path: instructions.md
content: You are a construction cost estimation assistant. You help users build detailed project estimates with proper cost categorization, markups, and reporting.

When the user asks to create or build an estimate:
1. Gather project name and scope (ask if not provided)
2. Collect line items: WBS code, description, quantity, unit, unit cost, and cost category (labor, material, equipment, subcontractor)
3. Apply standard construction markups: overhead (default 15%), profit (default 10%), contingency (default 5%)
4. Calculate totals by category and grand total with markups
5. Present results in a clear table format

When the user asks to analyze or review an estimate:
1. Check for missing descriptions, zero/negative quantities, or missing markups
2. Summarize cost breakdown by category (labor %, material %, equipment %)
3. Flag any items that look unusual

## Input Format
- Project name and number
- Line items as a list or table with: description, quantity, unit, unit cost, category
- Optional: custom markup rates

## Output Format
- Summary table with cost by category
- Markup breakdown (overhead, profit, contingency)
- Grand total
- Validation warnings if any issues found

## Key Classes (from SKILL.md)
- `EstimateBuilder`: Main class for building estimates
- `EstimateLineItem`: Individual cost line item
- `CostCategory`: LABOR, MATERIAL, EQUIPMENT, SUBCONTRACTOR, OTHER
- `Markup`: Overhead, profit, contingency definitions

## Constraints
- Always show cost breakdown by category
- Default markups: 15% overhead, 10% profit, 5% contingency (adjustable)
- Round all monetary values to 2 decimal places
- Use USD unless user specifies otherwise


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)