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,65 @@
1
+ """Migration implementations for Spec Kitty upgrade system.
2
+
3
+ Import all migrations here to register them with the MigrationRegistry.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ # Import migrations to register them
9
+ from . import m_0_2_0_specify_to_kittify
10
+ from . import m_0_4_8_gitignore_agents
11
+ from . import m_0_5_0_encoding_hooks
12
+ from . import m_0_6_5_commands_rename
13
+ from . import m_0_6_7_ensure_missions
14
+ from . import m_0_7_2_worktree_commands_dedup
15
+ from . import m_0_7_3_update_scripts
16
+ from . import m_0_8_0_remove_active_mission
17
+ from . import m_0_8_0_worktree_agents_symlink
18
+ from . import m_0_9_0_frontmatter_only_lanes
19
+ from . import m_0_9_1_complete_lane_migration
20
+ from . import m_0_9_2_research_mission_templates
21
+ from . import m_0_10_0_python_only
22
+ from . import m_0_10_1_populate_slash_commands
23
+ from . import m_0_10_2_update_slash_commands
24
+ from . import m_0_10_6_workflow_simplification
25
+ from . import m_0_10_8_fix_memory_structure
26
+ from . import m_0_10_9_repair_templates
27
+ from . import m_0_10_12_constitution_cleanup
28
+ from . import m_0_10_14_update_implement_slash_command
29
+ from . import m_0_11_0_workspace_per_wp
30
+ from . import m_0_11_1_improved_workflow_templates
31
+ from . import m_0_11_1_update_implement_slash_command
32
+ from . import m_0_11_2_improved_workflow_templates
33
+ from . import m_0_11_3_workflow_agent_flag
34
+ from . import m_0_12_0_documentation_mission
35
+ from . import m_0_12_1_remove_kitty_specs_from_gitignore
36
+
37
+ __all__ = [
38
+ "m_0_2_0_specify_to_kittify",
39
+ "m_0_4_8_gitignore_agents",
40
+ "m_0_5_0_encoding_hooks",
41
+ "m_0_6_5_commands_rename",
42
+ "m_0_6_7_ensure_missions",
43
+ "m_0_7_2_worktree_commands_dedup",
44
+ "m_0_7_3_update_scripts",
45
+ "m_0_8_0_remove_active_mission",
46
+ "m_0_8_0_worktree_agents_symlink",
47
+ "m_0_9_0_frontmatter_only_lanes",
48
+ "m_0_9_1_complete_lane_migration",
49
+ "m_0_9_2_research_mission_templates",
50
+ "m_0_10_0_python_only",
51
+ "m_0_10_1_populate_slash_commands",
52
+ "m_0_10_2_update_slash_commands",
53
+ "m_0_10_6_workflow_simplification",
54
+ "m_0_10_8_fix_memory_structure",
55
+ "m_0_10_9_repair_templates",
56
+ "m_0_10_12_constitution_cleanup",
57
+ "m_0_10_14_update_implement_slash_command",
58
+ "m_0_11_0_workspace_per_wp",
59
+ "m_0_11_1_improved_workflow_templates",
60
+ "m_0_11_1_update_implement_slash_command",
61
+ "m_0_11_2_improved_workflow_templates",
62
+ "m_0_11_3_workflow_agent_flag",
63
+ "m_0_12_0_documentation_mission",
64
+ "m_0_12_1_remove_kitty_specs_from_gitignore",
65
+ ]
@@ -0,0 +1,80 @@
1
+ """Base migration class for Spec Kitty upgrade system."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+ from typing import List, Optional
9
+
10
+
11
+ @dataclass
12
+ class MigrationResult:
13
+ """Result of a migration operation."""
14
+
15
+ success: bool
16
+ changes_made: List[str] = field(default_factory=list)
17
+ errors: List[str] = field(default_factory=list)
18
+ warnings: List[str] = field(default_factory=list)
19
+
20
+
21
+ class BaseMigration(ABC):
22
+ """Base class for all migrations.
23
+
24
+ Migrations should:
25
+ 1. Be idempotent (safe to run multiple times)
26
+ 2. Check preconditions before applying
27
+ 3. Report what changes were made
28
+ 4. Handle dry-run mode
29
+ """
30
+
31
+ # Migration identifier (e.g., "0.6.5_commands_rename")
32
+ # Format: {version}_{short_description}
33
+ migration_id: str = ""
34
+
35
+ # Human-readable description
36
+ description: str = ""
37
+
38
+ # Target version this migration brings project to
39
+ target_version: str = ""
40
+
41
+ # Minimum version this migration can be applied from (optional)
42
+ # If None, detection is used
43
+ min_version: Optional[str] = None
44
+
45
+ @abstractmethod
46
+ def detect(self, project_path: Path) -> bool:
47
+ """Detect if this migration is needed based on project state.
48
+
49
+ Returns True if the project has the OLD state that needs migration.
50
+ This is used for heuristic detection when metadata is missing.
51
+
52
+ Args:
53
+ project_path: Root of the project (.kittify parent)
54
+
55
+ Returns:
56
+ True if migration is needed
57
+ """
58
+
59
+ @abstractmethod
60
+ def can_apply(self, project_path: Path) -> tuple[bool, str]:
61
+ """Check if migration can be safely applied.
62
+
63
+ Args:
64
+ project_path: Root of the project
65
+
66
+ Returns:
67
+ (can_apply, reason) - True if safe, False with explanation if not
68
+ """
69
+
70
+ @abstractmethod
71
+ def apply(self, project_path: Path, dry_run: bool = False) -> MigrationResult:
72
+ """Apply the migration.
73
+
74
+ Args:
75
+ project_path: Root of the project (.kittify parent)
76
+ dry_run: If True, only simulate changes
77
+
78
+ Returns:
79
+ MigrationResult with details of what was changed
80
+ """
@@ -0,0 +1,359 @@
1
+ """Migration: Remove bash scripts and update templates to use Python CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ import shutil
7
+ from pathlib import Path
8
+ from typing import Dict, List, Tuple
9
+
10
+ from ..registry import MigrationRegistry
11
+ from .base import BaseMigration, MigrationResult
12
+
13
+
14
+ @MigrationRegistry.register
15
+ class PythonOnlyMigration(BaseMigration):
16
+ """Migrate from bash scripts to Python-only CLI commands.
17
+
18
+ As of v0.10.0, all spec-kitty commands are available through the
19
+ `spec-kitty agent` CLI namespace. Bash wrapper scripts in
20
+ `.kittify/scripts/bash/` are replaced with Python implementations.
21
+
22
+ This migration:
23
+ 1. Detects and removes bash scripts from .kittify/scripts/bash/
24
+ 2. Updates slash command templates to use `spec-kitty agent` commands
25
+ 3. Cleans up bash scripts in worktrees
26
+ 4. Detects custom modifications and warns users
27
+ 5. Is idempotent (safe to run multiple times)
28
+ """
29
+
30
+ migration_id = "0.10.0_python_only"
31
+ description = "Remove bash scripts and update templates to use Python CLI"
32
+ target_version = "0.10.0"
33
+
34
+ # Bash scripts that should be removed (package scripts only)
35
+ PACKAGE_SCRIPTS = (
36
+ "common.sh",
37
+ "create-new-feature.sh",
38
+ "check-prerequisites.sh",
39
+ "setup-plan.sh",
40
+ "update-agent-context.sh",
41
+ "accept-feature.sh",
42
+ "merge-feature.sh",
43
+ "tasks-move-to-lane.sh",
44
+ "tasks-list-lanes.sh",
45
+ "mark-task-status.sh",
46
+ "tasks-add-history-entry.sh",
47
+ "tasks-rollback-move.sh",
48
+ "validate-task-workflow.sh",
49
+ "move-task-to-doing.sh",
50
+ )
51
+
52
+ # Bash → Python command mappings for template updates
53
+ COMMAND_REPLACEMENTS = {
54
+ # Feature management
55
+ r"\.kittify/scripts/bash/create-new-feature\.sh": "spec-kitty agent create-feature",
56
+ r"scripts/bash/create-new-feature\.sh": "spec-kitty agent create-feature",
57
+ r"\.kittify/scripts/bash/check-prerequisites\.sh": "spec-kitty agent check-prerequisites",
58
+ r"scripts/bash/check-prerequisites\.sh": "spec-kitty agent check-prerequisites",
59
+ r"\.kittify/scripts/bash/setup-plan\.sh": "spec-kitty agent setup-plan",
60
+ r"scripts/bash/setup-plan\.sh": "spec-kitty agent setup-plan",
61
+ r"\.kittify/scripts/bash/update-agent-context\.sh": "spec-kitty agent update-context",
62
+ r"scripts/bash/update-agent-context\.sh": "spec-kitty agent update-context",
63
+ r"\.kittify/scripts/bash/accept-feature\.sh": "spec-kitty agent feature accept",
64
+ r"scripts/bash/accept-feature\.sh": "spec-kitty agent feature accept",
65
+ r"\.kittify/scripts/bash/merge-feature\.sh": "spec-kitty agent feature merge",
66
+ r"scripts/bash/merge-feature\.sh": "spec-kitty agent feature merge",
67
+
68
+ # Task workflow
69
+ r"\.kittify/scripts/bash/tasks-move-to-lane\.sh": "spec-kitty agent move-task",
70
+ r"scripts/bash/tasks-move-to-lane\.sh": "spec-kitty agent move-task",
71
+ r"\.kittify/scripts/bash/tasks-list-lanes\.sh": "spec-kitty agent list-tasks",
72
+ r"scripts/bash/tasks-list-lanes\.sh": "spec-kitty agent list-tasks",
73
+ r"\.kittify/scripts/bash/mark-task-status\.sh": "spec-kitty agent mark-status",
74
+ r"scripts/bash/mark-task-status\.sh": "spec-kitty agent mark-status",
75
+ r"\.kittify/scripts/bash/tasks-add-history-entry\.sh": "spec-kitty agent add-history",
76
+ r"scripts/bash/tasks-add-history-entry\.sh": "spec-kitty agent add-history",
77
+ r"\.kittify/scripts/bash/tasks-rollback-move\.sh": "spec-kitty agent rollback-move",
78
+ r"scripts/bash/tasks-rollback-move\.sh": "spec-kitty agent rollback-move",
79
+ r"\.kittify/scripts/bash/validate-task-workflow\.sh": "spec-kitty agent validate-workflow",
80
+ r"scripts/bash/validate-task-workflow\.sh": "spec-kitty agent validate-workflow",
81
+ r"\.kittify/scripts/bash/move-task-to-doing\.sh": "spec-kitty agent move-task",
82
+ r"scripts/bash/move-task-to-doing\.sh": "spec-kitty agent move-task",
83
+
84
+ # Legacy tasks_cli.py references
85
+ r"tasks_cli\.py move": "spec-kitty agent move-task",
86
+ r"tasks_cli\.py list": "spec-kitty agent list-tasks",
87
+ r"tasks_cli\.py mark": "spec-kitty agent mark-status",
88
+ r"tasks_cli\.py history": "spec-kitty agent add-history",
89
+ r"tasks_cli\.py rollback": "spec-kitty agent rollback-move",
90
+ r"tasks_cli\.py validate": "spec-kitty agent validate-workflow",
91
+ }
92
+
93
+ def detect(self, project_path: Path) -> bool:
94
+ """Check if bash scripts still exist in user's .kittify directory."""
95
+ kittify_bash = project_path / ".kittify" / "scripts" / "bash"
96
+
97
+ if not kittify_bash.exists():
98
+ return False
99
+
100
+ # Check if ANY .sh files exist (not just known scripts)
101
+ # This catches custom scripts and ensures complete cleanup
102
+ bash_scripts = list(kittify_bash.glob("*.sh"))
103
+ return len(bash_scripts) > 0
104
+
105
+ def can_apply(self, project_path: Path) -> tuple[bool, str]:
106
+ """Migration can always be applied if bash scripts are detected."""
107
+ return True, ""
108
+
109
+ def apply(self, project_path: Path, dry_run: bool = False) -> MigrationResult:
110
+ """Remove bash scripts and update templates."""
111
+ changes: List[str] = []
112
+ warnings: List[str] = []
113
+ errors: List[str] = []
114
+
115
+ # Step 1: Detect and remove bash scripts from .kittify
116
+ bash_changes, bash_warnings = self._remove_bash_scripts(
117
+ project_path, dry_run
118
+ )
119
+ changes.extend(bash_changes)
120
+ warnings.extend(bash_warnings)
121
+
122
+ # Step 2: Clean up bash scripts in worktrees
123
+ worktree_changes = self._cleanup_worktree_bash_scripts(
124
+ project_path, dry_run
125
+ )
126
+ changes.extend(worktree_changes)
127
+
128
+ # Step 2.5: Remove obsolete task helpers
129
+ tasks_changes, tasks_warnings = self._remove_tasks_helpers(
130
+ project_path, dry_run
131
+ )
132
+ changes.extend(tasks_changes)
133
+ warnings.extend(tasks_warnings)
134
+
135
+ # Step 3: Update slash command templates
136
+ template_changes, template_errors = self._update_command_templates(
137
+ project_path, dry_run
138
+ )
139
+ changes.extend(template_changes)
140
+ errors.extend(template_errors)
141
+
142
+ # Note: Custom script detection now happens in _remove_bash_scripts()
143
+ # before deletion, so users get warnings about custom scripts
144
+
145
+ success = len(errors) == 0
146
+ return MigrationResult(
147
+ success=success,
148
+ changes_made=changes,
149
+ errors=errors,
150
+ warnings=warnings,
151
+ )
152
+
153
+ def _remove_bash_scripts(
154
+ self, project_path: Path, dry_run: bool
155
+ ) -> Tuple[List[str], List[str]]:
156
+ """Remove bash scripts from .kittify/scripts/bash/."""
157
+ changes: List[str] = []
158
+ warnings: List[str] = []
159
+
160
+ kittify_bash = project_path / ".kittify" / "scripts" / "bash"
161
+
162
+ if not kittify_bash.exists():
163
+ warnings.append("No .kittify/scripts/bash/ directory found - already migrated?")
164
+ return changes, warnings
165
+
166
+ # First, detect custom scripts (not in PACKAGE_SCRIPTS) and warn user
167
+ all_bash_scripts = list(kittify_bash.glob("*.sh"))
168
+ custom_scripts = [s for s in all_bash_scripts if s.name not in self.PACKAGE_SCRIPTS]
169
+
170
+ if custom_scripts:
171
+ custom_names = [s.name for s in custom_scripts]
172
+ warnings.append(f"Custom bash scripts detected: {', '.join(custom_names)}")
173
+ warnings.append(
174
+ "These custom scripts will be removed as part of the migration to Python-only. "
175
+ "If you need this functionality, please migrate it manually before upgrading."
176
+ )
177
+
178
+ # Now delete ALL .sh files (including custom ones)
179
+ # This ensures complete cleanup, matching PowerShell/worktree behavior
180
+ scripts_removed = len(all_bash_scripts)
181
+
182
+ for script in all_bash_scripts:
183
+ if dry_run:
184
+ changes.append(f"Would remove: .kittify/scripts/bash/{script.name}")
185
+ else:
186
+ script.unlink()
187
+ changes.append(f"Removed: .kittify/scripts/bash/{script.name}")
188
+
189
+ # Remove PowerShell equivalents if they exist
190
+ kittify_ps = project_path / ".kittify" / "scripts" / "powershell"
191
+ if kittify_ps.exists():
192
+ ps_scripts = list(kittify_ps.glob("*.ps1"))
193
+ if ps_scripts:
194
+ for ps_script in ps_scripts:
195
+ if dry_run:
196
+ changes.append(f"Would remove: .kittify/scripts/powershell/{ps_script.name}")
197
+ else:
198
+ ps_script.unlink()
199
+ changes.append(f"Removed: .kittify/scripts/powershell/{ps_script.name}")
200
+ scripts_removed += 1
201
+
202
+ # Remove directories if empty
203
+ if not dry_run:
204
+ if kittify_bash.exists() and not any(kittify_bash.iterdir()):
205
+ kittify_bash.rmdir()
206
+ changes.append("Removed empty: .kittify/scripts/bash/")
207
+ if kittify_ps.exists() and not any(kittify_ps.iterdir()):
208
+ kittify_ps.rmdir()
209
+ changes.append("Removed empty: .kittify/scripts/powershell/")
210
+
211
+ if scripts_removed > 0:
212
+ changes.append(f"Total scripts removed: {scripts_removed}")
213
+ else:
214
+ warnings.append("No bash scripts found to remove - already migrated?")
215
+
216
+ return changes, warnings
217
+
218
+ def _cleanup_worktree_bash_scripts(
219
+ self, project_path: Path, dry_run: bool
220
+ ) -> List[str]:
221
+ """Remove bash scripts from all worktrees."""
222
+ changes: List[str] = []
223
+
224
+ worktrees_dir = project_path / ".worktrees"
225
+ if not worktrees_dir.exists():
226
+ return changes
227
+
228
+ for worktree in sorted(worktrees_dir.iterdir()):
229
+ if not worktree.is_dir():
230
+ continue
231
+
232
+ wt_bash = worktree / ".kittify" / "scripts" / "bash"
233
+ if not wt_bash.exists():
234
+ continue
235
+
236
+ scripts_found = list(wt_bash.glob("*.sh"))
237
+ if scripts_found:
238
+ if dry_run:
239
+ changes.append(f"Would remove {len(scripts_found)} scripts from worktree: {worktree.name}")
240
+ else:
241
+ for script in scripts_found:
242
+ script.unlink()
243
+ if not any(wt_bash.iterdir()):
244
+ wt_bash.rmdir()
245
+ changes.append(f"Removed {len(scripts_found)} scripts from worktree: {worktree.name}")
246
+
247
+ return changes
248
+
249
+ def _remove_tasks_helpers(
250
+ self, project_path: Path, dry_run: bool
251
+ ) -> Tuple[List[str], List[str]]:
252
+ """Remove obsolete .kittify/scripts/tasks/ directory."""
253
+ changes: List[str] = []
254
+ warnings: List[str] = []
255
+
256
+ tasks_dir = project_path / ".kittify" / "scripts" / "tasks"
257
+ if not tasks_dir.exists():
258
+ return changes, warnings
259
+
260
+ if dry_run:
261
+ changes.append("Would remove .kittify/scripts/tasks/ (obsolete task helpers)")
262
+ return changes, warnings
263
+
264
+ try:
265
+ shutil.rmtree(tasks_dir)
266
+ changes.append("Removed .kittify/scripts/tasks/ (obsolete task helpers)")
267
+ except OSError as e:
268
+ warnings.append(f"Failed to remove .kittify/scripts/tasks/: {e}")
269
+
270
+ return changes, warnings
271
+
272
+ def _update_command_templates(
273
+ self, project_path: Path, dry_run: bool
274
+ ) -> Tuple[List[str], List[str]]:
275
+ """Update slash command templates to use Python CLI."""
276
+ changes: List[str] = []
277
+ errors: List[str] = []
278
+
279
+ # Templates in .kittify/templates/command-templates/
280
+ templates_dir = project_path / ".kittify" / "templates" / "command-templates"
281
+
282
+ if not templates_dir.exists():
283
+ # Templates not in expected location - might be from old package install
284
+ # This is expected for projects initialized with older package versions
285
+ # Templates will be fixed when they upgrade to v0.10.9+ which has repair migration
286
+ changes.append(
287
+ "Templates directory not found at .kittify/templates/command-templates/. "
288
+ "This is expected for projects initialized with older package versions. "
289
+ "Run 'spec-kitty upgrade' after upgrading to v0.10.9+ to repair templates."
290
+ )
291
+ return changes, [] # No errors, defer to repair migration
292
+
293
+ templates_updated = 0
294
+ for template_path in sorted(templates_dir.glob("*.md")):
295
+ try:
296
+ updated, replacements = self._update_template_file(
297
+ template_path, dry_run
298
+ )
299
+ if updated:
300
+ templates_updated += 1
301
+ if dry_run:
302
+ changes.append(f"Would update: {template_path.name} ({replacements} replacements)")
303
+ else:
304
+ changes.append(f"Updated: {template_path.name} ({replacements} replacements)")
305
+ except Exception as e:
306
+ errors.append(f"Error updating {template_path.name}: {e}")
307
+
308
+ if templates_updated > 0:
309
+ changes.append(f"Total templates updated: {templates_updated}")
310
+
311
+ return changes, errors
312
+
313
+ def _update_template_file(
314
+ self, template_path: Path, dry_run: bool
315
+ ) -> Tuple[bool, int]:
316
+ """Update a single template file with bash → Python replacements."""
317
+ content = template_path.read_text(encoding="utf-8")
318
+ original_content = content
319
+ replacements_made = 0
320
+
321
+ # Apply all replacements
322
+ for pattern, replacement in self.COMMAND_REPLACEMENTS.items():
323
+ new_content, count = re.subn(pattern, replacement, content)
324
+ if count > 0:
325
+ content = new_content
326
+ replacements_made += count
327
+
328
+ # Write if changes were made
329
+ if content != original_content:
330
+ if not dry_run:
331
+ template_path.write_text(content, encoding="utf-8")
332
+ return True, replacements_made
333
+
334
+ return False, 0
335
+
336
+ def _detect_custom_modifications(self, project_path: Path) -> List[str]:
337
+ """Detect custom modifications to bash scripts."""
338
+ warnings: List[str] = []
339
+
340
+ kittify_bash = project_path / ".kittify" / "scripts" / "bash"
341
+ if not kittify_bash.exists():
342
+ return warnings
343
+
344
+ # Look for non-standard scripts (not in PACKAGE_SCRIPTS)
345
+ custom_scripts = []
346
+ for script_path in kittify_bash.glob("*.sh"):
347
+ if script_path.name not in self.PACKAGE_SCRIPTS:
348
+ custom_scripts.append(script_path.name)
349
+
350
+ if custom_scripts:
351
+ warnings.append(
352
+ f"Custom bash scripts detected: {', '.join(custom_scripts)}"
353
+ )
354
+ warnings.append(
355
+ "These scripts will NOT be removed automatically. "
356
+ "Please migrate them manually or remove if no longer needed."
357
+ )
358
+
359
+ return warnings
@@ -0,0 +1,99 @@
1
+ """Migration: Remove mission-specific constitution directories."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import shutil
6
+ from pathlib import Path
7
+ from typing import List
8
+
9
+ from ..registry import MigrationRegistry
10
+ from .base import BaseMigration, MigrationResult
11
+
12
+
13
+ @MigrationRegistry.register
14
+ class ConstitutionCleanupMigration(BaseMigration):
15
+ """Remove mission-specific constitution directories.
16
+
17
+ As of 0.10.12, spec-kitty uses only project-level constitutions
18
+ at .kittify/memory/constitution.md. Mission-specific constitutions
19
+ in .kittify/missions/*/constitution/ are removed.
20
+ """
21
+
22
+ migration_id = "0.10.12_constitution_cleanup"
23
+ description = "Remove mission-specific constitution directories"
24
+ target_version = "0.10.12"
25
+
26
+ def detect(self, project_path: Path) -> bool:
27
+ """Check if any mission has a constitution directory."""
28
+ missions_dir = project_path / ".kittify" / "missions"
29
+ if not missions_dir.exists():
30
+ return False
31
+
32
+ for mission_dir in missions_dir.iterdir():
33
+ if mission_dir.is_dir() and (mission_dir / "constitution").exists():
34
+ return True
35
+
36
+ return False
37
+
38
+ def can_apply(self, project_path: Path) -> tuple[bool, str]:
39
+ """Check if migration can be applied."""
40
+ kittify_dir = project_path / ".kittify"
41
+ if not kittify_dir.exists():
42
+ return False, "No .kittify directory (not a spec-kitty project)"
43
+
44
+ return True, ""
45
+
46
+ def apply(self, project_path: Path, dry_run: bool = False) -> MigrationResult:
47
+ """Remove constitution directories from all missions."""
48
+ changes: List[str] = []
49
+ warnings: List[str] = []
50
+ errors: List[str] = []
51
+
52
+ missions_dir = project_path / ".kittify" / "missions"
53
+ if not missions_dir.exists():
54
+ return MigrationResult(
55
+ success=True,
56
+ changes_made=[],
57
+ errors=[],
58
+ warnings=[],
59
+ )
60
+
61
+ removed_from: List[str] = []
62
+ for mission_dir in missions_dir.iterdir():
63
+ if not mission_dir.is_dir():
64
+ continue
65
+
66
+ constitution_dir = mission_dir / "constitution"
67
+ if not constitution_dir.exists():
68
+ continue
69
+
70
+ if dry_run:
71
+ changes.append(f"Would remove {mission_dir.name}/constitution/")
72
+ continue
73
+
74
+ try:
75
+ shutil.rmtree(constitution_dir)
76
+ changes.append(f"Removed {mission_dir.name}/constitution/")
77
+ removed_from.append(mission_dir.name)
78
+ except OSError as e:
79
+ errors.append(
80
+ f"Failed to remove {mission_dir.name}/constitution/: {e}"
81
+ )
82
+
83
+ if removed_from:
84
+ warnings.append(
85
+ "Mission-specific constitutions removed from: "
86
+ f"{', '.join(removed_from)}. "
87
+ "Spec-kitty now uses a single project-level constitution at "
88
+ ".kittify/memory/constitution.md."
89
+ )
90
+ elif not changes:
91
+ changes.append("No mission-specific constitutions found (already clean)")
92
+
93
+ success = len(errors) == 0
94
+ return MigrationResult(
95
+ success=success,
96
+ changes_made=changes,
97
+ errors=errors,
98
+ warnings=warnings,
99
+ )