Skip to content

fix: use skill references for adapterless tools#1181

Open
leno23 wants to merge 5 commits into
Fission-AI:mainfrom
leno23:codex/fix-adapterless-skill-guidance
Open

fix: use skill references for adapterless tools#1181
leno23 wants to merge 5 commits into
Fission-AI:mainfrom
leno23:codex/fix-adapterless-skill-guidance

Conversation

@leno23

@leno23 leno23 commented Jun 5, 2026

Copy link
Copy Markdown

Fixes #1155.

What changed

  • Transform generated /opsx:* references into skill references for tools that do not have a command adapter.
  • Use Kimi CLI-specific /skill:openspec-* invocations while keeping Mistral Vibe on plain openspec-* skill names.
  • Avoid reporting generated command counts when commands are skipped for adapterless tools.
  • Show skill-based getting-started guidance when init only creates skills.
  • Apply the same transformation during openspec update skill regeneration.

Validation

  • pnpm exec vitest run test/utils/command-references.test.ts test/core/init.test.ts test/core/update.test.ts
  • pnpm run build

Note: local build prints the existing Node engine warning because this machine has Node 20.11.1 and the package asks for >=20.19.0, but the build completed successfully.

Summary by CodeRabbit

  • New Features

    • Added skill reference transformation utilities for flexible instruction formatting
    • Enhanced getting started messages to display context-appropriate instructions based on tool capabilities
  • Refactor

    • Centralized command-instruction transformation logic for consistent handling across tools
  • Tests

    • Expanded test coverage for instruction generation with different tool configurations
    • Added comprehensive test suite for skill reference transformations

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Replace hardcoded /opsx:* references with tool-aware skill references for adapterless tools. Add a central workflow→skill mapping and instruction-transformer selector, wire it into init/update SKILL.md generation, adjust success/getting-started output, and extend tests for Kimi and Vibe adapterless flows.

Changes

Adapterless Tool Skill Reference Transformation

Layer / File(s) Summary
Skill reference transformation utilities
src/utils/command-references.ts, test/utils/command-references.test.ts
Workflow-to-skill mapping constant and new functions: transformToSkillReferences() replaces /opsx:* references, getSkillReferencePrefix() returns tool-specific prefixes (e.g., /skill: for Kimi), transformToToolSkillReferences() applies the prefix, and getSkillInstructionTransformer() selects the transformer. Tests cover prefix handling, multiple references, and unknown-command passthrough.
Init imports & getting-started helper
src/core/init.ts
Import new helpers and add formatSkillGettingStarted(workflowId, tools) to produce skill-based getting-started text when no command-capable tool exists.
Init SKILL generation and output changes
src/core/init.ts, test/core/init.test.ts
Per-tool determine adapter presence and pass (toolId, hasCommandAdapter) into getSkillInstructionTransformer() for SKILL.md generation; only report command totals when at least one adapter-capable tool was set up; choose /opsx: or skill-based getting-started instruction accordingly. Tests updated/added for Kimi and Vibe adapterless setups verifying SKILL.md content and console output.
Update flow: reuse transformer selection
src/core/update.ts
Use CommandAdapterRegistry.has(tool.value) + getSkillInstructionTransformer(tool.value, hasCommandAdapter) in both normal update and legacy-tool setup flows before calling generateSkillContent, replacing prior inline per-tool hyphen logic.
Profile sync mapping source
src/core/profile-sync-drift.ts
Replace inline WORKFLOW_TO_SKILL_DIR literal with WORKFLOW_TO_SKILL_REFERENCE import (cast to Record<WorkflowId,string>).
Tests: command-reference transformation suite
test/utils/command-references.test.ts, test/core/init.test.ts
Add tests for transformToSkillReferences behavior and extend init tests to assert adapterless skill SKILL.md contains skill references, no commands dir is created, and correct console messages are produced.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #879: Similar objective — adds transformToSkillReferences/WORKFLOW_TO_SKILL_REFERENCE and wires adapter-aware transformer selection into init/update flows.
  • #881: Implements and extends the same transform and wiring, matching the core fixes applied here.

Possibly related PRs

Suggested reviewers

  • TabishB
  • alfred-openspec

Poem

🐰 A rabbit tapped keys in the night,
And turned slash-commands into skilllight.
Kimi and Vibe now sing without fuss,
Adapterless blossoms — hop on and trust! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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 'fix: use skill references for adapterless tools' directly and clearly summarizes the main change: transforming command references to skill references for tools without adapters.
Linked Issues check ✅ Passed All coding requirements from issue #1155 are met: command counts are only reported when adapters exist, skill references replace /opsx commands in init output and generated prose, and proper per-tool skill invocations (Kimi /skill: prefix, Vibe plain names) are implemented.
Out of Scope Changes check ✅ Passed All changes are directly scoped to addressing issue #1155: skill reference transformation utilities, init/update command to use new transformation logic, test coverage for new behavior, and profile-sync-drift consolidation of workflow mappings.

