Skip to content

Agent catalog: self-bootstrapping agent packs with manifest schema, resolution stack, file-tracked teardown, and CLI#1925

Draft
Copilot wants to merge 11 commits intomainfrom
copilot/agent-catalog-self-bootstrapping
Draft

Agent catalog: self-bootstrapping agent packs with manifest schema, resolution stack, file-tracked teardown, and CLI#1925
Copilot wants to merge 11 commits intomainfrom
copilot/agent-catalog-self-bootstrapping

Conversation

Copy link
Contributor

Copilot AI commented Mar 20, 2026

Adding a new agent today requires changes to 10+ files across 5 languages. This PR introduces the foundational agent pack infrastructure that replaces the monolithic AGENT_CONFIG + case/switch architecture with self-contained, declarative agent packs — while preserving full backward compatibility during migration.

Agent pack module (src/specify_cli/agent_pack.py)

  • AgentManifest — parsed/validated speckit-agent.yml with schema version enforcement
  • AgentBootstrap — abstract base with setup() → List[Path] / teardown(force, files) / finalize_setup(agent_files, extension_files) lifecycle contract
  • 4-level resolution stack: user (~/.specify/agents/) → project (.specify/agents/) → catalog cache → embedded in wheel
  • load_bootstrap() dynamically imports pack's bootstrap module and discovers the AgentBootstrap subclass
  • validate_pack(), export_pack(), list_embedded_agents(), list_all_agents()

setup() owns scaffolding and returns actual installed files

Each bootstrap's setup() calls _scaffold_project(), a base-class helper that invokes scaffold_from_core_pack, snapshots the project directory before and after, and returns every new file created. This means setup() is the single entry point for agent installation — it runs the shared scaffolding and reports what it created.

  • _scaffold_project(project_path, script_type) — calls scaffold_from_core_pack, diffs before/after snapshots, returns List[Path] of all new files
  • collect_installed_files(project_path) — scans the agent's directory tree and returns all files found
  • finalize_setup(agent_files, extension_files) — filters agent_files to only track files under the agent's own directory tree (shared .specify/ infrastructure is not tracked per-agent), scans for additional files from later pipeline steps (skills, extensions), and writes .specify/agent-manifest-<agent_id>.json

Installed-file tracking with SHA-256 hashes

  • finalize_setup(agent_files, extension_files) accepts categorised file lists from the CLI and records them in .specify/agent-manifest-<agent_id>.json with separate agent_files and extension_files sections
  • get_tracked_files() returns an (agent_files, extension_files) tuple so the CLI can retrieve and split the tracked sets
  • check_modified_files() compares current file hashes against the recorded manifest to detect user modifications across both categories
  • remove_tracked_files(force, files) deletes only individual tracked files — directories are never deleted. Accepts an explicit files dict so the CLI can feed tracked file lists directly into teardown. Raises AgentFileModifiedError if any tracked file was modified since installation and force is False
  • _reregister_extension_commands() returns List[Path] of files created by extension registration, which are passed to finalize_setup() as extension_files

CLI-level modification check before teardown

specify agent switch checks for modified files before calling teardown and prompts the user for confirmation. The CLI retrieves the tracked file lists via get_tracked_files(), checks for modifications, lists any changed files, and only proceeds with teardown after the user confirms (or if --force is passed). The file lists are then fed directly into teardown().

--agent flag on specify init

specify init --agent claude opts into the pack-based init flow. The --agent flag:

  • Resolves the agent through the pack system (same 4-level priority stack)
  • Validates the pack exists and can be loaded
  • Routes scaffolding through setup() — the bootstrap owns the scaffolding process, always using bundled assets
  • After all files are created (skills, presets, extensions), calls finalize_setup(agent_files=setup_files) to record every installed file in .specify/agent-manifest-<id>.json
  • Records "agent_pack": true in init-options.json

--agent and --ai are mutually exclusive. --agent accepts the same agent IDs as --ai but additionally enables file tracking for clean teardown when switching agents.

specify init my-project --agent claude
specify init --here --agent gemini --ai-skills

25 embedded agent packs (src/specify_cli/core_pack/agents/<id>/)

Each pack contains speckit-agent.yml and bootstrap.py. Manifests carry command registration metadata (commands_dir, format, arg_placeholder, file_extension) sufficient for CommandRegistrar to render extension commands without importing the bootstrap module.

Bundled in the wheel via pyproject.toml force-include.

specify agent CLI subcommands

list, info, validate, export, switch, search, add, remove — following the same typer.Typer() + app.add_typer() pattern as specify extension and specify preset.

