Move file recording to finalize_setup() — called after init pipeline writes files

Address code review: setup() now only creates directories, while
finalize_setup() (on base class) scans the agent's commands_dir
for all files and records them. This ensures files are tracked
after the full init pipeline has written them, not before.

- Add AgentBootstrap.finalize_setup() that scans commands_dir
- Remove premature record_installed_files() from all 25 setup() methods
- agent_switch calls finalize_setup() after setup() completes
- Update test helper to match new pattern

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/779eabf6-21d5-428b-9f01-dd363df4c84a
This commit is contained in:
copilot-swe-agent[bot]
2026-03-20 21:20:22 +00:00
committed by GitHub
parent b5a5e3fc35
commit a63c248c80
28 changed files with 53 additions and 106 deletions

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Agy(AgentBootstrap):
@@ -16,9 +16,6 @@ class Agy(AgentBootstrap):
"""Install Antigravity agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Antigravity agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Amp(AgentBootstrap):
@@ -16,9 +16,6 @@ class Amp(AgentBootstrap):
"""Install Amp agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Amp agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Auggie(AgentBootstrap):
@@ -16,9 +16,6 @@ class Auggie(AgentBootstrap):
"""Install Auggie CLI agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Auggie CLI agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Bob(AgentBootstrap):
@@ -16,9 +16,6 @@ class Bob(AgentBootstrap):
"""Install IBM Bob agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove IBM Bob agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Claude(AgentBootstrap):
@@ -16,9 +16,6 @@ class Claude(AgentBootstrap):
"""Install Claude Code agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Claude Code agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Codebuddy(AgentBootstrap):
@@ -16,9 +16,6 @@ class Codebuddy(AgentBootstrap):
"""Install CodeBuddy agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove CodeBuddy agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Codex(AgentBootstrap):
@@ -16,9 +16,6 @@ class Codex(AgentBootstrap):
"""Install Codex CLI agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Codex CLI agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Copilot(AgentBootstrap):
@@ -16,9 +16,6 @@ class Copilot(AgentBootstrap):
"""Install GitHub Copilot agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove GitHub Copilot agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class CursorAgent(AgentBootstrap):
@@ -16,9 +16,6 @@ class CursorAgent(AgentBootstrap):
"""Install Cursor agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Cursor agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Gemini(AgentBootstrap):
@@ -16,9 +16,6 @@ class Gemini(AgentBootstrap):
"""Install Gemini CLI agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Gemini CLI agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Iflow(AgentBootstrap):
@@ -16,9 +16,6 @@ class Iflow(AgentBootstrap):
"""Install iFlow CLI agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove iFlow CLI agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Junie(AgentBootstrap):
@@ -16,9 +16,6 @@ class Junie(AgentBootstrap):
"""Install Junie agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Junie agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Kilocode(AgentBootstrap):
@@ -16,9 +16,6 @@ class Kilocode(AgentBootstrap):
"""Install Kilo Code agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Kilo Code agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Kimi(AgentBootstrap):
@@ -16,9 +16,6 @@ class Kimi(AgentBootstrap):
"""Install Kimi Code agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Kimi Code agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class KiroCli(AgentBootstrap):
@@ -16,9 +16,6 @@ class KiroCli(AgentBootstrap):
"""Install Kiro CLI agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Kiro CLI agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Opencode(AgentBootstrap):
@@ -16,9 +16,6 @@ class Opencode(AgentBootstrap):
"""Install opencode agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove opencode agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Pi(AgentBootstrap):
@@ -16,9 +16,6 @@ class Pi(AgentBootstrap):
"""Install Pi Coding Agent agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Pi Coding Agent agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Qodercli(AgentBootstrap):
@@ -16,9 +16,6 @@ class Qodercli(AgentBootstrap):
"""Install Qoder CLI agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Qoder CLI agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Qwen(AgentBootstrap):
@@ -16,9 +16,6 @@ class Qwen(AgentBootstrap):
"""Install Qwen Code agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Qwen Code agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Roo(AgentBootstrap):
@@ -16,9 +16,6 @@ class Roo(AgentBootstrap):
"""Install Roo Code agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Roo Code agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Shai(AgentBootstrap):
@@ -16,9 +16,6 @@ class Shai(AgentBootstrap):
"""Install SHAI agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove SHAI agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Tabnine(AgentBootstrap):
@@ -16,9 +16,6 @@ class Tabnine(AgentBootstrap):
"""Install Tabnine CLI agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Tabnine CLI agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Trae(AgentBootstrap):
@@ -16,9 +16,6 @@ class Trae(AgentBootstrap):
"""Install Trae agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Trae agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Vibe(AgentBootstrap):
@@ -16,9 +16,6 @@ class Vibe(AgentBootstrap):
"""Install Mistral Vibe agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Mistral Vibe agent files from the project.

View File

@@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict
from specify_cli.agent_pack import AgentBootstrap, record_installed_files, remove_tracked_files
from specify_cli.agent_pack import AgentBootstrap, remove_tracked_files
class Windsurf(AgentBootstrap):
@@ -16,9 +16,6 @@ class Windsurf(AgentBootstrap):
"""Install Windsurf agent files into the project."""
commands_dir = project_path / self.AGENT_DIR / self.COMMANDS_SUBDIR
commands_dir.mkdir(parents=True, exist_ok=True)
# Record installed files for tracked teardown
installed = [p for p in commands_dir.rglob("*") if p.is_file()]
record_installed_files(project_path, self.manifest.id, installed)
def teardown(self, project_path: Path, *, force: bool = False) -> None:
"""Remove Windsurf agent files from the project.