✏️ 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 and usage tips.

@leno23 leno23 marked this pull request as ready for review June 5, 2026 10:04
@leno23 leno23 requested a review from TabishB as a code owner June 5, 2026 10:04

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

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@src/core/update.ts`:
- Around line 58-69: The getSkillInstructionTransformer helper is duplicated;
extract the function into a single shared module named command-references,
export it, and have both callers import it instead of defining duplicates;
ensure the extracted implementation uses the same logic (return
transformToHyphenCommands for toolId 'opencode' or 'pi', return a closure that
calls transformToToolSkillReferences(instructions, toolId) when
hasCommandAdapter is false, otherwise return undefined), update imports in the
files that used the duplicate (replace local definitions with import {
getSkillInstructionTransformer } from the command-references module), and remove
the duplicated definitions in both places.

In `@src/utils/command-references.ts`:
- Around line 22-34: Duplicate WORKFLOW_TO_SKILL mapping exists as
WORKFLOW_TO_SKILL_REFERENCE and WORKFLOW_TO_SKILL_DIR; remove the duplication by
exporting WORKFLOW_TO_SKILL_REFERENCE from src/utils/command-references.ts and
updating the other module to import it instead of defining WORKFLOW_TO_SKILL_DIR
locally: export the constant WORKFLOW_TO_SKILL_REFERENCE, then in the module
that defines WORKFLOW_TO_SKILL_DIR replace that local constant with an import
and alias (e.g., import { WORKFLOW_TO_SKILL_REFERENCE as WORKFLOW_TO_SKILL_DIR
}) and delete the duplicate object to keep a single source of truth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0fad2a79-bf71-470c-abe4-7895bfcd6165

📥 Commits

Reviewing files that changed from the base of the PR and between 1b06fdd and 79b4828.

📒 Files selected for processing (5)
  • src/core/init.ts
  • src/core/update.ts
  • src/utils/command-references.ts
  • test/core/init.test.ts
  • test/utils/command-references.test.ts

Comment thread src/core/update.ts Outdated
Comment thread src/utils/command-references.ts Outdated

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/update.ts (1)

272-279: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Adapterless legacy-upgrade guidance still points to nonexistent /opsx:* commands.

Line 275-Line 277 always print command invocations, even when newlyConfiguredTools are adapterless and no commands exist. This reintroduces incorrect getting-started guidance on the update path.

💡 Minimal fix sketch
 import {
   getSkillInstructionTransformer,
+  transformToToolSkillReferences,
 } from '../utils/command-references.js';
@@
     if (newlyConfiguredTools.length > 0) {
       console.log();
       console.log(chalk.bold('Getting started:'));
-      console.log('  /opsx:new       Start a new change');
-      console.log('  /opsx:continue  Create the next artifact');
-      console.log('  /opsx:apply     Implement tasks');
+      const adapterlessOnly = newlyConfiguredTools.every((id) => !CommandAdapterRegistry.has(id));
+      if (adapterlessOnly) {
+        const toolId = newlyConfiguredTools[0];
+        console.log(`  ${transformToToolSkillReferences('/opsx:new', toolId)}       Start a new change`);
+        console.log(`  ${transformToToolSkillReferences('/opsx:continue', toolId)}  Create the next artifact`);
+        console.log(`  ${transformToToolSkillReferences('/opsx:apply', toolId)}     Implement tasks`);
+      } else {
+        console.log('  /opsx:new       Start a new change');
+        console.log('  /opsx:continue  Create the next artifact');
+        console.log('  /opsx:apply     Implement tasks');
+      }
       console.log();
       console.log(`Learn more: ${chalk.cyan('https://github.com/Fission-AI/OpenSpec')}`);
     }
🤖 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/update.ts` around lines 272 - 279, The getting-started block prints
/opsx:* commands unconditionally even for adapterless upgrades; modify the logic
around newlyConfiguredTools so the guidance only prints when at least one
newlyConfiguredTools item exposes commands (e.g., check for a non-adapterless
tool or tool.commands?.length > 0). Concretely, wrap the console.log lines (the
block that prints 'Getting started' and the /opsx:* examples) in an if that
tests newlyConfiguredTools.some(tool => tool.adapterless !== true ||
(tool.commands && tool.commands.length > 0)), so adapterless-only upgrades won't
show nonexistent command hints.
🧹 Nitpick comments (1)
src/core/profile-sync-drift.ts (1)

