Skip to content

fix: use skill references in SKILL.md for skills-only delivery#1194

Open
mc856 wants to merge 4 commits into
Fission-AI:mainfrom
mc856:fix/skills-only-references
Open

fix: use skill references in SKILL.md for skills-only delivery#1194
mc856 wants to merge 4 commits into
Fission-AI:mainfrom
mc856:fix/skills-only-references

Conversation

@mc856

@mc856 mc856 commented Jun 10, 2026

Copy link
Copy Markdown

Fixes #881, closes #879.

When delivery: 'skills' is configured, generated SKILL.md files contained hardcoded /opsx:* command references pointing to commands that were never generated. This implements the fix outlined in #881: adds transformToSkillReferences() with the explicit 11-entry command-to-skill mapping and wires it at every generateSkillContent call site — init.ts, both sites in update.ts (including upgradeLegacyTools), and both sites in workspace/skills.ts.

Deviations from the issue snapshot

  • Kept the existing pi branch in the transformer condition. The issue snippet only checks opencode, but main already routes pi through transformToHyphenCommands; copying the snippet verbatim would regress pi.
  • workspace/skills.ts wired in addition to the files listed in the issue. It generates SKILL.md from the same globalConfig.delivery and is itself a skills-only surface, so the bug persisted there. Regression test included (verified to fail against the unwired code).
  • Note: WORKFLOW_TO_SKILL_DIR exists in both init.ts (local copy) and profile-sync-drift.ts (exported); the two are identical and COMMAND_TO_SKILL_REFERENCE matches both.

Open question for maintainers

In skills-only mode, opencode/pi still take the transformToHyphenCommands branch, so their SKILL.md files contain /opsx-* references to commands that were also never generated — same class of bug. I didn't expand scope because it's unclear whether /openspec-<skill> slash references resolve in those tools. Happy to file a follow-up issue. The legacy-upgrade path also lacks a skills-only content assertion; same offer.

Testing

  • npx eslint src/ clean
  • transformToSkillReferences unit tests: per-mapping assertions for all 11 commands, multi-reference, backtick, unknown-command, and bulk-archive/archive boundary cases
  • Integration: init + update skills-only cases assert generated SKILL.md contains no /opsx: and does contain /openspec- (explore template verified to contain /opsx: references, so the assertion is discriminative); new workspace test mutation-verified
  • Full vitest suite passing

Generated with Claude (Cowork) using claude-fable-5.

Summary by CodeRabbit

  • Bug Fixes
    • Skill setup and update flows now generate the correct skill command references when using skills-only delivery.
    • Generated skill files no longer include unsupported command markers and instead link to the expected skill references.
    • Command references are now selected consistently based on delivery mode, improving output across supported tools.

@mc856 mc856 requested a review from TabishB as a code owner June 10, 2026 03:27
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 52eea1f3-c809-4fd3-a81d-53a4e37b85a7

📥 Commits

Reviewing files that changed from the base of the PR and between 8c66688 and 3e62cf9.

📒 Files selected for processing (8)
  • .changeset/skills-only-references.md
  • src/core/init.ts
  • src/core/update.ts
  • src/utils/command-references.ts
  • src/utils/index.ts
  • test/core/init.test.ts
  • test/core/update.test.ts
  • test/utils/command-references.test.ts
💤 Files with no reviewable changes (7)
  • src/utils/index.ts
  • test/core/init.test.ts
  • test/core/update.test.ts
  • test/utils/command-references.test.ts
  • src/core/init.ts
  • src/core/update.ts
  • src/utils/command-references.ts
✅ Files skipped from review due to trivial changes (1)
  • .changeset/skills-only-references.md

📝 Walkthrough

Walkthrough

Adds a delivery-aware transformer that rewrites /opsx:<command> tokens to /openspec-* skill references for skills-only delivery, wires it into init and update SKILL.md generation, re-exports the helper, and extends tests and the changeset note.

Changes

Skills-Only Delivery Reference Transformation