switch checks for modified files and prompts before teardown, tears down the old agent (file-by-file using the tracked file lists), sets up the new one, re-registers extension commands, then calls finalize_setup() with both agent and extension files to record the new install manifest. Updates init-options.json accordingly.

$ specify agent info claude
  Agent: Claude Code (claude)
  Source: embedded (bundled in specify-cli wheel)
  Commands Dir: .claude/commands
  Format: markdown

$ specify agent search --tag skills
  codex   Codex CLI   cli, openai, codex, skills   embedded
  kimi    Kimi Code   cli, moonshot, kimi, skills   embedded

Backward compatibility

AGENT_CONFIG and all existing code paths are untouched. The pack system is purely additive — specify init --ai <agent> continues to resolve through AGENT_CONFIG. Pack-based resolution is available via specify init --agent <agent> and specify agent commands and will become the default after validation.

Tests

452 new tests in test_agent_pack.py covering:

  • Manifest validation — schema errors, optional defaults, field parsing
  • Bootstrap contract — setup returns files, teardown with explicit files, dynamic loading
  • Resolution order — embedded, project-level override, user-level override, catalog override
  • Discovery and export — list_embedded_agents, list_all_agents, export_pack
  • Consistency — embedded packs match both AGENT_CONFIG and CommandRegistrar.AGENT_CONFIGS
  • File tracking — record/check unmodified, detect modification, refuse modified without force, allow modified with force, directory preservation, deleted-file handling, SHA-256 consistency, categorised manifest structure, get_tracked_files split, explicit-file teardown, extension file modification detection
  • setup() returns actual files (TestSetupReturnsFiles, 100 tests) — every agent's setup() returns non-empty list of existing absolute paths, including files under the agent's directory tree
  • End-to-end init parity (TestInitFlowParity, 300 tests) — uses typer.testing.CliRunner to invoke the real specify init command for both --ai and --agent flows across all 25 agents, comparing file sets, file contents, directory structure, tracking manifests, commands_dir alignment, and CommandRegistrar metadata byte-for-byte
  • Full lifecycle — setup → scaffold → finalize → teardown for all 25 agents verifies clean removal of every tracked file

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

…n, resolution, and CLI commands

- Create src/specify_cli/agent_pack.py with AgentBootstrap base class,
  AgentManifest schema/validation, pack resolution (user > project > catalog > embedded)
- Generate all 25 official agent packs under src/specify_cli/core_pack/agents/
  with speckit-agent.yml manifests and bootstrap.py modules
- Add 'specify agent' CLI subcommands: list, info, validate, export,
  switch, search, add, remove
