Skip to content

feat(extensions): provides.agents and provides.files, hash-tracked, per integration - #4635

Draft
YuraD wants to merge 2 commits into
github:mainfrom
YuraD:feat/extension-agents-and-files
Draft

YuraD wants to merge 2 commits into
github:mainfrom
YuraD:feat/extension-agents-and-files

Conversation

@YuraD

@YuraD YuraD commented Sep 18, 2026

Copy link
Copy Markdown

Description

Two artifact kinds an extension can ship that Spec Kit does not model today:

  • provides.agents — subagent definitions (Markdown with frontmatter: name, description, model, tools). They land in the active integration's subagent directory through a new agents_dir class attribute on the integration: .claude/agents for Claude Code, .cursor/agents for Cursor. An integration without a file-based subagent lane (Codex today) skips them with a one-line note; nothing else changes.
  • provides.files — verbatim project files copied to a declared dest, for harness features Spec Kit has no primitive for (a Claude Code Workflow script under .claude/workflows/, say). {integration_folder}/ at the start of dest resolves to the active integration's folder; a destination may not be absolute, climb out of the project, or land under .specify/.

Both are recorded in the extension registry with a content hash (registered_agents, registered_files). On reinstall (--force) or removal, a file a person edited since is left alone and reported; only unchanged copies are replaced or deleted. Manifest validation reuses _validate_provided_artifacts for name/file safety and adds project_dest_violation for destinations.

Why: an engineering workflow whose economics come from model tiers (a Haiku explorer, Sonnet implementers, an Opus reviewer per ticket) needs per-agent model:/tools:, which a forked skill cannot carry, and its fan-out stages are Workflow scripts. Both are files under the harness's own directory; the registrar already writes there, it only lacked the two kinds. Codex/Cursor both support subagents, so this is not a single-harness lane.

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests: tests/test_extensions.py tests/test_extension_skills.py tests/test_agent_config_consistency.py tests/extensions — 907 passed, 68 skipped
  • New tests in tests/test_extension_agents_files.py: manifest validation and bad destinations; Claude install places both and removal cleans them; a locally edited file survives --force and removal; Cursor gets .cursor/agents, Codex gets files only; an extension of only agents/files is valid
  • Tested with a sample project: an extension carrying 26 agent files and 6 Workflow scripts installs into a fresh specify init --integration claude project

Docs: docs/reference/extensions.md gains a "Subagent Definitions and Project Files" section; CHANGELOG.md has an Unreleased entry.

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (describe below)

Code, tests and docs were generated with Claude Code and reviewed by the author; the design (which artifact kinds, hash-tracked ownership, the {integration_folder} token, no .specify/ destinations) was decided by the author.

🤖 Generated with Claude Code

github-actions Bot and others added 2 commits September 10, 2026 13:26
…er integration

An extension can ship subagent definitions and verbatim project files. provides.agents lands in the active integration's agent directory (a new agents_dir on the integration: .claude/agents for Claude Code, .cursor/agents for Cursor; an integration without one skips them with a note). provides.files copies a file to a declared project-relative dest, with {integration_folder}/ resolving to the active integration's folder and .specify/ off limits. Both are recorded in the registry with a content hash, so a file a person edited since is never overwritten on reinstall nor deleted on removal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings September 18, 2026 15:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Symlink traversal, integration-switch lifecycle gaps, ownership collisions, and incorrect versioning remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds hash-tracked extension-provided subagents and project files.

Changes:

  • Validates, installs, tracks, and removes new artifact types.
  • Adds Claude and Cursor agent directories.
  • Adds tests and documentation.
File summaries
File Description
src/specify_cli/extensions/__init__.py Implements artifact lifecycle and safety checks.
src/specify_cli/integrations/base.py Defines optional agent directory metadata.
src/specify_cli/integrations/claude/__init__.py Configures Claude’s agent directory.
src/specify_cli/integrations/cursor_agent/__init__.py Configures Cursor’s agent directory.
tests/test_extension_agents_files.py Tests validation and file ownership behavior.
docs/reference/extensions.md Documents the new manifest fields.
CHANGELOG.md Records the feature.
pyproject.toml Updates package version metadata.
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 4
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1963 to +1967
def _ensure_inside_project(self, dest: Path) -> None:
normalized = Path(os.path.normpath(dest))
root = Path(os.path.normpath(self.project_root))
if not normalized.is_relative_to(root):
raise ExtensionError(f"Destination {dest} escapes the project root")
Comment thread pyproject.toml
[project]
name = "specify-cli"
version = "1.0.6.dev0"
version = "1.0.6"
Comment on lines +1986 to +1988
if previously is None and current != self._file_sha256(source):
_note(f"[yellow]⚠[/yellow] {rel} exists and is not this extension's; left alone")
return False
Comment on lines +2793 to +2796
# Subagent definitions and verbatim project files (provides.agents /
# provides.files) land in the active integration's own directories.
registered_agents = self._register_extension_agents(manifest, dest_dir)
registered_files = self._install_extension_files(manifest, dest_dir)
@mnriem mnriem added triage-can-wait Verdict: valid and in-scope but deprioritized; held behind the evidence gate author-needs-rebase Branch conflicts with main — rebase/resolve before merge labels Sep 18, 2026
@mnriem

mnriem commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author-needs-rebase Branch conflicts with main — rebase/resolve before merge triage-can-wait Verdict: valid and in-scope but deprioritized; held behind the evidence gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants