Skip to content
Draft
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
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,20 @@ cd your-project
openspec init
```

Now tell your AI: `/opsx:propose <what-you-want-to-build>`
By default, OpenSpec installs the `core` workflow set only:
- `propose`
- `explore`
- `apply`
- `archive`

If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:sync`, `/opsx:bulk-archive`, `/opsx:onboard`), select it with `openspec config profile` and apply with `openspec update`.
Then tell your AI using the syntax that matches your tool:
- **Claude Code and other colon-style tools:** `/opsx:propose <what-you-want-to-build>`
- **OpenCode, Cursor, Windsurf, Copilot IDE, and other hyphen-style tools:** `/opsx-propose <what-you-want-to-build>`
- **Skills-only delivery or skill-based tools:** `/openspec-propose <what-you-want-to-build>`

If command files are missing, your setup may be using `skills` delivery by design. See [Supported Tools](docs/supported-tools.md) for the full delivery and syntax model.

If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:sync`, `/opsx:bulk-archive`, `/opsx:onboard`), run `openspec config profile` and then `openspec update`.

> [!NOTE]
> Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 25+ tools and growing.
Expand Down
56 changes: 30 additions & 26 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,42 @@ This is the reference for OpenSpec's slash commands. These commands are invoked

For workflow patterns and when to use each command, see [Workflows](workflows.md). For CLI commands, see [CLI](cli.md).

## First: Use the Syntax Your Tool Expects

OpenSpec command intent is shared, but the invocation syntax is tool-dependent. Check this before debugging a setup.

| Tool family | Syntax example |
|-------------|----------------|
| Claude Code and other colon-style integrations | `/opsx:propose`, `/opsx:apply` |
| OpenCode, Cursor, Windsurf, GitHub Copilot IDE, Bob Shell, Pi, and other hyphen-style integrations | `/opsx-propose`, `/opsx-apply` |
| Skill-based tools or skills-only delivery | `/openspec-propose`, `/openspec-apply-change` |

If you are not sure which surface your tool uses, check [Supported Tools](supported-tools.md).

## Quick Reference

By default, OpenSpec installs the `core` profile only: `propose`, `explore`, `apply`, and `archive`. Expanded workflows are opt-in.

### Default Quick Path (`core` profile)

| Command | Purpose |
| Command intent | Purpose |
|---------|---------|
| `/opsx:propose` | Create a change and generate planning artifacts in one step |
| `/opsx:explore` | Think through ideas before committing to a change |
| `/opsx:apply` | Implement tasks from the change |
| `/opsx:archive` | Archive a completed change |
| `propose` | Create a change and generate planning artifacts in one step |
| `explore` | Think through ideas before committing to a change |
| `apply` | Implement tasks from the change |
| `archive` | Archive a completed change |

### Expanded Workflow Commands (custom workflow selection)
### Expanded Workflow Commands (opt-in via `openspec config profile` + `openspec update`)

| Command | Purpose |
| Command intent | Purpose |
|---------|---------|
| `/opsx:new` | Start a new change scaffold |
| `/opsx:continue` | Create the next artifact based on dependencies |
| `/opsx:ff` | Fast-forward: create all planning artifacts at once |
| `/opsx:verify` | Validate implementation matches artifacts |
| `/opsx:sync` | Merge delta specs into main specs |
| `/opsx:bulk-archive` | Archive multiple changes at once |
| `/opsx:onboard` | Guided tutorial through the complete workflow |
| `new` | Start a new change scaffold |
| `continue` | Create the next artifact based on dependencies |
| `ff` | Fast-forward: create all planning artifacts at once |
| `verify` | Validate implementation matches artifacts |
| `sync` | Merge delta specs into main specs |
| `bulk-archive` | Archive multiple changes at once |
| `onboard` | Guided tutorial through the complete workflow |

The default global profile is `core`. To enable expanded workflow commands, run `openspec config profile`, select workflows, then run `openspec update` in your project.

Expand Down Expand Up @@ -610,19 +624,9 @@ AI: Welcome to OpenSpec!

## Command Syntax by AI Tool

Different AI tools use slightly different command syntax. Use the format that matches your tool:

| Tool | Syntax Example |
|------|----------------|
| Claude Code | `/opsx:propose`, `/opsx:apply` |
| Cursor | `/opsx-propose`, `/opsx-apply` |
| Windsurf | `/opsx-propose`, `/opsx-apply` |
| Copilot (IDE) | `/opsx-propose`, `/opsx-apply` |
| Trae | Skill-based invocations such as `/openspec-propose`, `/openspec-apply-change` (no generated `opsx-*` command files) |

The intent is the same across tools, but how commands are surfaced can differ by integration.
The syntax table now lives near the top of this page because it is usually the first thing to verify.

> **Note:** GitHub Copilot commands (`.github/prompts/*.prompt.md`) are only available in IDE extensions (VS Code, JetBrains, Visual Studio). GitHub Copilot CLI does not currently support custom prompt files — see [Supported Tools](supported-tools.md) for details and workarounds.
> **Note:** GitHub Copilot commands (`.github/prompts/*.prompt.md`) are only available in IDE extensions (VS Code, JetBrains, Visual Studio). GitHub Copilot CLI does not currently support custom prompt files. See [Supported Tools](supported-tools.md) for details and workarounds.

---

Expand Down
3 changes: 3 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ OpenSpec helps you and your AI coding assistant agree on what to build before an

The default global profile is `core`, which includes `propose`, `explore`, `apply`, and `archive`. You can enable the expanded workflow commands with `openspec config profile` and then `openspec update`.

> [!NOTE]
> Examples on this page use Claude-style `/opsx:...` syntax for readability. If your tool expects `/opsx-...` or direct `openspec-*` skill invocations instead, check [Commands](commands.md#first-use-the-syntax-your-tool-expects).

## What OpenSpec Creates

After running `openspec init`, your project has this structure:
Expand Down
41 changes: 41 additions & 0 deletions docs/supported-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ By default, OpenSpec uses the `core` profile, which includes:

You can enable expanded workflows (`new`, `continue`, `ff`, `verify`, `sync`, `bulk-archive`, `onboard`) via `openspec config profile`, then run `openspec update`.

## What to Expect After `openspec init`

- **Only 4 workflows installed?** That is the default `core` profile. You should expect `propose`, `explore`, `apply`, and `archive` unless you explicitly opt into a broader workflow set.
- **Missing command files?** Check your delivery mode. `skills` delivery installs only `openspec-*` skills, `commands` delivery installs only `opsx-*` command files, and `both` installs both surfaces when the tool supports both.
- **Wrong command syntax?** The same workflow may surface as `/opsx:propose`, `/opsx-propose`, or `/openspec-propose` depending on the tool and delivery mode.

## Command Syntax by Tool Family

| Tool family | Syntax example |
|-------------|----------------|
| Claude Code and other colon-style integrations | `/opsx:propose`, `/opsx:apply` |
| OpenCode, Cursor, Windsurf, GitHub Copilot IDE, Bob Shell, Pi, and other hyphen-style integrations | `/opsx-propose`, `/opsx-apply` |
| ForgeCode, Trae, or any project using skills-only delivery | `/openspec-propose`, `/openspec-apply-change` |

## Tool Directory Reference

| Tool (ID) | Skills path pattern | Command path pattern |
Expand Down Expand Up @@ -101,6 +115,33 @@ When selected by profile/workflow config, OpenSpec generates these skills:

See [Commands](commands.md) for command behavior and [CLI](cli.md) for `init`/`update` options.

## Troubleshooting

### Why did I only get 4 workflows?

Because OpenSpec defaults to the `core` profile. Run `openspec config profile`, choose the expanded workflows you want, then run `openspec update` in the project.

### Why are command files missing?

Two common reasons:

1. **Delivery is set to `skills`**. That installs only `openspec-*` skills, so missing `opsx-*` files are expected.
2. **Your tool is skill-based**. Tools such as ForgeCode and Trae do not currently get generated `opsx-*` command files, so use the installed skills instead.

### Which syntax should I use?

Use the syntax family that matches your tool:

- `/opsx:...` for colon-style integrations
- `/opsx-...` for hyphen-style integrations
- `/openspec-...` when you are using skills directly

If you are unsure, compare your generated file paths against the table above or check [Commands](commands.md) first.

### Why does OpenCode documentation sometimes mention `.opencode/command/` and sometimes `.opencode/commands/`?

Current OpenSpec releases generate `.opencode/commands/`. Older OpenCode setups and older docs may still mention `.opencode/command/`. If you see the singular path in older reports, treat it as version drift rather than a separate workflow model.

## Related

- [CLI Reference](cli.md) — Terminal commands
Expand Down
17 changes: 11 additions & 6 deletions src/core/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { getGlobalConfig, type Delivery, type Profile } from './global-config.js
import { getProfileWorkflows, CORE_WORKFLOWS, ALL_WORKFLOWS } from './profiles.js';
import { getAvailableTools } from './available-tools.js';
import { migrateIfNeeded } from './migration.js';
import { buildOnboardingGuidance } from './onboarding-guidance.js';

const require = createRequire(import.meta.url);
const { version: OPENSPEC_VERSION } = require('../../package.json');
Expand Down Expand Up @@ -696,17 +697,21 @@ export class InitCommand {
console.log(chalk.dim(`Config: skipped (non-interactive mode)`));
}

// Getting started (task 7.6: show propose if in profile)
// Getting started
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')) {
if (activeWorkflows.length > 0) {
console.log(chalk.bold('Getting started:'));
console.log(' Start your first change: /opsx:propose "your idea"');
} else if (activeWorkflows.includes('new')) {
console.log(chalk.bold('Getting started:'));
console.log(' Start your first change: /opsx:new "your idea"');
const onboardingLines = buildOnboardingGuidance({
workflows: activeWorkflows,
delivery: globalCfg.delivery ?? 'both',
toolIds: successfulTools.map((tool) => tool.value),
});
for (const line of onboardingLines) {
console.log(` ${line}`);
}
} else {
console.log("Done. Run 'openspec config profile' to configure your workflows.");
}
Expand Down
10 changes: 9 additions & 1 deletion src/core/migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getGlobalConfig, getGlobalConfigPath, saveGlobalConfig, type Delivery }
import { CommandAdapterRegistry } from './command-generation/index.js';
import { WORKFLOW_TO_SKILL_DIR } from './profile-sync-drift.js';
import { ALL_WORKFLOWS } from './profiles.js';
import { buildOnboardingGuidance } from './onboarding-guidance.js';
import path from 'path';
import * as fs from 'fs';

Expand Down Expand Up @@ -127,5 +128,12 @@ export function migrateIfNeeded(projectPath: string, tools: AIToolOption[]): voi
saveGlobalConfig(config);

console.log(`Migrated: custom profile with ${installedWorkflows.length} workflows`);
console.log("New in this version: /opsx:propose. Try 'openspec config profile core' for the streamlined experience.");
const onboardingLines = buildOnboardingGuidance({
workflows: installedWorkflows,
delivery: config.delivery ?? inferDelivery(artifacts),
});
for (const line of onboardingLines) {
console.log(line);
}
console.log("New projects default to the streamlined core profile. Run 'openspec config profile core', then 'openspec update' if you want that setup here.");
}
144 changes: 144 additions & 0 deletions src/core/onboarding-guidance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import path from 'path';
import { AI_TOOLS } from './config.js';
import { CommandAdapterRegistry } from './command-generation/index.js';
import type { Delivery } from './global-config.js';

const WORKFLOW_TO_SKILL_NAME: Record<string, string> = {
'explore': 'openspec-explore',
'new': 'openspec-new-change',
'continue': 'openspec-continue-change',
'apply': 'openspec-apply-change',
'ff': 'openspec-ff-change',
'sync': 'openspec-sync-specs',
'archive': 'openspec-archive-change',
'bulk-archive': 'openspec-bulk-archive-change',
'verify': 'openspec-verify-change',
'onboard': 'openspec-onboard',
'propose': 'openspec-propose',
};

const EXPANDED_WORKFLOW_IDS = ['new', 'continue', 'ff', 'verify', 'sync'];

export interface OnboardingGuidanceOptions {
workflows: string[];
delivery: Delivery;
toolIds?: string[];
maxExamples?: number;
}

function describeDelivery(delivery: Delivery): string {
if (delivery === 'skills') return 'skills only';
if (delivery === 'commands') return 'commands only';
return 'both (skills + commands)';
}

function getToolLabel(toolId: string): string {
const tool = AI_TOOLS.find((candidate) => candidate.value === toolId);
return tool?.successLabel ?? tool?.name ?? toolId;
}

function getPrimaryWorkflow(workflows: string[]): string | undefined {
if (workflows.includes('propose')) return 'propose';
if (workflows.includes('new')) return 'new';
return workflows[0];
}

function getSkillInvocation(workflowId: string): string | null {
const skillName = WORKFLOW_TO_SKILL_NAME[workflowId];
if (!skillName) return null;
return `/${skillName}`;
}

function getCommandInvocation(toolId: string, workflowId: string): string | null {
const adapter = CommandAdapterRegistry.get(toolId);
if (!adapter) return null;

const normalizedPath = adapter.getFilePath(workflowId).replace(/\\/g, '/');
const basename = path.posix.basename(normalizedPath);

if (normalizedPath.includes('/opsx/')) {
return `/opsx:${workflowId}`;
}

if (basename.startsWith('opsx-')) {
return `/opsx-${workflowId}`;
}

return `/opsx:${workflowId}`;
}

function getInvocationExample(toolId: string, workflowId: string, delivery: Delivery): string | null {
if (delivery !== 'skills') {
const commandInvocation = getCommandInvocation(toolId, workflowId);
if (commandInvocation) {
return `${commandInvocation} \"your idea\"`;
}
}

if (delivery !== 'commands' || !getCommandInvocation(toolId, workflowId)) {
const skillInvocation = getSkillInvocation(workflowId);
if (skillInvocation) {
return `${skillInvocation} \"your idea\"`;
}
}

return null;
}

function getSkillOnlyTools(toolIds: string[]): string[] {
return toolIds.filter((toolId) => CommandAdapterRegistry.get(toolId) == null);
}

function buildGroupedExamples(toolIds: string[], workflowId: string, delivery: Delivery): string[] {
const grouped = new Map<string, string[]>();

for (const toolId of toolIds) {
const example = getInvocationExample(toolId, workflowId, delivery);
if (!example) continue;

const labels = grouped.get(example) ?? [];
labels.push(getToolLabel(toolId));
grouped.set(example, labels);
}

return [...grouped.entries()].map(([example, labels]) => `${labels.join(', ')}: ${example}`);
}

export function buildOnboardingGuidance(options: OnboardingGuidanceOptions): string[] {
const workflows = [...options.workflows];
const delivery = options.delivery;
const toolIds = [...new Set(options.toolIds ?? [])];
const maxExamples = options.maxExamples ?? 3;
const lines: string[] = [];

if (workflows.length > 0) {
lines.push(`Available workflows: ${workflows.join(', ')}`);
}
lines.push(`Delivery: ${describeDelivery(delivery)}`);

const primaryWorkflow = getPrimaryWorkflow(workflows);
if (primaryWorkflow && toolIds.length > 0) {
const examples = buildGroupedExamples(toolIds, primaryWorkflow, delivery).slice(0, maxExamples);
if (examples.length > 0) {
lines.push('Try:');
for (const example of examples) {
lines.push(` ${example}`);
}
}
}

if (!EXPANDED_WORKFLOW_IDS.every((workflowId) => workflows.includes(workflowId))) {
lines.push('Want expanded workflows like /opsx:new and /opsx:continue? Run `openspec config profile`, then `openspec update`.');
}

if (delivery === 'skills') {
lines.push('Command files were not generated because delivery is set to skills-only.');
}

const skillOnlyTools = getSkillOnlyTools(toolIds);
if (skillOnlyTools.length > 0 && delivery !== 'skills') {
lines.push(`These tools use skill-based invocations here: ${skillOnlyTools.map(getToolLabel).join(', ')}.`);
}

return lines;
}
12 changes: 9 additions & 3 deletions src/core/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { isInteractive } from '../utils/interactive.js';
import { getGlobalConfig, type Delivery } from './global-config.js';
import { getProfileWorkflows, ALL_WORKFLOWS } from './profiles.js';
import { getAvailableTools } from './available-tools.js';
import { buildOnboardingGuidance } from './onboarding-guidance.js';
import {
WORKFLOW_TO_SKILL_DIR,
getCommandConfiguredTools,
Expand Down Expand Up @@ -268,9 +269,14 @@ export class UpdateCommand {
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 onboardingLines = buildOnboardingGuidance({
workflows: desiredWorkflows,
delivery,
toolIds: newlyConfiguredTools,
});
for (const line of onboardingLines) {
console.log(` ${line}`);
}
console.log();
console.log(`Learn more: ${chalk.cyan('https://github.com/Fission-AI/OpenSpec')}`);
}
Expand Down
Loading
Loading