Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ openspec init [path] [options]

`--profile custom` uses whatever workflows are currently selected in global config (`openspec config profile`).

**Supported tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `bob`, `claude`, `cline`, `codex`, `forgecode`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `junie`, `kilocode`, `kimi`, `kiro`, `lingma`, `vibe`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`
**Supported tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `bob`, `claude`, `cline`, `codex`, `forgecode`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `junie`, `kilocode`, `kimi`, `kiro`, `lingma`, `vibe`, `oh-my-pi`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`

> This list mirrors `AI_TOOLS` in `src/core/config.ts`. See [Supported Tools](supported-tools.md) for each tool's skill and command paths.

Expand Down
1 change: 1 addition & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ Different AI tools use slightly different command syntax. Use the format that ma
| Cursor | `/opsx-propose`, `/opsx-apply` |
| Windsurf | `/opsx-propose`, `/opsx-apply` |
| Copilot (IDE) | `/opsx-propose`, `/opsx-apply` |
| Oh My Pi | `/opsx-propose`, `/opsx-apply` |
| Kimi CLI | Skill-based invocations such as `/skill:openspec-propose`, `/skill:openspec-apply-change` (no generated `opsx-*` command files) |
| Trae | Skill-based invocations such as `/openspec-propose`, `/openspec-apply-change` (no generated `opsx-*` command files) |

Expand Down
1 change: 1 addition & 0 deletions docs/how-commands-work.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ The intent is identical everywhere. The punctuation differs. Use the form that m
| Cursor | `/opsx-propose`, `/opsx-apply` |
| Windsurf | `/opsx-propose`, `/opsx-apply` |
| GitHub Copilot (IDE) | `/opsx-propose`, `/opsx-apply` |
| Oh My Pi | `/opsx-propose`, `/opsx-apply` |
| Kimi CLI | skill-style, e.g. `/skill:openspec-propose` |
| Trae | skill-style, e.g. `/openspec-propose` |

