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,286 @@
1
+ """
2
+ Manifest system for spec-kitty file verification.
3
+ This module generates and checks expected files based on the active mission.
4
+ """
5
+
6
+ from pathlib import Path
7
+ from typing import Dict, List, Optional, Set, Tuple
8
+ import yaml
9
+ import subprocess
10
+
11
+
12
+ class FileManifest:
13
+ """Manages the expected file manifest for spec-kitty missions."""
14
+
15
+ def __init__(self, kittify_dir: Path):
16
+ self.kittify_dir = kittify_dir
17
+ self.active_mission = self._detect_active_mission()
18
+ self.mission_dir = kittify_dir / "missions" / self.active_mission if self.active_mission else None
19
+
20
+ def _detect_active_mission(self) -> Optional[str]:
21
+ """Detect the active mission from the symlink or file."""
22
+ active_mission_path = self.kittify_dir / "active-mission"
23
+ if active_mission_path.exists():
24
+ if active_mission_path.is_symlink():
25
+ # It's a symlink, resolve it
26
+ target = active_mission_path.resolve()
27
+ return target.name
28
+ elif active_mission_path.is_file():
29
+ # It's a file with the mission name
30
+ return active_mission_path.read_text(encoding='utf-8-sig').strip()
31
+
32
+ # Default to software-dev if no active mission
33
+ return "software-dev"
34
+
35
+ def get_expected_files(self) -> Dict[str, List[str]]:
36
+ """
37
+ Get a categorized list of expected files for the active mission.
38
+
39
+ Returns:
40
+ Dict with categories as keys and file paths as values
41
+ """
42
+ if not self.mission_dir or not self.mission_dir.exists():
43
+ return {}
44
+
45
+ manifest = {
46
+ "commands": [],
47
+ "templates": [],
48
+ "scripts": [],
49
+ "mission_files": []
50
+ }
51
+
52
+ # Mission config file
53
+ mission_yaml = self.mission_dir / "mission.yaml"
54
+ if mission_yaml.exists():
55
+ manifest["mission_files"].append(str(mission_yaml.relative_to(self.kittify_dir)))
56
+
57
+ # Commands
58
+ commands_dir = self.mission_dir / "command-templates"
59
+ if commands_dir.exists():
60
+ for cmd_file in commands_dir.glob("*.md"):
61
+ manifest["commands"].append(str(cmd_file.relative_to(self.kittify_dir)))
62
+
63
+ # Templates
64
+ templates_dir = self.mission_dir / "templates"
65
+ if templates_dir.exists():
66
+ for tmpl_file in templates_dir.glob("*.md"):
67
+ manifest["templates"].append(str(tmpl_file.relative_to(self.kittify_dir)))
68
+
69
+ # Scripts referenced in commands
70
+ manifest["scripts"] = self._get_referenced_scripts()
71
+
72
+ return manifest
73
+
74
+ def _get_referenced_scripts(self) -> List[str]:
75
+ """Extract script references from command files, filtered by platform."""
76
+ import platform
77
+ scripts = set()
78
+
79
+ if not self.mission_dir:
80
+ return []
81
+
82
+ commands_dir = self.mission_dir / "command-templates"
83
+ if not commands_dir.exists():
84
+ return []
85
+
86
+ # Determine which script type to look for based on platform
87
+ is_windows = platform.system() == 'Windows'
88
+ script_key = 'ps:' if is_windows else 'sh:'
89
+
90
+ # Parse command files for script references
91
+ for cmd_file in commands_dir.glob("*.md"):
92
+ content = cmd_file.read_text(encoding='utf-8-sig')
93
+ lines = content.split('\n')
94
+
95
+ # Look for script references in YAML frontmatter
96
+ in_frontmatter = False
97
+ for line in lines:
98
+ if line.strip() == '---':
99
+ in_frontmatter = not in_frontmatter
100
+ if not in_frontmatter and in_frontmatter == False:
101
+ break # End of frontmatter
102
+ elif in_frontmatter:
103
+ # Only check for scripts relevant to this platform
104
+ if script_key in line:
105
+ # Extract script path
106
+ parts = line.split(':', 1)
107
+ if len(parts) == 2:
108
+ script_line = parts[1].strip().strip('"').strip("'")
109
+ # Extract just the script path, not the arguments
110
+ # Script path is the first part before any spaces or arguments
111
+ script_parts = script_line.split()
112
+ if script_parts:
113
+ script_path = script_parts[0]
114
+ # Only include actual .kittify/scripts/ files
115
+ # Skip CLI commands (spec-kitty, git, python, etc.)
116
+ if script_path.startswith('.kittify/scripts/'):
117
+ script_path = script_path.replace('.kittify/', '', 1)
118
+ scripts.add(script_path)
119
+
120
+ return sorted(list(scripts))
121
+
122
+ def check_files(self) -> Dict[str, Dict[str, str]]:
123
+ """
124
+ Check which expected files exist and which are missing.
125
+
126
+ Returns:
127
+ Dict with 'present', 'missing', and 'extra' keys
128
+ """
129
+ expected = self.get_expected_files()
130
+ result = {
131
+ "present": {},
132
+ "missing": {},
133
+ "modified": {},
134
+ "extra": []
135
+ }
136
+
137
+ # Check each category
138
+ for category, files in expected.items():
139
+ for file_path in files:
140
+ full_path = self.kittify_dir / file_path
141
+ if full_path.exists():
142
+ result["present"][file_path] = category
143
+ else:
144
+ result["missing"][file_path] = category
145
+
146
+ # TODO: Check for modifications using git or checksums
147
+ # TODO: Find extra files not in manifest
148
+
149
+ return result
150
+
151
+
152
+ class WorktreeStatus:
153
+ """Manages worktree and feature branch status."""
154
+
155
+ def __init__(self, repo_root: Path):
156
+ self.repo_root = repo_root
157
+
158
+ def get_all_features(self) -> List[str]:
159
+ """Get all feature branches and directories."""
160
+ features = set()
161
+
162
+ # Get features from branches
163
+ try:
164
+ result = subprocess.run(
165
+ ["git", "branch", "-a"],
166
+ cwd=self.repo_root,
167
+ capture_output=True,
168
+ text=True,
169
+ check=True
170
+ )
171
+ for line in result.stdout.split('\n'):
172
+ line = line.strip().replace('* ', '')
173
+ # Match feature branch pattern (###-name)
174
+ if line and not line.startswith('remotes/'):
175
+ parts = line.split('/')
176
+ branch = parts[-1]
177
+ if branch and branch[0].isdigit() and '-' in branch:
178
+ features.add(branch)
179
+ except subprocess.CalledProcessError:
180
+ pass
181
+
182
+ # Get features from kitty-specs
183
+ kitty_specs = self.repo_root / "kitty-specs"
184
+ if kitty_specs.exists():
185
+ for feature_dir in kitty_specs.iterdir():
186
+ if feature_dir.is_dir() and feature_dir.name[0].isdigit() and '-' in feature_dir.name:
187
+ features.add(feature_dir.name)
188
+
189
+ return sorted(list(features))
190
+
191
+ def get_feature_status(self, feature: str) -> Dict[str, any]:
192
+ """Get comprehensive status for a feature."""
193
+ status = {
194
+ "name": feature,
195
+ "branch_exists": False,
196
+ "branch_merged": False,
197
+ "worktree_exists": False,
198
+ "worktree_path": None,
199
+ "artifacts_in_main": [],
200
+ "artifacts_in_worktree": [],
201
+ "last_activity": None,
202
+ "state": "unknown" # not_started, in_development, ready_to_merge, merged, abandoned
203
+ }
204
+
205
+ # Check if branch exists
206
+ try:
207
+ result = subprocess.run(
208
+ ["git", "show-ref", f"refs/heads/{feature}"],
209
+ cwd=self.repo_root,
210
+ capture_output=True,
211
+ text=True
212
+ )
213
+ status["branch_exists"] = result.returncode == 0
214
+ except subprocess.CalledProcessError:
215
+ pass
216
+
217
+ # Check if merged
218
+ if status["branch_exists"]:
219
+ try:
220
+ result = subprocess.run(
221
+ ["git", "branch", "--merged", "main"],
222
+ cwd=self.repo_root,
223
+ capture_output=True,
224
+ text=True,
225
+ check=True
226
+ )
227
+ status["branch_merged"] = feature in result.stdout
228
+ except subprocess.CalledProcessError:
229
+ pass
230
+
231
+ # Check worktree
232
+ worktree_path = self.repo_root / ".worktrees" / feature
233
+ if worktree_path.exists():
234
+ status["worktree_exists"] = True
235
+ status["worktree_path"] = str(worktree_path)
236
+
237
+ # Check artifacts in main
238
+ main_artifacts_path = self.repo_root / "kitty-specs" / feature
239
+ if main_artifacts_path.exists():
240
+ for artifact in main_artifacts_path.glob("*.md"):
241
+ status["artifacts_in_main"].append(artifact.name)
242
+
243
+ # Check artifacts in worktree
244
+ if status["worktree_exists"]:
245
+ worktree_artifacts_path = worktree_path / "kitty-specs" / feature
246
+ if worktree_artifacts_path.exists():
247
+ for artifact in worktree_artifacts_path.glob("*.md"):
248
+ status["artifacts_in_worktree"].append(artifact.name)
249
+
250
+ # Determine state
251
+ if not status["branch_exists"] and not status["artifacts_in_main"]:
252
+ status["state"] = "not_started"
253
+ elif status["branch_merged"] and status["artifacts_in_main"]:
254
+ status["state"] = "merged"
255
+ elif status["worktree_exists"] or status["artifacts_in_worktree"]:
256
+ status["state"] = "in_development"
257
+ elif status["branch_exists"] and not status["worktree_exists"]:
258
+ status["state"] = "ready_to_merge"
259
+ elif not status["branch_exists"] and status["artifacts_in_main"]:
260
+ status["state"] = "merged" # Branch was deleted after merge
261
+
262
+ return status
263
+
264
+ def get_worktree_summary(self) -> Dict[str, int]:
265
+ """Get summary counts of worktree states."""
266
+ features = self.get_all_features()
267
+ summary = {
268
+ "total_features": len(features),
269
+ "active_worktrees": 0,
270
+ "merged_features": 0,
271
+ "in_development": 0,
272
+ "not_started": 0
273
+ }
274
+
275
+ for feature in features:
276
+ status = self.get_feature_status(feature)
277
+ if status["worktree_exists"]:
278
+ summary["active_worktrees"] += 1
279
+ if status["state"] == "merged":
280
+ summary["merged_features"] += 1
281
+ elif status["state"] == "in_development":
282
+ summary["in_development"] += 1
283
+ elif status["state"] == "not_started":
284
+ summary["not_started"] += 1
285
+
286
+ return summary
@@ -0,0 +1,63 @@
1
+ """Merge subpackage for spec-kitty merge operations.
2
+
3
+ This package provides functionality for merging work package branches
4
+ back into the main branch with pre-flight validation, conflict forecasting,
5
+ and automatic status file resolution.
6
+
7
+ Modules:
8
+ preflight: Pre-flight validation before merge
9
+ forecast: Conflict prediction for dry-run mode
10
+ ordering: Dependency-based merge ordering
11
+ status_resolver: Auto-resolution of status file conflicts
12
+ state: Merge state persistence and resume
13
+ executor: Core merge execution logic
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from specify_cli.merge.executor import (
19
+ MergeExecutionError,
20
+ MergeResult,
21
+ execute_legacy_merge,
22
+ execute_merge,
23
+ )
24
+ from specify_cli.merge.forecast import ConflictPrediction, is_status_file, predict_conflicts
25
+ from specify_cli.merge.ordering import MergeOrderError, get_merge_order, has_dependency_info
26
+ from specify_cli.merge.preflight import PreflightResult, WPStatus, run_preflight
27
+ from specify_cli.merge.state import (
28
+ MergeState,
29
+ clear_state,
30
+ has_active_merge,
31
+ load_state,
32
+ save_state,
33
+ )
34
+ from specify_cli.merge.status_resolver import ResolutionResult, resolve_status_conflicts
35
+
36
+ __all__ = [
37
+ # Executor
38
+ "execute_merge",
39
+ "execute_legacy_merge",
40
+ "MergeResult",
41
+ "MergeExecutionError",
42
+ # Forecast
43
+ "predict_conflicts",
44
+ "ConflictPrediction",
45
+ "is_status_file",
46
+ # Ordering
47
+ "get_merge_order",
48
+ "MergeOrderError",
49
+ "has_dependency_info",
50
+ # Preflight
51
+ "run_preflight",
52
+ "PreflightResult",
53
+ "WPStatus",
54
+ # Status resolver
55
+ "resolve_status_conflicts",
56
+ "ResolutionResult",
57
+ # State persistence
58
+ "MergeState",
59
+ "save_state",
60
+ "load_state",
61
+ "clear_state",
62
+ "has_active_merge",
63
+ ]