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.
- spec_kitty_cli-0.12.1.dist-info/METADATA +1767 -0
- spec_kitty_cli-0.12.1.dist-info/RECORD +242 -0
- spec_kitty_cli-0.12.1.dist-info/WHEEL +4 -0
- spec_kitty_cli-0.12.1.dist-info/entry_points.txt +2 -0
- spec_kitty_cli-0.12.1.dist-info/licenses/LICENSE +21 -0
- specify_cli/__init__.py +171 -0
- specify_cli/acceptance.py +627 -0
- specify_cli/agent_utils/README.md +157 -0
- specify_cli/agent_utils/__init__.py +9 -0
- specify_cli/agent_utils/status.py +356 -0
- specify_cli/cli/__init__.py +6 -0
- specify_cli/cli/commands/__init__.py +46 -0
- specify_cli/cli/commands/accept.py +189 -0
- specify_cli/cli/commands/agent/__init__.py +22 -0
- specify_cli/cli/commands/agent/config.py +382 -0
- specify_cli/cli/commands/agent/context.py +191 -0
- specify_cli/cli/commands/agent/feature.py +1057 -0
- specify_cli/cli/commands/agent/release.py +11 -0
- specify_cli/cli/commands/agent/tasks.py +1253 -0
- specify_cli/cli/commands/agent/workflow.py +801 -0
- specify_cli/cli/commands/context.py +246 -0
- specify_cli/cli/commands/dashboard.py +85 -0
- specify_cli/cli/commands/implement.py +973 -0
- specify_cli/cli/commands/init.py +827 -0
- specify_cli/cli/commands/init_help.py +62 -0
- specify_cli/cli/commands/merge.py +755 -0
- specify_cli/cli/commands/mission.py +240 -0
- specify_cli/cli/commands/ops.py +265 -0
- specify_cli/cli/commands/orchestrate.py +640 -0
- specify_cli/cli/commands/repair.py +175 -0
- specify_cli/cli/commands/research.py +165 -0
- specify_cli/cli/commands/sync.py +364 -0
- specify_cli/cli/commands/upgrade.py +249 -0
- specify_cli/cli/commands/validate_encoding.py +186 -0
- specify_cli/cli/commands/validate_tasks.py +186 -0
- specify_cli/cli/commands/verify.py +310 -0
- specify_cli/cli/helpers.py +123 -0
- specify_cli/cli/step_tracker.py +91 -0
- specify_cli/cli/ui.py +192 -0
- specify_cli/core/__init__.py +53 -0
- specify_cli/core/agent_context.py +311 -0
- specify_cli/core/config.py +96 -0
- specify_cli/core/context_validation.py +362 -0
- specify_cli/core/dependency_graph.py +351 -0
- specify_cli/core/git_ops.py +129 -0
- specify_cli/core/multi_parent_merge.py +323 -0
- specify_cli/core/paths.py +260 -0
- specify_cli/core/project_resolver.py +110 -0
- specify_cli/core/stale_detection.py +263 -0
- specify_cli/core/tool_checker.py +79 -0
- specify_cli/core/utils.py +43 -0
- specify_cli/core/vcs/__init__.py +114 -0
- specify_cli/core/vcs/detection.py +341 -0
- specify_cli/core/vcs/exceptions.py +85 -0
- specify_cli/core/vcs/git.py +1304 -0
- specify_cli/core/vcs/jujutsu.py +1208 -0
- specify_cli/core/vcs/protocol.py +285 -0
- specify_cli/core/vcs/types.py +249 -0
- specify_cli/core/version_checker.py +261 -0
- specify_cli/core/worktree.py +506 -0
- specify_cli/dashboard/__init__.py +28 -0
- specify_cli/dashboard/diagnostics.py +204 -0
- specify_cli/dashboard/handlers/__init__.py +17 -0
- specify_cli/dashboard/handlers/api.py +143 -0
- specify_cli/dashboard/handlers/base.py +65 -0
- specify_cli/dashboard/handlers/features.py +390 -0
- specify_cli/dashboard/handlers/router.py +81 -0
- specify_cli/dashboard/handlers/static.py +50 -0
- specify_cli/dashboard/lifecycle.py +541 -0
- specify_cli/dashboard/scanner.py +437 -0
- specify_cli/dashboard/server.py +123 -0
- specify_cli/dashboard/static/dashboard/dashboard.css +722 -0
- specify_cli/dashboard/static/dashboard/dashboard.js +1424 -0
- specify_cli/dashboard/static/spec-kitty.png +0 -0
- specify_cli/dashboard/templates/__init__.py +36 -0
- specify_cli/dashboard/templates/index.html +258 -0
- specify_cli/doc_generators.py +621 -0
- specify_cli/doc_state.py +408 -0
- specify_cli/frontmatter.py +384 -0
- specify_cli/gap_analysis.py +915 -0
- specify_cli/gitignore_manager.py +300 -0
- specify_cli/guards.py +145 -0
- specify_cli/legacy_detector.py +83 -0
- specify_cli/manifest.py +286 -0
- specify_cli/merge/__init__.py +63 -0
- specify_cli/merge/executor.py +653 -0
- specify_cli/merge/forecast.py +215 -0
- specify_cli/merge/ordering.py +126 -0
- specify_cli/merge/preflight.py +230 -0
- specify_cli/merge/state.py +185 -0
- specify_cli/merge/status_resolver.py +354 -0
- specify_cli/mission.py +654 -0
- specify_cli/missions/documentation/command-templates/implement.md +309 -0
- specify_cli/missions/documentation/command-templates/plan.md +275 -0
- specify_cli/missions/documentation/command-templates/review.md +344 -0
- specify_cli/missions/documentation/command-templates/specify.md +206 -0
- specify_cli/missions/documentation/command-templates/tasks.md +189 -0
- specify_cli/missions/documentation/mission.yaml +113 -0
- specify_cli/missions/documentation/templates/divio/explanation-template.md +192 -0
- specify_cli/missions/documentation/templates/divio/howto-template.md +168 -0
- specify_cli/missions/documentation/templates/divio/reference-template.md +179 -0
- specify_cli/missions/documentation/templates/divio/tutorial-template.md +146 -0
- specify_cli/missions/documentation/templates/generators/jsdoc.json.template +18 -0
- specify_cli/missions/documentation/templates/generators/sphinx-conf.py.template +36 -0
- specify_cli/missions/documentation/templates/plan-template.md +269 -0
- specify_cli/missions/documentation/templates/release-template.md +222 -0
- specify_cli/missions/documentation/templates/spec-template.md +172 -0
- specify_cli/missions/documentation/templates/task-prompt-template.md +140 -0
- specify_cli/missions/documentation/templates/tasks-template.md +159 -0
- specify_cli/missions/research/command-templates/merge.md +388 -0
- specify_cli/missions/research/command-templates/plan.md +125 -0
- specify_cli/missions/research/command-templates/review.md +144 -0
- specify_cli/missions/research/command-templates/tasks.md +225 -0
- specify_cli/missions/research/mission.yaml +115 -0
- specify_cli/missions/research/templates/data-model-template.md +33 -0
- specify_cli/missions/research/templates/plan-template.md +161 -0
- specify_cli/missions/research/templates/research/evidence-log.csv +18 -0
- specify_cli/missions/research/templates/research/source-register.csv +18 -0
- specify_cli/missions/research/templates/research-template.md +35 -0
- specify_cli/missions/research/templates/spec-template.md +64 -0
- specify_cli/missions/research/templates/task-prompt-template.md +148 -0
- specify_cli/missions/research/templates/tasks-template.md +114 -0
- specify_cli/missions/software-dev/command-templates/accept.md +75 -0
- specify_cli/missions/software-dev/command-templates/analyze.md +183 -0
- specify_cli/missions/software-dev/command-templates/checklist.md +286 -0
- specify_cli/missions/software-dev/command-templates/clarify.md +157 -0
- specify_cli/missions/software-dev/command-templates/constitution.md +432 -0
- specify_cli/missions/software-dev/command-templates/dashboard.md +101 -0
- specify_cli/missions/software-dev/command-templates/implement.md +41 -0
- specify_cli/missions/software-dev/command-templates/merge.md +383 -0
- specify_cli/missions/software-dev/command-templates/plan.md +171 -0
- specify_cli/missions/software-dev/command-templates/review.md +32 -0
- specify_cli/missions/software-dev/command-templates/specify.md +321 -0
- specify_cli/missions/software-dev/command-templates/tasks.md +566 -0
- specify_cli/missions/software-dev/mission.yaml +100 -0
- specify_cli/missions/software-dev/templates/plan-template.md +132 -0
- specify_cli/missions/software-dev/templates/spec-template.md +116 -0
- specify_cli/missions/software-dev/templates/task-prompt-template.md +140 -0
- specify_cli/missions/software-dev/templates/tasks-template.md +159 -0
- specify_cli/orchestrator/__init__.py +75 -0
- specify_cli/orchestrator/agent_config.py +224 -0
- specify_cli/orchestrator/agents/__init__.py +170 -0
- specify_cli/orchestrator/agents/augment.py +112 -0
- specify_cli/orchestrator/agents/base.py +243 -0
- specify_cli/orchestrator/agents/claude.py +112 -0
- specify_cli/orchestrator/agents/codex.py +106 -0
- specify_cli/orchestrator/agents/copilot.py +137 -0
- specify_cli/orchestrator/agents/cursor.py +139 -0
- specify_cli/orchestrator/agents/gemini.py +115 -0
- specify_cli/orchestrator/agents/kilocode.py +94 -0
- specify_cli/orchestrator/agents/opencode.py +132 -0
- specify_cli/orchestrator/agents/qwen.py +96 -0
- specify_cli/orchestrator/config.py +455 -0
- specify_cli/orchestrator/executor.py +642 -0
- specify_cli/orchestrator/integration.py +1230 -0
- specify_cli/orchestrator/monitor.py +898 -0
- specify_cli/orchestrator/scheduler.py +832 -0
- specify_cli/orchestrator/state.py +508 -0
- specify_cli/orchestrator/testing/__init__.py +122 -0
- specify_cli/orchestrator/testing/availability.py +346 -0
- specify_cli/orchestrator/testing/fixtures.py +684 -0
- specify_cli/orchestrator/testing/paths.py +218 -0
- specify_cli/plan_validation.py +107 -0
- specify_cli/scripts/debug-dashboard-scan.py +61 -0
- specify_cli/scripts/tasks/acceptance_support.py +695 -0
- specify_cli/scripts/tasks/task_helpers.py +506 -0
- specify_cli/scripts/tasks/tasks_cli.py +848 -0
- specify_cli/scripts/validate_encoding.py +180 -0
- specify_cli/task_metadata_validation.py +274 -0
- specify_cli/tasks_support.py +447 -0
- specify_cli/template/__init__.py +47 -0
- specify_cli/template/asset_generator.py +206 -0
- specify_cli/template/github_client.py +334 -0
- specify_cli/template/manager.py +193 -0
- specify_cli/template/renderer.py +99 -0
- specify_cli/templates/AGENTS.md +190 -0
- specify_cli/templates/POWERSHELL_SYNTAX.md +229 -0
- specify_cli/templates/agent-file-template.md +35 -0
- specify_cli/templates/checklist-template.md +42 -0
- specify_cli/templates/claudeignore-template +58 -0
- specify_cli/templates/command-templates/accept.md +141 -0
- specify_cli/templates/command-templates/analyze.md +253 -0
- specify_cli/templates/command-templates/checklist.md +352 -0
- specify_cli/templates/command-templates/clarify.md +224 -0
- specify_cli/templates/command-templates/constitution.md +432 -0
- specify_cli/templates/command-templates/dashboard.md +175 -0
- specify_cli/templates/command-templates/implement.md +190 -0
- specify_cli/templates/command-templates/merge.md +374 -0
- specify_cli/templates/command-templates/plan.md +171 -0
- specify_cli/templates/command-templates/research.md +88 -0
- specify_cli/templates/command-templates/review.md +510 -0
- specify_cli/templates/command-templates/specify.md +321 -0
- specify_cli/templates/command-templates/status.md +92 -0
- specify_cli/templates/command-templates/tasks.md +199 -0
- specify_cli/templates/git-hooks/pre-commit +22 -0
- specify_cli/templates/git-hooks/pre-commit-agent-check +37 -0
- specify_cli/templates/git-hooks/pre-commit-encoding-check +142 -0
- specify_cli/templates/plan-template.md +108 -0
- specify_cli/templates/spec-template.md +118 -0
- specify_cli/templates/task-prompt-template.md +165 -0
- specify_cli/templates/tasks-template.md +161 -0
- specify_cli/templates/vscode-settings.json +13 -0
- specify_cli/text_sanitization.py +225 -0
- specify_cli/upgrade/__init__.py +18 -0
- specify_cli/upgrade/detector.py +239 -0
- specify_cli/upgrade/metadata.py +182 -0
- specify_cli/upgrade/migrations/__init__.py +65 -0
- specify_cli/upgrade/migrations/base.py +80 -0
- specify_cli/upgrade/migrations/m_0_10_0_python_only.py +359 -0
- specify_cli/upgrade/migrations/m_0_10_12_constitution_cleanup.py +99 -0
- specify_cli/upgrade/migrations/m_0_10_14_update_implement_slash_command.py +176 -0
- specify_cli/upgrade/migrations/m_0_10_1_populate_slash_commands.py +174 -0
- specify_cli/upgrade/migrations/m_0_10_2_update_slash_commands.py +172 -0
- specify_cli/upgrade/migrations/m_0_10_6_workflow_simplification.py +174 -0
- specify_cli/upgrade/migrations/m_0_10_8_fix_memory_structure.py +252 -0
- specify_cli/upgrade/migrations/m_0_10_9_repair_templates.py +168 -0
- specify_cli/upgrade/migrations/m_0_11_0_workspace_per_wp.py +182 -0
- specify_cli/upgrade/migrations/m_0_11_1_improved_workflow_templates.py +173 -0
- specify_cli/upgrade/migrations/m_0_11_1_update_implement_slash_command.py +160 -0
- specify_cli/upgrade/migrations/m_0_11_2_improved_workflow_templates.py +173 -0
- specify_cli/upgrade/migrations/m_0_11_3_workflow_agent_flag.py +114 -0
- specify_cli/upgrade/migrations/m_0_12_0_documentation_mission.py +155 -0
- specify_cli/upgrade/migrations/m_0_12_1_remove_kitty_specs_from_gitignore.py +183 -0
- specify_cli/upgrade/migrations/m_0_2_0_specify_to_kittify.py +80 -0
- specify_cli/upgrade/migrations/m_0_4_8_gitignore_agents.py +118 -0
- specify_cli/upgrade/migrations/m_0_5_0_encoding_hooks.py +141 -0
- specify_cli/upgrade/migrations/m_0_6_5_commands_rename.py +169 -0
- specify_cli/upgrade/migrations/m_0_6_7_ensure_missions.py +228 -0
- specify_cli/upgrade/migrations/m_0_7_2_worktree_commands_dedup.py +89 -0
- specify_cli/upgrade/migrations/m_0_7_3_update_scripts.py +114 -0
- specify_cli/upgrade/migrations/m_0_8_0_remove_active_mission.py +82 -0
- specify_cli/upgrade/migrations/m_0_8_0_worktree_agents_symlink.py +148 -0
- specify_cli/upgrade/migrations/m_0_9_0_frontmatter_only_lanes.py +346 -0
- specify_cli/upgrade/migrations/m_0_9_1_complete_lane_migration.py +656 -0
- specify_cli/upgrade/migrations/m_0_9_2_research_mission_templates.py +221 -0
- specify_cli/upgrade/registry.py +121 -0
- specify_cli/upgrade/runner.py +284 -0
- specify_cli/validators/__init__.py +14 -0
- specify_cli/validators/paths.py +154 -0
- specify_cli/validators/research.py +428 -0
- specify_cli/verify_enhanced.py +270 -0
- 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
|
+
)
|