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