fix(extensions): rewrite extension-relative paths in generated SKILL.md files#2103
fix(extensions): rewrite extension-relative paths in generated SKILL.md files#2103mbachorik wants to merge 1 commit intogithub:mainfrom
Conversation
…md files Extension command bodies reference files using paths relative to the extension root (e.g. `agents/control/commander.md`, `knowledge-base/scores.yaml`). After install these files live at `.specify/extensions/<id>/...`, but the generated SKILL.md files were emitting bare relative paths that AI agents could not resolve from the workspace root. Add `CommandRegistrar.rewrite_extension_paths()` which discovers the subdirectories that exist in the installed extension directory and rewrites matching body references to `.specify/extensions/<id>/<subdir>/...`. The rewrite runs before `resolve_skill_placeholders()` so that extension-local `scripts/` and `templates/` subdirectories are not incorrectly redirected to the project-level `.specify/scripts/` and `.specify/templates/` paths. The method is called from `render_skill_command()` when `source_dir` is provided, which `register_commands()` now passes through for all agents. Affected agents: any using the `/SKILL.md` extension format (currently kimi and codex). Aliases receive the same rewriting. Closes github#2101 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
| if source_dir is not None: | ||
| body = self.rewrite_extension_paths(body, source_id, source_dir) | ||
|
|
There was a problem hiding this comment.
render_skill_command() rewrites paths whenever source_dir is provided, but register_commands() always passes source_dir for all sources (extensions and presets). For presets, source_dir typically contains a templates/ directory, so this logic will rewrite any templates/... references in preset command bodies to .specify/extensions/<preset-id>/templates/..., which is an incorrect location and can break preset-generated SKILL.md files.
Consider gating this rewrite so it only runs for extensions (e.g., only when an extension.yml is present in source_dir, or by threading an explicit is_extension/installed_prefix into render_skill_command()), rather than applying it to every SKILL.md source directory.
Fixes #2101
What
When an extension command body references files relative to the extension
root (e.g.
agents/control/commander.md,knowledge-base/scores.yaml),those paths are now correctly rewritten to
.specify/extensions/<id>/...in the generated SKILL.md files.
Why
Extension commands are registered as SKILL.md files for skill-format agents
(currently
kimiandcodex). After installation the extension files liveat
.specify/extensions/<id>/..., butrender_skill_command()had no pathrewriting — it wrote bare relative paths verbatim. The AI agent resolves
them from the workspace root where they don't exist, so the referenced files
were never found.
How
CommandRegistrar.rewrite_extension_paths(text, extension_id, extension_dir): discovers which subdirectories exist in the installed extension and rewrites matchingsubdir/…references to.specify/extensions/<id>/subdir/…using conservative regex boundary anchoringresolve_skill_placeholders()so extension-localscripts/andtemplates/aren't incorrectly redirected to project-level pathssource_dirfromregister_commands()through torender_skill_command()for both primary commands and aliasesTests
Unit tests in
test_extensions.py:agents/,templates/,scripts/,knowledge-base/)Integration tests in
test_integration_extension_skill_paths.py:SPECKIT_TEST_EXT_DIRenv var points to a local extension checkout🤖 Generated with Claude Code