Layer / File(s) Summary
Skill reference transformer utility
src/utils/command-references.ts, src/utils/index.ts, .changeset/skills-only-references.md
COMMAND_TO_SKILL_REFERENCE maps /opsx:<command> tokens to /openspec-* references, transformToSkillReferences() applies the mapping, getTransformerForTool() selects the transformer by tool and delivery, and the utilities are re-exported.
Core module transformer wiring
src/core/init.ts, src/core/update.ts
InitCommand and UpdateCommand now pass getTransformerForTool(tool.value, delivery) into generateSkillContent() for both the main and legacy SKILL.md generation paths.
Transformer and SKILL.md tests
test/utils/command-references.test.ts, test/core/init.test.ts, test/core/update.test.ts
Tests assert skills-only SKILL.md content uses /openspec- references and verify transformer selection for skills, commands, and tool-specific behavior.

Sequence Diagram(s)

sequenceDiagram
  participant InitCommand
  participant UpdateCommand
  participant getTransformerForTool
  participant generateSkillContent
  InitCommand->>getTransformerForTool: resolve transformer(tool.value, delivery)
  getTransformerForTool-->>InitCommand: transformToSkillReferences / transformToHyphenCommands / undefined
  InitCommand->>generateSkillContent: pass transformer for SKILL.md
  UpdateCommand->>getTransformerForTool: resolve transformer(tool.value, delivery)
  getTransformerForTool-->>UpdateCommand: transformToSkillReferences / transformToHyphenCommands / undefined
  UpdateCommand->>generateSkillContent: pass transformer for SKILL.md
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Fission-AI/OpenSpec#626: Updates the same command-reference rewriting flow in command-references.ts, init.ts, and update.ts, with OpenCode hyphen-command transformation as the shared code path.
  • Fission-AI/OpenSpec#950: Also changes SKILL content generation for opencode/pi by adjusting transformer behavior in init.ts and update.ts.

Suggested reviewers

  • TabishB

Poem

I hopped through SKILL.md with whiskers held high,
Swapping /opsx: for /openspec- as I fly.
One map, one hop, one tidy little switch,
Now each skill points home without a hitch.
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: using skill references in SKILL.md for skills-only delivery.
Linked Issues check ✅ Passed The PR adds the skills-only transformer, wires it into init and update flows, and adds coverage for the new reference format.
Out of Scope Changes check ✅ Passed The changes stay focused on skills-only SKILL.md reference rewriting and related tests, with no clear unrelated additions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
src/utils/command-references.ts (1)

22-26: ⚖️ Poor tradeoff

Sync burden: mapping duplicated across files.

