init: @trentuna/pi-openspec — 13-tool OpenSpec extension for pi

- extensions/openspec.ts — 13 tools wrapping openspec CLI
- skills/openspec/SKILL.md — full workflow guidance for agents
- package.json — pi manifest with pi-package keyword, @trentuna scope
- README.md — comprehensive: install, all 13 tools, quick-start, workflow
- LICENSE — MIT
- .gitignore

Adapted from a-team/extensions/openspec.ts (Hannibal, session 135).
Standalone package so any trentuna member can: pi install git:http://localhost:3001/trentuna/pi-openspec.git
This commit is contained in:
Vigilio Desto 2026-04-08 17:53:24 +02:00
commit 9e1f99a28f
Signed by: vigilio
GPG key ID: 159D6AD58C8E55E9
6 changed files with 808 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
node_modules/
.DS_Store
*.log
dist/

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Trentuna
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

166
README.md Normal file
View file

@ -0,0 +1,166 @@
# @trentuna/pi-openspec
**OpenSpec workflow tools for pi agent sessions.** Wraps the [OpenSpec CLI](https://github.com/Fission-AI/OpenSpec) as 13 native pi tools, making spec-driven development a first-class operation in any agent session.
This is the first protocol-level pi package built for the pi ecosystem — OpenSpec as infrastructure, not a team-internal tool.
---
## What it does
OpenSpec brings discipline to AI-assisted software development: write the spec before writing the code, track changes explicitly, archive what's done. This package makes those operations available directly in pi sessions — no manual CLI, no skill-file hunting.
---
## Install
### External (npm — preferred for external users)
```bash
pi install npm:@trentuna/pi-openspec
```
*Requires npm auth — see [npm publish status](#npm-publish-status).*
### Internal (Trentuna VM — git install)
```bash
pi install git:http://localhost:3001/trentuna/pi-openspec.git
```
### Trentuna members (bootstrap)
Included automatically when you run `commons/bin/bootstrap.sh`. No manual install needed.
---
## Requirements
- **openspec CLI**`npm install -g openspec` (v1.2.0+)
- **pi** — any recent version
- **Node.js** 18+
---
## Tools
All 13 tools are available immediately after install.
| Tool | Description |
|------|-------------|
| `openspec_init` | Initialize OpenSpec in a project (creates `openspec/` dir, config, skill files) |
| `openspec_update` | Regenerate skill/instruction files after config changes or schema switch |
| `openspec_list` | List active changes (work in progress) or stable spec domains |
| `openspec_show` | Show the full content of a change or spec |
| `openspec_new_change` | Create a new change directory with scaffolded artifacts |
| `openspec_status` | Artifact dependency graph — what's done, what's ready, what's blocked |
| `openspec_instructions` | Enriched instructions for creating a specific artifact (or implementing tasks) |
| `openspec_validate` | Validate change or spec for structural correctness |
| `openspec_archive` | Archive a completed change (merges delta specs → main, preserves history) |
| `openspec_spec_list` | List all spec domains (the stable source of truth) |
| `openspec_spec_show` | Display a specific spec by ID |
| `openspec_schema_list` | List available workflow schemas |
| `openspec_schema_fork` | Fork a schema for customization |
---
## Quick Start
**5 steps to ship a specified, implemented, archived change:**
```bash
# 1. Initialize in your project (once)
openspec_init(path=".", tools="pi")
# 2. Create a change
openspec_new_change(name="add-user-auth", description="Add JWT authentication")
# 3. Check what to write next
openspec_status(changeName="add-user-auth")
# 4. Get instructions for each artifact, then write it
openspec_instructions(changeName="add-user-auth", artifact="proposal")
# → write proposal.md, then specs, design, tasks
# 5. Implement and archive
openspec_instructions(changeName="add-user-auth", artifact="apply")
# → implement tasks, then:
openspec_validate(name="add-user-auth")
openspec_archive(changeName="add-user-auth")
```
---
## Workflow Skill
Load the workflow skill for step-by-step guidance on all operations:
```
Load the openspec skill
```
The skill covers the full workflow: init → new_change → status → instructions → implement → validate → archive. It includes common patterns, gotchas, and tool reference.
Skill location: `skills/openspec/SKILL.md`
---
## How It Works
OpenSpec organizes work into:
- **Specs** (`openspec/specs/`) — the source of truth. Describe how the system currently works.
- **Changes** (`openspec/changes/`) — proposed modifications. Each change is a folder containing a proposal, delta specs, design, and task list.
When a change is complete, `openspec_archive` merges its delta specs into the main specs and moves the folder to `changes/archive/YYYY-MM-DD-<name>/`. History preserved, source of truth updated.
The default `spec-driven` schema builds artifacts in dependency order:
```
proposal → specs → design → tasks → implement → archive
```
`openspec_status` shows the dependency graph at any point. `openspec_instructions` gives you enriched context before writing each artifact.
---
## Package Structure
```
pi-openspec/
package.json # pi manifest + npm metadata
extensions/
openspec.ts # 13-tool extension (wraps openspec CLI)
skills/
openspec/
SKILL.md # workflow guidance for agent sessions
README.md
LICENSE # MIT
```
---
## npm Publish Status
**Not yet published.** The package installs cleanly via git from Forgejo (Trentuna internal). npm publish requires:
1. `npm login` on the publishing machine
2. `@trentuna` npm org created at npmjs.com
3. `npm publish --access public` from this repo
This is a Ludo action. Track at [trentuna/a-team#107](http://localhost:3001/trentuna/a-team/issues/107).
---
## Contributing
This package is part of the Trentuna tooling stack. After it's stable and battle-tested, it will be submitted to the pi-mono package gallery as the first protocol-level pi package in the ecosystem.
Issues and PRs welcome at `trentuna/pi-openspec`.
---
## License
MIT — Trentuna, 2026.
OpenSpec CLI by [Fission AI](https://github.com/Fission-AI/OpenSpec). pi by [badlogic](https://github.com/badlogicgames/pi-mono).

438
extensions/openspec.ts Normal file
View file

@ -0,0 +1,438 @@
/**
* OpenSpec Pi Extension
*
* Native OpenSpec integration for pi spec-driven development workflow,
* change management, artifact creation, spec browsing, and project setup.
*
* Wraps the openspec CLI (https://github.com/Fission-AI/OpenSpec) as pi tools,
* making spec-driven workflow a first-class operation in any agent session.
*
* Package: @trentuna/pi-openspec
* Source: https://github.com/trentuna/pi-openspec (canonical)
* Origin: https://github.com/Fission-AI/OpenSpec (openspec CLI upstream)
* Docs: ~/.napkin/docs/fission-ai_openspec/ (octopus-adopted)
*
* ---
*
* WORKFLOW OVERVIEW
*
* OpenSpec organizes work into specs (source of truth) and changes (proposed
* modifications). The standard workflow:
*
* 1. openspec_new_change create a change folder with scaffolded artifacts
* 2. openspec_status see which artifacts need to be created
* 3. openspec_instructions get enriched instructions for each artifact
* [Write artifact files using Write/Edit tools]
* 4. openspec_instructions apply get task implementation instructions
* [Implement the tasks]
* 5. openspec_validate check implementation matches artifacts
* 6. openspec_archive finalize: merge delta specs + move to archive
*
* For new projects:
* openspec_init initialize OpenSpec (creates openspec/ directory, skills)
* openspec_update regenerate skill files after config changes
*
* ---
*
* CORE CONCEPTS
*
* Specs describe how the system CURRENTLY behaves (openspec/specs/)
* Contain requirements and scenarios (Given/When/Then).
* Source of truth for the project.
*
* Changes proposed modifications, each a self-contained folder
* (openspec/changes/<name>/) containing:
* proposal.md why and what (intent, scope, approach)
* design.md how (technical decisions, architecture)
* tasks.md implementation checklist with [ ] checkboxes
* specs/ delta specs (ADDED/MODIFIED/REMOVED requirements)
*
* Delta specs describe what's CHANGING, not the full spec. On archive,
* deltas merge into main specs. Enables parallel work on same spec.
*
* Schemas define artifact types and dependencies. Default: spec-driven
* (proposal specs design tasks implement archive).
*
* ---
*
* Install:
* ln -sf $(pwd)/extensions/openspec.ts ~/.pi/agent/extensions/openspec.ts
* # or: run install/pi/install.sh
*
* Requirements:
* openspec CLI on PATH: npm install -g @fission-ai/openspec
* Source: ~/upstream/openspec
*/
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "@sinclair/typebox";
// ── CLI helper ─────────────────────────────────────────────────────────────────
async function openspecCli(
args: string[],
exec: (command: string, args: string[]) => Promise<{ stdout: string; stderr: string; code: number | null; killed: boolean }>,
cwd?: string
): Promise<{ output: string; exitCode: number; cancelled: boolean }> {
const result = await exec("openspec", args);
return {
output: result.stdout || result.stderr || "",
exitCode: result.code ?? 1,
cancelled: result.killed ?? false,
};
}
function ok(text: string) {
return { content: [{ type: "text" as const, text: text }], details: { success: true } };
}
function err(text: string) {
return { content: [{ type: "text" as const, text: text }], details: { success: false } };
}
// ── Extension ──────────────────────────────────────────────────────────────────
export default function (pi: ExtensionAPI) {
const exec = pi.exec.bind(pi);
// ═══════════════════════ PROJECT SETUP ════════════════════════════════════
pi.registerTool({
name: "openspec_init",
label: "Initialize OpenSpec",
description:
"Initialize OpenSpec in the current project. Creates openspec/ directory, " +
"config.yaml, and skill files for AI tools. " +
"Run once per project before using any other openspec tools. " +
"Use tool='pi' to generate pi-compatible skill files (recommended for this environment). " +
"Use tool='claude' for Claude Code compatibility.",
parameters: Type.Object({
path: Type.Optional(
Type.String({ description: "Project path to initialize (default: current directory)" })
),
tools: Type.Optional(
Type.String({
description:
"AI tools to configure skill files for. " +
"Options: 'pi' (recommended), 'claude', 'all', 'none', " +
"or comma-separated list (pi,claude,cursor). Default: pi.",
})
),
}),
async execute(_toolCallId, params) {
const args = ["init"];
if (params.path) args.push(params.path);
if (params.tools) args.push("--tools", params.tools);
const result = await openspecCli(args, exec);
if (result.exitCode !== 0) return err(`Error initializing: ${result.output}`);
return ok(result.output || "OpenSpec initialized. Run openspec_list to see active changes.");
},
});
pi.registerTool({
name: "openspec_update",
label: "Update OpenSpec Skill Files",
description:
"Regenerate OpenSpec skill/instruction files for AI tools. " +
"Run after changing config.yaml, switching schemas, or adding custom workflow commands. " +
"This keeps the AI-tool integrations in sync with your OpenSpec configuration.",
parameters: Type.Object({
path: Type.Optional(
Type.String({ description: "Project path to update (default: current directory)" })
),
force: Type.Optional(
Type.Boolean({ description: "Force update even when tools appear up to date" })
),
}),
async execute(_toolCallId, params) {
const args = ["update"];
if (params.path) args.push(params.path);
if (params.force) args.push("--force");
const result = await openspecCli(args, exec);
if (result.exitCode !== 0) return err(`Error updating: ${result.output}`);
return ok(result.output || "OpenSpec skills updated.");
},
});
// ═══════════════════════ DISCOVERY ════════════════════════════════════════
pi.registerTool({
name: "openspec_list",
label: "List Changes or Specs",
description:
"List active changes or specs in the current project. " +
"Default: lists active changes (work in progress). " +
"Use listType='specs' to list the stable spec domains instead. " +
"Run at the start of a session to orient on what's in flight. " +
"JSON output includes change names, schemas, and artifact completion state.",
parameters: Type.Object({
listType: Type.Optional(
Type.Union([Type.Literal("changes"), Type.Literal("specs")], {
description: "'changes' (default) — work in progress | 'specs' — spec domains",
})
),
}),
async execute(_toolCallId, params) {
const args = ["list", "--json"];
if (params.listType === "specs") args.push("--specs");
const result = await openspecCli(args, exec);
if (result.exitCode !== 0) return err(`Error listing: ${result.output}`);
return ok(result.output);
},
});
pi.registerTool({
name: "openspec_show",
label: "Show Change or Spec",
description:
"Show the full content of a change or spec. " +
"For changes: displays proposal, design summary, tasks, and delta spec overview. " +
"For specs: displays requirements and scenarios. " +
"Use this to read existing work before continuing or before creating new artifacts.",
parameters: Type.Object({
name: Type.String({ description: "Change name (kebab-case) or spec ID" }),
itemType: Type.Optional(
Type.Union([Type.Literal("change"), Type.Literal("spec")], {
description: "Item type. Auto-detected from name if omitted.",
})
),
}),
async execute(_toolCallId, params) {
const args = ["show", params.name, "--json", "--no-interactive"];
if (params.itemType) args.push("--type", params.itemType);
const result = await openspecCli(args, exec);
if (result.exitCode !== 0) return err(`Error: ${result.output}`);
return ok(result.output);
},
});
// ═══════════════════════ CHANGE LIFECYCLE ═════════════════════════════════
pi.registerTool({
name: "openspec_new_change",
label: "Create New Change",
description:
"Create a new OpenSpec change directory with scaffolded artifacts. " +
"A change packages everything needed for one piece of work: " +
"proposal (why + what), specs (delta requirements), design (how), tasks (checklist). " +
"\n\nAfter creating: call openspec_status to see the artifact dependency graph, " +
"then openspec_instructions for each artifact before writing it. " +
"\n\nNaming: use kebab-case ('add-user-auth', 'fix-pagination', 'refactor-api'). " +
"Avoid generic names like 'update' or 'wip'.",
parameters: Type.Object({
name: Type.String({ description: "Change name in kebab-case" }),
description: Type.Optional(
Type.String({ description: "Short description added to the change README" })
),
schema: Type.Optional(
Type.String({
description:
"Workflow schema. Default: 'spec-driven' (proposal → specs → design → tasks). " +
"Other schemas available if configured in openspec/schemas/.",
})
),
}),
async execute(_toolCallId, params) {
const args = ["new", "change", params.name];
if (params.description) args.push("--description", params.description);
if (params.schema) args.push("--schema", params.schema);
const result = await openspecCli(args, exec);
if (result.exitCode !== 0) return err(`Error creating change '${params.name}': ${result.output}`);
return ok(
result.output ||
`Change '${params.name}' created at openspec/changes/${params.name}/. ` +
`Run openspec_status to see artifact build order.`
);
},
});
pi.registerTool({
name: "openspec_status",
label: "Change Status",
description:
"Get artifact completion status for a change. " +
"Shows the dependency graph: which artifacts exist (done), " +
"which are ready to create (dependencies satisfied), and which are blocked. " +
"The 'applyRequires' field lists which artifacts must be complete before implementation. " +
"\n\nCall this after openspec_new_change to see the artifact build order. " +
"Call again after writing each artifact to see what becomes available next.",
parameters: Type.Object({
changeName: Type.String({ description: "Change name (kebab-case)" }),
}),
async execute(_toolCallId, params) {
const result = await openspecCli(
["status", "--change", params.changeName, "--json"],
exec
);
if (result.exitCode !== 0) return err(`Error getting status: ${result.output}`);
return ok(result.output);
},
});
pi.registerTool({
name: "openspec_instructions",
label: "Get Artifact Instructions",
description:
"Get enriched instructions for creating a specific artifact or for implementing tasks. " +
"\n\nReturns: context (project background), rules (constraints), template (structure), " +
"outputPath (where to write the file), and dependencies (files to read first). " +
"\n\nIMPORTANT: 'context' and 'rules' guide what YOU write — do NOT copy them into the artifact file. " +
"Use 'template' as the structure for the output file. " +
"\n\nArtifact IDs (spec-driven schema): proposal, specs, design, tasks. " +
"Use artifact='apply' to get implementation instructions (task list + approach).",
parameters: Type.Object({
changeName: Type.String({ description: "Change name (kebab-case)" }),
artifact: Type.Optional(
Type.String({
description:
"Artifact ID: 'proposal', 'specs', 'design', 'tasks', or 'apply' (for implementation). " +
"If omitted, returns instructions for the next pending artifact.",
})
),
}),
async execute(_toolCallId, params) {
const args = ["instructions", "--change", params.changeName, "--json"];
if (params.artifact) args.push(params.artifact);
const result = await openspecCli(args, exec);
if (result.exitCode !== 0) return err(`Error getting instructions: ${result.output}`);
return ok(result.output);
},
});
pi.registerTool({
name: "openspec_validate",
label: "Validate Change or Spec",
description:
"Validate a change or spec for structural correctness. " +
"Checks that artifacts follow the expected format and that requirements are well-formed. " +
"\n\nFor changes: checks all artifacts are present and valid. " +
"For specs: checks requirement and scenario structure. " +
"\n\nRun before archiving to catch missing or malformed artifacts. " +
"Reports issues as CRITICAL, WARNING, or INFO.",
parameters: Type.Object({
name: Type.Optional(
Type.String({
description:
"Change or spec name to validate. If omitted, validates all active changes.",
})
),
}),
async execute(_toolCallId, params) {
const args = ["validate"];
if (params.name) args.push(params.name);
const result = await openspecCli(args, exec);
// Non-zero may mean validation issues (report them, don't error)
return ok(result.output || (result.exitCode === 0 ? "Validation passed." : "Validation issues found."));
},
});
pi.registerTool({
name: "openspec_archive",
label: "Archive Completed Change",
description:
"Archive a completed change after implementation. " +
"\n\nWhat happens: " +
"(1) Delta specs merge into main openspec/specs/ — the source of truth updates. " +
"(2) Change folder moves to openspec/changes/archive/YYYY-MM-DD-<name>/ for history. " +
"(3) All artifacts preserved for audit trail. " +
"\n\nCall openspec_validate first to catch issues. " +
"The archive will warn on incomplete tasks but won't block. " +
"Delta spec sync is offered if not already done.",
parameters: Type.Object({
changeName: Type.Optional(
Type.String({
description:
"Change name to archive. Provide explicitly for non-interactive use. " +
"If omitted, openspec selects interactively (may not work in agent context).",
})
),
}),
async execute(_toolCallId, params) {
const args = ["archive", "--no-interactive"];
if (params.changeName) args.push(params.changeName);
const result = await openspecCli(args, exec);
if (result.exitCode !== 0) return err(`Error archiving: ${result.output}`);
return ok(result.output || "Change archived. Delta specs merged into main specs.");
},
});
// ═══════════════════════ SPEC BROWSING ════════════════════════════════════
pi.registerTool({
name: "openspec_spec_list",
label: "List Specs",
description:
"List all spec domains in the current project. " +
"Specs (openspec/specs/) are the stable source of truth — they describe " +
"how the system CURRENTLY behaves. " +
"Browse them before creating a change to understand what's already specified " +
"and which domain your change touches.",
parameters: Type.Object({}),
async execute(_toolCallId, _params) {
const result = await openspecCli(["spec", "list", "--json"], exec);
if (result.exitCode !== 0) return err(`Error listing specs: ${result.output}`);
return ok(result.output);
},
});
pi.registerTool({
name: "openspec_spec_show",
label: "View Spec",
description:
"Display a specific spec by ID. " +
"Specs contain requirements (what the system must do) and scenarios (concrete examples). " +
"Read specs before implementing to understand what behavior is already defined " +
"and what your change's delta specs must satisfy or extend.",
parameters: Type.Object({
specId: Type.String({
description: "Spec ID (e.g. 'auth', 'payments', 'ui') — from openspec_spec_list",
}),
}),
async execute(_toolCallId, params) {
const result = await openspecCli(["spec", "show", params.specId, "--no-interactive"], exec);
if (result.exitCode !== 0) return err(`Error showing spec: ${result.output}`);
return ok(result.output);
},
});
// ═══════════════════════ SCHEMA MANAGEMENT ════════════════════════════════
pi.registerTool({
name: "openspec_schema_list",
label: "List Available Schemas",
description:
"List available workflow schemas. " +
"Schemas define artifact types and their dependencies. " +
"Default: 'spec-driven' (proposal → specs → design → tasks). " +
"Custom schemas can be created for specialized workflows " +
"(e.g. research-first, rapid, security-review).",
parameters: Type.Object({}),
async execute(_toolCallId, _params) {
// 'openspec schema which' lists the schema resolution path
// Try listing via schema which with no name
const result = await openspecCli(["schema", "which"], exec);
if (result.exitCode !== 0) return err(`Error: ${result.output}`);
return ok(result.output);
},
});
pi.registerTool({
name: "openspec_schema_fork",
label: "Fork a Schema",
description:
"Copy an existing schema to this project for customization. " +
"Forked schemas live at openspec/schemas/<name>/ and can be modified " +
"to define custom artifact types, dependencies, and instruction templates. " +
"\n\nExample: fork 'spec-driven' as 'rapid' to create a minimal workflow " +
"with just proposal + tasks (skipping design for small changes).",
parameters: Type.Object({
source: Type.String({ description: "Source schema name to fork (e.g. 'spec-driven')" }),
name: Type.String({ description: "New schema name (kebab-case)" }),
}),
async execute(_toolCallId, params) {
const result = await openspecCli(["schema", "fork", params.source, params.name], exec);
if (result.exitCode !== 0) return err(`Error forking schema: ${result.output}`);
return ok(result.output || `Schema '${params.source}' forked as '${params.name}'. Edit openspec/schemas/${params.name}/schema.yaml to customize.`);
},
});
}

20
package.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "@trentuna/pi-openspec",
"version": "0.1.0",
"description": "OpenSpec pi extension — spec-driven development workflow for pi agent sessions",
"keywords": ["pi-package", "openspec", "spec-driven", "ai-agents", "trentuna"],
"author": "Trentuna <ludo@trentuna.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "http://localhost:3001/trentuna/pi-openspec.git"
},
"homepage": "https://trentuna.com",
"pi": {
"extensions": ["./extensions"],
"skills": ["./skills"]
},
"engines": {
"node": ">=18.0.0"
}
}

159
skills/openspec/SKILL.md Normal file
View file

@ -0,0 +1,159 @@
---
name: openspec
description: Spec-driven development workflow using OpenSpec. Use when initializing OpenSpec in a project, creating change proposals, writing specs, generating designs and task lists, implementing changes, or archiving completed work. Provides step-by-step workflow guidance for all 13 openspec tools.
---
# OpenSpec Workflow Skill
OpenSpec organizes software work into **specs** (source of truth) and **changes** (proposed modifications). The workflow guides you from intent to implementation to archive.
## When to load this skill
Load when you are:
- Setting up OpenSpec in a new or existing project
- Creating a change proposal for a new feature or fix
- Writing specs, designs, or task lists for an active change
- Implementing tasks from a change
- Validating or archiving completed work
## Core Concepts
**Specs** (`openspec/specs/`) — the source of truth. Describe how the system currently behaves. Organized by domain (e.g., `specs/auth/`, `specs/payments/`).
**Changes** (`openspec/changes/`) — proposed modifications. Each change is a folder with proposal, delta specs, design, and task list. Changes merge into specs on archive.
**Artifacts** — the documents inside a change. The default `spec-driven` schema builds them in dependency order:
```
proposal → specs (delta) → design → tasks → implement → archive
```
**Key insight:** `openspec_instructions` is your north star. Call it before writing each artifact — it returns enriched context, rules, template structure, and output path. Do NOT skip this step.
## The Standard Workflow
### 0. Initialize (once per project)
```
openspec_init(path=".", tools="pi")
```
Creates `openspec/` directory, `config.yaml`, and pi-compatible skill files. Run once. Safe to skip if `openspec/` already exists.
### 1. Orient
```
openspec_list() # see active changes in progress
openspec_spec_list() # see stable spec domains
openspec_show(name="change-name") # read an existing change in full
```
Run at session start to understand what's in flight before creating anything new.
### 2. Create a Change
```
openspec_new_change(name="add-user-auth", description="Add JWT auth to API")
```
Creates `openspec/changes/add-user-auth/` with scaffolded artifact files. Name in kebab-case. Be specific — avoid generic names like `update` or `wip`.
Then immediately:
```
openspec_status(changeName="add-user-auth")
```
This shows the dependency graph: which artifacts exist, which are ready to write, which are blocked.
### 3. Write Each Artifact (in dependency order)
For each artifact the status shows as "ready":
```
openspec_instructions(changeName="add-user-auth", artifact="proposal")
```
Read the returned `context`, `rules`, and `template` carefully. The `outputPath` tells you exactly where to write. The `dependencies` list tells you what to read first.
**IMPORTANT:** `context` and `rules` guide what YOU write — do NOT copy them into the artifact file. Use `template` as the structure.
Write the artifact using the Write tool. Then call `openspec_status` again to see what unlocks next.
Repeat for: `specs`, `design`, `tasks`.
### 4. Implement Tasks
```
openspec_instructions(changeName="add-user-auth", artifact="apply")
```
Returns the task list and implementation approach. Work through tasks, checking them off in `tasks.md` as you go. Commit per completed task (not per file edit).
### 5. Validate
```
openspec_validate(name="add-user-auth")
```
Checks structural correctness of all artifacts. Fix any CRITICAL issues before archiving. WARNINGS are advisory.
### 6. Archive
```
openspec_archive(changeName="add-user-auth")
```
Merges delta specs into `openspec/specs/`. Moves the change folder to `openspec/changes/archive/YYYY-MM-DD-add-user-auth/`. The change is done.
## Schema Operations
```
openspec_schema_list() # see available workflow schemas
openspec_schema_fork(source="spec-driven", name="rapid") # fork for customization
```
Use `rapid` or other custom schemas when the full `spec-driven` workflow is more than the task needs (e.g., a small bugfix that doesn't warrant a full spec + design).
## Reading Existing Specs
```
openspec_spec_list() # list spec domains
openspec_spec_show(specId="auth") # read a spec in full
```
Always read relevant specs before creating a change — understand what's already specified and which domain your change touches.
## Common Patterns
**Starting fresh on an existing codebase:**
1. `openspec_init``openspec_spec_list` (probably empty) → `openspec_new_change` for your first feature → write artifacts → implement
**Resuming work in progress:**
1. `openspec_list``openspec_show` (read the change) → `openspec_status` (see what's ready) → `openspec_instructions` for next artifact → continue
**Quick change (small fix):**
1. `openspec_new_change(schema="rapid")` if you have a rapid schema, else use `spec-driven` and skip non-critical artifacts → `openspec_instructions(artifact="apply")` → implement → `openspec_archive`
## Gotchas
- **Don't skip `openspec_instructions`** before writing an artifact. The context and rules it returns are essential — they encode project-specific constraints that the template alone doesn't carry.
- **Commit per task, not per file.** The session commit protocol applies — each logical unit of work is one commit.
- **`openspec_update` after config changes.** If you modify `openspec/config.yaml` or switch schemas, run `openspec_update` to regenerate skill files.
- **Delta specs are not full specs.** A delta spec shows what's ADDED/MODIFIED/REMOVED relative to existing specs. Don't rewrite the entire domain — only the changes.
## Tool Reference
| Tool | Purpose |
|------|---------|
| `openspec_init` | Initialize OpenSpec in a project |
| `openspec_update` | Regenerate skill/instruction files after config changes |
| `openspec_list` | List active changes (WIP) or stable specs |
| `openspec_show` | Show a change or spec in full |
| `openspec_new_change` | Create a new change directory with scaffolded artifacts |
| `openspec_status` | Artifact dependency graph and completion state |
| `openspec_instructions` | Enriched instructions for creating a specific artifact |
| `openspec_validate` | Validate change/spec structural correctness |
| `openspec_archive` | Archive completed change (merges delta specs into main) |
| `openspec_spec_list` | List spec domains (stable source of truth) |
| `openspec_spec_show` | View a specific spec by ID |
| `openspec_schema_list` | List available workflow schemas |
| `openspec_schema_fork` | Fork a schema for customization |