Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

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

chelsealong wants to merge 3 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
@mnriem

mnriem commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

This supplies the generic skills layout requested in #4561, but the mode needs to be wired through beyond file generation. With --skills, the renderer creates speckit-plan/SKILL.md, while the effective invocation separator remains . and init still recommends /speckit.plan.

Please align the mode-specific separator, saved settings/shared-template references, and init guidance with the generated layout. Add an end-to-end regression covering those surfaces while retaining the default flat-command behavior, and document --skills in the integration reference. This does not require expanding generic preset/extension registration support.

Drafted for @mnriem with assistance from GitHub Copilot (model: GPT-6 Astra; interactive comment drafting).

@mnriem
mnriem requested a balanced review from Copilot September 15, 2026 16:51
@mnriem mnriem added the author-awaiting Waiting on author response label Sep 15, 2026

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

Generic skills currently misroute add-on skills and expose dotted invocations for hyphenated skill names.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds optional SKILL.md rendering to the generic integration.

Changes:

  • Adds the --skills layout option.
  • Adds skill rendering, manifest, uninstall, and regression tests.
File summaries
File Description
src/specify_cli/integrations/generic/__init__.py Implements generic skills mode.
tests/integrations/test_integration_generic.py Tests rendering and lifecycle behavior.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +60 to +64
IntegrationOption(
"--skills",
is_flag=True,
default=False,
help=(
Comment on lines +220 to +224
skills_enabled = bool((parsed_options or {}).get("skills"))
created: list[Path] = []

for src_file in templates:
if skills_enabled:
…kill registration

Reviewer feedback on github#4562: generic --skills persisted ai_skills=True but
rendered shared templates and init's next-step guidance with the dotted
/speckit.plan separator, and let extension/preset registration silently
fall back to writing add-on skills under .agents/skills instead of the
user's --commands-dir. GenericIntegration now overrides
effective_invoke_separator() to match the layout it actually writes,
"generic" is classified as a conditional-slash agent for next-step
display, and resolve_active_skills_dir() explicitly stays disabled for
generic in both layouts since its output directory is a runtime option,
not a static per-agent folder.
@chelsealong

Copy link
Copy Markdown
Contributor Author

Addressed the alignment feedback in one commit (0e6767c):

  • GenericIntegration.effective_invoke_separator() now returns - in --skills mode (. otherwise), so shared templates (e.g. plan-template.md) and the init "Next Steps" panel render /speckit-plan instead of the nonexistent /speckit.plan.
  • Added generic to CONDITIONAL_SLASH_AGENTS in _invocation_style.py so init's next-step display classifies it correctly.
  • resolve_active_skills_dir() now explicitly returns None for the generic agent in both layouts — generic's output dir is a runtime --commands-dir option, not a static per-agent folder, so extension/preset registration stays disabled rather than silently falling back to .agents/skills (no expansion of generic preset/extension support, per your note).
  • Documented --skills in docs/reference/integrations.md.
  • Added an end-to-end regression (tests/integrations/test_integration_generic.py) covering the separator, next-steps text, and disabled add-on registration, plus flat-mode regression guards. Full suite: 8064 passed, 12 skipped.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author-awaiting Waiting on author response 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

3 participants