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.
Files changed (242) hide show
  1. spec_kitty_cli-0.12.1.dist-info/METADATA +1767 -0
  2. spec_kitty_cli-0.12.1.dist-info/RECORD +242 -0
  3. spec_kitty_cli-0.12.1.dist-info/WHEEL +4 -0
  4. spec_kitty_cli-0.12.1.dist-info/entry_points.txt +2 -0
  5. spec_kitty_cli-0.12.1.dist-info/licenses/LICENSE +21 -0
  6. specify_cli/__init__.py +171 -0
  7. specify_cli/acceptance.py +627 -0
  8. specify_cli/agent_utils/README.md +157 -0
  9. specify_cli/agent_utils/__init__.py +9 -0
  10. specify_cli/agent_utils/status.py +356 -0
  11. specify_cli/cli/__init__.py +6 -0
  12. specify_cli/cli/commands/__init__.py +46 -0
  13. specify_cli/cli/commands/accept.py +189 -0
  14. specify_cli/cli/commands/agent/__init__.py +22 -0
  15. specify_cli/cli/commands/agent/config.py +382 -0
  16. specify_cli/cli/commands/agent/context.py +191 -0
  17. specify_cli/cli/commands/agent/feature.py +1057 -0
  18. specify_cli/cli/commands/agent/release.py +11 -0
  19. specify_cli/cli/commands/agent/tasks.py +1253 -0
  20. specify_cli/cli/commands/agent/workflow.py +801 -0
  21. specify_cli/cli/commands/context.py +246 -0
  22. specify_cli/cli/commands/dashboard.py +85 -0
  23. specify_cli/cli/commands/implement.py +973 -0
  24. specify_cli/cli/commands/init.py +827 -0
  25. specify_cli/cli/commands/init_help.py +62 -0
  26. specify_cli/cli/commands/merge.py +755 -0
  27. specify_cli/cli/commands/mission.py +240 -0
  28. specify_cli/cli/commands/ops.py +265 -0
  29. specify_cli/cli/commands/orchestrate.py +640 -0
  30. specify_cli/cli/commands/repair.py +175 -0
  31. specify_cli/cli/commands/research.py +165 -0
  32. specify_cli/cli/commands/sync.py +364 -0
  33. specify_cli/cli/commands/upgrade.py +249 -0
  34. specify_cli/cli/commands/validate_encoding.py +186 -0
  35. specify_cli/cli/commands/validate_tasks.py +186 -0
  36. specify_cli/cli/commands/verify.py +310 -0
  37. specify_cli/cli/helpers.py +123 -0
  38. specify_cli/cli/step_tracker.py +91 -0
  39. specify_cli/cli/ui.py +192 -0
  40. specify_cli/core/__init__.py +53 -0
  41. specify_cli/core/agent_context.py +311 -0
  42. specify_cli/core/config.py +96 -0
  43. specify_cli/core/context_validation.py +362 -0
  44. specify_cli/core/dependency_graph.py +351 -0
  45. specify_cli/core/git_ops.py +129 -0
  46. specify_cli/core/multi_parent_merge.py +323 -0
  47. specify_cli/core/paths.py +260 -0
  48. specify_cli/core/project_resolver.py +110 -0
  49. specify_cli/core/stale_detection.py +263 -0
  50. specify_cli/core/tool_checker.py +79 -0
  51. specify_cli/core/utils.py +43 -0
  52. specify_cli/core/vcs/__init__.py +114 -0
  53. specify_cli/core/vcs/detection.py +341 -0
  54. specify_cli/core/vcs/exceptions.py +85 -0
  55. specify_cli/core/vcs/git.py +1304 -0
  56. specify_cli/core/vcs/jujutsu.py +1208 -0
  57. specify_cli/core/vcs/protocol.py +285 -0
  58. specify_cli/core/vcs/types.py +249 -0
  59. specify_cli/core/version_checker.py +261 -0
  60. specify_cli/core/worktree.py +506 -0
  61. specify_cli/dashboard/__init__.py +28 -0
  62. specify_cli/dashboard/diagnostics.py +204 -0
  63. specify_cli/dashboard/handlers/__init__.py +17 -0
  64. specify_cli/dashboard/handlers/api.py +143 -0
  65. specify_cli/dashboard/handlers/base.py +65 -0
  66. specify_cli/dashboard/handlers/features.py +390 -0
  67. specify_cli/dashboard/handlers/router.py +81 -0
  68. specify_cli/dashboard/handlers/static.py +50 -0
  69. specify_cli/dashboard/lifecycle.py +541 -0
  70. specify_cli/dashboard/scanner.py +437 -0
  71. specify_cli/dashboard/server.py +123 -0
  72. specify_cli/dashboard/static/dashboard/dashboard.css +722 -0
  73. specify_cli/dashboard/static/dashboard/dashboard.js +1424 -0
  74. specify_cli/dashboard/static/spec-kitty.png +0 -0
  75. specify_cli/dashboard/templates/__init__.py +36 -0
  76. specify_cli/dashboard/templates/index.html +258 -0
  77. specify_cli/doc_generators.py +621 -0
  78. specify_cli/doc_state.py +408 -0
  79. specify_cli/frontmatter.py +384 -0
  80. specify_cli/gap_analysis.py +915 -0
  81. specify_cli/gitignore_manager.py +300 -0
  82. specify_cli/guards.py +145 -0
  83. specify_cli/legacy_detector.py +83 -0
  84. specify_cli/manifest.py +286 -0
  85. specify_cli/merge/__init__.py +63 -0
  86. specify_cli/merge/executor.py +653 -0
  87. specify_cli/merge/forecast.py +215 -0
  88. specify_cli/merge/ordering.py +126 -0
  89. specify_cli/merge/preflight.py +230 -0
  90. specify_cli/merge/state.py +185 -0
  91. specify_cli/merge/status_resolver.py +354 -0
  92. specify_cli/mission.py +654 -0
  93. specify_cli/missions/documentation/command-templates/implement.md +309 -0
  94. specify_cli/missions/documentation/command-templates/plan.md +275 -0
  95. specify_cli/missions/documentation/command-templates/review.md +344 -0
  96. specify_cli/missions/documentation/command-templates/specify.md +206 -0
  97. specify_cli/missions/documentation/command-templates/tasks.md +189 -0
  98. specify_cli/missions/documentation/mission.yaml +113 -0
  99. specify_cli/missions/documentation/templates/divio/explanation-template.md +192 -0
  100. specify_cli/missions/documentation/templates/divio/howto-template.md +168 -0
  101. specify_cli/missions/documentation/templates/divio/reference-template.md +179 -0
  102. specify_cli/missions/documentation/templates/divio/tutorial-template.md +146 -0
  103. specify_cli/missions/documentation/templates/generators/jsdoc.json.template +18 -0
  104. specify_cli/missions/documentation/templates/generators/sphinx-conf.py.template +36 -0
  105. specify_cli/missions/documentation/templates/plan-template.md +269 -0
  106. specify_cli/missions/documentation/templates/release-template.md +222 -0
  107. specify_cli/missions/documentation/templates/spec-template.md +172 -0
  108. specify_cli/missions/documentation/templates/task-prompt-template.md +140 -0
  109. specify_cli/missions/documentation/templates/tasks-template.md +159 -0
  110. specify_cli/missions/research/command-templates/merge.md +388 -0
  111. specify_cli/missions/research/command-templates/plan.md +125 -0
  112. specify_cli/missions/research/command-templates/review.md +144 -0
  113. specify_cli/missions/research/command-templates/tasks.md +225 -0
  114. specify_cli/missions/research/mission.yaml +115 -0
  115. specify_cli/missions/research/templates/data-model-template.md +33 -0
  116. specify_cli/missions/research/templates/plan-template.md +161 -0
  117. specify_cli/missions/research/templates/research/evidence-log.csv +18 -0
  118. specify_cli/missions/research/templates/research/source-register.csv +18 -0
  119. specify_cli/missions/research/templates/research-template.md +35 -0
  120. specify_cli/missions/research/templates/spec-template.md +64 -0
  121. specify_cli/missions/research/templates/task-prompt-template.md +148 -0
  122. specify_cli/missions/research/templates/tasks-template.md +114 -0
  123. specify_cli/missions/software-dev/command-templates/accept.md +75 -0
  124. specify_cli/missions/software-dev/command-templates/analyze.md +183 -0
  125. specify_cli/missions/software-dev/command-templates/checklist.md +286 -0
  126. specify_cli/missions/software-dev/command-templates/clarify.md +157 -0
  127. specify_cli/missions/software-dev/command-templates/constitution.md +432 -0
  128. specify_cli/missions/software-dev/command-templates/dashboard.md +101 -0
  129. specify_cli/missions/software-dev/command-templates/implement.md +41 -0
  130. specify_cli/missions/software-dev/command-templates/merge.md +383 -0
  131. specify_cli/missions/software-dev/command-templates/plan.md +171 -0
  132. specify_cli/missions/software-dev/command-templates/review.md +32 -0
  133. specify_cli/missions/software-dev/command-templates/specify.md +321 -0
  134. specify_cli/missions/software-dev/command-templates/tasks.md +566 -0
  135. specify_cli/missions/software-dev/mission.yaml +100 -0
  136. specify_cli/missions/software-dev/templates/plan-template.md +132 -0
  137. specify_cli/missions/software-dev/templates/spec-template.md +116 -0
  138. specify_cli/missions/software-dev/templates/task-prompt-template.md +140 -0
  139. specify_cli/missions/software-dev/templates/tasks-template.md +159 -0
  140. specify_cli/orchestrator/__init__.py +75 -0
  141. specify_cli/orchestrator/agent_config.py +224 -0
  142. specify_cli/orchestrator/agents/__init__.py +170 -0
  143. specify_cli/orchestrator/agents/augment.py +112 -0
  144. specify_cli/orchestrator/agents/base.py +243 -0
  145. specify_cli/orchestrator/agents/claude.py +112 -0
  146. specify_cli/orchestrator/agents/codex.py +106 -0
  147. specify_cli/orchestrator/agents/copilot.py +137 -0
  148. specify_cli/orchestrator/agents/cursor.py +139 -0
  149. specify_cli/orchestrator/agents/gemini.py +115 -0
  150. specify_cli/orchestrator/agents/kilocode.py +94 -0
  151. specify_cli/orchestrator/agents/opencode.py +132 -0
  152. specify_cli/orchestrator/agents/qwen.py +96 -0
  153. specify_cli/orchestrator/config.py +455 -0
  154. specify_cli/orchestrator/executor.py +642 -0
  155. specify_cli/orchestrator/integration.py +1230 -0
  156. specify_cli/orchestrator/monitor.py +898 -0
  157. specify_cli/orchestrator/scheduler.py +832 -0
  158. specify_cli/orchestrator/state.py +508 -0
  159. specify_cli/orchestrator/testing/__init__.py +122 -0
  160. specify_cli/orchestrator/testing/availability.py +346 -0
  161. specify_cli/orchestrator/testing/fixtures.py +684 -0
  162. specify_cli/orchestrator/testing/paths.py +218 -0
  163. specify_cli/plan_validation.py +107 -0
  164. specify_cli/scripts/debug-dashboard-scan.py +61 -0
  165. specify_cli/scripts/tasks/acceptance_support.py +695 -0
  166. specify_cli/scripts/tasks/task_helpers.py +506 -0
  167. specify_cli/scripts/tasks/tasks_cli.py +848 -0
  168. specify_cli/scripts/validate_encoding.py +180 -0
  169. specify_cli/task_metadata_validation.py +274 -0
  170. specify_cli/tasks_support.py +447 -0
  171. specify_cli/template/__init__.py +47 -0
  172. specify_cli/template/asset_generator.py +206 -0
  173. specify_cli/template/github_client.py +334 -0
  174. specify_cli/template/manager.py +193 -0
  175. specify_cli/template/renderer.py +99 -0
  176. specify_cli/templates/AGENTS.md +190 -0
  177. specify_cli/templates/POWERSHELL_SYNTAX.md +229 -0
  178. specify_cli/templates/agent-file-template.md +35 -0
  179. specify_cli/templates/checklist-template.md +42 -0
  180. specify_cli/templates/claudeignore-template +58 -0
  181. specify_cli/templates/command-templates/accept.md +141 -0
  182. specify_cli/templates/command-templates/analyze.md +253 -0
  183. specify_cli/templates/command-templates/checklist.md +352 -0
  184. specify_cli/templates/command-templates/clarify.md +224 -0
  185. specify_cli/templates/command-templates/constitution.md +432 -0
  186. specify_cli/templates/command-templates/dashboard.md +175 -0
  187. specify_cli/templates/command-templates/implement.md +190 -0
  188. specify_cli/templates/command-templates/merge.md +374 -0
  189. specify_cli/templates/command-templates/plan.md +171 -0
  190. specify_cli/templates/command-templates/research.md +88 -0
  191. specify_cli/templates/command-templates/review.md +510 -0
  192. specify_cli/templates/command-templates/specify.md +321 -0
  193. specify_cli/templates/command-templates/status.md +92 -0
  194. specify_cli/templates/command-templates/tasks.md +199 -0
  195. specify_cli/templates/git-hooks/pre-commit +22 -0
  196. specify_cli/templates/git-hooks/pre-commit-agent-check +37 -0
  197. specify_cli/templates/git-hooks/pre-commit-encoding-check +142 -0
  198. specify_cli/templates/plan-template.md +108 -0
  199. specify_cli/templates/spec-template.md +118 -0
  200. specify_cli/templates/task-prompt-template.md +165 -0
  201. specify_cli/templates/tasks-template.md +161 -0
  202. specify_cli/templates/vscode-settings.json +13 -0
  203. specify_cli/text_sanitization.py +225 -0
  204. specify_cli/upgrade/__init__.py +18 -0
  205. specify_cli/upgrade/detector.py +239 -0
  206. specify_cli/upgrade/metadata.py +182 -0
  207. specify_cli/upgrade/migrations/__init__.py +65 -0
  208. specify_cli/upgrade/migrations/base.py +80 -0
  209. specify_cli/upgrade/migrations/m_0_10_0_python_only.py +359 -0
  210. specify_cli/upgrade/migrations/m_0_10_12_constitution_cleanup.py +99 -0
  211. specify_cli/upgrade/migrations/m_0_10_14_update_implement_slash_command.py +176 -0
  212. specify_cli/upgrade/migrations/m_0_10_1_populate_slash_commands.py +174 -0
  213. specify_cli/upgrade/migrations/m_0_10_2_update_slash_commands.py +172 -0
  214. specify_cli/upgrade/migrations/m_0_10_6_workflow_simplification.py +174 -0
  215. specify_cli/upgrade/migrations/m_0_10_8_fix_memory_structure.py +252 -0
  216. specify_cli/upgrade/migrations/m_0_10_9_repair_templates.py +168 -0
  217. specify_cli/upgrade/migrations/m_0_11_0_workspace_per_wp.py +182 -0
  218. specify_cli/upgrade/migrations/m_0_11_1_improved_workflow_templates.py +173 -0
  219. specify_cli/upgrade/migrations/m_0_11_1_update_implement_slash_command.py +160 -0
  220. specify_cli/upgrade/migrations/m_0_11_2_improved_workflow_templates.py +173 -0
  221. specify_cli/upgrade/migrations/m_0_11_3_workflow_agent_flag.py +114 -0
  222. specify_cli/upgrade/migrations/m_0_12_0_documentation_mission.py +155 -0
  223. specify_cli/upgrade/migrations/m_0_12_1_remove_kitty_specs_from_gitignore.py +183 -0
  224. specify_cli/upgrade/migrations/m_0_2_0_specify_to_kittify.py +80 -0
  225. specify_cli/upgrade/migrations/m_0_4_8_gitignore_agents.py +118 -0
  226. specify_cli/upgrade/migrations/m_0_5_0_encoding_hooks.py +141 -0
  227. specify_cli/upgrade/migrations/m_0_6_5_commands_rename.py +169 -0
  228. specify_cli/upgrade/migrations/m_0_6_7_ensure_missions.py +228 -0
  229. specify_cli/upgrade/migrations/m_0_7_2_worktree_commands_dedup.py +89 -0
  230. specify_cli/upgrade/migrations/m_0_7_3_update_scripts.py +114 -0
  231. specify_cli/upgrade/migrations/m_0_8_0_remove_active_mission.py +82 -0
  232. specify_cli/upgrade/migrations/m_0_8_0_worktree_agents_symlink.py +148 -0
  233. specify_cli/upgrade/migrations/m_0_9_0_frontmatter_only_lanes.py +346 -0
  234. specify_cli/upgrade/migrations/m_0_9_1_complete_lane_migration.py +656 -0
  235. specify_cli/upgrade/migrations/m_0_9_2_research_mission_templates.py +221 -0
  236. specify_cli/upgrade/registry.py +121 -0
  237. specify_cli/upgrade/runner.py +284 -0
  238. specify_cli/validators/__init__.py +14 -0
  239. specify_cli/validators/paths.py +154 -0
  240. specify_cli/validators/research.py +428 -0
  241. specify_cli/verify_enhanced.py +270 -0
  242. 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,6 @@
1
+ """CLI helpers exposed for other modules."""
2
+
3
+ from .step_tracker import StepTracker
4
+ from .ui import get_key, select_with_arrows, multi_select_with_arrows
5
+
6
+ __all__ = ["StepTracker", "get_key", "select_with_arrows", "multi_select_with_arrows"]
@@ -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"]