15-15: ⚡ Quick win

Avoid the unchecked as Record<WorkflowId, string> cast on the shared mapping.

Line 15 hides key-coverage drift between ALL_WORKFLOWS and WORKFLOW_TO_SKILL_REFERENCE; if they diverge, later path.join(..., dirName) calls can receive undefined.

💡 Fail-fast guard to keep single source of truth
 type WorkflowId = (typeof ALL_WORKFLOWS)[number];
@@
-export const WORKFLOW_TO_SKILL_DIR = WORKFLOW_TO_SKILL_REFERENCE as Record<WorkflowId, string>;
+const missingWorkflowMappings = ALL_WORKFLOWS.filter(
+  (workflow) => WORKFLOW_TO_SKILL_REFERENCE[workflow] == null
+);
+if (missingWorkflowMappings.length > 0) {
+  throw new Error(
+    `Missing workflow-to-skill mapping for: ${missingWorkflowMappings.join(', ')}`
+  );
+}
+
+export const WORKFLOW_TO_SKILL_DIR =
+  WORKFLOW_TO_SKILL_REFERENCE as Record<WorkflowId, string>;
🤖 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/profile-sync-drift.ts` at line 15, The unchecked cast on
WORKFLOW_TO_SKILL_DIR hides missing keys between WORKFLOW_TO_SKILL_REFERENCE and
ALL_WORKFLOWS causing undefined dirs; remove the 'as Record<WorkflowId,string>'
cast and instead build/validate the record at runtime: iterate ALL_WORKFLOWS and
ensure each key exists on WORKFLOW_TO_SKILL_REFERENCE (use a reduce or map to
assemble a new object), throw a clear error if any workflow key is missing, then
export the validated Object.freeze(...) as WORKFLOW_TO_SKILL_DIR so downstream
path.join calls never receive undefined.
🤖 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.

Outside diff comments:
In `@src/core/update.ts`:
- Around line 272-279: The getting-started block prints /opsx:* commands
unconditionally even for adapterless upgrades; modify the logic around
newlyConfiguredTools so the guidance only prints when at least one
newlyConfiguredTools item exposes commands (e.g., check for a non-adapterless
tool or tool.commands?.length > 0). Concretely, wrap the console.log lines (the
block that prints 'Getting started' and the /opsx:* examples) in an if that
tests newlyConfiguredTools.some(tool => tool.adapterless !== true ||
(tool.commands && tool.commands.length > 0)), so adapterless-only upgrades won't
show nonexistent command hints.

---

Nitpick comments:
In `@src/core/profile-sync-drift.ts`:
- Line 15: The unchecked cast on WORKFLOW_TO_SKILL_DIR hides missing keys
between WORKFLOW_TO_SKILL_REFERENCE and ALL_WORKFLOWS causing undefined dirs;
remove the 'as Record<WorkflowId,string>' cast and instead build/validate the
record at runtime: iterate ALL_WORKFLOWS and ensure each key exists on
WORKFLOW_TO_SKILL_REFERENCE (use a reduce or map to assemble a new object),
throw a clear error if any workflow key is missing, then export the validated
Object.freeze(...) as WORKFLOW_TO_SKILL_DIR so downstream path.join calls never
receive undefined.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 30342f0f-6c67-4e95-9527-6b18b802d86e

📥 Commits

Reviewing files that changed from the base of the PR and between 79b4828 and c3c17a9.

📒 Files selected for processing (4)
  • src/core/init.ts
  • src/core/profile-sync-drift.ts
  • src/core/update.ts
  • src/utils/command-references.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/init.ts

@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 the follow-up. I rechecked this at c3c17a957af6e9ee298918fdd624e4297f311f8f and it still needs changes before merge.

Blocking issues:

  1. The branch does not compile. src/core/init.ts removed the local WORKFLOW_TO_SKILL_DIR map, but removeSkillDirs() still references it at line 749. A local pnpm install --frozen-lockfile --prefer-offline triggered the prepare/build step and failed with TS2304: Cannot find name 'WORKFLOW_TO_SKILL_DIR'.

  2. The legacy/newly-configured openspec update onboarding path still prints /opsx:new, /opsx:continue, and /opsx:apply unconditionally. That means adapterless-only tools like Kimi/Vibe can still be told to use slash commands that were not generated. The init path is improved, but update needs the same command-capable vs skills-only distinction and regression coverage.

The direction looks right, but please fix the compile break and add update-path coverage for adapterless-only tools before this lands.

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.

Kimi/Vibe 1.4.0 init generates skills that still reference /opsx commands

2 participants