feat(extensions): neutral behavior vocabulary with per-agent translation, fix LLM invocability#2103
feat(extensions): neutral behavior vocabulary with per-agent translation, fix LLM invocability#2103mbachorik wants to merge 1 commit intogithub:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes extension command bodies that reference extension-root-relative files (e.g., agents/..., knowledge-base/...) by rewriting those references to the installed location under .specify/extensions/<id>/... when generating SKILL.md files for skill-format agents (notably codex and kimi).
Changes:
- Add
CommandRegistrar.rewrite_extension_paths()and invoke it during SKILL.md rendering (before placeholder resolution). - Thread
source_dirthrough SKILL.md generation for primary commands and aliases so the renderer has enough context to rewrite paths. - Add unit tests (extensions) and an opt-in integration test (real extension install) to validate end-to-end behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/specify_cli/agents.py |
Adds extension-relative path rewriting and applies it during SKILL.md rendering; threads source_dir into SKILL.md generation paths. |
tests/test_extensions.py |
Adds unit tests covering extension path rewriting for codex/kimi, aliases, and conservative “no subdirs” behavior. |
tests/test_integration_extension_skill_paths.py |
Adds opt-in integration tests that install a real extension and validate rewritten paths across generated skill files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Fixed the path-rewriting scope issue raised in the inline review. Change: Gated Test added: |
There was a problem hiding this comment.
Pull request overview
This PR fixes extension command SKILL.md generation so that references to extension-relative files (e.g., agents/..., templates/...) are rewritten to their installed location under .specify/extensions/<id>/..., making those files resolvable by skill-format agents (Codex/Kimi). It addresses issue #2101 where generated SKILL.md files contained bare relative paths that don’t exist from the workspace root.
Changes:
- Add extension-relative path rewriting during SKILL.md rendering and thread
source_dirthrough skill rendering for primary commands and aliases. - Add unit tests covering rewriting across multiple subdirs, Kimi support, aliases, and “no subdirs” conservative behavior.
- Add an opt-in integration test (env-var gated) that installs a real extension and validates end-to-end SKILL.md outputs.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/agents.py |
Implements extension-relative path rewriting and applies it during SKILL.md rendering (with source_dir plumbing). |
tests/test_extensions.py |
Adds unit tests ensuring SKILL.md content rewrites extension-relative paths for Codex/Kimi and aliases. |
tests/test_integration_extension_skill_paths.py |
Adds env-gated integration tests to verify rewriting against a real installed extension. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 3/3 changed files
- Comments generated: 0 new
|
Just to make sure the non-rendered commands are installed in the .specify/extensions/ directory and then when the extension is enabled / disabled the SKILL.md files are put into the agent specific location using the skill rendering? |
mnriem
left a comment
There was a problem hiding this comment.
Can you clarify if comment above is correct?
|
yup i have the same question is this specifically to support to codex ? my original PR did work for Gemini and AGY when i tested it. |
|
@mnriem @dhilipkumars sorry for radio silence, I did not have time to come back to this. I hit several issues with recent change to spec-kit (originally I thought the fix is minimal to rewrite the relative paths). it has evolved into something bigger, I'll update the PR with proper explanation today or over the weekend. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@mbachorik Please address Copilot feedback |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 23 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
31dfd38 to
8852a23
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 23 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 23 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…#2103 - Issue 1 (agents.py): Broaden Copilot behavior branch from `cmd_type == "agent"` to any non-empty behavior dict, so `execution: isolated` → `mode: agent` is injected and `behavior:`/`agents:` keys are stripped for all Copilot behavior values - Issue 2 (extensions.py): Treat non-dict/empty source `behavior:` as absent when merging manifest-level behavior, so `execution: agent` in the manifest correctly triggers the agent-deployment skip even when the source file has an invalid value - Issue 3 (integrations/base.py): Pass `agents:` escape-hatch overrides from template frontmatter through to `translate_behavior()` in `SkillsIntegration.setup()` - Issue 4 (agents.py): Narrow `_behavior_overridable` to only the two injected boolean defaults (`disable-model-invocation`, `user-invocable`); explicit source frontmatter values for `model`, `effort`, `context`, `agent`, `allowed-tools` now win - Issue 5 (extensions.py): Move `get_deployment_type` import to module scope and read each source file once per loop iteration instead of twice Tests: 26 new tests in tests/test_pr2103_reviews.py covering all paths for each issue Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 24 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 25 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 24/25 changed files
- Comments generated: 3
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 25 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…extension path rewriting
d34f068 to
6bb1846
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 25 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Fixes #2101
Context
When spec-kit commands were moved to the SKILLS format, the SKILL.md renderer hard-coded
disable-model-invocation: truefor every built-in command. This silently broke the ability for LLMs and orchestrating agents (Claude, Copilot, etc.) to invoke spec-kit commands — only humans could trigger them via slash commands.At the same time, extension authors had no portable way to control agent behavior (model, tools, execution mode) without writing agent-specific frontmatter — meaning the same extension couldn't be written once and deployed to Claude Code, GitHub Copilot, and Codex with consistent behavior.
This PR addresses both issues together, as they share the same root: the need for a neutral behavior vocabulary that translates to correct per-agent frontmatter at render time.
What
Fix
disable-model-invocationregression — built-in spec-kit commands now declarebehavior: invocation: automaticin their source frontmatter, which translates todisable-model-invocation: falsein Claude SKILL.md files, restoring LLM invocability.Neutral
behavior:vocabulary — extension authors declare intent once using neutral keys; the renderer translates them to agent-specific frontmatter:invocationexplicit/automaticdisable-model-invocation: true/falseexecutioncommand/isolated/agentcontext: fork(isolated)capabilityfast/balanced/strongmodel: claude-haiku…/claude-sonnet…/claude-opus…effortlow/medium/high/maxthinking:budgettoolsnone/read-only/write/full/ custom listtools: […]visibilityuser/model/bothtype: promptvstype: backgroundcolorred/blue/ …color:(Claude Code UI)Agent-specific escape hatch —
agents: claude: …/agents: copilot: …blocks in source frontmatter pass through agent-specific keys that have no neutral equivalent (e.g.,argument-hint,handoffs).execution: agentrouting — commands withbehavior: execution: agentare deployed to.claude/agents/(Claude sub-agents) and Copilot.agent.mdfiles, with mode and tools injected automatically.Extension-relative path rewriting (original fix from fix: Extension SKILL.md files contain unresolvable relative paths to extension subdirectories #2101) — bare relative paths in extension command bodies (e.g.,
agents/control/commander.md) are rewritten to.specify/extensions/<id>/…in generated SKILL.md files, so AI agents can resolve them from the workspace root.Why it is backwards compatible
behavior:block continue to work exactly as before — the translator is a no-op when the key is absent.disable-model-invocation,context,model, etc.) are passed through unchanged unless abehavior:block also exists.extension.yml; preset directories are skipped.Testing
Tested primarily in GitHub Copilot and Claude Code with a real extension install.
Unit tests added:
tests/test_behavior_translator.py— all neutral → agent-specific translationstests/test_agent_deployment.py—execution: agentrouting to.claude/agents/and.agent.mdtests/test_extension_skills.py— agent-specific passthrough, behavior merging, backwards compattests/test_extensions.py— extension path rewriting (core fix, kimi, aliases, no-subdirs)tests/integrations/test_integration_claude.py— end-to-end Claude integrationOpt-in integration test:
tests/test_integration_extension_skill_paths.py— installs a real extension and validates all generated skill files end-to-end (requiresSPECKIT_TEST_EXT_DIRenv var)🤖 Generated with Claude Code