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
specify_cli/doc_state.py
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"""Documentation State Management for Spec Kitty.
|
|
2
|
+
|
|
3
|
+
This module manages documentation mission state persistence in feature meta.json files.
|
|
4
|
+
State includes iteration mode, selected Divio types, configured generators, and audit metadata.
|
|
5
|
+
|
|
6
|
+
Documentation State Schema for meta.json
|
|
7
|
+
========================================
|
|
8
|
+
|
|
9
|
+
The documentation_state field is added to feature meta.json files for
|
|
10
|
+
documentation mission features. It persists state between iterations.
|
|
11
|
+
|
|
12
|
+
Schema:
|
|
13
|
+
{
|
|
14
|
+
"documentation_state": {
|
|
15
|
+
"iteration_mode": "initial" | "gap_filling" | "feature_specific",
|
|
16
|
+
"divio_types_selected": ["tutorial", "how-to", "reference", "explanation"],
|
|
17
|
+
"generators_configured": [
|
|
18
|
+
{
|
|
19
|
+
"name": "sphinx" | "jsdoc" | "rustdoc",
|
|
20
|
+
"language": "python" | "javascript" | "typescript" | "rust",
|
|
21
|
+
"config_path": "relative/path/to/config.py"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"target_audience": "developers" | "end-users" | "contributors" | "operators",
|
|
25
|
+
"last_audit_date": "2026-01-12T00:00:00Z" | null,
|
|
26
|
+
"coverage_percentage": 0.75 # 0.0 to 1.0
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Fields:
|
|
31
|
+
- iteration_mode: How this documentation mission was run
|
|
32
|
+
- divio_types_selected: Which Divio types user chose to include
|
|
33
|
+
- generators_configured: Which generators were set up and where
|
|
34
|
+
- target_audience: Primary documentation audience
|
|
35
|
+
- last_audit_date: When gap analysis last ran (null if never)
|
|
36
|
+
- coverage_percentage: Overall doc coverage from most recent audit (0.0 if initial)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import json
|
|
42
|
+
from datetime import datetime
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
from typing import List, Literal, Optional, TypedDict
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class GeneratorConfig(TypedDict):
|
|
48
|
+
"""Generator configuration entry."""
|
|
49
|
+
|
|
50
|
+
name: Literal["sphinx", "jsdoc", "rustdoc"]
|
|
51
|
+
language: str
|
|
52
|
+
config_path: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class DocumentationState(TypedDict):
|
|
56
|
+
"""Documentation state schema for meta.json."""
|
|
57
|
+
|
|
58
|
+
iteration_mode: Literal["initial", "gap_filling", "feature_specific"]
|
|
59
|
+
divio_types_selected: List[str]
|
|
60
|
+
generators_configured: List[GeneratorConfig]
|
|
61
|
+
target_audience: str
|
|
62
|
+
last_audit_date: Optional[str] # ISO datetime or null
|
|
63
|
+
coverage_percentage: float # 0.0 to 1.0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ============================================================================
|
|
67
|
+
# Individual Field Setters (T041-T044)
|
|
68
|
+
# ============================================================================
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def set_iteration_mode(
|
|
72
|
+
meta_file: Path, iteration_mode: Literal["initial", "gap_filling", "feature_specific"]
|
|
73
|
+
) -> None:
|
|
74
|
+
"""Set iteration mode in feature meta.json.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
meta_file: Path to meta.json
|
|
78
|
+
iteration_mode: Iteration mode to store
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
FileNotFoundError: If meta.json doesn't exist
|
|
82
|
+
ValueError: If iteration_mode is invalid
|
|
83
|
+
"""
|
|
84
|
+
valid_modes = {"initial", "gap_filling", "feature_specific"}
|
|
85
|
+
if iteration_mode not in valid_modes:
|
|
86
|
+
raise ValueError(
|
|
87
|
+
f"Invalid iteration_mode: {iteration_mode}. Must be one of: {valid_modes}"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Read existing meta.json
|
|
91
|
+
with open(meta_file, "r") as f:
|
|
92
|
+
meta = json.load(f)
|
|
93
|
+
|
|
94
|
+
# Initialize documentation_state if not present
|
|
95
|
+
if "documentation_state" not in meta:
|
|
96
|
+
meta["documentation_state"] = {}
|
|
97
|
+
|
|
98
|
+
# Set iteration mode
|
|
99
|
+
meta["documentation_state"]["iteration_mode"] = iteration_mode
|
|
100
|
+
|
|
101
|
+
# Write back
|
|
102
|
+
with open(meta_file, "w") as f:
|
|
103
|
+
json.dump(meta, f, indent=2)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def set_divio_types_selected(meta_file: Path, divio_types: List[str]) -> None:
|
|
107
|
+
"""Set selected Divio types in feature meta.json.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
meta_file: Path to meta.json
|
|
111
|
+
divio_types: List of Divio types to store
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
FileNotFoundError: If meta.json doesn't exist
|
|
115
|
+
ValueError: If any type is invalid
|
|
116
|
+
"""
|
|
117
|
+
valid_types = {"tutorial", "how-to", "reference", "explanation"}
|
|
118
|
+
invalid_types = set(divio_types) - valid_types
|
|
119
|
+
if invalid_types:
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"Invalid Divio types: {invalid_types}. Must be one of: {valid_types}"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Read existing meta.json
|
|
125
|
+
with open(meta_file, "r") as f:
|
|
126
|
+
meta = json.load(f)
|
|
127
|
+
|
|
128
|
+
# Initialize documentation_state if not present
|
|
129
|
+
if "documentation_state" not in meta:
|
|
130
|
+
meta["documentation_state"] = {}
|
|
131
|
+
|
|
132
|
+
# Set Divio types
|
|
133
|
+
meta["documentation_state"]["divio_types_selected"] = divio_types
|
|
134
|
+
|
|
135
|
+
# Write back
|
|
136
|
+
with open(meta_file, "w") as f:
|
|
137
|
+
json.dump(meta, f, indent=2)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def set_generators_configured(meta_file: Path, generators: List[GeneratorConfig]) -> None:
|
|
141
|
+
"""Set configured generators in feature meta.json.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
meta_file: Path to meta.json
|
|
145
|
+
generators: List of generator configs, each with:
|
|
146
|
+
- name: Generator name (sphinx, jsdoc, rustdoc)
|
|
147
|
+
- language: Language (python, javascript, rust)
|
|
148
|
+
- config_path: Path to config file (relative to project root)
|
|
149
|
+
|
|
150
|
+
Raises:
|
|
151
|
+
FileNotFoundError: If meta.json doesn't exist
|
|
152
|
+
ValueError: If generator config is invalid
|
|
153
|
+
"""
|
|
154
|
+
# Validate generator configs
|
|
155
|
+
valid_names = {"sphinx", "jsdoc", "rustdoc"}
|
|
156
|
+
for gen in generators:
|
|
157
|
+
if "name" not in gen:
|
|
158
|
+
raise ValueError(f"Generator config missing 'name' field: {gen}")
|
|
159
|
+
if gen["name"] not in valid_names:
|
|
160
|
+
raise ValueError(
|
|
161
|
+
f"Invalid generator name: {gen['name']}. Must be one of: {valid_names}"
|
|
162
|
+
)
|
|
163
|
+
if "language" not in gen:
|
|
164
|
+
raise ValueError(f"Generator config missing 'language' field: {gen}")
|
|
165
|
+
if "config_path" not in gen:
|
|
166
|
+
raise ValueError(f"Generator config missing 'config_path' field: {gen}")
|
|
167
|
+
|
|
168
|
+
# Read existing meta.json
|
|
169
|
+
with open(meta_file, "r") as f:
|
|
170
|
+
meta = json.load(f)
|
|
171
|
+
|
|
172
|
+
# Initialize documentation_state if not present
|
|
173
|
+
if "documentation_state" not in meta:
|
|
174
|
+
meta["documentation_state"] = {}
|
|
175
|
+
|
|
176
|
+
# Set generators
|
|
177
|
+
meta["documentation_state"]["generators_configured"] = generators
|
|
178
|
+
|
|
179
|
+
# Write back
|
|
180
|
+
with open(meta_file, "w") as f:
|
|
181
|
+
json.dump(meta, f, indent=2)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def set_audit_metadata(
|
|
185
|
+
meta_file: Path, last_audit_date: Optional[datetime], coverage_percentage: float
|
|
186
|
+
) -> None:
|
|
187
|
+
"""Set audit metadata in feature meta.json.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
meta_file: Path to meta.json
|
|
191
|
+
last_audit_date: When gap analysis last ran (None if never)
|
|
192
|
+
coverage_percentage: Overall doc coverage (0.0 to 1.0)
|
|
193
|
+
|
|
194
|
+
Raises:
|
|
195
|
+
FileNotFoundError: If meta.json doesn't exist
|
|
196
|
+
ValueError: If coverage_percentage is out of range
|
|
197
|
+
"""
|
|
198
|
+
if not (0.0 <= coverage_percentage <= 1.0):
|
|
199
|
+
raise ValueError(
|
|
200
|
+
f"coverage_percentage must be 0.0-1.0, got {coverage_percentage}"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Read existing meta.json
|
|
204
|
+
with open(meta_file, "r") as f:
|
|
205
|
+
meta = json.load(f)
|
|
206
|
+
|
|
207
|
+
# Initialize documentation_state if not present
|
|
208
|
+
if "documentation_state" not in meta:
|
|
209
|
+
meta["documentation_state"] = {}
|
|
210
|
+
|
|
211
|
+
# Set audit metadata
|
|
212
|
+
meta["documentation_state"]["last_audit_date"] = (
|
|
213
|
+
last_audit_date.isoformat() if last_audit_date else None
|
|
214
|
+
)
|
|
215
|
+
meta["documentation_state"]["coverage_percentage"] = coverage_percentage
|
|
216
|
+
|
|
217
|
+
# Write back
|
|
218
|
+
with open(meta_file, "w") as f:
|
|
219
|
+
json.dump(meta, f, indent=2)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# ============================================================================
|
|
223
|
+
# Comprehensive State Read/Write Functions (T045)
|
|
224
|
+
# ============================================================================
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def read_documentation_state(meta_file: Path) -> Optional[DocumentationState]:
|
|
228
|
+
"""Read documentation state from feature meta.json.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
meta_file: Path to meta.json
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
DocumentationState dict if present, None if not a documentation mission
|
|
235
|
+
or if state is missing (backward compatibility)
|
|
236
|
+
|
|
237
|
+
Raises:
|
|
238
|
+
FileNotFoundError: If meta.json doesn't exist
|
|
239
|
+
json.JSONDecodeError: If meta.json is invalid JSON
|
|
240
|
+
"""
|
|
241
|
+
with open(meta_file, "r") as f:
|
|
242
|
+
meta = json.load(f)
|
|
243
|
+
|
|
244
|
+
# Check if this is a documentation mission
|
|
245
|
+
if meta.get("mission") != "documentation":
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
# Get documentation_state (may be missing for old features)
|
|
249
|
+
return meta.get("documentation_state")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def write_documentation_state(meta_file: Path, state: DocumentationState) -> None:
|
|
253
|
+
"""Write complete documentation state to feature meta.json.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
meta_file: Path to meta.json
|
|
257
|
+
state: Complete documentation state to write
|
|
258
|
+
|
|
259
|
+
Raises:
|
|
260
|
+
FileNotFoundError: If meta.json doesn't exist
|
|
261
|
+
ValueError: If state is invalid
|
|
262
|
+
"""
|
|
263
|
+
# Validate state structure
|
|
264
|
+
required_fields = {
|
|
265
|
+
"iteration_mode",
|
|
266
|
+
"divio_types_selected",
|
|
267
|
+
"generators_configured",
|
|
268
|
+
"target_audience",
|
|
269
|
+
"last_audit_date",
|
|
270
|
+
"coverage_percentage",
|
|
271
|
+
}
|
|
272
|
+
missing_fields = required_fields - set(state.keys())
|
|
273
|
+
if missing_fields:
|
|
274
|
+
raise ValueError(f"State missing required fields: {missing_fields}")
|
|
275
|
+
|
|
276
|
+
# Read existing meta.json
|
|
277
|
+
with open(meta_file, "r") as f:
|
|
278
|
+
meta = json.load(f)
|
|
279
|
+
|
|
280
|
+
# Update documentation_state
|
|
281
|
+
meta["documentation_state"] = state
|
|
282
|
+
|
|
283
|
+
# Write back
|
|
284
|
+
with open(meta_file, "w") as f:
|
|
285
|
+
json.dump(meta, f, indent=2)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def initialize_documentation_state(
|
|
289
|
+
meta_file: Path,
|
|
290
|
+
iteration_mode: str,
|
|
291
|
+
divio_types: List[str],
|
|
292
|
+
generators: List[GeneratorConfig],
|
|
293
|
+
target_audience: str,
|
|
294
|
+
) -> DocumentationState:
|
|
295
|
+
"""Initialize documentation state for a new documentation mission.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
meta_file: Path to meta.json
|
|
299
|
+
iteration_mode: initial, gap_filling, or feature_specific
|
|
300
|
+
divio_types: Selected Divio types
|
|
301
|
+
generators: Configured generators
|
|
302
|
+
target_audience: Primary documentation audience
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Initialized DocumentationState
|
|
306
|
+
|
|
307
|
+
Raises:
|
|
308
|
+
FileNotFoundError: If meta.json doesn't exist
|
|
309
|
+
"""
|
|
310
|
+
state: DocumentationState = {
|
|
311
|
+
"iteration_mode": iteration_mode, # type: ignore
|
|
312
|
+
"divio_types_selected": divio_types,
|
|
313
|
+
"generators_configured": generators,
|
|
314
|
+
"target_audience": target_audience,
|
|
315
|
+
"last_audit_date": None,
|
|
316
|
+
"coverage_percentage": 0.0,
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
write_documentation_state(meta_file, state)
|
|
320
|
+
return state
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def update_documentation_state(meta_file: Path, **updates) -> DocumentationState:
|
|
324
|
+
"""Update specific fields in documentation state.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
meta_file: Path to meta.json
|
|
328
|
+
**updates: Fields to update (iteration_mode, divio_types_selected, etc.)
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
Updated DocumentationState
|
|
332
|
+
|
|
333
|
+
Raises:
|
|
334
|
+
FileNotFoundError: If meta.json doesn't exist
|
|
335
|
+
ValueError: If state doesn't exist (call initialize first)
|
|
336
|
+
"""
|
|
337
|
+
# Read current state
|
|
338
|
+
state = read_documentation_state(meta_file)
|
|
339
|
+
|
|
340
|
+
if state is None:
|
|
341
|
+
raise ValueError(
|
|
342
|
+
f"No documentation state found in {meta_file}. "
|
|
343
|
+
f"Call initialize_documentation_state() first."
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
# Update fields
|
|
347
|
+
for key, value in updates.items():
|
|
348
|
+
if key in state:
|
|
349
|
+
state[key] = value # type: ignore
|
|
350
|
+
|
|
351
|
+
# Write back
|
|
352
|
+
write_documentation_state(meta_file, state)
|
|
353
|
+
return state
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
# ============================================================================
|
|
357
|
+
# Backward Compatibility / Migration (T046)
|
|
358
|
+
# ============================================================================
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def ensure_documentation_state(meta_file: Path) -> None:
|
|
362
|
+
"""Ensure meta.json has documentation_state field.
|
|
363
|
+
|
|
364
|
+
For backward compatibility with old documentation mission features.
|
|
365
|
+
If feature is a documentation mission but lacks documentation_state,
|
|
366
|
+
initialize with sensible defaults.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
meta_file: Path to meta.json
|
|
370
|
+
"""
|
|
371
|
+
with open(meta_file, "r") as f:
|
|
372
|
+
meta = json.load(f)
|
|
373
|
+
|
|
374
|
+
# Check if documentation mission
|
|
375
|
+
if meta.get("mission") != "documentation":
|
|
376
|
+
return # Not a documentation mission, nothing to do
|
|
377
|
+
|
|
378
|
+
# Check if state already exists
|
|
379
|
+
if "documentation_state" in meta:
|
|
380
|
+
return # Already has state
|
|
381
|
+
|
|
382
|
+
# Initialize with defaults
|
|
383
|
+
meta["documentation_state"] = {
|
|
384
|
+
"iteration_mode": "initial", # Assume first run
|
|
385
|
+
"divio_types_selected": [], # Unknown, user must specify
|
|
386
|
+
"generators_configured": [], # Unknown, user must configure
|
|
387
|
+
"target_audience": "developers", # Reasonable default
|
|
388
|
+
"last_audit_date": None,
|
|
389
|
+
"coverage_percentage": 0.0,
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
# Write back
|
|
393
|
+
with open(meta_file, "w") as f:
|
|
394
|
+
json.dump(meta, f, indent=2)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def get_state_version(state: DocumentationState) -> int:
|
|
398
|
+
"""Get state schema version for future migrations.
|
|
399
|
+
|
|
400
|
+
Currently all states are version 1.
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
state: Documentation state
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
Schema version number
|
|
407
|
+
"""
|
|
408
|
+
return state.get("_schema_version", 1) # type: ignore
|