Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/adapterless-skill-references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fission-ai/openspec': patch
---

Generated skills for tools without a command adapter (Kimi Code, Mistral Vibe, Hermes, ForgeCode, CodeArts) no longer reference `/opsx:*` commands that were never generated: skill cross-references, the init getting-started hint, and the profile-migration message now use each tool's documented skill invocation (Kimi Code: `/skill:openspec-*`; others: `/openspec-*`), and Codex — skills-invocable with no slash surface — gets a syntax-neutral hint that names the skill. Selections that mix invocation syntaxes print one labeled hint per distinct form, so every advertised instruction is usable by the tool it names. When `delivery: commands` would generate nothing for a selected tool, init prints a configuration correction naming that tool, even when other tools did get commands or skills. The committed skills.sh distribution is regenerated with skill references (default `/openspec-*` form, as that channel installs skills only).
7 changes: 6 additions & 1 deletion scripts/generate-skillssh.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

import { getSkillTemplates, generateSkillContent } from '../dist/core/shared/skill-generation.js';
import { transformToSkillReferences } from '../dist/utils/command-references.js';
import {
cleanSkillSubdirectories,
prepareSkillDirectory,
Expand All @@ -33,7 +34,11 @@ cleanSkillSubdirectories(outDir);

let count = 0;
for (const { template, dirName } of getSkillTemplates()) {
const content = stripVolatileFrontmatter(generateSkillContent(template, 'skills.sh'));
// skills.sh installs SKILL.md files only — no /opsx:* commands exist in
// that channel, so references must point at the skills themselves.
const content = stripVolatileFrontmatter(
generateSkillContent(template, 'skills.sh', transformToSkillReferences)
);
const skillDir = prepareSkillDirectory(outDir, dirName);
writeFileSync(join(skillDir, 'SKILL.md'), content, 'utf8');
count++;
Expand Down
2 changes: 1 addition & 1 deletion skills/openspec-apply-change/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Implement tasks from an OpenSpec change.
- Auto-select if only one active change exists
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select

Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
Always announce: "Using change: <name>" and how to override (e.g., `/openspec-apply-change <other>`).

2. **Check status to understand the schema**
```bash
Expand Down
2 changes: 1 addition & 1 deletion skills/openspec-explore/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ You: [reads codebase]

**User is stuck mid-implementation:**
```
User: /opsx:explore add-auth-system
User: /openspec-explore add-auth-system
The OAuth integration is more complex than expected

You: [reads change artifacts]
Expand Down
2 changes: 1 addition & 1 deletion skills/openspec-ff-change/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ After completing all artifacts, summarize:
- Change name and location
- List of artifacts created with brief descriptions
- What's ready: "All artifacts created! Ready for implementation."
- Prompt: "Run `/opsx:apply` or ask me to implement to start working on the tasks."
- Prompt: "Run `/openspec-apply-change` or ask me to implement to start working on the tasks."
**Artifact Creation Guidelines**
Expand Down
44 changes: 22 additions & 22 deletions skills/openspec-onboard/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ openspec --version 2>&1 || echo "CLI_NOT_INSTALLED"
```

**If CLI not installed:**
> OpenSpec CLI is not installed. Install it first, then come back to `/opsx:onboard`.
> OpenSpec CLI is not installed. Install it first, then come back to `/openspec-onboard`.

Stop here if not installed.

Expand Down Expand Up @@ -154,7 +154,7 @@ Spend 1-2 minutes investigating the relevant code:
│ [Optional: ASCII diagram if helpful] │
└─────────────────────────────────────────┘

Explore mode (`/opsx:explore`) is for this kind of thinking—investigating before implementing. You can use it anytime you need to think through a problem.
Explore mode (`/openspec-explore`) is for this kind of thinking—investigating before implementing. You can use it anytime you need to think through a problem.

Now let's create a change to hold our work.
```
Expand Down Expand Up @@ -470,25 +470,25 @@ This same rhythm works for any size change—a small fix or a major feature.

| Command | What it does |
|-------------------|--------------------------------------------|
| `/opsx:propose` | Create a change and generate all artifacts |
| `/opsx:explore` | Think through problems before/during work |
| `/opsx:apply` | Implement tasks from a change |
| `/opsx:archive` | Archive a completed change |
| `/openspec-propose` | Create a change and generate all artifacts |
| `/openspec-explore` | Think through problems before/during work |
| `/openspec-apply-change` | Implement tasks from a change |
| `/openspec-archive-change` | Archive a completed change |

**Additional commands:**

| Command | What it does |
|--------------------|----------------------------------------------------------|
| `/opsx:new` | Start a new change, step through artifacts one at a time |
| `/opsx:continue` | Continue working on an existing change |
| `/opsx:ff` | Fast-forward: create all artifacts at once |
| `/opsx:verify` | Verify implementation matches artifacts |
| `/openspec-new-change` | Start a new change, step through artifacts one at a time |
| `/openspec-continue-change` | Continue working on an existing change |
| `/openspec-ff-change` | Fast-forward: create all artifacts at once |
| `/openspec-verify-change` | Verify implementation matches artifacts |

---

## What's Next?

Try `/opsx:propose` on something you actually want to build. You've got the rhythm now!
Try `/openspec-propose` on something you actually want to build. You've got the rhythm now!
```

---
Expand All @@ -503,8 +503,8 @@ If the user says they need to stop, want to pause, or seem disengaged:
No problem! Your change is saved at the `changeRoot` reported by `openspec status --change "<name>" --json`.

To pick up where we left off later:
- `/opsx:continue <name>` - Resume artifact creation
- `/opsx:apply <name>` - Jump to implementation (if tasks exist)
- `/openspec-continue-change <name>` - Resume artifact creation
- `/openspec-apply-change <name>` - Jump to implementation (if tasks exist)

The work won't be lost. Come back whenever you're ready.
```
Expand All @@ -522,21 +522,21 @@ If the user says they just want to see the commands or skip the tutorial:

| Command | What it does |
|--------------------------|--------------------------------------------|
| `/opsx:propose <name>` | Create a change and generate all artifacts |
| `/opsx:explore` | Think through problems (no code changes) |
| `/opsx:apply <name>` | Implement tasks |
| `/opsx:archive <name>` | Archive when done |
| `/openspec-propose <name>` | Create a change and generate all artifacts |
| `/openspec-explore` | Think through problems (no code changes) |
| `/openspec-apply-change <name>` | Implement tasks |
| `/openspec-archive-change <name>` | Archive when done |

**Additional commands:**

| Command | What it does |
|---------------------------|-------------------------------------|
| `/opsx:new <name>` | Start a new change, step by step |
| `/opsx:continue <name>` | Continue an existing change |
| `/opsx:ff <name>` | Fast-forward: all artifacts at once |
| `/opsx:verify <name>` | Verify implementation |
| `/openspec-new-change <name>` | Start a new change, step by step |
| `/openspec-continue-change <name>` | Continue an existing change |
| `/openspec-ff-change <name>` | Fast-forward: all artifacts at once |
| `/openspec-verify-change <name>` | Verify implementation |

Try `/opsx:propose` to start your first change.
Try `/openspec-propose` to start your first change.
```

Exit gracefully.
Expand Down
4 changes: 2 additions & 2 deletions skills/openspec-propose/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ I'll create a change with artifacts:
- design.md (how)
- tasks.md (implementation steps)

When ready to implement, run /opsx:apply
When ready to implement, run /openspec-apply-change

---

Expand Down Expand Up @@ -93,7 +93,7 @@ After completing all artifacts, summarize:
- Change name and location
- List of artifacts created with brief descriptions
- What's ready: "All artifacts created! Ready for implementation."
- Prompt: "Run `/opsx:apply` or ask me to implement to start working on the tasks."
- Prompt: "Run `/openspec-apply-change` or ask me to implement to start working on the tasks."

**Artifact Creation Guidelines**

Expand Down
16 changes: 8 additions & 8 deletions skills/openspec-update-change/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
- Read the artifact(s) the request touches and the change's other existing artifacts.
- Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.
- Note everything that is now inconsistent, missing, or contradictory.
- Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.
- Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/openspec-continue-change` to create them.
- If the change is already coherent, say so and make no edits.

5. **Confirm and apply, one artifact at a time**
Expand All @@ -65,21 +65,21 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
```

6. **Point to the next step (guidance only - NEVER act on it)**
- Artifacts still missing -> suggest `/opsx:continue` to create them.
- Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.
- Everything done and implemented -> suggest `/opsx:archive`.
- Artifacts still missing -> suggest `/openspec-continue-change` to create them.
- Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/openspec-apply-change` to carry the delta into code.
- Everything done and implemented -> suggest `/openspec-archive-change`.

**Output**

After each invocation, show:
- Which artifacts were revised (and which proposed revisions were rejected)
- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)
- Anything deferred to `/openspec-continue-change` (not-yet-created artifacts or files)
- Where the change stands and the recommended next command

**Guardrails**
- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.
- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/openspec-apply-change`.
- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.
- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/openspec-continue-change`'s job.
- Confirm every edit with the user before writing.
- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the "Update vs. Start Fresh" heuristic).
- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/openspec-new-change` (the "Update vs. Start Fresh" heuristic).
93 changes: 83 additions & 10 deletions src/core/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { createRequire } from 'module';
import { FileSystemUtils } from '../utils/file-system.js';
import { classifyOpenSpecDir, storePointerProblem } from './project-config.js';
import { findRepoPlanningRootSync } from './planning-home.js';
import { getTransformerForTool } from '../utils/command-references.js';
import { getSkillReferenceTransformer, getTransformerForTool, transformToSkillReferences } from '../utils/command-references.js';
import {
AI_TOOLS,
OPENSPEC_DIR_NAME,
Expand Down Expand Up @@ -689,7 +689,7 @@ export class InitCommand {
const skillFile = path.join(skillDir, 'SKILL.md');

// Generate SKILL.md content with YAML frontmatter including generatedBy
const transformer = getTransformerForTool(tool.value, delivery);
const transformer = getTransformerForTool(tool.value, delivery, resolveCommandSurfaceCapability(tool.value));
const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);

// Write the skill file
Expand Down Expand Up @@ -868,13 +868,78 @@ export class InitCommand {
const globalCfg = getGlobalConfig();
const activeProfile: Profile = (this.profileOverride as Profile) ?? globalCfg.profile ?? 'core';
const activeWorkflows = [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
console.log();
if (activeWorkflows.includes('propose')) {
// When no tool got /opsx:* commands, point at the skill instead of a
// command that does not exist.
const activeDelivery: Delivery = globalCfg.delivery ?? 'both';
const commandsGenerated = successfulTools.some((tool) => shouldGenerateCommandsForTool(tool.value, activeDelivery));
const skillsGenerated = successfulTools.some((tool) => shouldGenerateSkillsForTool(tool.value, activeDelivery));
// Each hint line must be a usable instruction for the tool it serves.
// Tools that generated commands are told the /opsx:* command; tools that
// only got skills are told their documented skill invocation (Kimi Code:
// /skill:openspec-*; skills-invocable codex has no slash surface at all,
// so its hint names the skill; others: /openspec-*). Tools that got no
// artifacts are covered by the configuration correction instead. When
// the selection disagrees, print one line per distinct instruction,
// labeled with the tools it applies to.
const startHintLines = (command: string): string[] => {
const skillName = transformToSkillReferences(command).slice(1);
const hintToTools = new Map<string, string[]>();
for (const tool of successfulTools) {
let hint: string;
if (shouldGenerateCommandsForTool(tool.value, activeDelivery)) {
hint = `Start your first change: ${command} "your idea"`;
} else if (shouldGenerateSkillsForTool(tool.value, activeDelivery)) {
hint =
resolveCommandSurfaceCapability(tool.value) === 'skills-invocable'
? `Start your first change with the ${skillName} skill`
: `Start your first change: ${getSkillReferenceTransformer(tool.value)(command)} "your idea"`;
} else {
continue;
}
hintToTools.set(hint, [...(hintToTools.get(hint) ?? []), tool.name]);
}
if (hintToTools.size === 0) {
// No successful tools: keep the generic command hint
return [`Start your first change: ${command} "your idea"`];
}
if (hintToTools.size === 1) {
return [[...hintToTools.keys()][0]];
}
return [...hintToTools.entries()].map(([hint, toolNames]) => `${hint} (${toolNames.join(', ')})`);
};
const printStartHints = (command: string): void => {
console.log(chalk.bold('Getting started:'));
console.log(' Start your first change: /opsx:propose "your idea"');
for (const line of startHintLines(command)) {
console.log(` ${line}`);
}
};
console.log();
// delivery=commands with tools that only support skills: those tools get
// no artifacts at all, so print a per-tool configuration correction
// rather than leave them with a dead (or missing) instruction — even
// when other selected tools did get commands or skills.
const zeroArtifactTools = successfulTools.filter(
(tool) =>
!shouldGenerateSkillsForTool(tool.value, activeDelivery) &&
!shouldGenerateCommandsForTool(tool.value, activeDelivery)
);
if (zeroArtifactTools.length > 0) {
const names = zeroArtifactTools.map((tool) => tool.name).join(', ');
console.log(
chalk.yellow(
`No skills or commands were generated for ${names}: delivery is set to 'commands' but ` +
`${zeroArtifactTools.length === 1 ? 'it supports' : 'they support'} only skills. ` +
`Run 'openspec config set delivery both' to generate skills.`
)
);
}
if (successfulTools.length > 0 && !commandsGenerated && !skillsGenerated) {
// Nothing was generated for any tool: the correction above is the
// whole story, so don't advertise an invocation that doesn't exist.
} else if (activeWorkflows.includes('propose')) {
printStartHints('/opsx:propose');
} else if (activeWorkflows.includes('new')) {
console.log(chalk.bold('Getting started:'));
console.log(' Start your first change: /opsx:new "your idea"');
printStartHints('/opsx:new');
} else {
console.log("Done. Run 'openspec config profile' to configure your workflows.");
}
Expand All @@ -884,10 +949,18 @@ export class InitCommand {
console.log(`Learn more: ${chalk.cyan('https://github.com/Fission-AI/OpenSpec')}`);
console.log(`Feedback: ${chalk.cyan('https://github.com/Fission-AI/OpenSpec/issues')}`);

// Restart instruction if any tools were configured
if (results.createdTools.length > 0 || results.refreshedTools.length > 0) {
// Restart instruction if any tools were configured and got a surface
// (when nothing was generated there is nothing a restart would pick up);
// only mention slash commands when slash commands were actually generated
if ((results.createdTools.length > 0 || results.refreshedTools.length > 0) && (commandsGenerated || skillsGenerated)) {
console.log();
console.log(chalk.white('Restart your IDE for slash commands to take effect.'));
console.log(
chalk.white(
commandsGenerated
? 'Restart your IDE for slash commands to take effect.'
: 'Restart your IDE for the new skills to take effect.'
)
);
}

console.log();
Expand Down
Loading
Loading