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,621 @@
1
+ """Documentation generator integration for spec-kitty.
2
+
3
+ This module provides a protocol-based interface for integrating documentation
4
+ generators (JSDoc, Sphinx, rustdoc) into the documentation mission workflow.
5
+
6
+ Generators detect project languages, create configuration files, and invoke
7
+ generator tools via subprocess to produce API reference documentation.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import subprocess
13
+ from dataclasses import dataclass, field
14
+ from pathlib import Path
15
+ from typing import Any, Dict, List, Optional, Protocol
16
+
17
+
18
+ class DocGenerator(Protocol):
19
+ """Protocol for documentation generators.
20
+
21
+ All generators (JSDoc, Sphinx, rustdoc) implement this interface to provide
22
+ consistent detection, configuration, and generation capabilities.
23
+ """
24
+
25
+ name: str # Generator identifier (e.g., "jsdoc", "sphinx", "rustdoc")
26
+ languages: List[str] # Supported languages (e.g., ["javascript", "typescript"])
27
+
28
+ def detect(self, project_root: Path) -> bool:
29
+ """Detect if this generator is applicable to the project.
30
+
31
+ Args:
32
+ project_root: Root directory of the project to check
33
+
34
+ Returns:
35
+ True if generator should be used for this project, False otherwise
36
+ """
37
+ ...
38
+
39
+ def configure(self, output_dir: Path, options: Dict[str, Any]) -> Path:
40
+ """Generate configuration file for this generator.
41
+
42
+ Args:
43
+ output_dir: Directory where config file should be created
44
+ options: Generator-specific options (project_name, author, etc.)
45
+
46
+ Returns:
47
+ Path to the generated configuration file
48
+
49
+ Raises:
50
+ GeneratorError: If configuration generation fails
51
+ """
52
+ ...
53
+
54
+ def generate(self, source_dir: Path, output_dir: Path) -> 'GeneratorResult':
55
+ """Run generator to produce documentation.
56
+
57
+ Args:
58
+ source_dir: Directory containing source code to document
59
+ output_dir: Directory where documentation should be generated
60
+
61
+ Returns:
62
+ GeneratorResult with success status, errors, warnings, generated files
63
+
64
+ Raises:
65
+ GeneratorError: If generator invocation fails catastrophically
66
+ """
67
+ ...
68
+
69
+
70
+ class GeneratorError(Exception):
71
+ """Raised when a generator encounters an unrecoverable error."""
72
+ pass
73
+
74
+
75
+ def check_tool_available(tool_name: str, install_url: str) -> bool:
76
+ """Check if a command-line tool is available.
77
+
78
+ Args:
79
+ tool_name: Name of the tool to check (e.g., "npx", "sphinx-build", "cargo")
80
+ install_url: URL where tool can be downloaded
81
+
82
+ Returns:
83
+ True if tool is available, False otherwise
84
+
85
+ Raises:
86
+ GeneratorError: If tool is not available (with installation instructions)
87
+ """
88
+ check = subprocess.run(
89
+ [tool_name, "--version"],
90
+ capture_output=True,
91
+ text=True
92
+ )
93
+
94
+ if check.returncode != 0:
95
+ raise GeneratorError(
96
+ f"{tool_name} not found - install required tool\n"
97
+ f"Visit: {install_url}"
98
+ )
99
+
100
+ return True
101
+
102
+
103
+ @dataclass
104
+ class GeneratorResult:
105
+ """Result of running a documentation generator.
106
+
107
+ Attributes:
108
+ success: True if generation completed without errors
109
+ output_dir: Directory where documentation was generated
110
+ errors: List of error messages (empty if success=True)
111
+ warnings: List of warning messages (may be present even if success=True)
112
+ generated_files: List of generated documentation files
113
+ """
114
+ success: bool
115
+ output_dir: Path
116
+ errors: List[str] = field(default_factory=list)
117
+ warnings: List[str] = field(default_factory=list)
118
+ generated_files: List[Path] = field(default_factory=list)
119
+
120
+ def __repr__(self) -> str:
121
+ """Human-readable representation."""
122
+ status = "✓ Success" if self.success else "✗ Failed"
123
+ file_count = len(self.generated_files)
124
+ error_count = len(self.errors)
125
+ warning_count = len(self.warnings)
126
+ return (
127
+ f"GeneratorResult({status}, "
128
+ f"{file_count} files, "
129
+ f"{error_count} errors, "
130
+ f"{warning_count} warnings)"
131
+ )
132
+
133
+
134
+ @dataclass
135
+ class JSDocGenerator:
136
+ """JSDoc documentation generator for JavaScript/TypeScript projects.
137
+
138
+ Generates API reference documentation from JSDoc comments in JavaScript/TypeScript
139
+ code using the JSDoc tool (invoked via npx).
140
+ """
141
+
142
+ name: str = "jsdoc"
143
+ languages: List[str] = field(default_factory=lambda: ["javascript", "typescript"])
144
+
145
+ def detect(self, project_root: Path) -> bool:
146
+ """Detect if project uses JavaScript/TypeScript.
147
+
148
+ Checks for:
149
+ - .js, .jsx, .ts, .tsx files
150
+ - package.json file (Node.js project indicator)
151
+ - node_modules/ directory
152
+
153
+ Args:
154
+ project_root: Project root directory
155
+
156
+ Returns:
157
+ True if JavaScript/TypeScript files found
158
+ """
159
+ # Check for package.json (strongest indicator)
160
+ if (project_root / "package.json").exists():
161
+ return True
162
+
163
+ # Check for JS/TS files in common locations
164
+ for pattern in ["*.js", "*.jsx", "*.ts", "*.tsx"]:
165
+ if list(project_root.glob(f"**/{pattern}")):
166
+ return True
167
+
168
+ return False
169
+
170
+ def configure(self, output_dir: Path, options: Dict[str, Any]) -> Path:
171
+ """Generate jsdoc.json configuration file.
172
+
173
+ Args:
174
+ output_dir: Directory where jsdoc.json should be created
175
+ options: Configuration options:
176
+ - project_name: Project name
177
+ - source_dir: Source directory to document (default: "src/")
178
+ - template: JSDoc template to use (default: "docdash")
179
+
180
+ Returns:
181
+ Path to generated jsdoc.json
182
+
183
+ Raises:
184
+ GeneratorError: If config file cannot be written
185
+ """
186
+ source_dir = options.get("source_dir", "src/")
187
+ template = options.get("template", "docdash")
188
+ project_name = options.get("project_name", "Project")
189
+
190
+ config = {
191
+ "source": {
192
+ "include": [source_dir],
193
+ "includePattern": ".+\\.(js|jsx|ts|tsx)$",
194
+ "excludePattern": "(^|\\/|\\\\)_"
195
+ },
196
+ "opts": {
197
+ "destination": str(output_dir / "api" / "javascript"),
198
+ "recurse": True,
199
+ "readme": "README.md",
200
+ "template": f"node_modules/{template}"
201
+ },
202
+ "plugins": ["plugins/markdown"],
203
+ "templates": {
204
+ "cleverLinks": False,
205
+ "monospaceLinks": False
206
+ }
207
+ }
208
+
209
+ config_file = output_dir / "jsdoc.json"
210
+ output_dir.mkdir(parents=True, exist_ok=True)
211
+ try:
212
+ config_file.write_text(json.dumps(config, indent=2))
213
+ return config_file
214
+ except OSError as e:
215
+ raise GeneratorError(f"Failed to write jsdoc.json: {e}")
216
+
217
+ def generate(self, source_dir: Path, output_dir: Path) -> GeneratorResult:
218
+ """Run JSDoc to generate documentation.
219
+
220
+ Args:
221
+ source_dir: Directory containing JavaScript/TypeScript source
222
+ output_dir: Directory where docs should be generated (contains jsdoc.json)
223
+
224
+ Returns:
225
+ GeneratorResult with success status and generated files
226
+
227
+ Raises:
228
+ GeneratorError: If JSDoc is not installed
229
+ """
230
+ config_file = output_dir / "jsdoc.json"
231
+
232
+ if not config_file.exists():
233
+ return GeneratorResult(
234
+ success=False,
235
+ output_dir=output_dir,
236
+ errors=["jsdoc.json not found - run configure() first"],
237
+ )
238
+
239
+ # Check if npx is available
240
+ check_npx = subprocess.run(
241
+ ["npx", "--version"],
242
+ capture_output=True,
243
+ text=True
244
+ )
245
+
246
+ if check_npx.returncode != 0:
247
+ raise GeneratorError(
248
+ "npx not found - install Node.js to use JSDoc generator\n"
249
+ "Visit: https://nodejs.org/"
250
+ )
251
+
252
+ # Run JSDoc
253
+ cmd = ["npx", "jsdoc", "-c", str(config_file)]
254
+ result = subprocess.run(
255
+ cmd,
256
+ cwd=str(source_dir),
257
+ capture_output=True,
258
+ text=True
259
+ )
260
+
261
+ # Parse output
262
+ errors = []
263
+ warnings = []
264
+ if result.returncode != 0:
265
+ errors = result.stderr.splitlines()
266
+
267
+ # Extract warnings from stdout (JSDoc prints warnings to stdout)
268
+ for line in result.stdout.splitlines():
269
+ if "WARNING" in line.upper():
270
+ warnings.append(line)
271
+
272
+ # Find generated files
273
+ api_dir = output_dir / "api" / "javascript"
274
+ generated_files = []
275
+ if api_dir.exists():
276
+ generated_files = list(api_dir.rglob("*.html"))
277
+
278
+ return GeneratorResult(
279
+ success=(result.returncode == 0),
280
+ output_dir=api_dir if api_dir.exists() else output_dir,
281
+ errors=errors,
282
+ warnings=warnings,
283
+ generated_files=generated_files
284
+ )
285
+
286
+
287
+ @dataclass
288
+ class SphinxGenerator:
289
+ """Sphinx documentation generator for Python projects.
290
+
291
+ Generates API reference documentation from Python docstrings using Sphinx
292
+ with autodoc and napoleon extensions.
293
+ """
294
+
295
+ name: str = "sphinx"
296
+ languages: List[str] = field(default_factory=lambda: ["python"])
297
+
298
+ def detect(self, project_root: Path) -> bool:
299
+ """Detect if project uses Python.
300
+
301
+ Checks for:
302
+ - setup.py (Python package indicator)
303
+ - pyproject.toml (modern Python project)
304
+ - .py files in project
305
+
306
+ Args:
307
+ project_root: Project root directory
308
+
309
+ Returns:
310
+ True if Python files found
311
+ """
312
+ # Check for Python project indicators
313
+ if (project_root / "setup.py").exists():
314
+ return True
315
+ if (project_root / "pyproject.toml").exists():
316
+ return True
317
+
318
+ # Check for Python files
319
+ if list(project_root.glob("**/*.py")):
320
+ return True
321
+
322
+ return False
323
+
324
+ def configure(self, output_dir: Path, options: Dict[str, Any]) -> Path:
325
+ """Generate Sphinx conf.py configuration file.
326
+
327
+ Args:
328
+ output_dir: Directory where conf.py should be created
329
+ options: Configuration options:
330
+ - project_name: Project name
331
+ - author: Author name
332
+ - version: Project version
333
+ - theme: Sphinx theme (default: "sphinx_rtd_theme")
334
+
335
+ Returns:
336
+ Path to generated conf.py
337
+
338
+ Raises:
339
+ GeneratorError: If config file cannot be written
340
+ """
341
+ project_name = options.get("project_name", "Project")
342
+ author = options.get("author", "Author")
343
+ version = options.get("version", "0.1.0")
344
+ theme = options.get("theme", "sphinx_rtd_theme")
345
+
346
+ config_content = f'''# Sphinx configuration for {project_name}
347
+ # Auto-generated by spec-kitty documentation mission
348
+
349
+ project = '{project_name}'
350
+ author = '{author}'
351
+ version = '{version}'
352
+ release = version
353
+
354
+ # Extensions
355
+ extensions = [
356
+ 'sphinx.ext.autodoc', # Auto-generate docs from docstrings
357
+ 'sphinx.ext.napoleon', # Support Google/NumPy docstring styles
358
+ 'sphinx.ext.viewcode', # Add links to source code
359
+ 'sphinx.ext.intersphinx', # Link to other project docs
360
+ ]
361
+
362
+ # Napoleon settings for Google-style docstrings
363
+ napoleon_google_docstring = True
364
+ napoleon_numpy_docstring = True
365
+ napoleon_include_init_with_doc = True
366
+
367
+ # HTML output options
368
+ html_theme = '{theme}'
369
+ html_static_path = ['_static']
370
+
371
+ # Autodoc options
372
+ autodoc_default_options = {{
373
+ 'members': True,
374
+ 'undoc-members': True,
375
+ 'show-inheritance': True,
376
+ }}
377
+
378
+ # Path setup
379
+ import os
380
+ import sys
381
+ sys.path.insert(0, os.path.abspath('..'))
382
+ '''
383
+
384
+ config_file = output_dir / "conf.py"
385
+ output_dir.mkdir(parents=True, exist_ok=True)
386
+ try:
387
+ config_file.write_text(config_content)
388
+ return config_file
389
+ except OSError as e:
390
+ raise GeneratorError(f"Failed to write conf.py: {e}")
391
+
392
+ def generate(self, source_dir: Path, output_dir: Path) -> GeneratorResult:
393
+ """Run Sphinx to generate documentation.
394
+
395
+ Args:
396
+ source_dir: Directory containing Python source (not used directly - Sphinx uses conf.py paths)
397
+ output_dir: Directory where docs should be generated (contains conf.py)
398
+
399
+ Returns:
400
+ GeneratorResult with success status and generated files
401
+
402
+ Raises:
403
+ GeneratorError: If Sphinx is not installed
404
+ """
405
+ config_file = output_dir / "conf.py"
406
+
407
+ if not config_file.exists():
408
+ return GeneratorResult(
409
+ success=False,
410
+ output_dir=output_dir,
411
+ errors=["conf.py not found - run configure() first"],
412
+ )
413
+
414
+ # Check if sphinx-build is available
415
+ check_sphinx = subprocess.run(
416
+ ["sphinx-build", "--version"],
417
+ capture_output=True,
418
+ text=True
419
+ )
420
+
421
+ if check_sphinx.returncode != 0:
422
+ raise GeneratorError(
423
+ "sphinx-build not found - install Sphinx to use this generator\n"
424
+ "Run: pip install sphinx sphinx-rtd-theme"
425
+ )
426
+
427
+ # Create build directory
428
+ build_dir = output_dir / "_build" / "html"
429
+ build_dir.mkdir(parents=True, exist_ok=True)
430
+
431
+ # Run Sphinx
432
+ cmd = [
433
+ "sphinx-build",
434
+ "-b", "html", # HTML builder
435
+ "-W", # Treat warnings as errors (optional)
436
+ str(output_dir), # Source directory (contains conf.py)
437
+ str(build_dir) # Build directory
438
+ ]
439
+
440
+ result = subprocess.run(
441
+ cmd,
442
+ capture_output=True,
443
+ text=True
444
+ )
445
+
446
+ # Parse output
447
+ errors = []
448
+ warnings = []
449
+ if result.returncode != 0:
450
+ errors = result.stderr.splitlines()
451
+
452
+ # Extract warnings from stdout (Sphinx prints warnings to stdout)
453
+ for line in result.stdout.splitlines():
454
+ if "WARNING" in line.upper() or "warning:" in line.lower():
455
+ warnings.append(line)
456
+
457
+ # Find generated files
458
+ generated_files = []
459
+ if build_dir.exists():
460
+ generated_files = list(build_dir.rglob("*.html"))
461
+
462
+ return GeneratorResult(
463
+ success=(result.returncode == 0),
464
+ output_dir=build_dir,
465
+ errors=errors,
466
+ warnings=warnings,
467
+ generated_files=generated_files
468
+ )
469
+
470
+
471
+ @dataclass
472
+ class RustdocGenerator:
473
+ """rustdoc documentation generator for Rust projects.
474
+
475
+ Generates API reference documentation from Rust doc comments using rustdoc
476
+ (invoked via cargo doc).
477
+ """
478
+
479
+ name: str = "rustdoc"
480
+ languages: List[str] = field(default_factory=lambda: ["rust"])
481
+
482
+ def detect(self, project_root: Path) -> bool:
483
+ """Detect if project uses Rust.
484
+
485
+ Checks for:
486
+ - Cargo.toml (Rust project indicator)
487
+ - .rs files in project
488
+
489
+ Args:
490
+ project_root: Project root directory
491
+
492
+ Returns:
493
+ True if Rust project found
494
+ """
495
+ # Check for Cargo.toml (definitive Rust project indicator)
496
+ if (project_root / "Cargo.toml").exists():
497
+ return True
498
+
499
+ # Check for Rust source files
500
+ if list(project_root.glob("**/*.rs")):
501
+ return True
502
+
503
+ return False
504
+
505
+ def configure(self, output_dir: Path, options: Dict[str, Any]) -> Path:
506
+ """Generate rustdoc configuration (updates Cargo.toml).
507
+
508
+ For Rust projects, configuration is typically done in Cargo.toml metadata.
509
+ This method returns instructions rather than creating a separate config file.
510
+
511
+ Args:
512
+ output_dir: Not used (rustdoc configured via Cargo.toml)
513
+ options: Configuration options:
514
+ - document_private: Include private items (default: False)
515
+
516
+ Returns:
517
+ Path to instructions file
518
+
519
+ Raises:
520
+ GeneratorError: If instructions cannot be written
521
+ """
522
+ document_private = options.get("document_private", False)
523
+
524
+ instructions = f'''# rustdoc Configuration for spec-kitty
525
+
526
+ rustdoc is configured via Cargo.toml. Add the following to your Cargo.toml:
527
+
528
+ ```toml
529
+ [package.metadata.docs.rs]
530
+ all-features = true
531
+ rustdoc-args = ["{f"--document-private-items" if document_private else ""}"]
532
+ ```
533
+
534
+ This configuration:
535
+ - Documents all features
536
+ {"- Includes private items in documentation" if document_private else "- Documents only public items"}
537
+
538
+ No separate configuration file is needed for rustdoc.
539
+ '''
540
+
541
+ instructions_file = output_dir / "rustdoc-config.md"
542
+ output_dir.mkdir(parents=True, exist_ok=True)
543
+ try:
544
+ instructions_file.write_text(instructions)
545
+ return instructions_file
546
+ except OSError as e:
547
+ raise GeneratorError(f"Failed to write rustdoc instructions: {e}")
548
+
549
+ def generate(self, source_dir: Path, output_dir: Path) -> GeneratorResult:
550
+ """Run cargo doc to generate documentation.
551
+
552
+ Args:
553
+ source_dir: Directory containing Rust source (must have Cargo.toml)
554
+ output_dir: Directory where docs should be generated
555
+
556
+ Returns:
557
+ GeneratorResult with success status and generated files
558
+
559
+ Raises:
560
+ GeneratorError: If cargo is not installed
561
+ """
562
+ cargo_toml = source_dir / "Cargo.toml"
563
+
564
+ if not cargo_toml.exists():
565
+ return GeneratorResult(
566
+ success=False,
567
+ output_dir=output_dir,
568
+ errors=["Cargo.toml not found - not a Rust project"],
569
+ )
570
+
571
+ # Check if cargo is available
572
+ check_cargo = subprocess.run(
573
+ ["cargo", "--version"],
574
+ capture_output=True,
575
+ text=True
576
+ )
577
+
578
+ if check_cargo.returncode != 0:
579
+ raise GeneratorError(
580
+ "cargo not found - install Rust toolchain to use rustdoc generator\n"
581
+ "Visit: https://rustup.rs/"
582
+ )
583
+
584
+ # Run cargo doc
585
+ cmd = [
586
+ "cargo", "doc",
587
+ "--no-deps", # Don't document dependencies
588
+ "--target-dir", str(output_dir)
589
+ ]
590
+
591
+ result = subprocess.run(
592
+ cmd,
593
+ cwd=str(source_dir),
594
+ capture_output=True,
595
+ text=True
596
+ )
597
+
598
+ # Parse output
599
+ errors = []
600
+ warnings = []
601
+ if result.returncode != 0:
602
+ errors = result.stderr.splitlines()
603
+
604
+ # Extract warnings from stderr (cargo prints warnings to stderr)
605
+ for line in result.stderr.splitlines():
606
+ if "warning:" in line.lower():
607
+ warnings.append(line)
608
+
609
+ # Find generated files (cargo doc outputs to target/doc/)
610
+ doc_dir = output_dir / "doc"
611
+ generated_files = []
612
+ if doc_dir.exists():
613
+ generated_files = list(doc_dir.rglob("*.html"))
614
+
615
+ return GeneratorResult(
616
+ success=(result.returncode == 0),
617
+ output_dir=doc_dir if doc_dir.exists() else output_dir,
618
+ errors=errors,
619
+ warnings=warnings,
620
+ generated_files=generated_files
621
+ )