Expand Down
3 changes: 2 additions & 1 deletion docs/supported-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ You can enable expanded workflows (`new`, `continue`, `ff`, `verify`, `bulk-arch
| Kiro (`kiro`) | `.kiro/skills/openspec-*/SKILL.md` | `.kiro/prompts/opsx-<id>.prompt.md` |
| Lingma (`lingma`) | `.lingma/skills/openspec-*/SKILL.md` | `.lingma/commands/opsx/<id>.md` |
| Mistral Vibe (`vibe`) | `.vibe/skills/openspec-*/SKILL.md` | Not generated (no command adapter; use skill-based `/openspec-*` invocations) |
| Oh My Pi (`oh-my-pi`) | `.omp/skills/openspec-*/SKILL.md` | `.omp/commands/opsx-<id>.md` |
| OpenCode (`opencode`) | `.opencode/skills/openspec-*/SKILL.md` | `.opencode/commands/opsx-<id>.md` |
| Pi (`pi`) | `.pi/skills/openspec-*/SKILL.md` | `.pi/prompts/opsx-<id>.md` |
| Qoder (`qoder`) | `.qoder/skills/openspec-*/SKILL.md` | `.qoder/commands/opsx/<id>.md` |
Expand Down Expand Up @@ -75,7 +76,7 @@ openspec init --tools none
openspec init --profile core
```

**Available tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `bob`, `claude`, `cline`, `codex`, `forgecode`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `junie`, `kilocode`, `kimi`, `kiro`, `lingma`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `vibe`, `windsurf`
**Available tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `bob`, `claude`, `cline`, `codex`, `forgecode`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `junie`, `kilocode`, `kimi`, `kiro`, `lingma`, `vibe`, `oh-my-pi`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`

## Workflow-Dependent Installation

Expand Down
2 changes: 2 additions & 0 deletions openspec/changes/feat-add-omp-tool-support/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-29
59 changes: 59 additions & 0 deletions openspec/changes/feat-add-omp-tool-support/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
## Context

OpenSpec supports AI coding assistants by generating two artifact types per tool: skill files (for agent instruction loading) and command files (for slash-command invocation). Each tool has a `ToolCommandAdapter` that controls the output path and file format.

Oh My Pi (OMP) is a terminal AI coding agent that uses a `.omp/` project directory. Its command system uses the filename stem as the slash command name (e.g., `opsx-propose.md` → `/opsx-propose`), which requires command body references to be in hyphenated form (`/opsx-propose` rather than `/opsx:propose`). This is the same pattern already used by Pi and OpenCode.

## Goals / Non-Goals

**Goals:**
- Add a `ToolCommandAdapter` for Oh My Pi producing `.omp/commands/opsx-<id>.md` with `description` frontmatter.
- Inject `**Provided arguments**: $@` after the `**Input**:` heading in command bodies so user-supplied arguments are visible to the agent when a command is invoked with arguments.
- Register the adapter so `init` and `update` can generate command files and skill files for OMP.
- Apply `transformToHyphenCommands` to OMP skill bodies so `/opsx:` references become `/opsx-` for consistency with the command naming convention.
- Add OMP to `AI_TOOLS` so it appears in tool selection and auto-detection.

**Non-Goals:**
- Changing the file format used by Pi or OpenCode.
- Adding OMP-specific frontmatter fields beyond `description`.
- Auto-detecting OMP presence (the `.omp/` directory is sufficient as `skillsDir`).

## Decisions

### Reuse the existing `transformToHyphenCommands` transformer for skill files

**Decision**: Add `'oh-my-pi'` to the `tool.value` conditional in `init.ts` and `update.ts` that selects the hyphen transformer.

**Rationale**: Pi and OpenCode follow the same filename-as-command-name convention and are already handled by this branch. OMP has an identical convention. Extending the same conditional is minimal-diff and keeps the pattern consistent.

**Alternative considered**: Storing the transformer flag on the `AIToolOption` object (e.g., `useHyphenCommands: true`). This is cleaner long-term but is a larger refactor than this change warrants. It can be done separately if more tools adopt this convention.

### Use `description`-only frontmatter in command files

**Decision**: The `formatFile` method outputs only a `description` YAML field in frontmatter.

**Rationale**: OMP's command format uses filename for the slash command name and `description` for display. No additional frontmatter fields (name, category, tags) are needed, matching the minimalist approach used by Pi.

### Inject `$@` into command bodies (matching Pi)

**Decision**: Apply the same `injectArgs` logic as Pi's adapter — append `**Provided arguments**: $@` on the line after the `**Input**:` heading, skipping injection if `$@` or `$ARGUMENTS` is already present.

**Rationale**: OpenSpec command templates contain an `**Input**:` heading that describes what arguments the command accepts (e.g., `**Input**: The argument after /opsx-propose is the change name…`). Without injecting `$@`, a user running `/opsx-propose my-feature` passes `my-feature` as `$@` but the agent never sees it — the argument is silently discarded. OMP's prompt template spec explicitly supports `$@` and positional forms. Pi faces the same problem and already solves it with identical injection logic.

**Alternative considered**: Leaving injection out and relying on users to add `$@` manually to the template. Rejected: this would silently break argument passing for all OMP commands and diverge from Pi's established behavior.

### Tool ID is `'oh-my-pi'`, skills directory is `'.omp'`

**Decision**: `value: 'oh-my-pi'` in `AI_TOOLS`; `skillsDir: '.omp'`.

**Rationale**: The tool ID uses the full kebab-case name for human clarity. The `.omp/` directory is the short canonical path users will see on disk. The two are independent and follow the precedent set by `kilocode` (ID) → `.kilocode` (dir).

## Risks / Trade-offs

- **`.omp/` directory collision**: If a project uses `.omp/` for another purpose, OMP detection will yield a false positive. → Mitigation: This is consistent with how every other tool is detected; no special handling is warranted.
- **Conditional growth in init.ts / update.ts**: Adding a third value to the `tool.value === 'opencode' || tool.value === 'pi'` checks makes the long-term refactor to a per-tool flag more urgent. → Mitigation: Document in tasks; the refactor is low-risk and can follow separately.
- **Adapter missing `escapeYamlValue`**: If a command description contains special YAML characters, the description frontmatter could be malformed. → Mitigation: `escapeYamlValue` is applied in this implementation (task 1.2), consistent with Pi adapter.

## Open Questions

None — implementation is well-defined by the existing Pi/OpenCode/OMP pattern.
34 changes: 34 additions & 0 deletions openspec/changes/feat-add-omp-tool-support/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Why

Oh My Pi (OMP) is a terminal AI coding agent whose users expect OpenSpec workflows to be available as slash commands. Without an adapter, users who have OMP configured in their project cannot generate OMP-native command files or get the correct skill transformations from `openspec init` or `openspec update`.

## What Changes

- Add a `ToolCommandAdapter` for Oh My Pi that generates command files at `.omp/commands/opsx-<id>.md` with YAML `description` frontmatter, hyphen-based command references, and `$@` argument injection after the `**Input**:` heading (matching Pi's convention so user-supplied arguments are visible to the agent).
- Register `oh-my-pi` in `AI_TOOLS` with `skillsDir: '.omp'` so detection and skill generation work.
- Register the new adapter in `CommandAdapterRegistry` and `adapters/index.ts`.
- Add Oh My Pi to the `transformToHyphenCommands` whitelist in `init.ts` and `update.ts` so skill files use the correct `/opsx-*` invocation form that matches OMP's filename-based command naming.
- Add test coverage for the new adapter.
- Update `docs/supported-tools.md` with the new tool's directory reference.

## Capabilities

### New Capabilities

- `oh-my-pi-tool`: Command and skill generation support for the Oh My Pi (OMP) AI coding agent, following its `.omp/commands/opsx-<id>.md` format with `description` frontmatter, hyphen-based command references, and `$@` argument injection.

### Modified Capabilities

- `cli-init`: Oh My Pi is added to the supported tool list and the hyphen-command transformer whitelist.
- `cli-update`: Oh My Pi is added to the hyphen-command transformer whitelist for skill regeneration.

## Impact

- `src/core/command-generation/adapters/oh-my-pi.ts` — new adapter
- `src/core/command-generation/adapters/index.ts` — export new adapter
- `src/core/command-generation/registry.ts` — register adapter
- `src/core/config.ts` — add `oh-my-pi` entry to `AI_TOOLS`
- `src/core/init.ts` — extend hyphen-command transformer conditional
- `src/core/update.ts` — extend hyphen-command transformer conditional (two call sites)
- `test/core/command-generation/adapters.test.ts` — adapter unit tests
- `docs/supported-tools.md` — add Oh My Pi row to directory reference table
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## ADDED Requirements

### Requirement: Oh My Pi tool supported in init
The `openspec init` command SHALL support Oh My Pi as a configurable tool, generating both skill files and command files using Oh My Pi's conventions when selected.

#### Scenario: Selecting Oh My Pi during init
- **WHEN** a user selects Oh My Pi during `openspec init`
- **THEN** skill files are written to `.omp/skills/openspec-<id>/SKILL.md` for each active command
- **AND** command files are written to `.omp/commands/opsx-<id>.md` for each active command
- **AND** skill file bodies use hyphen-based `/opsx-<id>` command references
- **AND** command file bodies have `**Provided arguments**: $@` injected after any `**Input**:` heading

#### Scenario: Oh My Pi listed when .omp directory is detected
- **WHEN** the project root contains a `.omp/` directory
- **THEN** Oh My Pi is pre-checked in the tool selection during `openspec init`
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## ADDED Requirements

### Requirement: Oh My Pi tool supported in update
The `openspec update` command SHALL refresh Oh My Pi skill files and command files when Oh My Pi is configured, using Oh My Pi's hyphen-based command reference convention.

#### Scenario: Updating Oh My Pi skill files
- **WHEN** `openspec update` runs and Oh My Pi is a configured tool
- **THEN** skill files in `.omp/skills/openspec-<id>/SKILL.md` are refreshed with the latest templates
- **AND** skill file bodies use hyphen-based `/opsx-<id>` command references

#### Scenario: Updating Oh My Pi command files
- **WHEN** `openspec update` runs and Oh My Pi is a configured tool
- **THEN** command files are written to `.omp/commands/opsx-<id>.md` for each workflow in the active profile, creating them if they do not yet exist and overwriting them if they do
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
## ADDED Requirements

### Requirement: Oh My Pi command file generation
OpenSpec SHALL generate command files for Oh My Pi in `.omp/commands/opsx-<id>.md`, one per active workflow command.

Each file SHALL include a YAML frontmatter block with a `description` field. The command body SHALL have `/opsx:` references transformed to `/opsx-` to match Oh My Pi's filename-based slash command naming (e.g., `opsx-propose.md` → `/opsx-propose`). `**Provided arguments**: $@` SHALL be injected on the line immediately following any `**Input**:` heading, unless `$@` or `$ARGUMENTS` is already present in the body.

#### Scenario: Command file path follows OMP convention
- **WHEN** OpenSpec generates a command file for Oh My Pi for workflow command `propose`
- **THEN** the file is written to `.omp/commands/opsx-propose.md`

#### Scenario: Command file format includes description frontmatter
- **WHEN** OpenSpec writes a command file for Oh My Pi
- **THEN** the file begins with a YAML frontmatter block containing only a `description` field
- **AND** the body follows after the closing `---`

#### Scenario: Command body uses hyphen-based references
- **WHEN** OpenSpec writes a command file for Oh My Pi whose body contains `/opsx:apply` or similar colon-style references
- **THEN** those references are transformed to `/opsx-apply` in the output file

#### Scenario: Command body exposes user arguments via $@
- **WHEN** OpenSpec writes a command file for Oh My Pi whose body contains a `**Input**:` heading and no existing `$@` or `$ARGUMENTS` reference
- **THEN** `**Provided arguments**: $@` is injected on the line immediately after the `**Input**:` heading
- **AND** when the user invokes `/opsx-propose my-feature`, the agent receives `my-feature` as the value of `$@`

### Requirement: Oh My Pi skill file generation
OpenSpec SHALL generate skill files for Oh My Pi in `.omp/skills/openspec-<id>/SKILL.md`, one per active workflow command.

Skill file bodies SHALL have `/opsx:` references transformed to `/opsx-` so that skill invocations refer to the correct hyphen-based slash command names.

#### Scenario: Skill file path follows OMP convention
- **WHEN** OpenSpec generates a skill file for Oh My Pi for workflow command `explore`
- **THEN** the file is written to `.omp/skills/openspec-explore/SKILL.md`

#### Scenario: Skill body uses hyphen-based references
- **WHEN** OpenSpec writes a skill file for Oh My Pi whose body contains `/opsx:explore`
- **THEN** the reference is transformed to `/opsx-explore` in the output file

### Requirement: Oh My Pi tool detection
OpenSpec SHALL detect an Oh My Pi installation when the `.omp/` directory exists at the project root, and SHALL present Oh My Pi as a selectable tool in `openspec init` and `openspec update`.

#### Scenario: Auto-detection when .omp directory exists
- **WHEN** the project root contains a `.omp/` directory
- **THEN** Oh My Pi is listed as a detected tool during `openspec init` and `openspec update`

#### Scenario: Oh My Pi appears in the tool selection list
- **WHEN** a user runs `openspec init` interactively
- **THEN** Oh My Pi appears as a selectable option in the tool list
30 changes: 30 additions & 0 deletions openspec/changes/feat-add-omp-tool-support/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## 1. Adapter

- [x] 1.1 Create `src/core/command-generation/adapters/oh-my-pi.ts` with `ohMyPiAdapter` (toolId `'oh-my-pi'`, path `.omp/commands/opsx-<id>.md`, description-only frontmatter, `transformToHyphenCommands` on body)
- [x] 1.2 Use `escapeYamlValue` for the `description` frontmatter field (consistent with Pi adapter)
- [x] 1.3 Export `ohMyPiAdapter` from `src/core/command-generation/adapters/index.ts`
- [x] 1.4 Import and register `ohMyPiAdapter` in `src/core/command-generation/registry.ts`
- [x] 1.5 In `formatFile`, inject `**Provided arguments**: $@` on the line after the `**Input**:` heading (skip if `$@` or `$ARGUMENTS` already present) — matching Pi adapter's `injectPiArgs` logic

## 2. Tool Registration

- [x] 2.1 Add `{ name: 'Oh My Pi', value: 'oh-my-pi', available: true, successLabel: 'Oh My Pi', skillsDir: '.omp' }` to `AI_TOOLS` in `src/core/config.ts` (alphabetical by name, between Mistral Vibe and OpenCode)

## 3. Skill Transformer Wiring

- [x] 3.1 In `src/core/init.ts`, extend the skill transformer conditional to include `tool.value === 'oh-my-pi'` alongside `'opencode'` and `'pi'` (one occurrence, in `generateSkillsAndCommands`)
- [x] 3.2 In `src/core/update.ts`, extend the skill transformer conditional to include `tool.value === 'oh-my-pi'` alongside `'opencode'` and `'pi'` (two occurrences: primary update loop and `upgradeLegacyTools`)

## 4. Tests

- [x] 4.1 In `test/core/command-generation/adapters.test.ts`, add unit tests for `ohMyPiAdapter`: verify `toolId`, `getFilePath` output uses `path.join('.omp', 'commands', 'opsx-<id>.md')`, and `formatFile` produces correct description frontmatter and transformed body
- [x] 4.2 Verify all path assertions in the new tests use `path.join()` (not hardcoded slashes) for cross-platform correctness

## 5. Documentation

- [x] 5.1 Add Oh My Pi row to the tool directory reference table in `docs/supported-tools.md`: `| Oh My Pi (\`oh-my-pi\`) | \`.omp/skills/openspec-*/SKILL.md\` | \`.omp/commands/opsx-<id>.md\` |`

## 6. Verification

- [x] 6.1 Run `pnpm test` and confirm all tests pass, including the new adapter tests
- [x] 6.2 Run `pnpm build` to confirm TypeScript compilation succeeds with the new adapter
1 change: 1 addition & 0 deletions src/core/command-generation/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export { iflowAdapter } from './iflow.js';
export { junieAdapter } from './junie.js';
export { kilocodeAdapter } from './kilocode.js';
export { kiroAdapter } from './kiro.js';
export { ohMyPiAdapter } from './oh-my-pi.js';
export { opencodeAdapter } from './opencode.js';
export { piAdapter } from './pi.js';
export { qoderAdapter } from './qoder.js';
Expand Down
55 changes: 55 additions & 0 deletions src/core/command-generation/adapters/oh-my-pi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Oh My Pi (OMP) Command Adapter
*
* Formats commands for Oh My Pi following its slash command specification.
* OMP loads slash commands from .omp/commands/*.md with YAML frontmatter.
* The filename (minus .md) becomes the slash command name.
*/

import path from 'path';
import type { CommandContent, ToolCommandAdapter } from '../types.js';
import { transformToHyphenCommands } from '../../../utils/command-references.js';
import { escapeYamlValue } from '../yaml.js';

const OMP_INPUT_HEADING = /^\*\*Input\*\*:[^\n]*$/m;

function injectOmpArgs(body: string): string {
if (body.includes('$@') || body.includes('$ARGUMENTS')) {
return body;
}

return body.replace(
OMP_INPUT_HEADING,
(heading) => `${heading}\n**Provided arguments**: $@`
);
}

/**
* Oh My Pi adapter for command generation.
* File path: .omp/commands/opsx-<id>.md
* Frontmatter: description
*
* OMP uses the filename (minus .md) as the slash command name, so
* opsx-propose.md → /opsx-propose. Command references in the body
* are transformed from /opsx: to /opsx- for consistency, and
* $@ is injected after **Input**: headings so user-supplied arguments
* (e.g. /opsx-propose my-feature) are visible to the agent.
*/
export const ohMyPiAdapter: ToolCommandAdapter = {
toolId: 'oh-my-pi',

getFilePath(commandId: string): string {
return path.join('.omp', 'commands', `opsx-${commandId}.md`);
},

formatFile(content: CommandContent): string {
const transformedBody = transformToHyphenCommands(content.body);

return `---
description: ${escapeYamlValue(content.description)}
---

${injectOmpArgs(transformedBody)}
`;
},
};
Loading
Loading