Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate Gantt charts for construction scheduling. Create visual project timelines with dependencies and progress tracking.
.claude/skills/datadrivenconstruction-gantt-chart/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-11 | ✗→✓ | ▲ Improved | 142% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 85% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 146% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 116% | 0% |
Schedule visualization challenges:
Generate interactive Gantt charts from schedule data with dependency visualization, progress tracking, and export capabilities.
pythonimport 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 TaskStatus(Enum): NOT_STARTED = "not_started" IN_PROGRESS = "in_progress" COMPLETED = "completed" DELAYED = "delayed" ON_HOLD = "on_hold" class DependencyType(Enum): FS = "finish_to_start" SS = "start_to_start" FF = "finish_to_finish" SF = "start_to_finish" @dataclass class Task: task_id: str name: str start_date: date end_date: date wbs_code: str = "" progress: float = 0 # 0-100 status: TaskStatus = TaskStatus.NOT_STARTED assignee: str = "" level: int = 0 is_milestone: bool = False is_summary: bool = False parent_id: str = "" @dataclass class Dependency: predecessor_id: str successor_id: str dep_type: DependencyType = DependencyType.FS lag: int = 0 class GanttChartGenerator: """Generate Gantt charts for construction scheduling.""" def __init__(self, project_name: str): self.project_name = project_name self.tasks: Dict[str, Task] = {} self.dependencies: List[Dependency] = [] def add_task(self, task: Task): """Add task to chart.""" self.tasks[task.task_id] = task def add_dependency(self, predecessor_id: str, successor_id: str, dep_type: DependencyType = DependencyType.FS, lag: int = 0): """Add dependency between tasks.""" self.dependencies.append(Dependency( predecessor_id=predecessor_id, successor_id=successor_id, dep_type=dep_type, lag=lag )) def import_from_df(self, df: pd.DataFrame): """Import tasks from DataFrame.""" for _, row in df.iterrows(): task = Task( task_id=str(row['task_id']), name=row['name'], start_date=pd.to_datetime(row['start_date']).date(), end_date=pd.to_datetime(row['end_date']).date(), wbs_code=str(row.get('wbs_code', '')), progress=float(row.get('progress', 0)), level=int(row.get('level', 0)), is_milestone=bool(row.get('is_milestone', False)), is_summary=bool(row.get('is_summary', False)), parent_id=str(row.get('parent_id', '')) ) self.add_task(task) def get_project_range(self) -> tuple: """Get project date range.""" if not self.tasks: return (date.today(), date.today()) min_date = min(t.start_date for t in self.tasks.values()) max_date = max(t.end_date for t in self.tasks.values()) return (min_date, max_date) def get_duration(self, task_id: str) -> int: """Get task duration in days.""" task = self.tasks.get(task_id) if task: return (task.end_date - task.start_date).days + 1 return 0 def generate_text_gantt(self, width: int = 60) -> str: """Generate text-based Gantt chart.""" if not self.tasks: return "No tasks" lines = [] start, end = self.get_project_range() total_days = (end - start).days + 1 scale = width / total_days if total_days > 0 else 1 # Header lines.append(f"Project: {self.project_name}") lines.append(f"Period: {start} to {end}") lines.append("-" * (40 + width)) # Tasks for task in sorted(self.tasks.values(), key=lambda t: (t.level, t.start_date)): indent = " " * task.level name = f"{indent}{task.name}"[:35].ljust(35) # Bar position bar_start = int((task.start_date - start).days * scale) bar_length = max(1, int(self.get_duration(task.task_id) * scale)) # Progress bar progress_length = int(bar_length * task.progress / 100) bar = " " * bar_start bar += "█" * progress_length bar += "░" * (bar_length - progress_length) bar = bar[:width].ljust(width) status_char = "◆" if task.is_milestone else "│" lines.append(f"{name} {status_char}{bar}│ {task.progress:.0f}%") return "\n".join(lines) def generate_mermaid_gantt(self) -> str: """Generate Mermaid Gantt diagram.""" lines = [ "gantt", f" title {self.project_name}", " dateFormat YYYY-MM-DD", "" ] # Group by WBS prefix sections = {} for task in self.tasks.values(): section = task.wbs_code.split('.')[0] if task.wbs_code else "Tasks" if section not in sections: sections[section] = [] sections[section].append(task) for section, tasks in sections.items(): lines.append(f" section {section}") for task in sorted(tasks, key=lambda t: t.start_date): duration = self.get_duration(task.task_id) status = "" if task.status == TaskStatus.COMPLETED: status = "done, " elif task.status == TaskStatus.IN_PROGRESS: status = "active, " if task.is_milestone: lines.append(f" {task.name} :milestone, {task.start_date}, 0d") else: lines.append(f" {task.name} :{status}{task.task_id}, {task.start_date}, {duration}d") return "\n".join(lines) def generate_html_gantt(self) -> str: """Generate HTML/CSS Gantt chart.""" start, end = self.get_project_range() total_days = (end - start).days + 1 html = f""" <!DOCTYPE html> <html> <head> <title>Gantt Chart - {self.project_name}</title> <style> .gantt {{ font-family: Arial, sans-serif; }} .task {{ display: flex; margin: 2px 0; height: 25px; align-items: center; }} .task-name {{ width: 200px; padding-right: 10px; font-size: 12px; }} .task-bar {{ position: relative; height: 20px; background: #e0e0e0; flex: 1; }} .bar {{ position: absolute; height: 100%; }} .bar-fill {{ background: #4CAF50; }} .bar-progress {{ background: #2196F3; }} .milestone {{ width: 10px; height: 10px; background: #FF5722; transform: rotate(45deg); margin-left: 10px; }} </style> </head> <body> <div class="gantt"> <h2>{self.project_name}</h2> <p>{start} - {end}</p> """ for task in sorted(self.tasks.values(), key=lambda t: (t.level, t.start_date)): left = ((task.start_date - start).days / total_days) * 100 width = (self.get_duration(task.task_id) / total_days) * 100 progress_width = width * task.progress / 100 indent = " " * (task.level * 4) if task.is_milestone: html += f'<div class="task"><div class="task-name">{indent}{task.name}</div><div class="task-bar"><div class="milestone" style="left:{left}%"></div></div></div>\n' else: html += f'''<div class="task"> <div class="task-name">{indent}{task.name}</div> <div class="task-bar"> <div class="bar bar-fill" style="left:{left}%; width:{width}%"></div> <div class="bar bar-progress" style="left:{left}%; width:{progress_width}%"></div> </div> </div>\n''' html += "</div></body></html>" return html def get_critical_path(self) -> List[str]: """Identify critical path tasks (simplified).""" if not self.dependencies: return [t.task_id for t in sorted(self.tasks.values(), key=lambda x: x.end_date)[-5:]] # Find tasks with no slack (simplified approach) critical = [] _, project_end = self.get_project_range() for task in self.tasks.values(): if task.end_date == project_end: critical.append(task.task_id) # Trace predecessors for dep in self.dependencies: if dep.successor_id == task.task_id: critical.append(dep.predecessor_id) return list(set(critical)) def export_to_excel(self, output_path: str) -> str: """Export Gantt data to Excel.""" with pd.ExcelWriter(output_path, engine='openpyxl') as writer: # Tasks tasks_df = pd.DataFrame([{ 'ID': t.task_id, 'WBS': t.wbs_code, 'Name': t.name, 'Start': t.start_date, 'End': t.end_date, 'Duration': self.get_duration(t.task_id), 'Progress': t.progress, 'Status': t.status.value, 'Level': t.level } for t in self.tasks.values()]) tasks_df.to_excel(writer, sheet_name='Tasks', index=False) # Dependencies deps_df = pd.DataFrame([{ 'Predecessor': d.predecessor_id, 'Successor': d.successor_id, 'Type': d.dep_type.value, 'Lag': d.lag } for d in self.dependencies]) deps_df.to_excel(writer, sheet_name='Dependencies', index=False) return output_path
pythonfrom datetime import date, timedelta # Create Gantt chart gantt = GanttChartGenerator("Office Building A") # Add tasks gantt.add_task(Task("T1", "Foundation", date(2024, 6, 1), date(2024, 6, 30), "01", progress=100)) gantt.add_task(Task("T2", "Structure", date(2024, 7, 1), date(2024, 9, 30), "02", progress=60)) gantt.add_task(Task("T3", "MEP Rough-in", date(2024, 8, 1), date(2024, 10, 31), "03", progress=30)) gantt.add_task(Task("M1", "Topping Out", date(2024, 9, 30), date(2024, 9, 30), is_milestone=True)) # Add dependencies gantt.add_dependency("T1", "T2") gantt.add_dependency("T2", "T3") # Generate text Gantt print(gantt.generate_text_gantt())
pythonmermaid = gantt.generate_mermaid_gantt() print(mermaid) # Copy to Mermaid editor
pythonhtml = gantt.generate_html_gantt() with open("gantt.html", "w") as f: f.write(html)
pythoncritical = gantt.get_critical_path() print(f"Critical tasks: {critical}")
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-10 | fail→fail | 12,523 | 15,328 | +22% | 1 | 1 | 0% | 2,114 | 6,470 | +206% | 0 | 0 | — |
case-11 | fail→pass | 12,490 | 8,702 | -30% | 1 | 1 | 0% | 2,120 | 5,130 | +142% | 0 | 0 | — |
case-01 | fail→pass | 18,374 | 15,564 | -15% | 1 | 1 | 0% | 3,866 | 6,701 | +73% | 0 | 0 | — |
case-02 | fail→fail | 25,690 | 24,021 | -6% | 1 | 1 | 0% | 5,661 | 8,546 | +51% | 0 | 0 | — |
case-03 | fail→fail | 16,531 | 16,703 | +1% | 1 | 1 | 0% | 3,567 | 7,137 | +100% | 0 | 0 | — |
case-04 | fail→fail | 18,421 | 19,032 | +3% | 1 | 1 | 0% | 3,385 | 7,173 | +112% | 0 | 0 | — |
case-05 | fail→fail | 27,827 | 27,426 | -1% | 1 | 1 | 0% | 5,408 | 9,196 | +70% | 0 | 0 | — |
case-12 | fail→pass | 13,089 | 4,592 | -65% | 1 | 1 | 0% | 2,284 | 4,232 | +85% | 0 | 0 | — |
case-06 | fail→pass | 8,947 | 4,788 | -46% | 1 | 1 | 0% | 1,769 | 4,349 | +146% | 0 | 0 | — |
case-07 | fail→pass | 11,247 | 7,077 | -37% | 1 | 1 | 0% | 2,178 | 4,711 | +116% | 0 | 0 | — |
case-08 | fail→pass | 13,402 | 1,875 | -86% | 1 | 1 | 0% | 2,379 | 3,652 | +54% | 0 | 0 | — |
case-09 | pass→pass | 9,802 | 3,804 | -61% | 1 | 1 | 0% | 1,781 | 4,143 | +133% | 0 | 0 | — |
case-13 | fail→pass | 8,097 | 5,524 | -32% | 1 | 1 | 0% | 1,404 | 4,443 | +216% | 0 | 0 | — |
case-14 | pass→pass | 10,423 | 2,680 | -74% | 1 | 1 | 0% | 1,769 | 3,839 | +117% | 0 | 0 | — |
case-15 | fail→pass | 7,846 | 2,946 | -62% | 1 | 1 | 0% | 1,416 | 3,871 | +173% | 0 | 0 | — |
case-16 | fail→pass | 11,292 | 4,656 | -59% | 1 | 1 | 0% | 2,271 | 4,225 | +86% | 0 | 0 | — |
case-17 | pass→pass | 12,426 | 6,431 | -48% | 1 | 1 | 0% | 2,241 | 4,558 | +103% | 0 | 0 | — |
case-18 | pass→pass | 7,106 | 1,507 | -79% | 1 | 1 | 0% | 1,163 | 3,553 | +206% | 0 | 0 | — |
case-19 | fail→pass | 14,521 | 4,387 | -70% | 1 | 1 | 0% | 2,306 | 4,169 | +81% | 0 | 0 | — |
case-20 | pass→pass | 13,226 | 8,018 | -39% | 1 | 1 | 0% | 2,339 | 4,938 | +111% | 0 | 0 | — |
case-21 | fail→pass | 23,180 | 2,385 | -90% | 1 | 1 | 0% | 1,488 | 3,758 | +153% | 0 | 0 | — |
case-22 | pass→pass | 13,163 | 4,309 | -67% | 1 | 1 | 0% | 2,128 | 4,119 | +94% | 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 +50 percentage points is the difference between those two pass rates over the 22 comparable cases.
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.