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,65 @@
|
|
|
1
|
+
"""Migration implementations for Spec Kitty upgrade system.
|
|
2
|
+
|
|
3
|
+
Import all migrations here to register them with the MigrationRegistry.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
# Import migrations to register them
|
|
9
|
+
from . import m_0_2_0_specify_to_kittify
|
|
10
|
+
from . import m_0_4_8_gitignore_agents
|
|
11
|
+
from . import m_0_5_0_encoding_hooks
|
|
12
|
+
from . import m_0_6_5_commands_rename
|
|
13
|
+
from . import m_0_6_7_ensure_missions
|
|
14
|
+
from . import m_0_7_2_worktree_commands_dedup
|
|
15
|
+
from . import m_0_7_3_update_scripts
|
|
16
|
+
from . import m_0_8_0_remove_active_mission
|
|
17
|
+
from . import m_0_8_0_worktree_agents_symlink
|
|
18
|
+
from . import m_0_9_0_frontmatter_only_lanes
|
|
19
|
+
from . import m_0_9_1_complete_lane_migration
|
|
20
|
+
from . import m_0_9_2_research_mission_templates
|
|
21
|
+
from . import m_0_10_0_python_only
|
|
22
|
+
from . import m_0_10_1_populate_slash_commands
|
|
23
|
+
from . import m_0_10_2_update_slash_commands
|
|
24
|
+
from . import m_0_10_6_workflow_simplification
|
|
25
|
+
from . import m_0_10_8_fix_memory_structure
|
|
26
|
+
from . import m_0_10_9_repair_templates
|
|
27
|
+
from . import m_0_10_12_constitution_cleanup
|
|
28
|
+
from . import m_0_10_14_update_implement_slash_command
|
|
29
|
+
from . import m_0_11_0_workspace_per_wp
|
|
30
|
+
from . import m_0_11_1_improved_workflow_templates
|
|
31
|
+
from . import m_0_11_1_update_implement_slash_command
|
|
32
|
+
from . import m_0_11_2_improved_workflow_templates
|
|
33
|
+
from . import m_0_11_3_workflow_agent_flag
|
|
34
|
+
from . import m_0_12_0_documentation_mission
|
|
35
|
+
from . import m_0_12_1_remove_kitty_specs_from_gitignore
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"m_0_2_0_specify_to_kittify",
|
|
39
|
+
"m_0_4_8_gitignore_agents",
|
|
40
|
+
"m_0_5_0_encoding_hooks",
|
|
41
|
+
"m_0_6_5_commands_rename",
|
|
42
|
+
"m_0_6_7_ensure_missions",
|
|
43
|
+
"m_0_7_2_worktree_commands_dedup",
|
|
44
|
+
"m_0_7_3_update_scripts",
|
|
45
|
+
"m_0_8_0_remove_active_mission",
|
|
46
|
+
"m_0_8_0_worktree_agents_symlink",
|
|
47
|
+
"m_0_9_0_frontmatter_only_lanes",
|
|
48
|
+
"m_0_9_1_complete_lane_migration",
|
|
49
|
+
"m_0_9_2_research_mission_templates",
|
|
50
|
+
"m_0_10_0_python_only",
|
|
51
|
+
"m_0_10_1_populate_slash_commands",
|
|
52
|
+
"m_0_10_2_update_slash_commands",
|
|
53
|
+
"m_0_10_6_workflow_simplification",
|
|
54
|
+
"m_0_10_8_fix_memory_structure",
|
|
55
|
+
"m_0_10_9_repair_templates",
|
|
56
|
+
"m_0_10_12_constitution_cleanup",
|
|
57
|
+
"m_0_10_14_update_implement_slash_command",
|
|
58
|
+
"m_0_11_0_workspace_per_wp",
|
|
59
|
+
"m_0_11_1_improved_workflow_templates",
|
|
60
|
+
"m_0_11_1_update_implement_slash_command",
|
|
61
|
+
"m_0_11_2_improved_workflow_templates",
|
|
62
|
+
"m_0_11_3_workflow_agent_flag",
|
|
63
|
+
"m_0_12_0_documentation_mission",
|
|
64
|
+
"m_0_12_1_remove_kitty_specs_from_gitignore",
|
|
65
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Base migration class for Spec Kitty upgrade system."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class MigrationResult:
|
|
13
|
+
"""Result of a migration operation."""
|
|
14
|
+
|
|
15
|
+
success: bool
|
|
16
|
+
changes_made: List[str] = field(default_factory=list)
|
|
17
|
+
errors: List[str] = field(default_factory=list)
|
|
18
|
+
warnings: List[str] = field(default_factory=list)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseMigration(ABC):
|
|
22
|
+
"""Base class for all migrations.
|
|
23
|
+
|
|
24
|
+
Migrations should:
|
|
25
|
+
1. Be idempotent (safe to run multiple times)
|
|
26
|
+
2. Check preconditions before applying
|
|
27
|
+
3. Report what changes were made
|
|
28
|
+
4. Handle dry-run mode
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
# Migration identifier (e.g., "0.6.5_commands_rename")
|
|
32
|
+
# Format: {version}_{short_description}
|
|
33
|
+
migration_id: str = ""
|
|
34
|
+
|
|
35
|
+
# Human-readable description
|
|
36
|
+
description: str = ""
|
|
37
|
+
|
|
38
|
+
# Target version this migration brings project to
|
|
39
|
+
target_version: str = ""
|
|
40
|
+
|
|
41
|
+
# Minimum version this migration can be applied from (optional)
|
|
42
|
+
# If None, detection is used
|
|
43
|
+
min_version: Optional[str] = None
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def detect(self, project_path: Path) -> bool:
|
|
47
|
+
"""Detect if this migration is needed based on project state.
|
|
48
|
+
|
|
49
|
+
Returns True if the project has the OLD state that needs migration.
|
|
50
|
+
This is used for heuristic detection when metadata is missing.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
project_path: Root of the project (.kittify parent)
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
True if migration is needed
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
def can_apply(self, project_path: Path) -> tuple[bool, str]:
|
|
61
|
+
"""Check if migration can be safely applied.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
project_path: Root of the project
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
(can_apply, reason) - True if safe, False with explanation if not
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
@abstractmethod
|
|
71
|
+
def apply(self, project_path: Path, dry_run: bool = False) -> MigrationResult:
|
|
72
|
+
"""Apply the migration.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
project_path: Root of the project (.kittify parent)
|
|
76
|
+
dry_run: If True, only simulate changes
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
MigrationResult with details of what was changed
|
|
80
|
+
"""
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"""Migration: Remove bash scripts and update templates to use Python CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, List, Tuple
|
|
9
|
+
|
|
10
|
+
from ..registry import MigrationRegistry
|
|
11
|
+
from .base import BaseMigration, MigrationResult
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@MigrationRegistry.register
|
|
15
|
+
class PythonOnlyMigration(BaseMigration):
|
|
16
|
+
"""Migrate from bash scripts to Python-only CLI commands.
|
|
17
|
+
|
|
18
|
+
As of v0.10.0, all spec-kitty commands are available through the
|
|
19
|
+
`spec-kitty agent` CLI namespace. Bash wrapper scripts in
|
|
20
|
+
`.kittify/scripts/bash/` are replaced with Python implementations.
|
|
21
|
+
|
|
22
|
+
This migration:
|
|
23
|
+
1. Detects and removes bash scripts from .kittify/scripts/bash/
|
|
24
|
+
2. Updates slash command templates to use `spec-kitty agent` commands
|
|
25
|
+
3. Cleans up bash scripts in worktrees
|
|
26
|
+
4. Detects custom modifications and warns users
|
|
27
|
+
5. Is idempotent (safe to run multiple times)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
migration_id = "0.10.0_python_only"
|
|
31
|
+
description = "Remove bash scripts and update templates to use Python CLI"
|
|
32
|
+
target_version = "0.10.0"
|
|
33
|
+
|
|
34
|
+
# Bash scripts that should be removed (package scripts only)
|
|
35
|
+
PACKAGE_SCRIPTS = (
|
|
36
|
+
"common.sh",
|
|
37
|
+
"create-new-feature.sh",
|
|
38
|
+
"check-prerequisites.sh",
|
|
39
|
+
"setup-plan.sh",
|
|
40
|
+
"update-agent-context.sh",
|
|
41
|
+
"accept-feature.sh",
|
|
42
|
+
"merge-feature.sh",
|
|
43
|
+
"tasks-move-to-lane.sh",
|
|
44
|
+
"tasks-list-lanes.sh",
|
|
45
|
+
"mark-task-status.sh",
|
|
46
|
+
"tasks-add-history-entry.sh",
|
|
47
|
+
"tasks-rollback-move.sh",
|
|
48
|
+
"validate-task-workflow.sh",
|
|
49
|
+
"move-task-to-doing.sh",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Bash → Python command mappings for template updates
|
|
53
|
+
COMMAND_REPLACEMENTS = {
|
|
54
|
+
# Feature management
|
|
55
|
+
r"\.kittify/scripts/bash/create-new-feature\.sh": "spec-kitty agent create-feature",
|
|
56
|
+
r"scripts/bash/create-new-feature\.sh": "spec-kitty agent create-feature",
|
|
57
|
+
r"\.kittify/scripts/bash/check-prerequisites\.sh": "spec-kitty agent check-prerequisites",
|
|
58
|
+
r"scripts/bash/check-prerequisites\.sh": "spec-kitty agent check-prerequisites",
|
|
59
|
+
r"\.kittify/scripts/bash/setup-plan\.sh": "spec-kitty agent setup-plan",
|
|
60
|
+
r"scripts/bash/setup-plan\.sh": "spec-kitty agent setup-plan",
|
|
61
|
+
r"\.kittify/scripts/bash/update-agent-context\.sh": "spec-kitty agent update-context",
|
|
62
|
+
r"scripts/bash/update-agent-context\.sh": "spec-kitty agent update-context",
|
|
63
|
+
r"\.kittify/scripts/bash/accept-feature\.sh": "spec-kitty agent feature accept",
|
|
64
|
+
r"scripts/bash/accept-feature\.sh": "spec-kitty agent feature accept",
|
|
65
|
+
r"\.kittify/scripts/bash/merge-feature\.sh": "spec-kitty agent feature merge",
|
|
66
|
+
r"scripts/bash/merge-feature\.sh": "spec-kitty agent feature merge",
|
|
67
|
+
|
|
68
|
+
# Task workflow
|
|
69
|
+
r"\.kittify/scripts/bash/tasks-move-to-lane\.sh": "spec-kitty agent move-task",
|
|
70
|
+
r"scripts/bash/tasks-move-to-lane\.sh": "spec-kitty agent move-task",
|
|
71
|
+
r"\.kittify/scripts/bash/tasks-list-lanes\.sh": "spec-kitty agent list-tasks",
|
|
72
|
+
r"scripts/bash/tasks-list-lanes\.sh": "spec-kitty agent list-tasks",
|
|
73
|
+
r"\.kittify/scripts/bash/mark-task-status\.sh": "spec-kitty agent mark-status",
|
|
74
|
+
r"scripts/bash/mark-task-status\.sh": "spec-kitty agent mark-status",
|
|
75
|
+
r"\.kittify/scripts/bash/tasks-add-history-entry\.sh": "spec-kitty agent add-history",
|
|
76
|
+
r"scripts/bash/tasks-add-history-entry\.sh": "spec-kitty agent add-history",
|
|
77
|
+
r"\.kittify/scripts/bash/tasks-rollback-move\.sh": "spec-kitty agent rollback-move",
|
|
78
|
+
r"scripts/bash/tasks-rollback-move\.sh": "spec-kitty agent rollback-move",
|
|
79
|
+
r"\.kittify/scripts/bash/validate-task-workflow\.sh": "spec-kitty agent validate-workflow",
|
|
80
|
+
r"scripts/bash/validate-task-workflow\.sh": "spec-kitty agent validate-workflow",
|
|
81
|
+
r"\.kittify/scripts/bash/move-task-to-doing\.sh": "spec-kitty agent move-task",
|
|
82
|
+
r"scripts/bash/move-task-to-doing\.sh": "spec-kitty agent move-task",
|
|
83
|
+
|
|
84
|
+
# Legacy tasks_cli.py references
|
|
85
|
+
r"tasks_cli\.py move": "spec-kitty agent move-task",
|
|
86
|
+
r"tasks_cli\.py list": "spec-kitty agent list-tasks",
|
|
87
|
+
r"tasks_cli\.py mark": "spec-kitty agent mark-status",
|
|
88
|
+
r"tasks_cli\.py history": "spec-kitty agent add-history",
|
|
89
|
+
r"tasks_cli\.py rollback": "spec-kitty agent rollback-move",
|
|
90
|
+
r"tasks_cli\.py validate": "spec-kitty agent validate-workflow",
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def detect(self, project_path: Path) -> bool:
|
|
94
|
+
"""Check if bash scripts still exist in user's .kittify directory."""
|
|
95
|
+
kittify_bash = project_path / ".kittify" / "scripts" / "bash"
|
|
96
|
+
|
|
97
|
+
if not kittify_bash.exists():
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
# Check if ANY .sh files exist (not just known scripts)
|
|
101
|
+
# This catches custom scripts and ensures complete cleanup
|
|
102
|
+
bash_scripts = list(kittify_bash.glob("*.sh"))
|
|
103
|
+
return len(bash_scripts) > 0
|
|
104
|
+
|
|
105
|
+
def can_apply(self, project_path: Path) -> tuple[bool, str]:
|
|
106
|
+
"""Migration can always be applied if bash scripts are detected."""
|
|
107
|
+
return True, ""
|
|
108
|
+
|
|
109
|
+
def apply(self, project_path: Path, dry_run: bool = False) -> MigrationResult:
|
|
110
|
+
"""Remove bash scripts and update templates."""
|
|
111
|
+
changes: List[str] = []
|
|
112
|
+
warnings: List[str] = []
|
|
113
|
+
errors: List[str] = []
|
|
114
|
+
|
|
115
|
+
# Step 1: Detect and remove bash scripts from .kittify
|
|
116
|
+
bash_changes, bash_warnings = self._remove_bash_scripts(
|
|
117
|
+
project_path, dry_run
|
|
118
|
+
)
|
|
119
|
+
changes.extend(bash_changes)
|
|
120
|
+
warnings.extend(bash_warnings)
|
|
121
|
+
|
|
122
|
+
# Step 2: Clean up bash scripts in worktrees
|
|
123
|
+
worktree_changes = self._cleanup_worktree_bash_scripts(
|
|
124
|
+
project_path, dry_run
|
|
125
|
+
)
|
|
126
|
+
changes.extend(worktree_changes)
|
|
127
|
+
|
|
128
|
+
# Step 2.5: Remove obsolete task helpers
|
|
129
|
+
tasks_changes, tasks_warnings = self._remove_tasks_helpers(
|
|
130
|
+
project_path, dry_run
|
|
131
|
+
)
|
|
132
|
+
changes.extend(tasks_changes)
|
|
133
|
+
warnings.extend(tasks_warnings)
|
|
134
|
+
|
|
135
|
+
# Step 3: Update slash command templates
|
|
136
|
+
template_changes, template_errors = self._update_command_templates(
|
|
137
|
+
project_path, dry_run
|
|
138
|
+
)
|
|
139
|
+
changes.extend(template_changes)
|
|
140
|
+
errors.extend(template_errors)
|
|
141
|
+
|
|
142
|
+
# Note: Custom script detection now happens in _remove_bash_scripts()
|
|
143
|
+
# before deletion, so users get warnings about custom scripts
|
|
144
|
+
|
|
145
|
+
success = len(errors) == 0
|
|
146
|
+
return MigrationResult(
|
|
147
|
+
success=success,
|
|
148
|
+
changes_made=changes,
|
|
149
|
+
errors=errors,
|
|
150
|
+
warnings=warnings,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def _remove_bash_scripts(
|
|
154
|
+
self, project_path: Path, dry_run: bool
|
|
155
|
+
) -> Tuple[List[str], List[str]]:
|
|
156
|
+
"""Remove bash scripts from .kittify/scripts/bash/."""
|
|
157
|
+
changes: List[str] = []
|
|
158
|
+
warnings: List[str] = []
|
|
159
|
+
|
|
160
|
+
kittify_bash = project_path / ".kittify" / "scripts" / "bash"
|
|
161
|
+
|
|
162
|
+
if not kittify_bash.exists():
|
|
163
|
+
warnings.append("No .kittify/scripts/bash/ directory found - already migrated?")
|
|
164
|
+
return changes, warnings
|
|
165
|
+
|
|
166
|
+
# First, detect custom scripts (not in PACKAGE_SCRIPTS) and warn user
|
|
167
|
+
all_bash_scripts = list(kittify_bash.glob("*.sh"))
|
|
168
|
+
custom_scripts = [s for s in all_bash_scripts if s.name not in self.PACKAGE_SCRIPTS]
|
|
169
|
+
|
|
170
|
+
if custom_scripts:
|
|
171
|
+
custom_names = [s.name for s in custom_scripts]
|
|
172
|
+
warnings.append(f"Custom bash scripts detected: {', '.join(custom_names)}")
|
|
173
|
+
warnings.append(
|
|
174
|
+
"These custom scripts will be removed as part of the migration to Python-only. "
|
|
175
|
+
"If you need this functionality, please migrate it manually before upgrading."
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Now delete ALL .sh files (including custom ones)
|
|
179
|
+
# This ensures complete cleanup, matching PowerShell/worktree behavior
|
|
180
|
+
scripts_removed = len(all_bash_scripts)
|
|
181
|
+
|
|
182
|
+
for script in all_bash_scripts:
|
|
183
|
+
if dry_run:
|
|
184
|
+
changes.append(f"Would remove: .kittify/scripts/bash/{script.name}")
|
|
185
|
+
else:
|
|
186
|
+
script.unlink()
|
|
187
|
+
changes.append(f"Removed: .kittify/scripts/bash/{script.name}")
|
|
188
|
+
|
|
189
|
+
# Remove PowerShell equivalents if they exist
|
|
190
|
+
kittify_ps = project_path / ".kittify" / "scripts" / "powershell"
|
|
191
|
+
if kittify_ps.exists():
|
|
192
|
+
ps_scripts = list(kittify_ps.glob("*.ps1"))
|
|
193
|
+
if ps_scripts:
|
|
194
|
+
for ps_script in ps_scripts:
|
|
195
|
+
if dry_run:
|
|
196
|
+
changes.append(f"Would remove: .kittify/scripts/powershell/{ps_script.name}")
|
|
197
|
+
else:
|
|
198
|
+
ps_script.unlink()
|
|
199
|
+
changes.append(f"Removed: .kittify/scripts/powershell/{ps_script.name}")
|
|
200
|
+
scripts_removed += 1
|
|
201
|
+
|
|
202
|
+
# Remove directories if empty
|
|
203
|
+
if not dry_run:
|
|
204
|
+
if kittify_bash.exists() and not any(kittify_bash.iterdir()):
|
|
205
|
+
kittify_bash.rmdir()
|
|
206
|
+
changes.append("Removed empty: .kittify/scripts/bash/")
|
|
207
|
+
if kittify_ps.exists() and not any(kittify_ps.iterdir()):
|
|
208
|
+
kittify_ps.rmdir()
|
|
209
|
+
changes.append("Removed empty: .kittify/scripts/powershell/")
|
|
210
|
+
|
|
211
|
+
if scripts_removed > 0:
|
|
212
|
+
changes.append(f"Total scripts removed: {scripts_removed}")
|
|
213
|
+
else:
|
|
214
|
+
warnings.append("No bash scripts found to remove - already migrated?")
|
|
215
|
+
|
|
216
|
+
return changes, warnings
|
|
217
|
+
|
|
218
|
+
def _cleanup_worktree_bash_scripts(
|
|
219
|
+
self, project_path: Path, dry_run: bool
|
|
220
|
+
) -> List[str]:
|
|
221
|
+
"""Remove bash scripts from all worktrees."""
|
|
222
|
+
changes: List[str] = []
|
|
223
|
+
|
|
224
|
+
worktrees_dir = project_path / ".worktrees"
|
|
225
|
+
if not worktrees_dir.exists():
|
|
226
|
+
return changes
|
|
227
|
+
|
|
228
|
+
for worktree in sorted(worktrees_dir.iterdir()):
|
|
229
|
+
if not worktree.is_dir():
|
|
230
|
+
continue
|
|
231
|
+
|
|
232
|
+
wt_bash = worktree / ".kittify" / "scripts" / "bash"
|
|
233
|
+
if not wt_bash.exists():
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
scripts_found = list(wt_bash.glob("*.sh"))
|
|
237
|
+
if scripts_found:
|
|
238
|
+
if dry_run:
|
|
239
|
+
changes.append(f"Would remove {len(scripts_found)} scripts from worktree: {worktree.name}")
|
|
240
|
+
else:
|
|
241
|
+
for script in scripts_found:
|
|
242
|
+
script.unlink()
|
|
243
|
+
if not any(wt_bash.iterdir()):
|
|
244
|
+
wt_bash.rmdir()
|
|
245
|
+
changes.append(f"Removed {len(scripts_found)} scripts from worktree: {worktree.name}")
|
|
246
|
+
|
|
247
|
+
return changes
|
|
248
|
+
|
|
249
|
+
def _remove_tasks_helpers(
|
|
250
|
+
self, project_path: Path, dry_run: bool
|
|
251
|
+
) -> Tuple[List[str], List[str]]:
|
|
252
|
+
"""Remove obsolete .kittify/scripts/tasks/ directory."""
|
|
253
|
+
changes: List[str] = []
|
|
254
|
+
warnings: List[str] = []
|
|
255
|
+
|
|
256
|
+
tasks_dir = project_path / ".kittify" / "scripts" / "tasks"
|
|
257
|
+
if not tasks_dir.exists():
|
|
258
|
+
return changes, warnings
|
|
259
|
+
|
|
260
|
+
if dry_run:
|
|
261
|
+
changes.append("Would remove .kittify/scripts/tasks/ (obsolete task helpers)")
|
|
262
|
+
return changes, warnings
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
shutil.rmtree(tasks_dir)
|
|
266
|
+
changes.append("Removed .kittify/scripts/tasks/ (obsolete task helpers)")
|
|
267
|
+
except OSError as e:
|
|
268
|
+
warnings.append(f"Failed to remove .kittify/scripts/tasks/: {e}")
|
|
269
|
+
|
|
270
|
+
return changes, warnings
|
|
271
|
+
|
|
272
|
+
def _update_command_templates(
|
|
273
|
+
self, project_path: Path, dry_run: bool
|
|
274
|
+
) -> Tuple[List[str], List[str]]:
|
|
275
|
+
"""Update slash command templates to use Python CLI."""
|
|
276
|
+
changes: List[str] = []
|
|
277
|
+
errors: List[str] = []
|
|
278
|
+
|
|
279
|
+
# Templates in .kittify/templates/command-templates/
|
|
280
|
+
templates_dir = project_path / ".kittify" / "templates" / "command-templates"
|
|
281
|
+
|
|
282
|
+
if not templates_dir.exists():
|
|
283
|
+
# Templates not in expected location - might be from old package install
|
|
284
|
+
# This is expected for projects initialized with older package versions
|
|
285
|
+
# Templates will be fixed when they upgrade to v0.10.9+ which has repair migration
|
|
286
|
+
changes.append(
|
|
287
|
+
"Templates directory not found at .kittify/templates/command-templates/. "
|
|
288
|
+
"This is expected for projects initialized with older package versions. "
|
|
289
|
+
"Run 'spec-kitty upgrade' after upgrading to v0.10.9+ to repair templates."
|
|
290
|
+
)
|
|
291
|
+
return changes, [] # No errors, defer to repair migration
|
|
292
|
+
|
|
293
|
+
templates_updated = 0
|
|
294
|
+
for template_path in sorted(templates_dir.glob("*.md")):
|
|
295
|
+
try:
|
|
296
|
+
updated, replacements = self._update_template_file(
|
|
297
|
+
template_path, dry_run
|
|
298
|
+
)
|
|
299
|
+
if updated:
|
|
300
|
+
templates_updated += 1
|
|
301
|
+
if dry_run:
|
|
302
|
+
changes.append(f"Would update: {template_path.name} ({replacements} replacements)")
|
|
303
|
+
else:
|
|
304
|
+
changes.append(f"Updated: {template_path.name} ({replacements} replacements)")
|
|
305
|
+
except Exception as e:
|
|
306
|
+
errors.append(f"Error updating {template_path.name}: {e}")
|
|
307
|
+
|
|
308
|
+
if templates_updated > 0:
|
|
309
|
+
changes.append(f"Total templates updated: {templates_updated}")
|
|
310
|
+
|
|
311
|
+
return changes, errors
|
|
312
|
+
|
|
313
|
+
def _update_template_file(
|
|
314
|
+
self, template_path: Path, dry_run: bool
|
|
315
|
+
) -> Tuple[bool, int]:
|
|
316
|
+
"""Update a single template file with bash → Python replacements."""
|
|
317
|
+
content = template_path.read_text(encoding="utf-8")
|
|
318
|
+
original_content = content
|
|
319
|
+
replacements_made = 0
|
|
320
|
+
|
|
321
|
+
# Apply all replacements
|
|
322
|
+
for pattern, replacement in self.COMMAND_REPLACEMENTS.items():
|
|
323
|
+
new_content, count = re.subn(pattern, replacement, content)
|
|
324
|
+
if count > 0:
|
|
325
|
+
content = new_content
|
|
326
|
+
replacements_made += count
|
|
327
|
+
|
|
328
|
+
# Write if changes were made
|
|
329
|
+
if content != original_content:
|
|
330
|
+
if not dry_run:
|
|
331
|
+
template_path.write_text(content, encoding="utf-8")
|
|
332
|
+
return True, replacements_made
|
|
333
|
+
|
|
334
|
+
return False, 0
|
|
335
|
+
|
|
336
|
+
def _detect_custom_modifications(self, project_path: Path) -> List[str]:
|
|
337
|
+
"""Detect custom modifications to bash scripts."""
|
|
338
|
+
warnings: List[str] = []
|
|
339
|
+
|
|
340
|
+
kittify_bash = project_path / ".kittify" / "scripts" / "bash"
|
|
341
|
+
if not kittify_bash.exists():
|
|
342
|
+
return warnings
|
|
343
|
+
|
|
344
|
+
# Look for non-standard scripts (not in PACKAGE_SCRIPTS)
|
|
345
|
+
custom_scripts = []
|
|
346
|
+
for script_path in kittify_bash.glob("*.sh"):
|
|
347
|
+
if script_path.name not in self.PACKAGE_SCRIPTS:
|
|
348
|
+
custom_scripts.append(script_path.name)
|
|
349
|
+
|
|
350
|
+
if custom_scripts:
|
|
351
|
+
warnings.append(
|
|
352
|
+
f"Custom bash scripts detected: {', '.join(custom_scripts)}"
|
|
353
|
+
)
|
|
354
|
+
warnings.append(
|
|
355
|
+
"These scripts will NOT be removed automatically. "
|
|
356
|
+
"Please migrate them manually or remove if no longer needed."
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return warnings
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Migration: Remove mission-specific constitution directories."""
|
|
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 ConstitutionCleanupMigration(BaseMigration):
|
|
15
|
+
"""Remove mission-specific constitution directories.
|
|
16
|
+
|
|
17
|
+
As of 0.10.12, spec-kitty uses only project-level constitutions
|
|
18
|
+
at .kittify/memory/constitution.md. Mission-specific constitutions
|
|
19
|
+
in .kittify/missions/*/constitution/ are removed.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
migration_id = "0.10.12_constitution_cleanup"
|
|
23
|
+
description = "Remove mission-specific constitution directories"
|
|
24
|
+
target_version = "0.10.12"
|
|
25
|
+
|
|
26
|
+
def detect(self, project_path: Path) -> bool:
|
|
27
|
+
"""Check if any mission has a constitution directory."""
|
|
28
|
+
missions_dir = project_path / ".kittify" / "missions"
|
|
29
|
+
if not missions_dir.exists():
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
for mission_dir in missions_dir.iterdir():
|
|
33
|
+
if mission_dir.is_dir() and (mission_dir / "constitution").exists():
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
def can_apply(self, project_path: Path) -> tuple[bool, str]:
|
|
39
|
+
"""Check if migration can be applied."""
|
|
40
|
+
kittify_dir = project_path / ".kittify"
|
|
41
|
+
if not kittify_dir.exists():
|
|
42
|
+
return False, "No .kittify directory (not a spec-kitty project)"
|
|
43
|
+
|
|
44
|
+
return True, ""
|
|
45
|
+
|
|
46
|
+
def apply(self, project_path: Path, dry_run: bool = False) -> MigrationResult:
|
|
47
|
+
"""Remove constitution directories from all missions."""
|
|
48
|
+
changes: List[str] = []
|
|
49
|
+
warnings: List[str] = []
|
|
50
|
+
errors: List[str] = []
|
|
51
|
+
|
|
52
|
+
missions_dir = project_path / ".kittify" / "missions"
|
|
53
|
+
if not missions_dir.exists():
|
|
54
|
+
return MigrationResult(
|
|
55
|
+
success=True,
|
|
56
|
+
changes_made=[],
|
|
57
|
+
errors=[],
|
|
58
|
+
warnings=[],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
removed_from: List[str] = []
|
|
62
|
+
for mission_dir in missions_dir.iterdir():
|
|
63
|
+
if not mission_dir.is_dir():
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
constitution_dir = mission_dir / "constitution"
|
|
67
|
+
if not constitution_dir.exists():
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
if dry_run:
|
|
71
|
+
changes.append(f"Would remove {mission_dir.name}/constitution/")
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
shutil.rmtree(constitution_dir)
|
|
76
|
+
changes.append(f"Removed {mission_dir.name}/constitution/")
|
|
77
|
+
removed_from.append(mission_dir.name)
|
|
78
|
+
except OSError as e:
|
|
79
|
+
errors.append(
|
|
80
|
+
f"Failed to remove {mission_dir.name}/constitution/: {e}"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if removed_from:
|
|
84
|
+
warnings.append(
|
|
85
|
+
"Mission-specific constitutions removed from: "
|
|
86
|
+
f"{', '.join(removed_from)}. "
|
|
87
|
+
"Spec-kitty now uses a single project-level constitution at "
|
|
88
|
+
".kittify/memory/constitution.md."
|
|
89
|
+
)
|
|
90
|
+
elif not changes:
|
|
91
|
+
changes.append("No mission-specific constitutions found (already clean)")
|
|
92
|
+
|
|
93
|
+
success = len(errors) == 0
|
|
94
|
+
return MigrationResult(
|
|
95
|
+
success=success,
|
|
96
|
+
changes_made=changes,
|
|
97
|
+
errors=errors,
|
|
98
|
+
warnings=warnings,
|
|
99
|
+
)
|