Skip to main content
ClaudeWave
Skill254 repo starsupdated 5mo ago

punch-list-manager

The Punch List Manager Claude Code skill tracks construction project deficiencies through closeout by creating, assigning, and monitoring correction items across work categories and priority levels. Use this skill when managing project handover documentation, coordinating closeout activities among multiple trades, or monitoring completion rates against target closeout dates.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction /tmp/punch-list-manager && cp -r /tmp/punch-list-manager/1_DDC_Toolkit/Project-Closeout/punch-list-manager ~/.claude/skills/punch-list-manager
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Punch List Manager

## Business Case

### Problem Statement
Project closeout challenges:
- Tracking hundreds of items
- Assigning responsibility
- Monitoring completion
- Documentation for handover

### Solution
Systematic punch list management to track deficiencies, assignments, and completion through project closeout.

## Technical Implementation

```python
import pandas as pd
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field
from datetime import date, timedelta
from enum import Enum


class PunchItemStatus(Enum):
    OPEN = "open"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    VERIFIED = "verified"
    REJECTED = "rejected"


class PunchItemPriority(Enum):
    CRITICAL = "critical"   # Life safety, code violation
    HIGH = "high"           # Functionality impaired
    MEDIUM = "medium"       # Cosmetic/minor
    LOW = "low"             # Nice to have


class PunchItemCategory(Enum):
    STRUCTURAL = "structural"
    ARCHITECTURAL = "architectural"
    MECHANICAL = "mechanical"
    ELECTRICAL = "electrical"
    PLUMBING = "plumbing"
    FIRE_PROTECTION = "fire_protection"
    EXTERIOR = "exterior"
    SITE = "site"
    GENERAL = "general"


@dataclass
class PunchItem:
    item_id: str
    location: str
    description: str
    category: PunchItemCategory
    priority: PunchItemPriority
    status: PunchItemStatus
    assigned_to: str
    created_date: date
    due_date: date
    completed_date: Optional[date] = None
    verified_date: Optional[date] = None
    verified_by: str = ""
    photos: List[str] = field(default_factory=list)
    notes: str = ""


@dataclass
class PunchListSummary:
    total_items: int
    open_items: int
    in_progress: int
    completed: int
    verified: int
    rejected: int
    completion_rate: float
    by_category: Dict[str, int]
    by_priority: Dict[str, int]
    by_assignee: Dict[str, int]
    overdue_count: int


class PunchListManager:
    """Manage construction punch lists."""

    def __init__(self, project_name: str, target_closeout_date: date):
        self.project_name = project_name
        self.target_date = target_closeout_date
        self.items: Dict[str, PunchItem] = {}
        self._next_id = 1

    def add_item(self,
                 location: str,
                 description: str,
                 category: PunchItemCategory,
                 priority: PunchItemPriority,
                 assigned_to: str,
                 due_date: date = None,
                 notes: str = "") -> PunchItem:
        """Add punch list item."""

        item_id = f"PL-{self._next_id:04d}"
        self._next_id += 1

        if due_date is None:
            # Default based on priority
            if priority == PunchItemPriority.CRITICAL:
                due_date = date.today() + timedelta(days=3)
            elif priority == PunchItemPriority.HIGH:
                due_date = date.today() + timedelta(days=7)
            else:
                due_date = date.today() + timedelta(days=14)

        item = PunchItem(
            item_id=item_id,
            location=location,
            description=description,
            category=category,
            priority=priority,
            status=PunchItemStatus.OPEN,
            assigned_to=assigned_to,
            created_date=date.today(),
            due_date=due_date,
            notes=notes
        )

        self.items[item_id] = item
        return item

    def update_status(self,
                      item_id: str,
                      status: PunchItemStatus,
                      verified_by: str = ""):
        """Update item status."""

        if item_id not in self.items:
            return

        item = self.items[item_id]
        item.status = status

        if status == PunchItemStatus.COMPLETED:
            item.completed_date = date.today()
        elif status == PunchItemStatus.VERIFIED:
            item.verified_date = date.today()
            item.verified_by = verified_by

    def reassign_item(self, item_id: str, new_assignee: str, new_due_date: date = None):
        """Reassign item to different contractor."""

        if item_id not in self.items:
            return

        item = self.items[item_id]
        item.assigned_to = new_assignee

        if new_due_date:
            item.due_date = new_due_date

        item.status = PunchItemStatus.OPEN

    def add_note(self, item_id: str, note: str):
        """Add note to item."""
        if item_id in self.items:
            self.items[item_id].notes += f"\n{date.today()}: {note}"

    def add_photo(self, item_id: str, photo_path: str):
        """Add photo reference to item."""
        if item_id in self.items:
            self.items[item_id].photos.append(photo_path)

    def get_summary(self) -> PunchListSummary:
        """Get punch list summary."""

        items = list(self.items.values())
        today = date.today()

        # Status counts
        open_items = sum(1 for i in items if i.status == PunchItemStatus.OPEN)
        in_progress = sum(1 for i in items if i.status == PunchItemStatus.IN_PROGRESS)
        completed = sum(1 for i in items if i.status == PunchItemStatus.COMPLETED)
        verified = sum(1 for i in items if i.status == PunchItemStatus.VERIFIED)
        rejected = sum(1 for i in items if i.status == PunchItemStatus.REJECTED)

        # Completion rate (verified / total)
        completion_rate = (verified / len(items) * 100) if items else 0

        # By category
        by_category = {}
        for cat in PunchItemCategory:
            count = sum(1 for i in items if i.category == cat and i.status != PunchItemStatus.VERIFIED)
            if count > 0:
                by_category[cat.value] = count

        # By priority
        by_priority = {}
        for pri in PunchItemPriority:
            count = sum(1 for i in items if i.priority == pri and i.status != PunchItemStatus.VERIFIED)
            if count > 0:
                by_priority[pri.value] = c