mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
* Stage 6: Complete migration — remove legacy scaffold path (#1924) Remove the legacy GitHub download and offline scaffold code paths. All 26 agents now use the integration system exclusively. Code removal (~1073 lines from __init__.py): - download_template_from_github(), download_and_extract_template() - scaffold_from_core_pack(), _locate_release_script() - install_ai_skills(), _get_skills_dir (restored slim version for presets) - _has_bundled_skills(), _migrate_legacy_kimi_dotted_skills() - AGENT_SKILLS_MIGRATIONS, _handle_agent_skills_migration() - _parse_rate_limit_headers(), _format_rate_limit_error() - Three-way branch in init() collapsed to integration-only Config derivation (single source of truth): - AGENT_CONFIG derived from INTEGRATION_REGISTRY (replaced 180-line dict) - CommandRegistrar.AGENT_CONFIGS derived from INTEGRATION_REGISTRY (replaced 160-line dict) - Backward-compat constants kept for presets/extensions: SKILL_DESCRIPTIONS, NATIVE_SKILLS_AGENTS, DEFAULT_SKILLS_DIR Release pipeline cleanup: - Deleted create-release-packages.sh/.ps1 (948 lines of ZIP packaging) - Deleted create-github-release.sh, generate-release-notes.sh - Deleted simulate-release.sh, get-next-version.sh, update-version.sh - Removed .github/workflows/scripts/ directory entirely - release.yml is now self-contained: check, notes, release all inlined - Install instructions use uv tool install with version tag Test cleanup: - Deleted test_ai_skills.py (tested removed functions) - Deleted test_core_pack_scaffold.py (tested removed scaffold) - Cleaned test_agent_config_consistency.py (removed 19 release-script tests) - Fixed test_branch_numbering.py (removed dead monkeypatches) - Updated auto-promote tests (verify files created, not tip messages) 1089 tests pass, 0 failures, ruff clean. * fix: resolve merge conflicts with #2051 (claude as skills) - Fix circular import: move CommandRegistrar import in claude integration to inside method bodies (was at module level) - Lazy-populate AGENT_CONFIGS via _ensure_configs() to avoid circular import at class definition time - Set claude registrar_config to .claude/commands (extension/preset target) since the integration handles .claude/skills in setup() - Update tests from #2051 to match: registrar_config assertions, remove --integration tip assertions, remove install_ai_skills mocks 1086 tests pass. * fix: properly preserve claude skills migration from #2051 Restore ClaudeIntegration.registrar_config to .claude/skills (not .claude/commands) so extension/preset registrations write to the correct skills directory. Update tests that simulate claude setup to use .claude/skills and check for SKILL.md layout. Some tests still need updating for the full skills path — 10 remaining failures from the #2051 test expectations around the extension/preset skill registration flow. WIP: 1076/1086 pass. * fix: properly handle SKILL.md paths in extension update rollback and tests Fix extension update rollback using _compute_output_name() for SKILL.md agents (converts dots to hyphens in skill directory names). Previously the backup and cleanup code constructed paths with raw command names (e.g. speckit.test-ext.hello/SKILL.md) instead of the correct computed names (speckit-test-ext-hello/SKILL.md). Test fixes for claude skills migration: - Update claude tests to use .claude/skills paths and SKILL.md layout - Use qwen (not claude) for skills-guard tests since claude's agent dir IS the skills dir — creating it triggers command registration - Fix test_extension_command_registered_when_extension_present to check skills path format 1086 tests pass, 0 failures, ruff clean. * fix: address PR review — lazy init, assertions, deprecated flags - _ensure_configs(): catch ImportError (not Exception), don't set _configs_loaded on failure so retries work - Move _ensure_configs() before unregister loop (not inside it) - Module-level try/except catches ImportError specifically - Remove tautology assertion (or True) in test_extensions.py - Strengthen preset provenance assertion to check source: field - Mark --offline, --skip-tls, --debug, --github-token as hidden deprecated no-ops in init() 1086 tests pass. * fix: remove deleted release scripts from pyproject.toml force-include Removes force-include entries for create-release-packages.sh/.ps1 which were deleted but still referenced in [tool.hatch.build].
This commit is contained in:
@@ -1772,19 +1772,20 @@ class TestSelfTestPreset:
|
||||
assert "preset:self-test" in content
|
||||
|
||||
def test_self_test_registers_commands_for_claude(self, project_dir):
|
||||
"""Test that installing self-test registers commands in .claude/commands/."""
|
||||
# Create Claude agent directory to simulate Claude being set up
|
||||
claude_dir = project_dir / ".claude" / "commands"
|
||||
"""Test that installing self-test registers skills in .claude/skills/."""
|
||||
# Create Claude skills directory to simulate Claude being set up
|
||||
claude_dir = project_dir / ".claude" / "skills"
|
||||
claude_dir.mkdir(parents=True)
|
||||
|
||||
manager = PresetManager(project_dir)
|
||||
manager.install_from_directory(SELF_TEST_PRESET_DIR, "0.1.5")
|
||||
|
||||
# Check the command was registered
|
||||
cmd_file = claude_dir / "speckit.specify.md"
|
||||
assert cmd_file.exists(), "Command not registered in .claude/commands/"
|
||||
# Check the skill was registered
|
||||
cmd_file = claude_dir / "speckit-specify" / "SKILL.md"
|
||||
assert cmd_file.exists(), "Skill not registered in .claude/skills/"
|
||||
content = cmd_file.read_text()
|
||||
assert "preset:self-test" in content
|
||||
assert "self-test" in content
|
||||
assert "source:" in content # skill frontmatter includes metadata.source
|
||||
|
||||
def test_self_test_registers_commands_for_gemini(self, project_dir):
|
||||
"""Test that installing self-test registers commands in .gemini/commands/ as TOML."""
|
||||
@@ -1804,13 +1805,13 @@ class TestSelfTestPreset:
|
||||
|
||||
def test_self_test_unregisters_commands_on_remove(self, project_dir):
|
||||
"""Test that removing self-test cleans up registered commands."""
|
||||
claude_dir = project_dir / ".claude" / "commands"
|
||||
claude_dir = project_dir / ".claude" / "skills"
|
||||
claude_dir.mkdir(parents=True)
|
||||
|
||||
manager = PresetManager(project_dir)
|
||||
manager.install_from_directory(SELF_TEST_PRESET_DIR, "0.1.5")
|
||||
|
||||
cmd_file = claude_dir / "speckit.specify.md"
|
||||
cmd_file = claude_dir / "speckit-specify" / "SKILL.md"
|
||||
assert cmd_file.exists()
|
||||
|
||||
manager.remove("self-test")
|
||||
@@ -1826,7 +1827,7 @@ class TestSelfTestPreset:
|
||||
|
||||
def test_extension_command_skipped_when_extension_missing(self, project_dir, temp_dir):
|
||||
"""Test that extension command overrides are skipped if the extension isn't installed."""
|
||||
claude_dir = project_dir / ".claude" / "commands"
|
||||
claude_dir = project_dir / ".claude" / "skills"
|
||||
claude_dir.mkdir(parents=True)
|
||||
|
||||
preset_dir = temp_dir / "ext-override-preset"
|
||||
@@ -1869,7 +1870,7 @@ class TestSelfTestPreset:
|
||||
|
||||
def test_extension_command_registered_when_extension_present(self, project_dir, temp_dir):
|
||||
"""Test that extension command overrides ARE registered when the extension is installed."""
|
||||
claude_dir = project_dir / ".claude" / "commands"
|
||||
claude_dir = project_dir / ".claude" / "skills"
|
||||
claude_dir.mkdir(parents=True)
|
||||
(project_dir / ".specify" / "extensions" / "fakeext").mkdir(parents=True)
|
||||
|
||||
@@ -1905,8 +1906,8 @@ class TestSelfTestPreset:
|
||||
manager = PresetManager(project_dir)
|
||||
manager.install_from_directory(preset_dir, "0.1.5")
|
||||
|
||||
cmd_file = claude_dir / "speckit.fakeext.cmd.md"
|
||||
assert cmd_file.exists(), "Command not registered despite extension being present"
|
||||
cmd_file = claude_dir / "speckit-fakeext-cmd" / "SKILL.md"
|
||||
assert cmd_file.exists(), "Skill not registered despite extension being present"
|
||||
|
||||
|
||||
# ===== Init Options and Skills Tests =====
|
||||
@@ -1964,7 +1965,7 @@ class TestPresetSkills:
|
||||
self._create_skill(skills_dir, "speckit-specify")
|
||||
|
||||
# Also create the claude commands dir so commands get registered
|
||||
(project_dir / ".claude" / "commands").mkdir(parents=True, exist_ok=True)
|
||||
(project_dir / ".claude" / "skills").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Install self-test preset (has a command override for speckit.specify)
|
||||
manager = PresetManager(project_dir)
|
||||
@@ -1983,12 +1984,10 @@ class TestPresetSkills:
|
||||
|
||||
def test_skill_not_updated_when_ai_skills_disabled(self, project_dir, temp_dir):
|
||||
"""When --ai-skills was NOT used, preset install should not touch skills."""
|
||||
self._write_init_options(project_dir, ai="claude", ai_skills=False)
|
||||
skills_dir = project_dir / ".claude" / "skills"
|
||||
self._write_init_options(project_dir, ai="qwen", ai_skills=False)
|
||||
skills_dir = project_dir / ".qwen" / "skills"
|
||||
self._create_skill(skills_dir, "speckit-specify", body="untouched")
|
||||
|
||||
(project_dir / ".claude" / "commands").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
manager = PresetManager(project_dir)
|
||||
SELF_TEST_DIR = Path(__file__).parent.parent / "presets" / "self-test"
|
||||
manager.install_from_directory(SELF_TEST_DIR, "0.1.5")
|
||||
@@ -2019,18 +2018,16 @@ class TestPresetSkills:
|
||||
|
||||
def test_skill_not_updated_without_init_options(self, project_dir, temp_dir):
|
||||
"""When no init-options.json exists, preset install should not touch skills."""
|
||||
skills_dir = project_dir / ".claude" / "skills"
|
||||
skills_dir = project_dir / ".qwen" / "skills"
|
||||
self._create_skill(skills_dir, "speckit-specify", body="untouched")
|
||||
|
||||
(project_dir / ".claude" / "commands").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
manager = PresetManager(project_dir)
|
||||
SELF_TEST_DIR = Path(__file__).parent.parent / "presets" / "self-test"
|
||||
manager.install_from_directory(SELF_TEST_DIR, "0.1.5")
|
||||
|
||||
skill_file = skills_dir / "speckit-specify" / "SKILL.md"
|
||||
content = skill_file.read_text()
|
||||
assert "untouched" in content
|
||||
file_content = skill_file.read_text()
|
||||
assert "untouched" in file_content
|
||||
|
||||
def test_skill_restored_on_preset_remove(self, project_dir, temp_dir):
|
||||
"""When a preset is removed, skills should be restored from core templates."""
|
||||
@@ -2038,7 +2035,7 @@ class TestPresetSkills:
|
||||
skills_dir = project_dir / ".claude" / "skills"
|
||||
self._create_skill(skills_dir, "speckit-specify")
|
||||
|
||||
(project_dir / ".claude" / "commands").mkdir(parents=True, exist_ok=True)
|
||||
(project_dir / ".claude" / "skills").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Set up core command template in the project so restoration works
|
||||
core_cmds = project_dir / ".specify" / "templates" / "commands"
|
||||
@@ -2068,7 +2065,7 @@ class TestPresetSkills:
|
||||
self._write_init_options(project_dir, ai="claude", ai_skills=True, script="sh")
|
||||
skills_dir = project_dir / ".claude" / "skills"
|
||||
self._create_skill(skills_dir, "speckit-specify", body="old")
|
||||
(project_dir / ".claude" / "commands").mkdir(parents=True, exist_ok=True)
|
||||
(project_dir / ".claude" / "skills").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
core_cmds = project_dir / ".specify" / "templates" / "commands"
|
||||
core_cmds.mkdir(parents=True, exist_ok=True)
|
||||
@@ -2094,13 +2091,11 @@ class TestPresetSkills:
|
||||
|
||||
def test_skill_not_overridden_when_skill_path_is_file(self, project_dir):
|
||||
"""Preset install should skip non-directory skill targets."""
|
||||
self._write_init_options(project_dir, ai="claude")
|
||||
skills_dir = project_dir / ".claude" / "skills"
|
||||
self._write_init_options(project_dir, ai="qwen")
|
||||
skills_dir = project_dir / ".qwen" / "skills"
|
||||
skills_dir.mkdir(parents=True, exist_ok=True)
|
||||
(skills_dir / "speckit-specify").write_text("not-a-directory")
|
||||
|
||||
(project_dir / ".claude" / "commands").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
manager = PresetManager(project_dir)
|
||||
SELF_TEST_DIR = Path(__file__).parent.parent / "presets" / "self-test"
|
||||
manager.install_from_directory(SELF_TEST_DIR, "0.1.5")
|
||||
@@ -2114,8 +2109,6 @@ class TestPresetSkills:
|
||||
self._write_init_options(project_dir, ai="claude")
|
||||
# Don't create skills dir — simulate --ai-skills never created them
|
||||
|
||||
(project_dir / ".claude" / "commands").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
manager = PresetManager(project_dir)
|
||||
SELF_TEST_DIR = Path(__file__).parent.parent / "presets" / "self-test"
|
||||
manager.install_from_directory(SELF_TEST_DIR, "0.1.5")
|
||||
@@ -2516,16 +2509,15 @@ class TestPresetSkills:
|
||||
init_options.parent.mkdir(parents=True, exist_ok=True)
|
||||
init_options.write_text("[]")
|
||||
|
||||
skills_dir = project_dir / ".claude" / "skills"
|
||||
skills_dir = project_dir / ".qwen" / "skills"
|
||||
self._create_skill(skills_dir, "speckit-specify", body="untouched")
|
||||
(project_dir / ".claude" / "commands").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
manager = PresetManager(project_dir)
|
||||
self_test_dir = Path(__file__).parent.parent / "presets" / "self-test"
|
||||
manager.install_from_directory(self_test_dir, "0.1.5")
|
||||
|
||||
content = (skills_dir / "speckit-specify" / "SKILL.md").read_text()
|
||||
assert "untouched" in content
|
||||
skill_content = (skills_dir / "speckit-specify" / "SKILL.md").read_text()
|
||||
assert "untouched" in skill_content
|
||||
|
||||
|
||||
class TestPresetSetPriority:
|
||||
|
||||
Reference in New Issue
Block a user