The comment warns that COMMAND_TO_SKILL_REFERENCE must stay in sync with WORKFLOW_TO_SKILL_DIR in two other locations. This creates a maintenance risk: if one copy is updated without updating the others, skill references will break at runtime. Consider consolidating these mappings into a single source of truth (e.g., exporting WORKFLOW_TO_SKILL_DIR from a shared module and deriving the skill reference map from it).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/command-references.ts` around lines 22 - 26, The duplicate mapping
risk: consolidate the source of truth by exporting WORKFLOW_TO_SKILL_DIR from a
single shared module and derive COMMAND_TO_SKILL_REFERENCE from that exported
mapping instead of maintaining separate copies; update code that currently
defines COMMAND_TO_SKILL_REFERENCE (and the local copies of
WORKFLOW_TO_SKILL_DIR) to import the shared WORKFLOW_TO_SKILL_DIR and build the
command-to-skill map (e.g., compute COMMAND_TO_SKILL_REFERENCE by mapping
keys/values from WORKFLOW_TO_SKILL_DIR) so only one mapping needs to be
maintained.
src/core/init.ts (1)

540-546: ⚡ Quick win

Extract duplicated transformer selection logic into a helper function.

The transformer selection conditional appears identically in 5 locations across 3 files. This violates DRY and creates maintenance risk: if the logic needs to change (e.g., adding a new delivery mode or tool-specific behavior), all 5 sites must be updated consistently.

Extract this logic into a shared helper function in src/utils/command-references.ts:

♻️ Proposed refactor
/**
 * Selects the appropriate command-reference transformer based on tool and delivery mode.
 * `@param` toolId - The AI tool identifier (e.g., 'claude', 'opencode', 'pi')
 * `@param` delivery - The delivery mode ('skills', 'commands', or 'both')
 * `@returns` The transformer function or undefined
 */
export function getTransformerForTool(
  toolId: string,
  delivery: 'skills' | 'commands' | 'both'
): ((text: string) => string) | undefined {
  if (toolId === 'opencode' || toolId === 'pi') {
    return transformToHyphenCommands;
  }
  if (delivery === 'skills') {
    return transformToSkillReferences;
  }
  return undefined;
}

Then replace each of the 5 duplicated blocks with:

const transformer = getTransformerForTool(tool.value, delivery);
// or for workspace/skills.ts:
const transformer = getTransformerForTool(tool.value, profileContext.delivery);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/init.ts` around lines 540 - 546, Duplicate transformer-selection
logic used in multiple places should be pulled into a helper; create a function
getTransformerForTool(toolId: string, delivery: 'skills' | 'commands' | 'both')
that returns transformToHyphenCommands when toolId === 'opencode' || toolId ===
'pi', returns transformToSkillReferences when delivery === 'skills', and
otherwise returns undefined, then replace the inline conditional in
src/core/init.ts (the const transformer assignment) and the four other
occurrences with calls to getTransformerForTool(tool.value, delivery) (or
getTransformerForTool(tool.value, profileContext.delivery) where appropriate) so
all sites use the single shared implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/core/init.ts`:
- Around line 540-546: Duplicate transformer-selection logic used in multiple
places should be pulled into a helper; create a function
getTransformerForTool(toolId: string, delivery: 'skills' | 'commands' | 'both')
that returns transformToHyphenCommands when toolId === 'opencode' || toolId ===
'pi', returns transformToSkillReferences when delivery === 'skills', and
otherwise returns undefined, then replace the inline conditional in
src/core/init.ts (the const transformer assignment) and the four other
occurrences with calls to getTransformerForTool(tool.value, delivery) (or
getTransformerForTool(tool.value, profileContext.delivery) where appropriate) so
all sites use the single shared implementation.

In `@src/utils/command-references.ts`:
- Around line 22-26: The duplicate mapping risk: consolidate the source of truth
by exporting WORKFLOW_TO_SKILL_DIR from a single shared module and derive
COMMAND_TO_SKILL_REFERENCE from that exported mapping instead of maintaining
separate copies; update code that currently defines COMMAND_TO_SKILL_REFERENCE
(and the local copies of WORKFLOW_TO_SKILL_DIR) to import the shared
WORKFLOW_TO_SKILL_DIR and build the command-to-skill map (e.g., compute
COMMAND_TO_SKILL_REFERENCE by mapping keys/values from WORKFLOW_TO_SKILL_DIR) so
only one mapping needs to be maintained.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9c67c585-1070-4587-8421-be46f4b1dcbb

📥 Commits

Reviewing files that changed from the base of the PR and between 1b06fdd and 45f3e56.

📒 Files selected for processing (10)
  • .changeset/skills-only-references.md
  • src/core/init.ts
  • src/core/update.ts
  • src/core/workspace/skills.ts
  • src/utils/command-references.ts
  • src/utils/index.ts
  • test/core/init.test.ts
  • test/core/update.test.ts
  • test/core/workspace/skills.test.ts
  • test/utils/command-references.test.ts

@mc856

mc856 commented Jun 10, 2026

Copy link
Copy Markdown
Author

Pushed 4ef4c48 addressing the CodeRabbit review: extracted the duplicated transformer selection into getTransformerForTool() in command-references.ts (with JSDoc and unit tests locking the selection matrix), replacing all five call sites.

On consolidating the WORKFLOW_TO_SKILL_DIR copies: agreed in principle, but deriving the map in src/utils from src/core/profile-sync-drift.ts would invert the utils ← core layering, and moving the canonical map into utils touches profile-sync-drift.ts typing and init.ts beyond this bugfix's scope. Happy to do it as a follow-up PR if maintainers agree.

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tightening this up. The Claude/init/update/workspace path is in much better shape, but I found one remaining skills-only edge case before this can merge.

getTransformerForTool() currently returns transformToHyphenCommands for OpenCode and Pi before it checks delivery === 'skills'. That means delivery: 'skills' still generates OpenCode/Pi SKILL.md files that point at /opsx-* commands even though command files were removed or never generated. For skills-only delivery, the missing-command fix needs to win for every tool, including OpenCode and Pi. Please make delivery === 'skills' return skill references first, then keep the hyphen transform for OpenCode/Pi when commands are actually generated, and add focused coverage for those two tools.

Checked: focused command-reference/init/update/workspace tests pass locally.

@mc856

mc856 commented Jun 12, 2026

Copy link
Copy Markdown
Author

Done in 8c66688delivery === 'skills' now wins for every tool: getTransformerForTool checks skills-only delivery first and only falls back to the hyphen transform for opencode/pi when commands are actually generated. Added unit coverage for the opencode/pi selection matrix and an opencode skills-only init integration test asserting the generated SKILL.md contains no /opsx: or /opsx- references. This also closes out the open question from the PR description — thanks for the call.

@mc856 mc856 requested a review from alfred-openspec June 12, 2026 02:22

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving latest head. I verified the skills-only transformer now wins for every tool, including OpenCode/Pi, and init/update/workspace skill generation have focused regression coverage. Targeted install, tsc, lint on src, and the relevant Vitest suites all pass.

mc856 added 4 commits June 25, 2026 12:12
When delivery is configured as 'skills', generated SKILL.md files
contained hardcoded /opsx:* command references pointing to commands
that were never generated, breaking cross-skill workflows.

Add transformToSkillReferences() with the explicit command-to-skill
mapping (kept in sync with WORKFLOW_TO_SKILL_DIR) and wire it in
init and update wherever skill content is generated, following the
approach outlined in Fission-AI#881.

Closes Fission-AI#881
Closes Fission-AI#879

Generated with Claude (Cowork) using claude-fable-5; verified with the
full vitest suite (1683 tests passing).
Review follow-up for the skills-only delivery fix: workspace skill
setup (src/core/workspace/skills.ts) generates SKILL.md via the same
generateSkillContent path but was not wired with
transformToSkillReferences, leaving dangling /opsx:* references when
delivery is 'skills'. Wire both call sites, add a regression test
(verified to fail against the unwired code), strengthen the update
skills-only test with content assertions, and correct the
COMMAND_TO_SKILL_REFERENCE comment (WORKFLOW_TO_SKILL_DIR exists in
both profile-sync-drift.ts and init.ts).

Generated with Claude (Cowork) using claude-fable-5; verified with
eslint and targeted vitest suites (144 tests passing).
…rTool

Address CodeRabbit review: the tool/delivery transformer selection was
duplicated at five call sites across init.ts, update.ts, and
workspace/skills.ts. Extract it into a documented helper in
command-references.ts with unit tests locking the selection matrix
(opencode/pi precedence, skills-only delivery, default).

Generated with Claude (Cowork) using claude-fable-5; verified with
eslint, tsc, and targeted vitest suites (147 tests passing).
…ls-only delivery

Address review: getTransformerForTool returned transformToHyphenCommands
for opencode/pi before checking delivery, so skills-only delivery still
emitted /opsx-* references to commands that were never generated. Check
delivery === 'skills' first so skill references win for every tool, and
keep the hyphen transform for opencode/pi only when commands are
generated. Add unit coverage for the opencode/pi selection matrix and an
opencode skills-only init integration test asserting no /opsx: or /opsx-
references remain.

Generated with Claude (Cowork) using claude-fable-5; verified with
eslint, tsc, and targeted vitest suites (148 tests passing).
@mc856

mc856 commented Jun 25, 2026

Copy link
Copy Markdown
Author

@alfred-openspec I rebased this onto the latest upstream main and resolved the merge conflict; GitHub now reports it as mergeable and CodeRabbit is green again. The previous approval was marked stale after the force-push, so could you please re-confirm when you have a chance?

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved after the rebase. I rechecked the diff: skills-only delivery now emits skill references for all tools, OpenCode/Pi precedence is covered, and the affected init/update paths plus transformer helper have focused regression tests. Verified locally with install, tsc, eslint on src, and targeted Vitest suites.

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.

[Fix] Add transformToSkillReferences for skills-only delivery mode [Bug] Skills-only delivery emits /opsx:* command references

2 participants