Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-21
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## Context

OpenSpec supports 21 AI coding assistants through a plugin-based architecture:
- `ToolConfigurator` for root config files (e.g., CLAUDE.md)
- `SlashCommandConfigurator` for slash command files (e.g., `.claude/commands/`)
- `ToolRegistry` and `SlashCommandRegistry` for registration
- `AI_TOOLS` array in `config.ts` for UI display

Pi-coding-agent from badlogic/pi-mono already supports AGENTS.md files natively, so only slash command support is needed.

## Goals / Non-Goals

**Goals:**
- Enable pi users to select "Pi" during `openspec init`
- Generate `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` commands in `.pi/prompts/`
- Follow pi's expected format: `.md` files with YAML frontmatter containing `description`

**Non-Goals:**
- Creating a separate PI.md root config file (pi reads AGENTS.md already)
- Supporting pi's `$1`/`$@` argument syntax (not needed for OpenSpec commands)
- Global installation support (pi prompts are project-local in `.pi/prompts/`)

## Decisions

### 1. Slash command file location: `.pi/prompts/openspec-*.md`

**Rationale**: Pi-coding-agent loads custom commands from `.pi/prompts/*.md` where the filename becomes the command name. Using `openspec-proposal.md` creates `/openspec-proposal`.

**Alternatives considered**:
- `.pi/commands/` - Not a valid pi location
- Global `~/.pi/agent/prompts/` - Would apply to all projects, not project-specific

### 2. YAML frontmatter with `description` only

**Rationale**: Pi requires minimal frontmatter - just `description`. Unlike Continue (which needs `invokable: true`) or Gemini (TOML format), pi keeps it simple.

```yaml
---
description: Scaffold a new OpenSpec change and validate strictly.
---
```

### 3. No ToolConfigurator needed

**Rationale**: Pi-coding-agent already reads AGENTS.md files in priority order (global → parent dirs → current dir). The existing root AGENTS.md stub is sufficient. Adding a separate PI.md would be redundant.

## Risks / Trade-offs

**[Risk]** Pi's format may change in future versions → **Mitigation**: Implementation follows current documented format from pi-mono README. Format is stable and matches standard markdown with YAML frontmatter pattern.

**[Trade-off]** No root PI.md file means pi users don't get the visual confirmation of a dedicated config file → **Acceptable**: Pi's AGENTS.md support means the existing stub works. Less file clutter is preferable.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## Why

