spec-kitty-cli 0.12.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spec_kitty_cli-0.12.1.dist-info/METADATA +1767 -0
- spec_kitty_cli-0.12.1.dist-info/RECORD +242 -0
- spec_kitty_cli-0.12.1.dist-info/WHEEL +4 -0
- spec_kitty_cli-0.12.1.dist-info/entry_points.txt +2 -0
- spec_kitty_cli-0.12.1.dist-info/licenses/LICENSE +21 -0
- specify_cli/__init__.py +171 -0
- specify_cli/acceptance.py +627 -0
- specify_cli/agent_utils/README.md +157 -0
- specify_cli/agent_utils/__init__.py +9 -0
- specify_cli/agent_utils/status.py +356 -0
- specify_cli/cli/__init__.py +6 -0
- specify_cli/cli/commands/__init__.py +46 -0
- specify_cli/cli/commands/accept.py +189 -0
- specify_cli/cli/commands/agent/__init__.py +22 -0
- specify_cli/cli/commands/agent/config.py +382 -0
- specify_cli/cli/commands/agent/context.py +191 -0
- specify_cli/cli/commands/agent/feature.py +1057 -0
- specify_cli/cli/commands/agent/release.py +11 -0
- specify_cli/cli/commands/agent/tasks.py +1253 -0
- specify_cli/cli/commands/agent/workflow.py +801 -0
- specify_cli/cli/commands/context.py +246 -0
- specify_cli/cli/commands/dashboard.py +85 -0
- specify_cli/cli/commands/implement.py +973 -0
- specify_cli/cli/commands/init.py +827 -0
- specify_cli/cli/commands/init_help.py +62 -0
- specify_cli/cli/commands/merge.py +755 -0
- specify_cli/cli/commands/mission.py +240 -0
- specify_cli/cli/commands/ops.py +265 -0
- specify_cli/cli/commands/orchestrate.py +640 -0
- specify_cli/cli/commands/repair.py +175 -0
- specify_cli/cli/commands/research.py +165 -0
- specify_cli/cli/commands/sync.py +364 -0
- specify_cli/cli/commands/upgrade.py +249 -0
- specify_cli/cli/commands/validate_encoding.py +186 -0
- specify_cli/cli/commands/validate_tasks.py +186 -0
- specify_cli/cli/commands/verify.py +310 -0
- specify_cli/cli/helpers.py +123 -0
- specify_cli/cli/step_tracker.py +91 -0
- specify_cli/cli/ui.py +192 -0
- specify_cli/core/__init__.py +53 -0
- specify_cli/core/agent_context.py +311 -0
- specify_cli/core/config.py +96 -0
- specify_cli/core/context_validation.py +362 -0
- specify_cli/core/dependency_graph.py +351 -0
- specify_cli/core/git_ops.py +129 -0
- specify_cli/core/multi_parent_merge.py +323 -0
- specify_cli/core/paths.py +260 -0
- specify_cli/core/project_resolver.py +110 -0
- specify_cli/core/stale_detection.py +263 -0
- specify_cli/core/tool_checker.py +79 -0
- specify_cli/core/utils.py +43 -0
- specify_cli/core/vcs/__init__.py +114 -0
- specify_cli/core/vcs/detection.py +341 -0
- specify_cli/core/vcs/exceptions.py +85 -0
- specify_cli/core/vcs/git.py +1304 -0
- specify_cli/core/vcs/jujutsu.py +1208 -0
- specify_cli/core/vcs/protocol.py +285 -0
- specify_cli/core/vcs/types.py +249 -0
- specify_cli/core/version_checker.py +261 -0
- specify_cli/core/worktree.py +506 -0
- specify_cli/dashboard/__init__.py +28 -0
- specify_cli/dashboard/diagnostics.py +204 -0
- specify_cli/dashboard/handlers/__init__.py +17 -0
- specify_cli/dashboard/handlers/api.py +143 -0
- specify_cli/dashboard/handlers/base.py +65 -0
- specify_cli/dashboard/handlers/features.py +390 -0
- specify_cli/dashboard/handlers/router.py +81 -0
- specify_cli/dashboard/handlers/static.py +50 -0
- specify_cli/dashboard/lifecycle.py +541 -0
- specify_cli/dashboard/scanner.py +437 -0
- specify_cli/dashboard/server.py +123 -0
- specify_cli/dashboard/static/dashboard/dashboard.css +722 -0
- specify_cli/dashboard/static/dashboard/dashboard.js +1424 -0
- specify_cli/dashboard/static/spec-kitty.png +0 -0
- specify_cli/dashboard/templates/__init__.py +36 -0
- specify_cli/dashboard/templates/index.html +258 -0
- specify_cli/doc_generators.py +621 -0
- specify_cli/doc_state.py +408 -0
- specify_cli/frontmatter.py +384 -0
- specify_cli/gap_analysis.py +915 -0
- specify_cli/gitignore_manager.py +300 -0
- specify_cli/guards.py +145 -0
- specify_cli/legacy_detector.py +83 -0
- specify_cli/manifest.py +286 -0
- specify_cli/merge/__init__.py +63 -0
- specify_cli/merge/executor.py +653 -0
- specify_cli/merge/forecast.py +215 -0
- specify_cli/merge/ordering.py +126 -0
- specify_cli/merge/preflight.py +230 -0
- specify_cli/merge/state.py +185 -0
- specify_cli/merge/status_resolver.py +354 -0
- specify_cli/mission.py +654 -0
- specify_cli/missions/documentation/command-templates/implement.md +309 -0
- specify_cli/missions/documentation/command-templates/plan.md +275 -0
- specify_cli/missions/documentation/command-templates/review.md +344 -0
- specify_cli/missions/documentation/command-templates/specify.md +206 -0
- specify_cli/missions/documentation/command-templates/tasks.md +189 -0
- specify_cli/missions/documentation/mission.yaml +113 -0
- specify_cli/missions/documentation/templates/divio/explanation-template.md +192 -0
- specify_cli/missions/documentation/templates/divio/howto-template.md +168 -0
- specify_cli/missions/documentation/templates/divio/reference-template.md +179 -0
- specify_cli/missions/documentation/templates/divio/tutorial-template.md +146 -0
- specify_cli/missions/documentation/templates/generators/jsdoc.json.template +18 -0
- specify_cli/missions/documentation/templates/generators/sphinx-conf.py.template +36 -0
- specify_cli/missions/documentation/templates/plan-template.md +269 -0
- specify_cli/missions/documentation/templates/release-template.md +222 -0
- specify_cli/missions/documentation/templates/spec-template.md +172 -0
- specify_cli/missions/documentation/templates/task-prompt-template.md +140 -0
- specify_cli/missions/documentation/templates/tasks-template.md +159 -0
- specify_cli/missions/research/command-templates/merge.md +388 -0
- specify_cli/missions/research/command-templates/plan.md +125 -0
- specify_cli/missions/research/command-templates/review.md +144 -0
- specify_cli/missions/research/command-templates/tasks.md +225 -0
- specify_cli/missions/research/mission.yaml +115 -0
- specify_cli/missions/research/templates/data-model-template.md +33 -0
- specify_cli/missions/research/templates/plan-template.md +161 -0
- specify_cli/missions/research/templates/research/evidence-log.csv +18 -0
- specify_cli/missions/research/templates/research/source-register.csv +18 -0
- specify_cli/missions/research/templates/research-template.md +35 -0
- specify_cli/missions/research/templates/spec-template.md +64 -0
- specify_cli/missions/research/templates/task-prompt-template.md +148 -0
- specify_cli/missions/research/templates/tasks-template.md +114 -0
- specify_cli/missions/software-dev/command-templates/accept.md +75 -0
- specify_cli/missions/software-dev/command-templates/analyze.md +183 -0
- specify_cli/missions/software-dev/command-templates/checklist.md +286 -0
- specify_cli/missions/software-dev/command-templates/clarify.md +157 -0
- specify_cli/missions/software-dev/command-templates/constitution.md +432 -0
- specify_cli/missions/software-dev/command-templates/dashboard.md +101 -0
- specify_cli/missions/software-dev/command-templates/implement.md +41 -0
- specify_cli/missions/software-dev/command-templates/merge.md +383 -0
- specify_cli/missions/software-dev/command-templates/plan.md +171 -0
- specify_cli/missions/software-dev/command-templates/review.md +32 -0
- specify_cli/missions/software-dev/command-templates/specify.md +321 -0
- specify_cli/missions/software-dev/command-templates/tasks.md +566 -0
- specify_cli/missions/software-dev/mission.yaml +100 -0
- specify_cli/missions/software-dev/templates/plan-template.md +132 -0
- specify_cli/missions/software-dev/templates/spec-template.md +116 -0
- specify_cli/missions/software-dev/templates/task-prompt-template.md +140 -0
- specify_cli/missions/software-dev/templates/tasks-template.md +159 -0
- specify_cli/orchestrator/__init__.py +75 -0
- specify_cli/orchestrator/agent_config.py +224 -0
- specify_cli/orchestrator/agents/__init__.py +170 -0
- specify_cli/orchestrator/agents/augment.py +112 -0
- specify_cli/orchestrator/agents/base.py +243 -0
- specify_cli/orchestrator/agents/claude.py +112 -0
- specify_cli/orchestrator/agents/codex.py +106 -0
- specify_cli/orchestrator/agents/copilot.py +137 -0
- specify_cli/orchestrator/agents/cursor.py +139 -0
- specify_cli/orchestrator/agents/gemini.py +115 -0
- specify_cli/orchestrator/agents/kilocode.py +94 -0
- specify_cli/orchestrator/agents/opencode.py +132 -0
- specify_cli/orchestrator/agents/qwen.py +96 -0
- specify_cli/orchestrator/config.py +455 -0
- specify_cli/orchestrator/executor.py +642 -0
- specify_cli/orchestrator/integration.py +1230 -0
- specify_cli/orchestrator/monitor.py +898 -0
- specify_cli/orchestrator/scheduler.py +832 -0
- specify_cli/orchestrator/state.py +508 -0
- specify_cli/orchestrator/testing/__init__.py +122 -0
- specify_cli/orchestrator/testing/availability.py +346 -0
- specify_cli/orchestrator/testing/fixtures.py +684 -0
- specify_cli/orchestrator/testing/paths.py +218 -0
- specify_cli/plan_validation.py +107 -0
- specify_cli/scripts/debug-dashboard-scan.py +61 -0
- specify_cli/scripts/tasks/acceptance_support.py +695 -0
- specify_cli/scripts/tasks/task_helpers.py +506 -0
- specify_cli/scripts/tasks/tasks_cli.py +848 -0
- specify_cli/scripts/validate_encoding.py +180 -0
- specify_cli/task_metadata_validation.py +274 -0
- specify_cli/tasks_support.py +447 -0
- specify_cli/template/__init__.py +47 -0
- specify_cli/template/asset_generator.py +206 -0
- specify_cli/template/github_client.py +334 -0
- specify_cli/template/manager.py +193 -0
- specify_cli/template/renderer.py +99 -0
- specify_cli/templates/AGENTS.md +190 -0
- specify_cli/templates/POWERSHELL_SYNTAX.md +229 -0
- specify_cli/templates/agent-file-template.md +35 -0
- specify_cli/templates/checklist-template.md +42 -0
- specify_cli/templates/claudeignore-template +58 -0
- specify_cli/templates/command-templates/accept.md +141 -0
- specify_cli/templates/command-templates/analyze.md +253 -0
- specify_cli/templates/command-templates/checklist.md +352 -0
- specify_cli/templates/command-templates/clarify.md +224 -0
- specify_cli/templates/command-templates/constitution.md +432 -0
- specify_cli/templates/command-templates/dashboard.md +175 -0
- specify_cli/templates/command-templates/implement.md +190 -0
- specify_cli/templates/command-templates/merge.md +374 -0
- specify_cli/templates/command-templates/plan.md +171 -0
- specify_cli/templates/command-templates/research.md +88 -0
- specify_cli/templates/command-templates/review.md +510 -0
- specify_cli/templates/command-templates/specify.md +321 -0
- specify_cli/templates/command-templates/status.md +92 -0
- specify_cli/templates/command-templates/tasks.md +199 -0
- specify_cli/templates/git-hooks/pre-commit +22 -0
- specify_cli/templates/git-hooks/pre-commit-agent-check +37 -0
- specify_cli/templates/git-hooks/pre-commit-encoding-check +142 -0
- specify_cli/templates/plan-template.md +108 -0
- specify_cli/templates/spec-template.md +118 -0
- specify_cli/templates/task-prompt-template.md +165 -0
- specify_cli/templates/tasks-template.md +161 -0
- specify_cli/templates/vscode-settings.json +13 -0
- specify_cli/text_sanitization.py +225 -0
- specify_cli/upgrade/__init__.py +18 -0
- specify_cli/upgrade/detector.py +239 -0
- specify_cli/upgrade/metadata.py +182 -0
- specify_cli/upgrade/migrations/__init__.py +65 -0
- specify_cli/upgrade/migrations/base.py +80 -0
- specify_cli/upgrade/migrations/m_0_10_0_python_only.py +359 -0
- specify_cli/upgrade/migrations/m_0_10_12_constitution_cleanup.py +99 -0
- specify_cli/upgrade/migrations/m_0_10_14_update_implement_slash_command.py +176 -0
- specify_cli/upgrade/migrations/m_0_10_1_populate_slash_commands.py +174 -0
- specify_cli/upgrade/migrations/m_0_10_2_update_slash_commands.py +172 -0
- specify_cli/upgrade/migrations/m_0_10_6_workflow_simplification.py +174 -0
- specify_cli/upgrade/migrations/m_0_10_8_fix_memory_structure.py +252 -0
- specify_cli/upgrade/migrations/m_0_10_9_repair_templates.py +168 -0
- specify_cli/upgrade/migrations/m_0_11_0_workspace_per_wp.py +182 -0
- specify_cli/upgrade/migrations/m_0_11_1_improved_workflow_templates.py +173 -0
- specify_cli/upgrade/migrations/m_0_11_1_update_implement_slash_command.py +160 -0
- specify_cli/upgrade/migrations/m_0_11_2_improved_workflow_templates.py +173 -0
- specify_cli/upgrade/migrations/m_0_11_3_workflow_agent_flag.py +114 -0
- specify_cli/upgrade/migrations/m_0_12_0_documentation_mission.py +155 -0
- specify_cli/upgrade/migrations/m_0_12_1_remove_kitty_specs_from_gitignore.py +183 -0
- specify_cli/upgrade/migrations/m_0_2_0_specify_to_kittify.py +80 -0
- specify_cli/upgrade/migrations/m_0_4_8_gitignore_agents.py +118 -0
- specify_cli/upgrade/migrations/m_0_5_0_encoding_hooks.py +141 -0
- specify_cli/upgrade/migrations/m_0_6_5_commands_rename.py +169 -0
- specify_cli/upgrade/migrations/m_0_6_7_ensure_missions.py +228 -0
- specify_cli/upgrade/migrations/m_0_7_2_worktree_commands_dedup.py +89 -0
- specify_cli/upgrade/migrations/m_0_7_3_update_scripts.py +114 -0
- specify_cli/upgrade/migrations/m_0_8_0_remove_active_mission.py +82 -0
- specify_cli/upgrade/migrations/m_0_8_0_worktree_agents_symlink.py +148 -0
- specify_cli/upgrade/migrations/m_0_9_0_frontmatter_only_lanes.py +346 -0
- specify_cli/upgrade/migrations/m_0_9_1_complete_lane_migration.py +656 -0
- specify_cli/upgrade/migrations/m_0_9_2_research_mission_templates.py +221 -0
- specify_cli/upgrade/registry.py +121 -0
- specify_cli/upgrade/runner.py +284 -0
- specify_cli/validators/__init__.py +14 -0
- specify_cli/validators/paths.py +154 -0
- specify_cli/validators/research.py +428 -0
- specify_cli/verify_enhanced.py +270 -0
- specify_cli/workspace_context.py +224 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Agent Utilities
|
|
2
|
+
|
|
3
|
+
Python utilities that AI agents can import and call directly, without running CLI commands.
|
|
4
|
+
|
|
5
|
+
## Quick Status Check
|
|
6
|
+
|
|
7
|
+
### `show_kanban_status(feature_slug)`
|
|
8
|
+
|
|
9
|
+
Display a beautiful kanban status board with parallelization analysis.
|
|
10
|
+
|
|
11
|
+
**Usage:**
|
|
12
|
+
```python
|
|
13
|
+
from specify_cli.agent_utils.status import show_kanban_status
|
|
14
|
+
|
|
15
|
+
# Auto-detect feature from current directory
|
|
16
|
+
result = show_kanban_status()
|
|
17
|
+
|
|
18
|
+
# Or specify feature explicitly
|
|
19
|
+
result = show_kanban_status("012-documentation-mission")
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Why use this instead of CLI command?**
|
|
23
|
+
- ✅ **No truncation** - Full output displays inline (CLI gets truncated at ~50 lines)
|
|
24
|
+
- ✅ **Direct import** - No need to run `spec-kitty` via Bash tool
|
|
25
|
+
- ✅ **Instant output** - Displays immediately in agent's console
|
|
26
|
+
- ✅ **Structured data** - Returns dict for programmatic decision-making
|
|
27
|
+
|
|
28
|
+
**What it displays:**
|
|
29
|
+
|
|
30
|
+
1. **Feature header** with cyan border
|
|
31
|
+
2. **Progress bar** visual (████████░░░) with percentage
|
|
32
|
+
3. **Kanban Board Table** showing all WPs in 4 lanes:
|
|
33
|
+
- 📋 Planned
|
|
34
|
+
- 🔄 Doing
|
|
35
|
+
- 👀 For Review
|
|
36
|
+
- ✅ Done
|
|
37
|
+
4. **🔀 Parallelization Strategy** - NEW! Shows:
|
|
38
|
+
- Which WPs are ready to start (all dependencies satisfied)
|
|
39
|
+
- Which can run in parallel (no inter-dependencies)
|
|
40
|
+
- Which must run sequentially (depend on each other)
|
|
41
|
+
- Exact `spec-kitty implement` commands with correct `--base` flags
|
|
42
|
+
5. **Next Steps** - What's ready for review, in progress, or next up
|
|
43
|
+
6. **Summary Panel** - Total WPs, completed %, in progress, planned
|
|
44
|
+
|
|
45
|
+
**Example Output:**
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
╭──────────────────────────────────────────────────────────────────╮
|
|
49
|
+
│ 📊 Work Package Status: 012-documentation-mission │
|
|
50
|
+
╰──────────────────────────────────────────────────────────────────╯
|
|
51
|
+
Progress: 8/10 (80.0%)
|
|
52
|
+
████████████████████████████████░░░░░░░░
|
|
53
|
+
|
|
54
|
+
Kanban Board
|
|
55
|
+
┏━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
|
|
56
|
+
┃ 📋 Planned ┃ 🔄 Doing ┃ 👀 For Rev ┃ ✅ Done ┃
|
|
57
|
+
┡━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
|
|
58
|
+
│ WP09 │ │ │ WP01 │
|
|
59
|
+
│ Testing │ │ │ Mission... │
|
|
60
|
+
│ WP10 │ │ │ WP02 │
|
|
61
|
+
│ Docs │ │ │ Core... │
|
|
62
|
+
│ │ │ │ ... │
|
|
63
|
+
│ 2 WPs │ 0 WPs │ 0 WPs │ 8 WPs │
|
|
64
|
+
└─────────────┴───────────┴─────────────┴─────────────┘
|
|
65
|
+
|
|
66
|
+
🔀 Parallelization Strategy:
|
|
67
|
+
|
|
68
|
+
▶️ Ready to start:
|
|
69
|
+
• WP09 - Testing & Validation
|
|
70
|
+
spec-kitty implement WP09 --base WP08
|
|
71
|
+
|
|
72
|
+
╭─────────────────── Summary ───────────────────╮
|
|
73
|
+
│ Total WPs: 10 │
|
|
74
|
+
│ Completed: 8 (80.0%) │
|
|
75
|
+
│ In Progress: 0 │
|
|
76
|
+
│ Planned: 2 │
|
|
77
|
+
╰───────────────────────────────────────────────╯
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Returns (structured dict):**
|
|
81
|
+
```python
|
|
82
|
+
{
|
|
83
|
+
'feature': '012-documentation-mission',
|
|
84
|
+
'total_wps': 10,
|
|
85
|
+
'done_count': 8,
|
|
86
|
+
'progress_percentage': 80.0,
|
|
87
|
+
'in_progress': 0,
|
|
88
|
+
'planned_count': 2,
|
|
89
|
+
'by_lane': {'done': 8, 'planned': 2},
|
|
90
|
+
'work_packages': [
|
|
91
|
+
{
|
|
92
|
+
'id': 'WP01',
|
|
93
|
+
'title': 'Mission Infrastructure',
|
|
94
|
+
'lane': 'done',
|
|
95
|
+
'phase': 'Phase 0 - Foundation',
|
|
96
|
+
'dependencies': []
|
|
97
|
+
},
|
|
98
|
+
# ... more WPs
|
|
99
|
+
],
|
|
100
|
+
'parallelization': {
|
|
101
|
+
'ready_wps': [
|
|
102
|
+
{
|
|
103
|
+
'id': 'WP09',
|
|
104
|
+
'title': 'Testing & Validation',
|
|
105
|
+
'lane': 'planned',
|
|
106
|
+
'dependencies': ['WP01', 'WP02', ..., 'WP08']
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
'can_parallelize': False, # True if multiple WPs can run simultaneously
|
|
110
|
+
'parallel_groups': [
|
|
111
|
+
{
|
|
112
|
+
'type': 'single', # 'parallel' | 'single' | 'sequential'
|
|
113
|
+
'wps': [...],
|
|
114
|
+
'note': 'Ready to start'
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Use Cases:**
|
|
122
|
+
|
|
123
|
+
1. **Check status before starting work:**
|
|
124
|
+
```python
|
|
125
|
+
result = show_kanban_status("012-documentation-mission")
|
|
126
|
+
if result['parallelization']['ready_wps']:
|
|
127
|
+
print(f"✅ Can start {result['parallelization']['ready_wps'][0]['id']}")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
2. **Find parallelization opportunities:**
|
|
131
|
+
```python
|
|
132
|
+
result = show_kanban_status()
|
|
133
|
+
if result['parallelization']['can_parallelize']:
|
|
134
|
+
parallel_wps = [g for g in result['parallelization']['parallel_groups']
|
|
135
|
+
if g['type'] == 'parallel'][0]
|
|
136
|
+
print(f"🚀 Can run {len(parallel_wps['wps'])} WPs in parallel!")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
3. **Track progress:**
|
|
140
|
+
```python
|
|
141
|
+
result = show_kanban_status()
|
|
142
|
+
print(f"Progress: {result['progress_percentage']}%")
|
|
143
|
+
print(f"Remaining: {result['planned_count'] + result['in_progress']} WPs")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## When to Use Agent Utilities
|
|
147
|
+
|
|
148
|
+
**Always prefer Python functions over CLI commands when:**
|
|
149
|
+
- Output might be truncated (>50 lines)
|
|
150
|
+
- You need structured data for decision-making
|
|
151
|
+
- You want instant inline display
|
|
152
|
+
- You're working programmatically
|
|
153
|
+
|
|
154
|
+
**Use CLI commands when:**
|
|
155
|
+
- Running from terminal manually
|
|
156
|
+
- Output is short and won't truncate
|
|
157
|
+
- You need the command for documentation/user instructions
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Utilities for AI agents working with spec-kitty.
|
|
2
|
+
|
|
3
|
+
This package provides helper functions that agents can import and use directly,
|
|
4
|
+
without needing to go through CLI commands.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .status import show_kanban_status
|
|
8
|
+
|
|
9
|
+
__all__ = ["show_kanban_status"]
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""Status board utilities for AI agents.
|
|
2
|
+
|
|
3
|
+
This module provides functions that agents can import and call directly
|
|
4
|
+
to display beautiful status boards without going through the CLI.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections import Counter
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.panel import Panel
|
|
15
|
+
from rich.table import Table
|
|
16
|
+
from rich.text import Text
|
|
17
|
+
|
|
18
|
+
from specify_cli.core.paths import locate_project_root, get_main_repo_root, find_feature_slug
|
|
19
|
+
from specify_cli.tasks_support import extract_scalar, split_frontmatter
|
|
20
|
+
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def show_kanban_status(feature_slug: Optional[str] = None) -> dict:
|
|
25
|
+
"""Display kanban status board for work packages in a feature.
|
|
26
|
+
|
|
27
|
+
This function can be called directly by agents to get a beautiful
|
|
28
|
+
status display without running a CLI command.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
feature_slug: Feature slug (e.g., "012-documentation-mission").
|
|
32
|
+
If None, attempts to auto-detect from current directory.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
dict: Status data including work packages, metrics, and progress
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
>>> from specify_cli.agent_utils.status import show_kanban_status
|
|
39
|
+
>>> show_kanban_status("012-documentation-mission")
|
|
40
|
+
"""
|
|
41
|
+
try:
|
|
42
|
+
cwd = Path.cwd().resolve()
|
|
43
|
+
repo_root = locate_project_root(cwd)
|
|
44
|
+
|
|
45
|
+
if repo_root is None:
|
|
46
|
+
console.print("[red]Error:[/red] Not in a spec-kitty project")
|
|
47
|
+
return {"error": "Not in a spec-kitty project"}
|
|
48
|
+
|
|
49
|
+
# Auto-detect feature if not provided
|
|
50
|
+
if not feature_slug:
|
|
51
|
+
feature_slug = find_feature_slug(repo_root)
|
|
52
|
+
if not feature_slug:
|
|
53
|
+
console.print("[red]Error:[/red] Could not auto-detect feature")
|
|
54
|
+
return {"error": "Could not auto-detect feature"}
|
|
55
|
+
|
|
56
|
+
# Get main repo root for correct path resolution
|
|
57
|
+
main_repo_root = get_main_repo_root(repo_root)
|
|
58
|
+
|
|
59
|
+
# Locate feature directory
|
|
60
|
+
feature_dir = main_repo_root / "kitty-specs" / feature_slug
|
|
61
|
+
|
|
62
|
+
if not feature_dir.exists():
|
|
63
|
+
console.print(f"[red]Error:[/red] Feature directory not found: {feature_dir}")
|
|
64
|
+
return {"error": f"Feature directory not found: {feature_dir}"}
|
|
65
|
+
|
|
66
|
+
tasks_dir = feature_dir / "tasks"
|
|
67
|
+
|
|
68
|
+
if not tasks_dir.exists():
|
|
69
|
+
console.print(f"[red]Error:[/red] Tasks directory not found: {tasks_dir}")
|
|
70
|
+
return {"error": f"Tasks directory not found: {tasks_dir}"}
|
|
71
|
+
|
|
72
|
+
# Collect all work packages with dependencies
|
|
73
|
+
work_packages = []
|
|
74
|
+
for wp_file in sorted(tasks_dir.glob("WP*.md")):
|
|
75
|
+
front, body, padding = split_frontmatter(wp_file.read_text(encoding="utf-8-sig"))
|
|
76
|
+
|
|
77
|
+
wp_id = extract_scalar(front, "work_package_id")
|
|
78
|
+
title = extract_scalar(front, "title")
|
|
79
|
+
lane = extract_scalar(front, "lane") or "planned"
|
|
80
|
+
phase = extract_scalar(front, "phase") or "Unknown Phase"
|
|
81
|
+
|
|
82
|
+
# Parse dependencies
|
|
83
|
+
dependencies = []
|
|
84
|
+
if "dependencies:" in front:
|
|
85
|
+
import re
|
|
86
|
+
dep_match = re.search(r'dependencies:\s*\n((?:\s+-\s+"[^"]+"\s*\n)*)', front, re.MULTILINE)
|
|
87
|
+
if dep_match:
|
|
88
|
+
dep_text = dep_match.group(1)
|
|
89
|
+
dependencies = re.findall(r'"([^"]+)"', dep_text)
|
|
90
|
+
|
|
91
|
+
work_packages.append({
|
|
92
|
+
"id": wp_id,
|
|
93
|
+
"title": title,
|
|
94
|
+
"lane": lane,
|
|
95
|
+
"phase": phase,
|
|
96
|
+
"file": wp_file.name,
|
|
97
|
+
"dependencies": dependencies
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
if not work_packages:
|
|
101
|
+
console.print(f"[yellow]No work packages found in {tasks_dir}[/yellow]")
|
|
102
|
+
return {"error": "No work packages found", "work_packages": []}
|
|
103
|
+
|
|
104
|
+
# Group by lane
|
|
105
|
+
by_lane = {"planned": [], "doing": [], "for_review": [], "done": []}
|
|
106
|
+
for wp in work_packages:
|
|
107
|
+
lane = wp["lane"]
|
|
108
|
+
if lane in by_lane:
|
|
109
|
+
by_lane[lane].append(wp)
|
|
110
|
+
else:
|
|
111
|
+
by_lane.setdefault("other", []).append(wp)
|
|
112
|
+
|
|
113
|
+
# Calculate metrics
|
|
114
|
+
total = len(work_packages)
|
|
115
|
+
done_count = len(by_lane["done"])
|
|
116
|
+
in_progress = len(by_lane["doing"]) + len(by_lane["for_review"])
|
|
117
|
+
planned_count = len(by_lane["planned"])
|
|
118
|
+
progress_pct = round((done_count / total * 100), 1) if total > 0 else 0
|
|
119
|
+
|
|
120
|
+
# Analyze parallelization opportunities
|
|
121
|
+
done_wp_ids = {wp["id"] for wp in by_lane["done"]}
|
|
122
|
+
parallel_info = _analyze_parallelization(work_packages, done_wp_ids)
|
|
123
|
+
|
|
124
|
+
# Display the status board
|
|
125
|
+
_display_status_board(feature_slug, work_packages, by_lane, total, done_count,
|
|
126
|
+
in_progress, planned_count, progress_pct, parallel_info)
|
|
127
|
+
|
|
128
|
+
# Return structured data
|
|
129
|
+
lane_counts = Counter(wp["lane"] for wp in work_packages)
|
|
130
|
+
return {
|
|
131
|
+
"feature": feature_slug,
|
|
132
|
+
"total_wps": total,
|
|
133
|
+
"by_lane": dict(lane_counts),
|
|
134
|
+
"work_packages": work_packages,
|
|
135
|
+
"progress_percentage": progress_pct,
|
|
136
|
+
"done_count": done_count,
|
|
137
|
+
"in_progress": in_progress,
|
|
138
|
+
"planned_count": planned_count,
|
|
139
|
+
"parallelization": parallel_info
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
except Exception as e:
|
|
143
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
144
|
+
return {"error": str(e)}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _analyze_parallelization(work_packages: list, done_wp_ids: set) -> dict:
|
|
148
|
+
"""Analyze which work packages can be started and parallelized.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
work_packages: List of all work packages with dependencies
|
|
152
|
+
done_wp_ids: Set of WP IDs that are already done
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
dict with 'ready' (WPs that can start now) and 'parallel_groups' (groups that can run together)
|
|
156
|
+
"""
|
|
157
|
+
# Find WPs that are ready (all dependencies satisfied)
|
|
158
|
+
ready_wps = []
|
|
159
|
+
for wp in work_packages:
|
|
160
|
+
# Skip if already done or in progress
|
|
161
|
+
if wp["lane"] in ["done", "doing", "for_review"]:
|
|
162
|
+
continue
|
|
163
|
+
|
|
164
|
+
# Check if all dependencies are satisfied
|
|
165
|
+
deps = wp.get("dependencies", [])
|
|
166
|
+
if all(dep in done_wp_ids for dep in deps):
|
|
167
|
+
ready_wps.append(wp)
|
|
168
|
+
|
|
169
|
+
# Group ready WPs by parallelization potential
|
|
170
|
+
# WPs can run in parallel if they don't depend on each other
|
|
171
|
+
parallel_groups = []
|
|
172
|
+
if ready_wps:
|
|
173
|
+
# Build dependency relationships among ready WPs
|
|
174
|
+
ready_ids = {wp["id"] for wp in ready_wps}
|
|
175
|
+
|
|
176
|
+
# Check which ready WPs don't depend on any other ready WPs
|
|
177
|
+
independent = []
|
|
178
|
+
dependent = []
|
|
179
|
+
|
|
180
|
+
for wp in ready_wps:
|
|
181
|
+
wp_deps = set(wp.get("dependencies", []))
|
|
182
|
+
if not (wp_deps & ready_ids): # No dependencies on other ready WPs
|
|
183
|
+
independent.append(wp)
|
|
184
|
+
else:
|
|
185
|
+
dependent.append(wp)
|
|
186
|
+
|
|
187
|
+
if independent:
|
|
188
|
+
if len(independent) > 1:
|
|
189
|
+
parallel_groups.append({
|
|
190
|
+
"type": "parallel",
|
|
191
|
+
"wps": independent,
|
|
192
|
+
"note": f"These {len(independent)} WPs can run in parallel"
|
|
193
|
+
})
|
|
194
|
+
else:
|
|
195
|
+
parallel_groups.append({
|
|
196
|
+
"type": "single",
|
|
197
|
+
"wps": independent,
|
|
198
|
+
"note": "Ready to start"
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
if dependent:
|
|
202
|
+
parallel_groups.append({
|
|
203
|
+
"type": "sequential",
|
|
204
|
+
"wps": dependent,
|
|
205
|
+
"note": "Must wait for other ready WPs to complete first"
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
"ready_wps": ready_wps,
|
|
210
|
+
"parallel_groups": parallel_groups,
|
|
211
|
+
"can_parallelize": len(ready_wps) > 1 and any(g["type"] == "parallel" for g in parallel_groups)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _display_status_board(feature_slug: str, work_packages: list, by_lane: dict,
|
|
216
|
+
total: int, done_count: int, in_progress: int,
|
|
217
|
+
planned_count: int, progress_pct: float, parallel_info: dict) -> None:
|
|
218
|
+
"""Display the rich-formatted status board."""
|
|
219
|
+
# Create title panel
|
|
220
|
+
title_text = Text()
|
|
221
|
+
title_text.append(f"📊 Work Package Status: ", style="bold cyan")
|
|
222
|
+
title_text.append(feature_slug, style="bold white")
|
|
223
|
+
|
|
224
|
+
console.print()
|
|
225
|
+
console.print(Panel(title_text, border_style="cyan"))
|
|
226
|
+
|
|
227
|
+
# Progress bar
|
|
228
|
+
progress_text = Text()
|
|
229
|
+
progress_text.append(f"Progress: ", style="bold")
|
|
230
|
+
progress_text.append(f"{done_count}/{total}", style="bold green")
|
|
231
|
+
progress_text.append(f" ({progress_pct}%)", style="dim")
|
|
232
|
+
|
|
233
|
+
# Create visual progress bar
|
|
234
|
+
bar_width = 40
|
|
235
|
+
filled = int(bar_width * progress_pct / 100)
|
|
236
|
+
bar = "█" * filled + "░" * (bar_width - filled)
|
|
237
|
+
progress_text.append(f"\n{bar}", style="green")
|
|
238
|
+
|
|
239
|
+
console.print(progress_text)
|
|
240
|
+
console.print()
|
|
241
|
+
|
|
242
|
+
# Kanban board table
|
|
243
|
+
table = Table(title="Kanban Board", show_header=True, header_style="bold magenta", border_style="dim")
|
|
244
|
+
table.add_column("📋 Planned", style="yellow", no_wrap=False, width=25)
|
|
245
|
+
table.add_column("🔄 Doing", style="blue", no_wrap=False, width=25)
|
|
246
|
+
table.add_column("👀 For Review", style="cyan", no_wrap=False, width=25)
|
|
247
|
+
table.add_column("✅ Done", style="green", no_wrap=False, width=25)
|
|
248
|
+
|
|
249
|
+
# Find max length for rows
|
|
250
|
+
max_rows = max(len(by_lane["planned"]), len(by_lane["doing"]),
|
|
251
|
+
len(by_lane["for_review"]), len(by_lane["done"]))
|
|
252
|
+
|
|
253
|
+
# Add rows
|
|
254
|
+
for i in range(max_rows):
|
|
255
|
+
row = []
|
|
256
|
+
for lane in ["planned", "doing", "for_review", "done"]:
|
|
257
|
+
if i < len(by_lane[lane]):
|
|
258
|
+
wp = by_lane[lane][i]
|
|
259
|
+
cell = f"{wp['id']}\n{wp['title'][:22]}..." if len(wp['title']) > 22 else f"{wp['id']}\n{wp['title']}"
|
|
260
|
+
row.append(cell)
|
|
261
|
+
else:
|
|
262
|
+
row.append("")
|
|
263
|
+
table.add_row(*row)
|
|
264
|
+
|
|
265
|
+
# Add count row
|
|
266
|
+
table.add_row(
|
|
267
|
+
f"[bold]{len(by_lane['planned'])} WPs[/bold]",
|
|
268
|
+
f"[bold]{len(by_lane['doing'])} WPs[/bold]",
|
|
269
|
+
f"[bold]{len(by_lane['for_review'])} WPs[/bold]",
|
|
270
|
+
f"[bold]{len(by_lane['done'])} WPs[/bold]",
|
|
271
|
+
style="dim"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
console.print(table)
|
|
275
|
+
console.print()
|
|
276
|
+
|
|
277
|
+
# Next steps section
|
|
278
|
+
if by_lane["for_review"]:
|
|
279
|
+
console.print("[bold cyan]👀 Ready for Review:[/bold cyan]")
|
|
280
|
+
for wp in by_lane["for_review"]:
|
|
281
|
+
console.print(f" • {wp['id']} - {wp['title']}")
|
|
282
|
+
console.print()
|
|
283
|
+
|
|
284
|
+
if by_lane["doing"]:
|
|
285
|
+
console.print("[bold blue]🔄 In Progress:[/bold blue]")
|
|
286
|
+
for wp in by_lane["doing"]:
|
|
287
|
+
console.print(f" • {wp['id']} - {wp['title']}")
|
|
288
|
+
console.print()
|
|
289
|
+
|
|
290
|
+
if by_lane["planned"]:
|
|
291
|
+
console.print("[bold yellow]📋 Next Up (Planned):[/bold yellow]")
|
|
292
|
+
# Show first 3 planned items
|
|
293
|
+
for wp in by_lane["planned"][:3]:
|
|
294
|
+
console.print(f" • {wp['id']} - {wp['title']}")
|
|
295
|
+
if len(by_lane["planned"]) > 3:
|
|
296
|
+
console.print(f" [dim]... and {len(by_lane['planned']) - 3} more[/dim]")
|
|
297
|
+
console.print()
|
|
298
|
+
|
|
299
|
+
# Parallelization opportunities
|
|
300
|
+
if parallel_info["ready_wps"]:
|
|
301
|
+
console.print("[bold magenta]🔀 Parallelization Strategy:[/bold magenta]")
|
|
302
|
+
|
|
303
|
+
# Get latest done WP for base
|
|
304
|
+
done_wps = sorted([wp for wp in work_packages if wp["lane"] == "done"],
|
|
305
|
+
key=lambda x: x["id"], reverse=True)
|
|
306
|
+
latest_base = done_wps[0]["id"] if done_wps else "main"
|
|
307
|
+
|
|
308
|
+
for group in parallel_info["parallel_groups"]:
|
|
309
|
+
if group["type"] == "parallel":
|
|
310
|
+
console.print(f"\n [bold green]✨ Can run in PARALLEL:[/bold green]")
|
|
311
|
+
for wp in group["wps"]:
|
|
312
|
+
console.print(f" • {wp['id']} - {wp['title']}")
|
|
313
|
+
console.print(f" [dim] → All dependencies satisfied, no inter-dependencies[/dim]")
|
|
314
|
+
|
|
315
|
+
# Show implementation commands
|
|
316
|
+
console.print(f"\n [bold]Start commands:[/bold]")
|
|
317
|
+
for wp in group["wps"]:
|
|
318
|
+
# Find best base for this WP
|
|
319
|
+
wp_deps = wp.get("dependencies", [])
|
|
320
|
+
base = wp_deps[-1] if wp_deps else latest_base
|
|
321
|
+
console.print(f" spec-kitty implement {wp['id']} --base {base} &")
|
|
322
|
+
|
|
323
|
+
elif group["type"] == "single":
|
|
324
|
+
console.print(f"\n [bold yellow]▶️ Ready to start:[/bold yellow]")
|
|
325
|
+
for wp in group["wps"]:
|
|
326
|
+
console.print(f" • {wp['id']} - {wp['title']}")
|
|
327
|
+
# Find best base for this WP
|
|
328
|
+
wp_deps = wp.get("dependencies", [])
|
|
329
|
+
base = wp_deps[-1] if wp_deps else latest_base
|
|
330
|
+
console.print(f" spec-kitty implement {wp['id']} --base {base}")
|
|
331
|
+
|
|
332
|
+
elif group["type"] == "sequential":
|
|
333
|
+
console.print(f"\n [bold blue]⏭️ Sequential (blocked by other ready WPs):[/bold blue]")
|
|
334
|
+
for wp in group["wps"]:
|
|
335
|
+
deps_in_ready = [d for d in wp.get("dependencies", [])
|
|
336
|
+
if d in {w["id"] for w in parallel_info["ready_wps"]}]
|
|
337
|
+
console.print(f" • {wp['id']} - {wp['title']}")
|
|
338
|
+
console.print(f" [dim]Waiting for: {', '.join(deps_in_ready)}[/dim]")
|
|
339
|
+
|
|
340
|
+
console.print()
|
|
341
|
+
elif by_lane["planned"] and not by_lane["doing"] and not by_lane["for_review"]:
|
|
342
|
+
# All planned WPs are blocked
|
|
343
|
+
console.print("[bold red]⚠️ All remaining WPs are blocked[/bold red]")
|
|
344
|
+
console.print(" Check dependency status above\n")
|
|
345
|
+
|
|
346
|
+
# Summary metrics
|
|
347
|
+
summary = Table.grid(padding=(0, 2))
|
|
348
|
+
summary.add_column(style="bold")
|
|
349
|
+
summary.add_column()
|
|
350
|
+
summary.add_row("Total WPs:", str(total))
|
|
351
|
+
summary.add_row("Completed:", f"[green]{done_count}[/green] ({progress_pct}%)")
|
|
352
|
+
summary.add_row("In Progress:", f"[blue]{in_progress}[/blue]")
|
|
353
|
+
summary.add_row("Planned:", f"[yellow]{planned_count}[/yellow]")
|
|
354
|
+
|
|
355
|
+
console.print(Panel(summary, title="[bold]Summary[/bold]", border_style="dim"))
|
|
356
|
+
console.print()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Command registration helpers for Spec Kitty CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from . import accept as accept_module
|
|
8
|
+
from . import agent as agent_module
|
|
9
|
+
from . import context as context_module
|
|
10
|
+
from . import dashboard as dashboard_module
|
|
11
|
+
from . import implement as implement_module
|
|
12
|
+
from . import merge as merge_module
|
|
13
|
+
from . import mission as mission_module
|
|
14
|
+
from . import ops as ops_module
|
|
15
|
+
from . import orchestrate as orchestrate_module
|
|
16
|
+
from . import repair as repair_module
|
|
17
|
+
from . import research as research_module
|
|
18
|
+
from . import sync as sync_module
|
|
19
|
+
from . import upgrade as upgrade_module
|
|
20
|
+
from . import validate_encoding as validate_encoding_module
|
|
21
|
+
from . import validate_tasks as validate_tasks_module
|
|
22
|
+
from . import verify as verify_module
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def register_commands(app: typer.Typer) -> None:
|
|
26
|
+
"""Attach all extracted commands to the root Typer application."""
|
|
27
|
+
app.command()(accept_module.accept)
|
|
28
|
+
app.add_typer(agent_module.app, name="agent")
|
|
29
|
+
app.add_typer(context_module.app, name="context")
|
|
30
|
+
app.command()(dashboard_module.dashboard)
|
|
31
|
+
app.command()(implement_module.implement)
|
|
32
|
+
app.command()(merge_module.merge)
|
|
33
|
+
app.add_typer(mission_module.app, name="mission")
|
|
34
|
+
app.add_typer(ops_module.app, name="ops")
|
|
35
|
+
app.add_typer(orchestrate_module.app, name="orchestrate")
|
|
36
|
+
app.add_typer(repair_module.app, name="repair", help="Repair broken templates")
|
|
37
|
+
app.command()(research_module.research)
|
|
38
|
+
app.command()(sync_module.sync)
|
|
39
|
+
app.command()(upgrade_module.upgrade)
|
|
40
|
+
app.command(name="list-legacy-features")(upgrade_module.list_legacy_features)
|
|
41
|
+
app.command(name="validate-encoding")(validate_encoding_module.validate_encoding)
|
|
42
|
+
app.command(name="validate-tasks")(validate_tasks_module.validate_tasks)
|
|
43
|
+
app.command()(verify_module.verify_setup)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
__all__ = ["register_commands"]
|