diff --git a/openspec/changes/archive/2026-01-21-add-pi-coding-agent/.openspec.yaml b/openspec/changes/archive/2026-01-21-add-pi-coding-agent/.openspec.yaml new file mode 100644 index 000000000..06c30fb92 --- /dev/null +++ b/openspec/changes/archive/2026-01-21-add-pi-coding-agent/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-01-21 diff --git a/openspec/changes/archive/2026-01-21-add-pi-coding-agent/design.md b/openspec/changes/archive/2026-01-21-add-pi-coding-agent/design.md new file mode 100644 index 000000000..ca3bf97af --- /dev/null +++ b/openspec/changes/archive/2026-01-21-add-pi-coding-agent/design.md @@ -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. diff --git a/openspec/changes/archive/2026-01-21-add-pi-coding-agent/proposal.md b/openspec/changes/archive/2026-01-21-add-pi-coding-agent/proposal.md new file mode 100644 index 000000000..80c4d0453 --- /dev/null +++ b/openspec/changes/archive/2026-01-21-add-pi-coding-agent/proposal.md @@ -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 diff --git a/openspec/changes/archive/2026-01-21-add-pi-coding-agent/tasks.md b/openspec/changes/archive/2026-01-21-add-pi-coding-agent/tasks.md new file mode 100644 index 000000000..b002303de --- /dev/null +++ b/openspec/changes/archive/2026-01-21-add-pi-coding-agent/tasks.md @@ -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/` diff --git a/openspec/specs/cli-init/spec.md b/openspec/specs/cli-init/spec.md index 99248841e..27f1ec6d1 100644 --- a/openspec/specs/cli-init/spec.md +++ b/openspec/specs/cli-init/spec.md @@ -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` diff --git a/src/core/config.ts b/src/core/config.ts index a27d6eafb..bb6c259e4 100644 --- a/src/core/config.ts +++ b/src/core/config.ts @@ -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' }, diff --git a/src/core/configurators/slash/pi.ts b/src/core/configurators/slash/pi.ts new file mode 100644 index 000000000..81d68fbda --- /dev/null +++ b/src/core/configurators/slash/pi.ts @@ -0,0 +1,42 @@ +import { SlashCommandConfigurator } from './base.js'; +import { SlashCommandId } from '../../templates/index.js'; + +const FILE_PATHS: Record = { + 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 = { + 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]; + } +} diff --git a/src/core/configurators/slash/registry.ts b/src/core/configurators/slash/registry.ts index 43fb245c7..d9ce172eb 100644 --- a/src/core/configurators/slash/registry.ts +++ b/src/core/configurators/slash/registry.ts @@ -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 = new Map(); @@ -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); @@ -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 {