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 the triage-can-wait Verdict: valid and in-scope but deprioritized; held behind the evidence gate label Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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