commit 9e1f99a28f2f9dc988a9fa4091e8ebbafbd1f9fb Author: vigilio Date: Wed Apr 8 17:53:24 2026 +0200 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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..91db72a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +.DS_Store +*.log +dist/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d15c57d --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5cfc022 --- /dev/null +++ b/README.md @@ -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-/`. 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). diff --git a/extensions/openspec.ts b/extensions/openspec.ts new file mode 100644 index 0000000..dc6ef9f --- /dev/null +++ b/extensions/openspec.ts @@ -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//) 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-/ 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// 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.`); + }, + }); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c8737a5 --- /dev/null +++ b/package.json @@ -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 ", + "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" + } +} diff --git a/skills/openspec/SKILL.md b/skills/openspec/SKILL.md new file mode 100644 index 0000000..948f4ef --- /dev/null +++ b/skills/openspec/SKILL.md @@ -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 |