- Update pyproject.toml to bundle agent packs in the wheel
- Add comprehensive tests (39 tests): manifest validation, bootstrap API,
  resolution order, discovery, consistency with AGENT_CONFIG

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/ef8b4682-7f1a-4b04-a112-df0878236b6b
"""

import json
import shutil
…est assertions

- Copilot: only remove .github/agents/ (preserves workflows, templates)
- Tabnine: only remove .tabnine/agent/ (preserves other config)
- Amp/Codex: only remove respective subdirs (commands/skills)
  to avoid deleting each other's files in shared .agents/ dir
- Tests: use flexible assertions instead of hardcoded >= 25 counts

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/ef8b4682-7f1a-4b04-a112-df0878236b6b
Copilot AI changed the title [WIP] Update agent catalog for self-bootstrapping agent packs Agent catalog: self-bootstrapping agent packs with manifest schema, resolution stack, and CLI Mar 20, 2026
Copilot AI requested a review from mnriem March 20, 2026 21:06
Copy link
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot The setup should store a list of installed files with their hash in the .specify directory so we can track what was installed by the agent. At teardown time that file needs to be used to determine if ANY of the installed files was updated and if so the remove should require a --force. Note you will NOT delete any directories but rather just the individual files.

Setup records installed files and their SHA-256 hashes in
.specify/agent-manifest-<agent_id>.json. Teardown uses the manifest
to remove only individual files (never directories). If any tracked
file was modified since installation, teardown requires --force.

- Add record_installed_files(), check_modified_files(), remove_tracked_files()
  and AgentFileModifiedError to agent_pack.py
- Update all 25 bootstrap modules to use file-tracked setup/teardown
- Add --force flag to 'specify agent switch'
- Add 11 new tests for file tracking (record, check, remove, force,
  directory preservation, deleted-file handling, manifest structure)

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
…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
Copy link
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot The setup should store a list of installed files with their hash in the .specify directory so we can track what was installed by the agent. At teardown time that file needs to be used to dete...

Implemented in b5a5e3f (file tracking) and a63c248 (finalize_setup fix):

  • setup() creates directories; finalize_setup() (base class method) scans the agent's commands_dir and writes .specify/agent-manifest-<agent_id>.json with SHA-256 hashes of all installed files
  • teardown(force=False) reads the manifest and removes only individual tracked files — directories are never deleted
  • If any tracked file was modified since installation, teardown raises AgentFileModifiedError unless --force is passed
  • specify agent switch now accepts --force to pass through to teardown
  • 11 new tests cover: record/check unmodified, detect modification, refuse without force, allow with force, directory preservation, deleted-file handling, manifest JSON structure

Copilot AI changed the title Agent catalog: self-bootstrapping agent packs with manifest schema, resolution stack, and CLI Agent catalog: self-bootstrapping agent packs with manifest schema, resolution stack, file-tracked teardown, and CLI Mar 20, 2026
Copilot AI requested a review from mnriem March 20, 2026 21:21
Copy link
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot the agent setup should report the files it installed as the cli needs to record both what was installed by the agent for the core commands / templates as well as what installed by any extensions. It should the record that. And then BEFORE agent teardown is called determine if any file was changed and ask for confirmation. And then feed those lists into the teardown of the agent and/or the installed extensions.

…wn, categorised manifest

- setup() returns List[Path] of installed files so CLI can record them
- finalize_setup() accepts agent_files + extension_files for combined tracking
- Install manifest categorises files: agent_files and extension_files
- get_tracked_files() returns (agent_files, extension_files) split
- remove_tracked_files() accepts explicit files dict for CLI-driven teardown
- agent_switch checks for modifications BEFORE teardown and prompts user
- _reregister_extension_commands() returns List[Path] of created files
- teardown() accepts files parameter to receive explicit file lists
- All 25 bootstraps updated with new signatures
- 5 new tests: categorised manifest, get_tracked_files, explicit file teardown,
  extension file modification detection

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/32e470fc-6bf5-453c-bf6c-79a8521efa56
Comment on lines +15 to +38
from specify_cli.agent_pack import (
BOOTSTRAP_FILENAME,
MANIFEST_FILENAME,
MANIFEST_SCHEMA_VERSION,
AgentBootstrap,
AgentFileModifiedError,
AgentManifest,
AgentPackError,
ManifestValidationError,
PackResolutionError,
ResolvedPack,
_manifest_path,
_sha256,
check_modified_files,
export_pack,
get_tracked_files,
list_all_agents,
list_embedded_agents,
load_bootstrap,
record_installed_files,
remove_tracked_files,
resolve_agent_pack,
validate_pack,
)
if not manifest_file.is_file():
continue
# Should not raise
warnings = validate_pack(child)
Copy link
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot the agent setup should report the files it installed as the cli needs to record both what was installed by the agent for the core commands / templates as well as what installed by any extensi...

Implemented in e190116:

  • setup() now returns List[Path] of installed files
  • finalize_setup(agent_files=, extension_files=) records both agent and extension files in .specify/agent-manifest-<id>.json with separate agent_files / extension_files categories
  • _reregister_extension_commands() returns List[Path] of files it created, which get passed to finalize_setup() as extension_files
  • Before teardown: CLI calls check_modified_files(), lists modified files, prompts for confirmation — teardown is only called after the user confirms
  • get_tracked_files() returns (agent_files, extension_files) tuple so the CLI can retrieve both sets
  • teardown(files=...) accepts explicit file dict — the CLI feeds the tracked file lists directly into teardown
  • 5 new tests cover: categorised manifest structure, get_tracked_files split, explicit-file teardown, extension file modification detection

Copilot AI requested a review from mnriem March 20, 2026 21:38
Copy link
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Are extensions hooked up for setup and teardown? Also are you testing that it similar to our --offline support? We must make sure the install between old (current way of installing) and new (our agent setup/teardown) deliver the exact same files. How do we trigger the new flow? I would suggest --agent

Copilot AI and others added 2 commits March 20, 2026 21:53
- `specify init --agent claude` resolves through the pack system and
  records all installed files in .specify/agent-manifest-<id>.json via
  finalize_setup() after the init pipeline finishes
- --agent and --ai are mutually exclusive; --agent additionally enables
  tracked teardown/switch
- init-options.json gains "agent_pack" key when --agent is used
- 4 new parity tests verify: pack resolution matches AGENT_CONFIG,
  commands_dir parity, finalize_setup records pipeline-created files,
  pack metadata matches CommandRegistrar configuration

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/930d8c4d-ce42-41fb-a40f-561fb1468e81
Copy link
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot Are extensions hooked up for setup and teardown? Also are you testing that it similar to our --offline support? We must make sure the install between old (current way of installing) and new (...

Implemented in c2227a7:

Extensions: Yes — fully hooked up in agent_switch. During setup, _reregister_extension_commands() returns List[Path] of files created by extension registration, which get passed to finalize_setup(extension_files=...) and recorded in the install manifest. During teardown, get_tracked_files() retrieves both agent and extension file lists, the CLI checks for modifications and prompts, then feeds both lists into teardown(files=...).

Parity testing: 4 new tests verify that the pack-based flow produces the same results as the AGENT_CONFIG flow:

  • Every AGENT_CONFIG agent resolves through the pack system
  • commands_dir derived from AGENT_CONFIG matches the pack manifest
  • finalize_setup records files created by the init pipeline
  • Pack command_registration matches CommandRegistrar.AGENT_CONFIGS (same dir, format, args, extension)

Triggering the new flow: Added --agent flag to specify init:

specify init my-project --agent claude          # pack-based flow
specify init --here --agent gemini --ai-skills  # with skills

--agent and --ai are mutually exclusive. --agent resolves through the pack system, validates the pack, runs the same init pipeline (scaffold/download → skills → presets → extensions), then calls finalize_setup() to record all installed files. This enables specify agent switch to tear down cleanly later. init-options.json gains "agent_pack": true when --agent is used.

Copilot AI requested a review from mnriem March 20, 2026 21:56
Copy link
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You must include parity tests that use the init command for both cases and compare that the ACTUAL generated files and directory structure are identical between the --ai and the --agent flows All agents MUST be tested!

- AgentBootstrap._scaffold_project() calls scaffold_from_core_pack,
  snapshots before/after, returns all new files
- finalize_setup() filters agent_files to only track files under the
  agent's own directory tree (shared .specify/ files not tracked)
- All 25 bootstrap setup() methods call _scaffold_project() and return
  the actual file list instead of []
- --agent init flow routes through setup() for scaffolding instead of
  calling scaffold_from_core_pack directly
- 100 new tests (TestSetupReturnsFiles): verify every agent's setup()
  returns non-empty, existing, absolute paths including agent-dir files
- Parity tests use CliRunner to invoke the real init command
- finalize_setup bug fix: skills-migrated agents (agy) now have their
  skills directory scanned correctly
- 1262 tests pass (452 in test_agent_pack.py alone)

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/054690bb-c048-41e0-b553-377d5cb36b78
try:
tracker.start(key)
tracker.complete(key, "mocked")
except Exception:
from specify_cli import AGENT_SKILLS_MIGRATIONS
if agent in AGENT_SKILLS_MIGRATIONS:
args.append("--ai-skills")
except (ImportError, AttributeError):
f"'{agent_dir.relative_to(project)}' missing "
f"(skills migration removes commands)")
return
except (ImportError, AttributeError):
Copy link
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot Are extensions hooked up for setup and teardown? Also are you testing that it similar to our --offline support? We must make sure the install between old (current way of installing) and new (...

Implemented in 9b580a5 and 978addc:

Extensions hooked up: Yes — _reregister_extension_commands() returns List[Path] of files it creates, which are passed to finalize_setup(extension_files=...) and recorded in the install manifest. During teardown, get_tracked_files() retrieves both agent and extension file lists, the CLI checks for modifications and prompts, then feeds both lists into teardown(files=...).

setup() owns scaffolding and returns files: Each bootstrap's setup() now calls _scaffold_project() which invokes scaffold_from_core_pack, snapshots before/after, and returns all new files. finalize_setup() filters to only track files under the agent's directory tree — shared .specify/ infrastructure is not tracked per-agent. 100 new TestSetupReturnsFiles tests verify every agent's setup() returns non-empty, existing, absolute paths including agent-dir files.

--agent triggers the new flow: specify init --agent claude routes through setup() for scaffolding instead of calling scaffold directly. Parity tests via CliRunner invoke the real specify init command for both --ai and --agent flows across all 25 agents, comparing file sets, file contents, and directory structure byte-for-byte. Full lifecycle tests (setup → scaffold → finalize → teardown) verify clean teardown for every agent.

try:
p.resolve().relative_to(agent_root_resolved)
all_agent.append(p)
except ValueError:
try:
emb = AgentManifest.from_yaml(emb_file)
overrides = f"embedded v{emb.version}"
except AgentPackError:
Copilot AI requested a review from mnriem March 20, 2026 22:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent Catalog -- Self-Bootstrapping Agent Packs with Catalog Override

2 participants