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,174 @@
1
+ """Migration: Simplify implement and review templates to use workflow commands."""
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
+ from .m_0_9_1_complete_lane_migration import get_agent_dirs_for_project
12
+
13
+
14
+ @MigrationRegistry.register
15
+ class WorkflowSimplificationMigration(BaseMigration):
16
+ """Update implement and review slash commands to use new workflow commands.
17
+
18
+ This migration simplifies the agent workflow by:
19
+ 1. Replacing complex implement.md template (78 lines) with minimal version (9 lines)
20
+ 2. Replacing complex review.md template (72 lines) with minimal version (9 lines)
21
+ 3. Templates now call `spec-kitty agent workflow implement/review` which:
22
+ - Displays the full WP prompt directly to the agent
23
+ - Shows clear "when done" instructions
24
+ - No more file navigation or path confusion
25
+ """
26
+
27
+ migration_id = "0.10.6_workflow_simplification"
28
+ description = "Simplify implement and review templates to use workflow commands"
29
+ target_version = "0.10.6"
30
+
31
+ def detect(self, project_path: Path) -> bool:
32
+ """Check if slash commands need updating to workflow commands."""
33
+ # Check configured agent directories for old complex templates
34
+ agent_dirs = get_agent_dirs_for_project(project_path)
35
+
36
+ for agent_root, subdir in agent_dirs:
37
+ agent_dir = project_path / agent_root / subdir
38
+
39
+ if not agent_dir.exists():
40
+ continue
41
+
42
+ # Check implement.md for old structure (looking for complex instructions)
43
+ implement_file = agent_dir / "spec-kitty.implement.md"
44
+ if implement_file.exists():
45
+ content = implement_file.read_text(encoding="utf-8")
46
+ # Old template has "Work Package Selection" or "Setup (Do This First)"
47
+ if "Work Package Selection" in content or "Setup (Do This First)" in content:
48
+ return True
49
+ # Or doesn't have the new workflow command
50
+ if "spec-kitty agent workflow implement" not in content:
51
+ return True
52
+
53
+ # Check review.md for old structure
54
+ review_file = agent_dir / "spec-kitty.review.md"
55
+ if review_file.exists():
56
+ content = review_file.read_text(encoding="utf-8")
57
+ # Old template has complex outline
58
+ if "Location Pre-flight Check" in content or "Conduct the review:" in content:
59
+ return True
60
+ # Or doesn't have the new workflow command
61
+ if "spec-kitty agent workflow review" not in content:
62
+ return True
63
+
64
+ return False
65
+
66
+ def can_apply(self, project_path: Path) -> tuple[bool, str]:
67
+ """Check if we can apply this migration."""
68
+ kittify_dir = project_path / ".kittify"
69
+ if not kittify_dir.exists():
70
+ return False, "No .kittify directory (not a spec-kitty project)"
71
+
72
+ return True, ""
73
+
74
+ def apply(self, project_path: Path, dry_run: bool = False) -> MigrationResult:
75
+ """Update implement and review slash commands with new workflow-based templates."""
76
+ changes: List[str] = []
77
+ warnings: List[str] = []
78
+ errors: List[str] = []
79
+
80
+ missions_dir = project_path / ".kittify" / "missions"
81
+ software_dev_templates = missions_dir / "software-dev" / "command-templates"
82
+
83
+ # Copy updated mission templates from package first (if available)
84
+ try:
85
+ import specify_cli
86
+ except ImportError as exc:
87
+ errors.append(f"Failed to import specify_cli: {exc}")
88
+ return MigrationResult(
89
+ success=False,
90
+ changes_made=changes,
91
+ errors=errors,
92
+ warnings=warnings,
93
+ )
94
+
95
+ pkg_root = Path(specify_cli.__file__).parent
96
+ pkg_templates = pkg_root / "missions" / "software-dev" / "command-templates"
97
+ if not pkg_templates.exists():
98
+ pkg_templates = pkg_root / ".kittify" / "missions" / "software-dev" / "command-templates"
99
+
100
+ if pkg_templates.exists():
101
+ if not dry_run:
102
+ software_dev_templates.mkdir(parents=True, exist_ok=True)
103
+ for template_name in ("implement.md", "review.md"):
104
+ src = pkg_templates / template_name
105
+ if not src.exists():
106
+ warnings.append(f"Package template missing: {template_name}")
107
+ continue
108
+ if dry_run:
109
+ changes.append(f"Would update mission template: software-dev/{template_name}")
110
+ else:
111
+ try:
112
+ shutil.copy2(src, software_dev_templates / template_name)
113
+ changes.append(f"Updated mission template: software-dev/{template_name}")
114
+ except OSError as e:
115
+ warnings.append(f"Failed to copy mission template {template_name}: {e}")
116
+ else:
117
+ warnings.append(
118
+ "Mission templates not found in package. "
119
+ "Slash commands may already be updated or require manual repair."
120
+ )
121
+
122
+ # Update implement.md and review.md in ALL agent directories
123
+ templates_to_update = ["implement.md", "review.md"]
124
+ total_updated = 0
125
+
126
+ agent_dirs = get_agent_dirs_for_project(project_path)
127
+ for agent_root, subdir in agent_dirs:
128
+ agent_dir = project_path / agent_root / subdir
129
+
130
+ if not agent_dir.exists():
131
+ continue
132
+
133
+ updated_count = 0
134
+ for template_name in templates_to_update:
135
+ source_template = software_dev_templates / template_name
136
+ if not source_template.exists():
137
+ continue
138
+
139
+ dest_filename = f"spec-kitty.{template_name}"
140
+ dest_path = agent_dir / dest_filename
141
+
142
+ if dry_run:
143
+ changes.append(f"Would update {agent_root}: {dest_filename}")
144
+ else:
145
+ try:
146
+ dest_path.write_text(
147
+ source_template.read_text(encoding="utf-8"),
148
+ encoding="utf-8",
149
+ )
150
+ updated_count += 1
151
+ except OSError as e:
152
+ warnings.append(f"Failed to update {agent_root}/{dest_filename}: {e}")
153
+
154
+ if updated_count > 0:
155
+ agent_name = agent_root.strip(".")
156
+ changes.append(f"Updated {updated_count} templates for {agent_name}")
157
+ total_updated += updated_count
158
+
159
+ if total_updated > 0:
160
+ changes.append(f"Total: Updated {total_updated} slash command templates")
161
+ changes.append("Templates now use 'spec-kitty agent workflow' commands")
162
+ changes.append("Agents now see prompts directly, no file navigation needed")
163
+ elif not changes:
164
+ warnings.append(
165
+ "No templates were updated (already updated or mission templates missing)"
166
+ )
167
+
168
+ success = len(errors) == 0
169
+ return MigrationResult(
170
+ success=success,
171
+ changes_made=changes,
172
+ errors=errors,
173
+ warnings=warnings,
174
+ )
@@ -0,0 +1,252 @@
1
+ """Migration: Fix memory/ and AGENTS.md structure - move from root to .kittify/."""
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 FixMemoryStructureMigration(BaseMigration):
15
+ """Move memory/ directory and AGENTS.md from root to .kittify/.
16
+
17
+ Historical context: When spec-kitty was developed in a worktree, symlinks
18
+ were created (.kittify/memory -> ../../../.kittify/memory) that became
19
+ circular when merged to main. All code expects files in .kittify/ but some
20
+ user projects may have them at root level.
21
+
22
+ This migration:
23
+ 1. Moves memory/ from root to .kittify/memory/ (if needed)
24
+ 2. Removes broken .kittify/memory symlink (if exists)
25
+ 3. Ensures .kittify/AGENTS.md exists (not symlink)
26
+ 4. Updates worktrees to use proper .kittify/ paths
27
+ """
28
+
29
+ migration_id = "0.10.8_fix_memory_structure"
30
+ description = "Move memory/ and AGENTS.md from root to .kittify/"
31
+ target_version = "0.10.8"
32
+
33
+ def detect(self, project_path: Path) -> bool:
34
+ """Check if project has broken memory structure."""
35
+ # Check for root-level memory/ directory
36
+ root_memory = project_path / "memory"
37
+ kittify_memory = project_path / ".kittify" / "memory"
38
+
39
+ # If root memory exists and .kittify/memory doesn't (or is a broken symlink)
40
+ if root_memory.exists() and root_memory.is_dir():
41
+ if not kittify_memory.exists():
42
+ return True
43
+ if kittify_memory.is_symlink() and not kittify_memory.resolve().exists():
44
+ return True # Broken symlink
45
+
46
+ # Check for broken .kittify/memory symlink
47
+ if kittify_memory.is_symlink():
48
+ try:
49
+ # Try to resolve - if it points to itself or doesn't exist, it's broken
50
+ resolved = kittify_memory.resolve()
51
+ if not resolved.exists() or resolved == kittify_memory:
52
+ return True
53
+ except (OSError, RuntimeError):
54
+ return True # Circular symlink or resolution error
55
+
56
+ # Check for broken .kittify/AGENTS.md symlink
57
+ kittify_agents = project_path / ".kittify" / "AGENTS.md"
58
+ if kittify_agents.is_symlink():
59
+ try:
60
+ resolved = kittify_agents.resolve()
61
+ if not resolved.exists() or resolved == kittify_agents:
62
+ return True
63
+ except (OSError, RuntimeError):
64
+ return True
65
+
66
+ return False
67
+
68
+ def can_apply(self, project_path: Path) -> tuple[bool, str]:
69
+ """Check if migration can be safely applied."""
70
+ root_memory = project_path / "memory"
71
+ kittify_dir = project_path / ".kittify"
72
+
73
+ # .kittify must exist
74
+ if not kittify_dir.exists():
75
+ return False, ".kittify directory must exist before migration"
76
+
77
+ # If root memory exists, we can migrate it
78
+ if root_memory.exists() and root_memory.is_dir():
79
+ return True, "Ready to migrate memory/ to .kittify/"
80
+
81
+ # If broken symlinks exist, we can fix them
82
+ kittify_memory = kittify_dir / "memory"
83
+ kittify_agents = kittify_dir / "AGENTS.md"
84
+
85
+ if kittify_memory.is_symlink() or kittify_agents.is_symlink():
86
+ return True, "Ready to fix broken symlinks"
87
+
88
+ return True, "Ready to apply"
89
+
90
+ def apply(self, project_path: Path, *, dry_run: bool = False) -> MigrationResult:
91
+ """Move memory/ and fix broken symlinks."""
92
+ warnings: List[str] = []
93
+ changes_made: List[str] = []
94
+
95
+ root_memory = project_path / "memory"
96
+ kittify_dir = project_path / ".kittify"
97
+ kittify_memory = kittify_dir / "memory"
98
+ kittify_agents = kittify_dir / "AGENTS.md"
99
+ templates_agents = kittify_dir / "templates" / "AGENTS.md"
100
+
101
+ # Step 1: Fix .kittify/memory
102
+ if kittify_memory.exists():
103
+ if kittify_memory.is_symlink():
104
+ # Remove broken symlink
105
+ if dry_run:
106
+ changes_made.append(f"Would remove broken symlink: {kittify_memory}")
107
+ else:
108
+ kittify_memory.unlink()
109
+ changes_made.append(f"Removed broken symlink: {kittify_memory}")
110
+
111
+ # Step 2: Move or copy root memory/ to .kittify/memory/
112
+ if root_memory.exists() and root_memory.is_dir():
113
+ if not kittify_memory.exists():
114
+ if dry_run:
115
+ changes_made.append(f"Would move {root_memory} -> {kittify_memory}")
116
+ else:
117
+ try:
118
+ # Move the directory
119
+ shutil.move(str(root_memory), str(kittify_memory))
120
+ changes_made.append(f"Moved {root_memory} -> {kittify_memory}")
121
+ except Exception as e:
122
+ # If move fails, try copy
123
+ try:
124
+ shutil.copytree(root_memory, kittify_memory)
125
+ changes_made.append(f"Copied {root_memory} -> {kittify_memory} (move failed: {e})")
126
+ warnings.append(f"Could not move (copied instead): {e}")
127
+ except Exception as copy_error:
128
+ return MigrationResult(
129
+ success=False,
130
+ changes_made=changes_made,
131
+ warnings=warnings,
132
+ errors=[f"Failed to move or copy memory/: {copy_error}"]
133
+ )
134
+ else:
135
+ warnings.append(f"{kittify_memory} already exists, skipping root memory/ migration")
136
+
137
+ # Step 3: Create .kittify/memory/ from template if missing
138
+ if not kittify_memory.exists():
139
+ # Check if there's a template in missions
140
+ template_constitution = None
141
+ missions_dir = kittify_dir / "missions" / "software-dev"
142
+ if missions_dir.exists():
143
+ # Look for constitution template in command templates
144
+ template_path = missions_dir / "command-templates" / "constitution.md"
145
+ if not template_path.exists():
146
+ template_path = kittify_dir / "templates" / "command-templates" / "constitution.md"
147
+ if template_path.exists():
148
+ template_constitution = template_path
149
+
150
+ if template_constitution:
151
+ if dry_run:
152
+ changes_made.append(f"Would create {kittify_memory} from template")
153
+ else:
154
+ kittify_memory.mkdir(parents=True, exist_ok=True)
155
+ constitution_dest = kittify_memory / "constitution.md"
156
+ shutil.copy2(template_constitution, constitution_dest)
157
+ changes_made.append(f"Created {kittify_memory} from template")
158
+ else:
159
+ warnings.append(f"{kittify_memory} doesn't exist and no template found")
160
+
161
+ # Step 4: Fix .kittify/AGENTS.md
162
+ if kittify_agents.exists() and kittify_agents.is_symlink():
163
+ # Remove broken symlink
164
+ if dry_run:
165
+ changes_made.append(f"Would remove broken symlink: {kittify_agents}")
166
+ else:
167
+ kittify_agents.unlink()
168
+ changes_made.append(f"Removed broken symlink: {kittify_agents}")
169
+
170
+ # Step 5: Create .kittify/AGENTS.md from template if missing
171
+ if not kittify_agents.exists() or kittify_agents.is_symlink():
172
+ if templates_agents.exists():
173
+ if dry_run:
174
+ changes_made.append(f"Would copy {templates_agents} -> {kittify_agents}")
175
+ else:
176
+ shutil.copy2(templates_agents, kittify_agents)
177
+ changes_made.append(f"Copied {templates_agents} -> {kittify_agents}")
178
+ else:
179
+ warnings.append(f"No AGENTS.md template found at {templates_agents}")
180
+
181
+ # Step 6: Update worktrees if they exist
182
+ worktrees_dir = project_path / ".worktrees"
183
+ if worktrees_dir.exists():
184
+ for worktree_path in worktrees_dir.iterdir():
185
+ if not worktree_path.is_dir():
186
+ continue
187
+
188
+ wt_kittify = worktree_path / ".kittify"
189
+ if not wt_kittify.exists():
190
+ continue
191
+
192
+ wt_memory = wt_kittify / "memory"
193
+ wt_agents = wt_kittify / "AGENTS.md"
194
+
195
+ # Remove broken symlinks in worktrees
196
+ if wt_memory.is_symlink():
197
+ try:
198
+ resolved = wt_memory.resolve()
199
+ if not resolved.exists() or resolved == wt_memory:
200
+ if dry_run:
201
+ changes_made.append(f"Would remove broken worktree symlink: {wt_memory}")
202
+ else:
203
+ wt_memory.unlink()
204
+ changes_made.append(f"Removed broken worktree symlink: {wt_memory}")
205
+ except (OSError, RuntimeError):
206
+ if not dry_run:
207
+ wt_memory.unlink()
208
+ changes_made.append(f"Removed broken worktree symlink: {wt_memory}")
209
+
210
+ # Recreate worktree symlink to point to main repo's .kittify/memory
211
+ if not wt_memory.exists() and kittify_memory.exists():
212
+ relative_path = Path("../../../.kittify/memory")
213
+ if dry_run:
214
+ changes_made.append(f"Would create worktree symlink: {wt_memory} -> {relative_path}")
215
+ else:
216
+ try:
217
+ wt_memory.symlink_to(relative_path, target_is_directory=True)
218
+ changes_made.append(f"Created worktree symlink: {wt_memory} -> {relative_path}")
219
+ except OSError:
220
+ # Fallback: copy instead of symlink
221
+ shutil.copytree(kittify_memory, wt_memory)
222
+ changes_made.append(f"Copied to worktree (symlink failed): {wt_memory}")
223
+
224
+ # Fix AGENTS.md in worktree
225
+ if wt_agents.is_symlink():
226
+ try:
227
+ resolved = wt_agents.resolve()
228
+ if not resolved.exists() or resolved == wt_agents:
229
+ if not dry_run:
230
+ wt_agents.unlink()
231
+ except (OSError, RuntimeError):
232
+ if not dry_run:
233
+ wt_agents.unlink()
234
+
235
+ if not wt_agents.exists() and kittify_agents.exists():
236
+ relative_path = Path("../../../.kittify/AGENTS.md")
237
+ if dry_run:
238
+ changes_made.append(f"Would create worktree symlink: {wt_agents} -> {relative_path}")
239
+ else:
240
+ try:
241
+ wt_agents.symlink_to(relative_path)
242
+ changes_made.append(f"Created worktree symlink: {wt_agents} -> {relative_path}")
243
+ except OSError:
244
+ shutil.copy2(kittify_agents, wt_agents)
245
+ changes_made.append(f"Copied to worktree (symlink failed): {wt_agents}")
246
+
247
+ return MigrationResult(
248
+ success=True,
249
+ changes_made=changes_made,
250
+ warnings=warnings,
251
+ errors=[]
252
+ )
@@ -0,0 +1,168 @@
1
+ """Migration: Repair broken templates for users affected by #62, #63, #64."""
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 RepairTemplatesMigration(BaseMigration):
15
+ """Repair templates for projects with broken bash script references.
16
+
17
+ This migration addresses issues #62, #63, #64 where PyPI installations
18
+ received outdated templates with bash script references. It detects
19
+ broken templates and regenerates them from the correct source.
20
+ """
21
+
22
+ migration_id = "0.10.9_repair_templates"
23
+ description = "Repair broken templates with bash script references"
24
+ target_version = "0.10.9"
25
+
26
+ def detect(self, project_path: Path) -> bool:
27
+ """Detect if project has broken templates with bash script references."""
28
+ # Check all agent directories for broken slash commands
29
+ agent_dirs = [
30
+ (".claude", "commands"),
31
+ (".github", "prompts"),
32
+ (".gemini", "commands"),
33
+ (".cursor", "commands"),
34
+ (".qwen", "commands"),
35
+ (".opencode", "command"),
36
+ (".windsurf", "workflows"),
37
+ (".codex", "prompts"),
38
+ (".kilocode", "workflows"),
39
+ (".augment", "commands"),
40
+ (".roo", "commands"),
41
+ (".amazonq", "prompts"),
42
+ ]
43
+
44
+ for agent_dir, subdir in agent_dirs:
45
+ commands_dir = project_path / agent_dir / subdir
46
+ if not commands_dir.exists():
47
+ continue
48
+
49
+ # Check for bash script references in any command file
50
+ for cmd_file in commands_dir.glob("spec-kitty.*.md"):
51
+ try:
52
+ content = cmd_file.read_text(encoding="utf-8")
53
+ if "scripts/bash/" in content or "scripts/powershell/" in content:
54
+ return True # Found broken template
55
+ except Exception:
56
+ # Skip files we can't read
57
+ continue
58
+
59
+ return False
60
+
61
+ def can_apply(self, project_path: Path) -> tuple[bool, str]:
62
+ """Migration can always be applied if broken templates detected."""
63
+ return True, ""
64
+
65
+ def apply(self, project_path: Path, dry_run: bool = False) -> MigrationResult:
66
+ """Regenerate templates from correct source."""
67
+ changes: List[str] = []
68
+ errors: List[str] = []
69
+ warnings: List[str] = []
70
+
71
+ # Step 1: Remove broken templates from .kittify/templates/
72
+ kittify_templates = project_path / ".kittify" / "templates"
73
+ if kittify_templates.exists():
74
+ if not dry_run:
75
+ shutil.rmtree(kittify_templates)
76
+ changes.append("Removed broken templates from .kittify/templates/")
77
+ else:
78
+ changes.append("Would remove broken templates from .kittify/templates/")
79
+
80
+ # Step 2: Copy correct templates from package
81
+ try:
82
+ # Import here to avoid circular dependencies
83
+ from specify_cli.template.manager import (
84
+ get_local_repo_root,
85
+ copy_specify_base_from_local,
86
+ copy_specify_base_from_package,
87
+ )
88
+
89
+ local_repo = get_local_repo_root()
90
+ command_templates_dir = None
91
+
92
+ if local_repo:
93
+ # For local dev, get templates from .kittify/templates/
94
+ if not dry_run:
95
+ command_templates_dir = copy_specify_base_from_local(
96
+ local_repo, project_path, "sh"
97
+ )
98
+ changes.append("Copied correct templates from local repo")
99
+ else:
100
+ changes.append("Would copy correct templates from local repo")
101
+ else:
102
+ # For package install, use bundled templates (now fixed)
103
+ if not dry_run:
104
+ command_templates_dir = copy_specify_base_from_package(
105
+ project_path, "sh"
106
+ )
107
+ changes.append("Copied correct templates from package")
108
+ else:
109
+ changes.append("Would copy correct templates from package")
110
+
111
+ # Step 3: Regenerate all agent slash commands
112
+ if not dry_run and command_templates_dir:
113
+ # Import here to avoid circular dependencies
114
+ from specify_cli.cli.commands.init import generate_agent_assets
115
+ import yaml
116
+
117
+ # Get AI configuration from metadata
118
+ metadata_file = project_path / ".kittify" / "metadata.yaml"
119
+ ai_config = "claude" # default
120
+ if metadata_file.exists():
121
+ try:
122
+ with open(metadata_file, encoding="utf-8") as f:
123
+ metadata = yaml.safe_load(f)
124
+ ai_config = metadata.get("ai", "claude")
125
+ except Exception:
126
+ # Use default if we can't read metadata
127
+ pass
128
+
129
+ # Regenerate commands
130
+ try:
131
+ generate_agent_assets(
132
+ command_templates_dir=command_templates_dir,
133
+ project_path=project_path,
134
+ agent_key=ai_config,
135
+ script_type="sh"
136
+ )
137
+ changes.append("Regenerated all agent slash commands")
138
+ except Exception as e:
139
+ errors.append(f"Failed to regenerate agent commands: {e}")
140
+
141
+ # Cleanup temporary templates
142
+ if kittify_templates.exists():
143
+ shutil.rmtree(kittify_templates)
144
+ changes.append("Cleaned up temporary templates")
145
+ else:
146
+ changes.append("Would regenerate all agent slash commands")
147
+
148
+ except Exception as e:
149
+ errors.append(f"Failed to repair templates: {e}")
150
+
151
+ # Step 4: Verify repair
152
+ if not dry_run and len(errors) == 0:
153
+ still_broken = self.detect(project_path)
154
+ if still_broken:
155
+ warnings.append(
156
+ "Some bash script references may still remain. "
157
+ "Please run 'spec-kitty upgrade' again or report an issue."
158
+ )
159
+ else:
160
+ changes.append("✓ Templates successfully repaired - no bash script references found")
161
+
162
+ success = len(errors) == 0
163
+ return MigrationResult(
164
+ success=success,
165
+ changes_made=changes,
166
+ errors=errors,
167
+ warnings=warnings,
168
+ )