Skip to content

fix: rewrite extension skill command references#3474

Open
NgoQuocViet2001 wants to merge 1 commit into
github:mainfrom
NgoQuocViet2001:fix/3451-skill-command-references
Open

fix: rewrite extension skill command references#3474
NgoQuocViet2001 wants to merge 1 commit into
github:mainfrom
NgoQuocViet2001:fix/3451-skill-command-references

Conversation

@NgoQuocViet2001

@NgoQuocViet2001 NgoQuocViet2001 commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Description

  • rewrite standalone portable slash-dot references in extension skill bodies using the active agent's native invocation syntax
  • share the invocation renderer with hook execution so Codex, Kimi, Cline, and slash-based integrations stay consistent
  • preserve URL, filesystem-path, Markdown-link, HTML-attribute, and query-path references
  • document the portable extension command-reference behavior

Closes #3451

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest
  • Tested with a sample project (if applicable)

Additional validation:

  • uv run pytest tests/test_extension_skills.py tests/test_extensions.py tests/integrations/test_base.py tests/integrations/test_integration_kimi.py tests/integrations/test_integration_zcode.py -q (527 passed, 14 skipped)
  • uvx ruff check src/specify_cli/extensions/__init__.py tests/test_extension_skills.py
  • git diff --check

The sample-project coverage installs an extension into temporary Codex, Kimi, and Claude projects and verifies both rewritten invocations and preserved path/link references.

AI Disclosure

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

Codex (GPT-5, autonomous) analyzed the issue, implemented and iteratively tested the change on behalf of @NgoQuocViet2001. The operator requested this contribution but did not manually author or line-by-line review the patch.

Closes github#3451

Assisted-by: Codex (model: GPT-5, autonomous)

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.

Pull request overview

Rewrites portable extension command references into agent-native skill invocations while preserving links and paths.

Changes:

  • Adds agent-specific reference rendering shared with hooks.
  • Adds Codex, Kimi, and Claude coverage.
  • Documents portable slash-dot references.
Show a summary per file
File Description
src/specify_cli/extensions/__init__.py Implements reference detection and rewriting.
tests/test_extension_skills.py Tests invocation rendering and preservation.
extensions/EXTENSION-USER-GUIDE.md Documents portable references.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Medium

EXTENSION_COMMAND_NAME_PATTERN = re.compile(r"^speckit\.([a-z0-9-]+)\.([a-z0-9-]+)$")
_SPECKIT_SLASH_REF_PATTERN = re.compile(
r"(?<!\]\()(?<![.\w:/\\])"
r"/(speckit\.[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*)\b"
)
EXTENSION_COMMAND_NAME_PATTERN = re.compile(r"^speckit\.([a-z0-9-]+)\.([a-z0-9-]+)$")
_SPECKIT_SLASH_REF_PATTERN = re.compile(
r"(?<!\]\()(?<![.\w:/\\])"
Comment on lines +57 to +59
_MARKDOWN_LINK_DESTINATION_PREFIX_PATTERN = re.compile(r"\]\(\s*$")
_HTML_LINK_ATTRIBUTE_PREFIX_PATTERN = re.compile(
r"\b(?:href|src)\s*=\s*['\"]\s*$", re.IGNORECASE
@mnriem

mnriem commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the detailed writeup and the PR — the diagnosis of where skills rendering diverges is accurate and genuinely useful. I'd like to reframe the fix, though, because I think the root cause is different from "command markdown is copied verbatim."

Spec Kit already ships a first-class, agent-neutral way to reference sibling commands from a command body: the __SPECKIT_COMMAND_<NAME>__ token, resolved by IntegrationBase.resolve_command_refs(). Our own first-party bundled extensions use it exclusively — see extensions/bug/ and extensions/git/, which reference each other via __SPECKIT_COMMAND_BUG_FIX__, __SPECKIT_COMMAND_SPECIFY__, etc. A command file is a template that gets rendered per agent; the neutral dialect ({SCRIPT}, {ARGS}, __AGENT__, __SPECKIT_COMMAND_*__) is how authors stay portable, and rendering it into each agent's native invocation is Spec Kit's job.

spec-kit-memory-hub hard-coded /speckit.memory-md.prepare-context — a literal in one agent's surface syntax — instead of the provided token. So I don't think the right fix is to teach the renderer to detect and rewrite arbitrary literal slash-dot strings. That requires heuristically telling a command reference apart from a URL, filesystem path, Markdown link, HTML attribute, and query path — which is exactly what the PR's negative-lookahead regex tries to do, and it still mis-handles inline code spans, fenced code blocks, autolinks (</speckit.foo.bar>), and reference-style link definitions. The token has none of this ambiguity because intent is explicit.

That said, this surfaced two legitimate defects on our side, plus a docs gap. I'd like to fix all three, phased:

1. Discoverability (docs only). EXTENSION-USER-GUIDE.md never documents __SPECKIT_COMMAND_*__ and its own examples use /speckit.foo.bar throughout — so an author has no signal the token exists. We'll document it as the required way to write cross-command references and fix the guide's examples to model it. (Independent, shippable on its own.)

2. Enable the render path and define the default skill render. _register_extension_skills only calls resolve_skill_placeholders (handles {SCRIPT}/{ARGS}/__AGENT__/paths); it never resolves __SPECKIT_COMMAND_*__. This work wires token resolution into that path and establishes the default invocation for a skills agent — the generic slash-skill form /speckit-<name> — as the baseline every skills integration inherits. This is independently correct: it immediately fixes every default slash-skills agent (claude, copilot, cursor, …) and doesn't regress Codex.

3. Codex-specific render override. On top of that default, some agents override the invocation form: Codex renders $speckit-<name> (Kimi /skill:..., Cline its own form) rather than the default /speckit-<name>. The invocation logic already exists in _render_hook_invocation / _invocation_style.py — this hooks that per-agent override into the same renderer so the default from #2 resolves to Codex's dispatchable form.

Plan: three tracked issues, three PRs. #1 (docs) is fully independent. #2 wires token resolution into the skill path with the default /speckit-<name> render — that's independently correct and immediately fixes every default slash-skills agent without regressing Codex. #3 layers the Codex-specific $speckit-<name> override (and Kimi/Cline) on top of #2's default. #3 sequences after #2, but each PR lands a self-contained, correct increment, so they don't need to be bundled. This supersedes the regex-rewrite approach here, so I'd propose closing this PR in favor of that work.

You clearly did the hard part already — tracing the divergence and the render paths — and I'd love to see you address these if you have the bandwidth. If you're up for it, any of the three (starting with the docs fix, then #2, then #3) would be a great contribution, and I'm happy to review and help along the way. No pressure if the timing doesn't work — just let me know either way.

The outcome is the same thing you're after — existing-style extensions render correctly for Codex — but achieved by making Spec Kit's own portability mechanism work end-to-end rather than by guessing intent from un-templated literals.

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.

[Bug]: Extension command references not rewritten for skills-based integrations (Codex), breaking cross-command invocation

3 participants