Pi-coding-agent from [badlogic/pi-mono](https://github.com/badlogic/pi-mono) is a growing terminal-based coding agent with multi-model support, mid-session model switching, and RPC mode for programmatic integration. It supports the AGENTS.md standard and has its own slash command system via `.pi/prompts/*.md` files. Adding OpenSpec support enables pi users to use the proposal/apply/archive workflow natively.

## What Changes

- Add "Pi" as a new supported AI tool in the tool selection list
- Create slash command configurator for pi's `.pi/prompts/*.md` format
- Register pi in the SlashCommandRegistry

Pi-coding-agent already reads AGENTS.md files, so no separate tool configurator is needed for project instructions - the existing AGENTS.md support covers that.

## Capabilities

### New Capabilities
- `pi-slash-commands`: Slash command generation for pi-coding-agent using `.pi/prompts/*.md` format with YAML frontmatter

### Modified Capabilities
- `tool-registry`: Add pi to the AI_TOOLS list and SlashCommandRegistry

## Impact

- **Code**:
- `src/core/config.ts` - add pi to AI_TOOLS array
- `src/core/configurators/slash/pi.ts` - new slash command configurator
- `src/core/configurators/slash/registry.ts` - register pi configurator
- **User-facing**: Pi users can select "Pi" during `openspec init` and get `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` commands
- **Dependencies**: None - uses existing slash command infrastructure
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## 1. Configuration

- [x] 1.1 Add pi to AI_TOOLS array in `src/core/config.ts` with name "Pi", value "pi", available true, successLabel "Pi"

## 2. Slash Command Configurator

- [x] 2.1 Create `src/core/configurators/slash/pi.ts` implementing `PiSlashCommandConfigurator`
- [x] 2.2 Define FILE_PATHS mapping for `.pi/prompts/openspec-{proposal,apply,archive}.md`
- [x] 2.3 Define FRONTMATTER with `description` field for each command
- [x] 2.4 Implement `getRelativePath()` and `getFrontmatter()` methods

## 3. Registry

- [x] 3.1 Import `PiSlashCommandConfigurator` in `src/core/configurators/slash/registry.ts`
- [x] 3.2 Instantiate and register pi configurator in the static block

## 4. Verification

- [x] 4.1 Run `bun run build` to verify TypeScript compiles
- [x] 4.2 Run `bun run test` to verify existing tests pass
- [x] 4.3 Test `openspec init --tools pi` generates expected files in `.pi/prompts/`
7 changes: 7 additions & 0 deletions openspec/specs/cli-init/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,13 @@ The init command SHALL generate slash command files for supported editors using
- **AND** populate each file from shared templates so command text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage

#### Scenario: Generating slash commands for Pi
- **WHEN** the user selects Pi during initialization
- **THEN** create `.pi/prompts/openspec-proposal.md`, `.pi/prompts/openspec-apply.md`, and `.pi/prompts/openspec-archive.md`
- **AND** populate each file with YAML frontmatter containing only a `description` field that summarizes the workflow stage
- **AND** wrap the shared template body with OpenSpec markers so `openspec update` can refresh the content
- **AND** each template includes instructions for the relevant OpenSpec workflow stage

#### Scenario: Generating slash commands for Windsurf
- **WHEN** the user selects Windsurf during initialization
- **THEN** create `.windsurf/workflows/openspec-proposal.md`, `.windsurf/workflows/openspec-apply.md`, and `.windsurf/workflows/openspec-archive.md`
Expand Down
1 change: 1 addition & 0 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const AI_TOOLS: AIToolOption[] = [
{ name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow' },
{ name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code' },
{ name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode' },
{ name: 'Pi', value: 'pi', available: true, successLabel: 'Pi' },
{ name: 'Qoder (CLI)', value: 'qoder', available: true, successLabel: 'Qoder' },
{ name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code' },
{ name: 'RooCode', value: 'roocode', available: true, successLabel: 'RooCode' },
Expand Down
42 changes: 42 additions & 0 deletions src/core/configurators/slash/pi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { SlashCommandConfigurator } from './base.js';
import { SlashCommandId } from '../../templates/index.js';

const FILE_PATHS: Record<SlashCommandId, string> = {
proposal: '.pi/prompts/openspec-proposal.md',
apply: '.pi/prompts/openspec-apply.md',
archive: '.pi/prompts/openspec-archive.md'
};

/*
* Pi-coding-agent .pi/prompts format uses YAML frontmatter with description:
* ---
* description: description text
* ---
* Body...
*
* The filename becomes the command name, so openspec-proposal.md becomes /openspec-proposal.
*/
const FRONTMATTER: Record<SlashCommandId, string> = {
proposal: `---
description: Scaffold a new OpenSpec change and validate strictly.
---`,
apply: `---
description: Implement an approved OpenSpec change and keep tasks in sync.
---`,
archive: `---
description: Archive a deployed OpenSpec change and update specs.
---`
};

export class PiSlashCommandConfigurator extends SlashCommandConfigurator {
readonly toolId = 'pi';
readonly isAvailable = true;

protected getRelativePath(id: SlashCommandId): string {
return FILE_PATHS[id];
}

protected getFrontmatter(id: SlashCommandId): string {
return FRONTMATTER[id];
}
}
3 changes: 3 additions & 0 deletions src/core/configurators/slash/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { RooCodeSlashCommandConfigurator } from './roocode.js';
import { AntigravitySlashCommandConfigurator } from './antigravity.js';
import { IflowSlashCommandConfigurator } from './iflow.js';
import { ContinueSlashCommandConfigurator } from './continue.js';
import { PiSlashCommandConfigurator } from './pi.js';

export class SlashCommandRegistry {
private static configurators: Map<string, SlashCommandConfigurator> = new Map();
Expand All @@ -46,6 +47,7 @@ export class SlashCommandRegistry {
const antigravity = new AntigravitySlashCommandConfigurator();
const iflow = new IflowSlashCommandConfigurator();
const continueTool = new ContinueSlashCommandConfigurator();
const pi = new PiSlashCommandConfigurator();

this.configurators.set(claude.toolId, claude);
this.configurators.set(codeBuddy.toolId, codeBuddy);
Expand All @@ -68,6 +70,7 @@ export class SlashCommandRegistry {
this.configurators.set(antigravity.toolId, antigravity);
this.configurators.set(iflow.toolId, iflow);
this.configurators.set(continueTool.toolId, continueTool);
this.configurators.set(pi.toolId, pi);
}

static register(configurator: SlashCommandConfigurator): void {
Expand Down