Skip to content

fix: support SKILL.md rendering for the generic integration - #4562

Open
chelsealong wants to merge 2 commits into
github:mainfrom
chelsealong:fix/4561-generic-skills-rendering
Open

fix: support SKILL.md rendering for the generic integration#4562
chelsealong wants to merge 2 commits into
github:mainfrom
chelsealong:fix/4561-generic-skills-rendering

Conversation

@chelsealong

Copy link
Copy Markdown
Contributor

Fixes #4561

Problem

--integration generic (the "bring your own agent" escape hatch) could only
ever emit flat speckit.<name>.md command files. There was no way to get the
speckit-<name>/SKILL.md layout that every skills-format agent (Claude,
Codex, Kimi, Bob, Copilot in skills mode, …) uses, even though GenericIntegration
already has all the machinery (process_template, write_file_and_record,
manifest tracking) needed to produce it.

Fix

Added a --skills flag to GenericIntegration.options() (parsed the same way
every other integration's --integration-options flags are — no special
casing needed). When set, setup() renders each command template as
<commands-dir>/speckit-<name>/SKILL.md with the same frontmatter shape
(name, description, compatibility, metadata) that
SkillsIntegration.setup() produces for other agents, instead of the flat
speckit.<name>.md file. Default behavior (no --skills) is unchanged
byte-for-byte.

This is scoped to GenericIntegration's own setup() only — it does not
touch CommandRegistrar.AGENT_CONFIGS, presets, or extensions, which already
exclude generic entirely (its output directory is a runtime CLI option, not
a static per-agent convention) regardless of layout. So skills-mode generic
gets the exact same (lack of) preset/extension registration support that
flat-mode generic already has today — no new inconsistency introduced.

Also, _build_skill_content() now calls post_process_skill_content() (via a
small internal _GenericSkillsHelper(SkillsIntegration), the same delegation
pattern CopilotIntegration uses for its own skills mode) so generated
SKILL.md bodies get the shared dot-to-hyphen hook-invocation note
(_HOOK_COMMAND_NOTE in base.py) before every "For each executable hook,
output the following" instruction — matching what every other
SkillsIntegration subclass (Claude, Codex, Kimi, Bob, …) already emits.
Without it, a hook configured in .specify/extensions.yml (e.g.
speckit.git.commit) would have been invoked verbatim as /speckit.git.commit,
which doesn't exist under the speckit-<name>/SKILL.md layout this PR
introduces — the real skill lives at speckit-git-commit/SKILL.md, invoked
as /speckit-git-commit.

Testing

Added tests to tests/integrations/test_integration_generic.py:

  • --skills is a declared, non-required boolean flag defaulting to False
  • setup() with --skills writes speckit-<name>/SKILL.md files under
    --commands-dir
  • the generated SKILL.md has the expected frontmatter and fully-processed
    body (no leftover {SCRIPT} / __AGENT__ / __SPECKIT_COMMAND_*__ tokens)
  • the generated SKILL.md body includes the dot-to-hyphen hook-invocation
    note with the /-prefixed (not $- or /skill:-prefixed) example, since
    generic isn't in DOLLAR_SKILLS_AGENTS/SKILL_COLON_AGENTS
  • without --skills, output is still flat speckit.<name>.md (regression
    guard)
  • skill files are tracked in the manifest and survive an install/uninstall
    round trip

Verified the new tests fail without the fix and pass with it:

$ git checkout HEAD~1 -- src/specify_cli/integrations/generic/__init__.py
$ .venv/bin/python -m pytest tests/integrations/test_integration_generic.py -q
...
FAILED ...::test_options_include_commands_dir - AssertionError: assert 1 == 2
FAILED ...::test_options_include_skills_flag - StopIteration
FAILED ...::test_setup_writes_skill_md_when_skills_flag_set - AssertionError: assert 'speckit.analyze.md' == 'SKILL.md'
FAILED ...::test_skill_content_has_expected_frontmatter - AssertionError: assert False
4 failed, 44 passed in 1.17s

$ git checkout HEAD -- src/specify_cli/integrations/generic/__init__.py
$ .venv/bin/python -m pytest tests/integrations/test_integration_generic.py tests/test_agent_config_consistency.py -q
77 passed in 1.27s

Separately, reverting only the post_process_skill_content() call (keeping
everything else) reproduces the missing-hook-note failure in isolation:
test_skill_content_has_hook_command_note fails with the note absent, and
passes once the call is restored. Also ran the full tests/integrations/
suite: 2911 passed, 5 skipped — no regressions.

Also ran the full suite (.venv/bin/python -m pytest tests -q): 8049 passed,
12 skipped, 10 failed. The 10 failures are pre-existing *_python_parity
tests (test_check_prerequisites_python_parity.py,
test_create_new_feature_python_parity.py,
test_resolve_template_python_parity.py,
test_setup_plan_python_parity.py, test_setup_tasks_python_parity.py) that
fail identically on unmodified main in this sandbox (verified via
git stash), unrelated to this change.

Manually exercised the CLI end-to-end:

$ specify init --here --integration generic \
    --integration-options="--commands-dir .myagent/skills --skills" --script sh
$ find .myagent/skills -type f
.myagent/skills/speckit-analyze/SKILL.md
.myagent/skills/speckit-checklist/SKILL.md
.myagent/skills/speckit-clarify/SKILL.md
.myagent/skills/speckit-constitution/SKILL.md
.myagent/skills/speckit-converge/SKILL.md
.myagent/skills/speckit-implement/SKILL.md
.myagent/skills/speckit-plan/SKILL.md
.myagent/skills/speckit-specify/SKILL.md
.myagent/skills/speckit-tasks/SKILL.md
.myagent/skills/speckit-taskstoissues/SKILL.md

and confirmed omitting --skills still produces the original flat
.myagent/commands/speckit.<name>.md layout.

AI disclosure

This PR was written by an autonomous Claude Code agent (Claude Sonnet 5),
including the code change, tests, and this description. I (the human
submitter) reviewed the diff and the test run output above before opening it.

🤖 Generated with Claude Code

The generic (bring-your-own-agent) escape hatch could only ever emit
flat speckit.<name>.md command files, with no way to opt into the
speckit-<name>/SKILL.md layout every skills-format agent (Claude,
Codex, etc.) uses. Add a --skills flag to --integration-options that
renders the same command templates as SKILL.md directories under
--commands-dir instead, matching the agentskills.io layout used
elsewhere. Default behavior (flat .md files) is unchanged.

Fixes github#4561
_build_skill_content() duplicated SkillsIntegration.setup()'s per-file
body but dropped the call to post_process_skill_content(), which
injects the dot-to-hyphen hook-invocation note before every "For each
executable hook" instruction. Without it, a configured extension hook
(e.g. speckit.git.commit) would be invoked verbatim as
/speckit.git.commit, which doesn't exist under the
speckit-<name>/SKILL.md layout this feature introduces.

Add a small _GenericSkillsHelper(SkillsIntegration) — the same
delegation pattern CopilotIntegration uses for its own skills mode —
and call its post_process_skill_content() after building the SKILL.md
body. Add a regression test asserting the note appears.
@chelsealong
chelsealong requested a review from mnriem as a code owner September 12, 2026 14:39
@mnriem mnriem added the triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review label Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Generic escape hatch does not support SKILL.md rendering

2 participants