Compare commits

...
Author SHA1 Message Date
openspec-release-bot[bot]andgithub-actions[bot] f6b415cb9b Version Packages (#597)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-26 19:33:25 -08:00
Tabish Bidiwale e91568deb9 Add changeset for spec naming clarification (#596) 2026-01-26 19:12:01 -08:00
Tabish Bidiwale fc0d798f93 fix: clarify spec naming convention and task checkbox format (#595)
* fix: clarify spec naming convention and task checkbox format

- Update docs, schema, and templates to clarify that specs should be
  named after capabilities (specs/<capability>/spec.md), not changes
- Emphasize that tasks MUST use checkbox format for apply phase tracking

* fix: clarify delta spec location for modified capabilities

Address review feedback: explicitly state that the delta spec is created
at specs/<capability>/spec.md, not in openspec/specs/<capability>/.
2026-01-26 18:12:56 -08:00
openspec-release-bot[bot]andgithub-actions[bot] d155126235 Version Packages (#588)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-26 02:51:24 -08:00
Tabish Bidiwale 943e0d4102 Add changeset for archive path fix (#587) 2026-01-26 02:47:28 -08:00
Tabish Bidiwale 12a7224dc6 chore: remove TDD schema and all references (#586)
* chore: remove TDD schema and all references

TDD was an internal test example that should not be in user-facing docs.
This removes:
- The schemas/tdd directory and all its templates
- All TDD references from documentation
- TDD examples from skill templates and source code comments

* test: update tests to remove TDD schema references

All tests that referenced the removed TDD schema have been updated
to use spec-driven or custom-schema names instead.

* chore: remove accidentally committed files
2026-01-26 02:31:48 -08:00
Tabish Bidiwale c773ef6feb fix: correct archive path in onboarding template (#585)
The onboarding template incorrectly documented the archive path as
`openspec/archive/YYYY-MM-DD--<name>/` when the actual implementation
uses `openspec/changes/archive/YYYY-MM-DD-<name>/`.

This fixes two issues:
- Missing `changes/` directory in the path
- Double dash `--` instead of single dash `-`

Fixes discussion #583
2026-01-26 02:04:46 -08:00
Tabish Bidiwale 0bfe1d4426 docs: rewrite customization guide to document schema commands (#582)
* docs: rewrite customization guide to document schema commands

The old guide described a painful manual process for schema customization
(mkdir, npm list, cp commands) and even listed "No scaffolding" as a
limitation. But the `openspec schema` commands have existed for a while:

- `schema fork` - copy existing schema to customize
- `schema init` - create new schema from scratch
- `schema validate` - check schema structure
- `schema which` - debug resolution precedence

Rewrote the guide to:
- Lead with the actual CLI commands instead of manual steps
- Remove the misleading "Current Limitations" section
- Add practical examples (TDD workflow, adding review artifact)
- Structure progressively: config → custom schemas → global overrides

* docs: add language tags to code blocks in customization guide

Address review feedback from CodeRabbit:
- Add 'text' language tag to directory tree code blocks
- Satisfies MD040 markdown lint rule
2026-01-25 23:37:46 -08:00
openspec-release-bot[bot]andgithub-actions[bot] 0e6f42c81c Version Packages (#579)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-25 16:09:16 -08:00
Tabish Bidiwale 0cc9d9025a chore: add changeset for 1.0.0 release (#578)
* Add changeset for v0.24.0 features

* Replace changeset with 1.0.0 release notes

Update from minor to major version bump. Rewrite release notes to
properly capture the OPSX workflow changes:

- Dynamic instructions based on artifact state
- Semantic spec syncing with ADDED/MODIFIED/REMOVED parsing
- Step-through artifacts with /opsx:continue
- Skills support alongside tool commands
- Breaking: old /openspec:* commands removed

* Rewrite 1.0.0 changeset with comprehensive release notes

Based on deep research into old vs new workflow:

Old workflow:
- 3 phase-locked commands (proposal → apply → archive)
- 8+ config files scattered at project root
- Static prompts, same instructions every time
- Text-based spec merging

New OPSX workflow:
- 10 action-based commands (do any action anytime)
- Dynamic instructions (context + rules + template layers)
- Artifact graph with dependency awareness
- Semantic spec syncing (ADDED/MODIFIED/REMOVED/RENAMED)
- Agent Skills standard for cross-editor compatibility
- 21 AI tools supported
- Onboarding skill for guided first experience
2026-01-25 16:04:10 -08:00
Tabish Bidiwale 3261ccf6dc feat: onboarding skill and comprehensive documentation overhaul (#574)
* feat(skills): add opsx:onboard guided workflow skill

Add a new onboard skill that walks users through their first complete
OpenSpec workflow cycle. The skill provides interactive guidance through
task selection, change creation, artifact building, implementation, and
archiving.

Also includes:
- New README with updated branding and workflow examples
- Documentation structure placeholders
- Change artifacts for the onboard skill feature

* test(skills): update skill-generation tests for onboard skill

Update test expectations from 9 to 10 skills after adding opsx:onboard.

* docs: update README links and add doc cleanup checklist

- Replace placeholder links in README_NEW.md with actual doc paths
- Add documentation cleanup checklist to README_RENEWAL_PROMPTS.md

* docs: overhaul documentation with new workflows, getting-started, and customization guides

- Rewrite workflows.md with action-based philosophy and workflow patterns
- Rewrite getting-started.md with clearer onboarding flow
- Rewrite customization.md with schema customization guidance
- Add cross-references between docs (Commands, Customization links)
- Remove obsolete docs: artifact_poc, experimental-release-plan, project-config-demo, schema-customization, schema-workflow-gaps
- Update README_RENEWAL_PROMPTS.md checklist

* docs: continue documentation overhaul with expanded guides and restructuring

- Expand cli.md, commands.md, and concepts.md with comprehensive content
- Add installation.md, multi-language.md, and supported-tools.md
- Rename experimental-workflow.md to opsx.md
- Remove i18n.md (replaced by multi-language.md)
- Update README links and cleanup prompts

* chore(assets): consolidate logo images

* docs: enhance README with badges, usage notes, and contributing guidelines

- Update Discord badge to show member count
- Add collapsible section with stars/downloads/contributors badges
- Add OpenSpec Dashboard preview section
- Add usage notes for model selection and context hygiene
- Expand contributing section with guidelines for small/large changes
- Clarify AI-generated code policy

* docs: remove misleading mid-flight update claims

The documentation claimed users could edit artifacts mid-implementation
and seamlessly continue, but no such mechanism exists. This removes:

- "Mid-Flight Correction" section from workflows.md
- Feedback arrows and "update as you learn" from all diagrams
- Mid-flight claims from commands.md, opsx.md, concepts.md
- Example blocks showing edit-then-continue workflow

Also adds a proposal for future artifact regeneration support that
would actually make this workflow possible.

* docs: fix PR review comments (markdown linting and accuracy)

- Add language tags to fenced code blocks (MD040)
- Remove blank line between blockquotes (MD028)
- Capitalize "Markdown" as proper noun
- Update deprecated command reference (experimental -> update)
- Update skill count from 9 to 10, add openspec-onboard
- Fix typo: fix-midlight -> fix-midflight

* chore: remove polish-release-notes CI workflow

Replaced with local /polish-release skill. The claude-code-action
doesn't work well with repository_dispatch triggers (no PR context).

* docs: clarify /opsx:sync is optional (archive prompts if needed)

Remove sync from main workflow flows and diagrams since archive
already prompts to sync when needed. Most users will never need
to call sync directly.

- Remove sync from completion flow diagrams
- Remove "Sync Specs Regularly" best practice section
- Update command descriptions to note it's optional
- Update "When to sync" to "When to use manually"

* docs: redesign README with simplified content and new OPSX callout

- Simplify badges and logo presentation
- Add collapsible "most loved" section
- Replace detailed explanation with concise philosophy
- Add prominent /opsx:onboard callout for new workflow
- Remove README_NEW.md (content merged into README.md)
- Remove renewal prompts documentation

* docs: add README_OLD.md as reference backup

* docs: fix command directory paths for multiple tools

Correct commands locations for Antigravity, Codex, Crush, OpenCode,
and Qoder in the supported tools table.
2026-01-25 15:43:52 -08:00
zhing2006andClaude Opus 4.5 26ed336a16 fix: correct regex trailing whitespace and add missing projectRoot param (#575)
- Add \s* to parseTasksFile regex to handle trailing whitespace in task lines
- Add missing projectRoot argument to resolveSchema call in generateApplyInstructions

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 18:48:05 -08:00
Tabish Bidiwale 847aa81c0f revert: undo README update from init merge (premature) (#567)
Reverts the README documentation changes that were included in PR #565.
These changes document features that aren't released yet.

A separate PR will be opened to re-apply these changes when the package
is released.
2026-01-23 20:06:22 -08:00
Tabish Bidiwale 39bebefcc4 feat(cli): merge init and experimental commands (#565)
* feat(core): add legacy cleanup detection functions for init migration

Implement src/core/legacy-cleanup.ts with detection and cleanup functions
for all legacy OpenSpec artifact types:

Detection functions:
- detectLegacyConfigFiles() - checks for config files with OpenSpec markers
  (CLAUDE.md, CLINE.md, CODEBUDDY.md, COSTRICT.md, QODER.md, IFLOW.md,
  AGENTS.md, QWEN.md)
- detectLegacySlashCommands() - checks for old /openspec:* command
  directories and files across all 21 tool integrations
- detectLegacyStructureFiles() - checks for openspec/AGENTS.md and
  openspec/project.md (project.md preserved for migration hint)
- detectLegacyArtifacts() - orchestrates all detection

Utility functions:
- hasOpenSpecMarkers() - checks if content has OpenSpec markers
- isOnlyOpenSpecContent() - checks if file is 100% OpenSpec content
- removeMarkerBlock() - surgically removes marker blocks from mixed content

Cleanup functions:
- cleanupLegacyArtifacts() - orchestrates removal with proper edge cases:
  - Deletes files that are 100% OpenSpec content
  - Removes marker blocks from files with mixed content
  - Deletes legacy slash command directories and files
  - Preserves openspec/project.md (shows migration hint only)

Formatting functions:
- formatDetectionSummary() - formats what was detected before cleanup
- formatCleanupSummary() - formats what was cleaned up after

This is task 1.1 for the merge-init-experimental change.

* feat(utils): add removeMarkerBlock() for surgically removing marker blocks

- Add removeMarkerBlock() function to file-system.ts that properly handles
  inline marker mentions by using findMarkerIndex/isMarkerOnOwnLine
- Refactor legacy-cleanup.ts to use the shared utility
- Export removeMarkerBlock from utils/index.ts for reusability
- Add comprehensive tests for inline marker mention edge cases
- Add tests for shell-style markers and various whitespace scenarios

The new implementation correctly ignores markers mentioned inline within
text and only removes actual marker blocks that are on their own lines.

* feat(core): add formatProjectMdMigrationHint() for migration messaging

- Add standalone formatProjectMdMigrationHint() function for reusable
  migration hint output directing users to migrate project.md content
  to config.yaml's "context:" field
- Update formatDetectionSummary() to include the migration hint when
  project.md is detected (not just in cleanup summary)
- Refactor formatCleanupSummary() to use the new function for
  consistency
- Add unit tests for the new function and updated behavior

* test(init): rewrite init tests for experimental workflow approach

Rewrites the init command tests to verify the new experimental workflow
implementation. The new tests cover:

- OpenSpec directory structure creation (specs, changes, archive)
- config.yaml generation with default schema
- 9 Agent Skills creation for various tools (Claude, Cursor, Windsurf, etc.)
- 9 slash commands generation using tool-specific adapters
- Multi-tool support (--tools all, --tools none, specific tools)
- Extend mode (re-running init)
- Tool-specific adapters (Gemini TOML, Continue .prompt, etc.)
- Error handling for invalid tools and permissions

Removes old tests for legacy config file generation (AGENTS.md, CLAUDE.md,
project.md, etc.) as the new init command uses Agent Skills instead.

* test(update): rewrite tests for skills/commands refresh behavior

Update the update command tests to match the new implementation that
refreshes skills and opsx commands instead of config files.

Changes:
- Remove old ToolRegistry import (deleted module)
- Rewrite tests to verify skill file updates
- Rewrite tests to verify opsx command generation
- Add tests for multi-tool support (Claude, Cursor, Qwen, Windsurf)
- Add tests for error handling and tool detection
- Fix test assertions to match actual skill template names

The update command now:
- Detects configured tools by checking skill directories
- Updates SKILL.md files with latest skill templates
- Generates opsx commands using tool-specific adapters

* docs(readme): update documentation for new init behavior

- Replace tool list with simplified supported tools section (skills-based)
- Update init instructions to document --tools flag, --force, and legacy cleanup
- Replace project.md with config.yaml documentation
- Update workflow examples to use /opsx:* commands instead of /openspec:*
- Add command reference table for slash commands
- Update Team Adoption and Updating sections for new workflow
- Replace Experimental Features with Workflow Customization section

* refactor(cli): remove legacy configurators and merge experimental into workflow

- Delete src/core/configurators/ directory (ToolRegistry, all config generators)
- Delete legacy templates (agents-template, claude-template, project-template, etc.)
- Move experimental commands to src/commands/workflow/ with cleaner structure
- Remove experimental setup.ts and index.ts (functionality merged into init)
- Update CLI to register workflow commands directly instead of through experimental
- Update openspec update command to refresh skills/commands instead of config files
- Update tests for new command structure

* refactor: extract shared modules and move AGENTS.md to root

- Move AGENTS.md from openspec/ to project root
- Add shared module with tool-detection and skill-generation utilities
- Update legacy-cleanup with improved cleanup logic
- Enhance update.ts with additional functionality
- Add comprehensive tests for shared modules

* fix(ui): update welcome screen tagline

Change from experimental reference to reflect the merged workflow.

* fix: improve Windows cross-platform compatibility

- Handle both forward and backward slashes in path parsing
- Normalize paths before regex matching for legacy artifact detection
- Use regex split for both path separators in tool directory extraction
- Handle CRLF line endings when cleaning up multiple blank lines
- Add retry logic for test file cleanup to handle Windows file locking

* fix(init): use dynamic counts for skills and commands in success message

Replace hard-coded "9 skills and 9 commands" with dynamic values from
getSkillTemplates().length and getCommandContents().length to prevent
the message from diverging from reality when skills/commands change.

* fix: various small improvements across init, cleanup, and file handling

- Remove shell prompt characters from README bash examples (MD014)
- Show actual config filename (config.yaml vs config.yml) in init output
- Include hasProjectMd in hasLegacyArtifacts to show migration hint
- Add existence check before AGENTS.md deletion to avoid spurious errors
- Preserve leading whitespace and original newline style in file operations
- Use dynamic tool list from CommandAdapterRegistry in tests
2026-01-23 19:51:31 -08:00
Tabish Bidiwale cf8b6212c8 feat(cli): merge init and experimental commands (#564)
* feat(cli): add change proposal to merge init and experimental commands

This change merges `openspec init` and `openspec experimental` into a
single command that uses the skill-based workflow as the default.

Key changes:
- BREAKING: init generates skills and /opsx:* commands instead of config files
- BREAKING: Config files (CLAUDE.md, .cursorrules, etc.) no longer generated
- BREAKING: Old slash commands (/openspec:proposal, etc.) no longer generated
- BREAKING: openspec/AGENTS.md and project.md no longer generated
- Add legacy detection and cleanup with Y/N confirmation
- Keep experimental as hidden alias for backward compatibility

Artifacts:
- proposal.md: Motivation and scope
- design.md: Architecture decisions and edge case handling
- specs/legacy-cleanup/spec.md: New capability for legacy artifact cleanup
- specs/cli-init/spec.md: Modified init spec with skill-based workflow
- tasks.md: 37 implementation tasks across 7 groups

* docs(change): preserve project.md with migration hint instead of deleting

Update merge-init-experimental change artifacts to preserve openspec/project.md
during legacy cleanup instead of auto-deleting it. Users will see a migration
hint directing them to move content to config.yaml's context field.

Changes:
- design.md: Add Decision 6 documenting rationale and migration path
- spec.md: Add project.md migration hint requirement and scenarios
- tasks.md: Add task 1.7 for migration hint output

This avoids losing user-written project documentation while guiding them
to the new config.yaml approach.

* docs(change): resolve open questions about update command and experimental labels
2026-01-22 23:15:18 -08:00
Tabish Bidiwale c157483685 feat(skills): add Agent Skills spec optional metadata fields (#563)
Add optional fields to SkillTemplate interface and all skill templates
to improve compliance with the Agent Skills specification:

- license: MIT (matching project license)
- compatibility: Requires openspec CLI.
- metadata: { author: openspec, version: 1.0 }

Updates skill file generation to include these fields in YAML frontmatter.
2026-01-22 22:25:46 -08:00
Tabish Bidiwale f90c7c3354 refactor(commands): modularize artifact workflow into separate files (#562)
* refactor(commands): modularize artifact workflow into separate files

Split the monolithic artifact-workflow.ts into separate modules under
src/commands/experimental/:
- index.ts: main exports and command registration
- status.ts: status display logic
- new-change.ts: change creation logic
- schemas.ts: Zod schemas
- setup.ts: setup command logic
- templates.ts: template generation
- shared.ts: shared utilities
- instructions.ts: instruction generation

Also extracted init wizard logic to src/core/init/wizard.ts.

* fix(commands): address code review feedback from PR #562

- Fix template source detection using path.relative instead of startsWith
  to prevent misclassification of paths with shared prefixes
- Fix config file log message to show actual file name (config.yaml vs config.yml)
- Fix selectedTools option to be honored when provided programmatically
2026-01-22 21:21:05 -08:00
Tabish Bidiwale 9381bd3b24 feat(cli): improve artifact experimental setup with refresh detection (#561)
* feat(cli): improve artifact experimental setup with refresh detection

- Add functions to detect which tools already have experimental skills configured
- Pre-select configured tools in interactive mode for easy refresh
- Sort configured tools to appear first in the selection list
- Show "(refresh)" indicator for already-configured tools in selection
- Distinguish between "Created" and "Refreshed" tools in output
- Streamline success output to be more concise and scannable

* test: update experimental command test assertions for new output format

The output format changed from '.claude/skills/' to '.claude/' (showing
summary counts instead of directory paths), so update assertions to match.

* feat(cli): rename artifact-experimental-setup to experimental

Shorter command name for better usability. Updates command registration,
documentation, and README references.
2026-01-22 20:13:06 -08:00
Tabish Bidiwale ae83b4e16d feat(cli): add interactive UI for artifact experimental setup (#560)
* feat(cli): add interactive UI for artifact experimental setup

Add animated welcome screen and searchable multi-select prompt when
running `openspec artifact-experimental-setup` without the --tool flag
in interactive mode. Users can now browse and select multiple tools
for setup instead of requiring the --tool flag.

- Add welcome screen with ASCII art animation
- Add searchable multi-select prompt component
- Support multi-tool setup in single command invocation

* fix(nix): update flake version and reset hash for rebuild

- Update version from 0.20.0 to 0.23.0 to match package.json
- Set pnpmDeps hash to empty string to trigger rebuild
- Fix update-flake.sh to work on macOS (use portable grep/sed)

CI will fail with correct hash which we'll then apply.

* fix(nix): set correct pnpmDeps hash

* feat(cli): improve error handling for multi-tool setup

- Continue setup for remaining tools when one fails
- Collect and report all failures at the end
- Only throw if all tools fail
- Show partial success summary (configured vs failed)
2026-01-22 18:39:38 -08:00
Tabish Bidiwale d48528134b feat(cli): add multi-provider skill generation support (#556)
* feat(cli): add multi-provider skill generation support

Add --tool flag to artifact-experimental-setup command to generate
skills and commands for different AI tools (Claude, Cursor, Windsurf).

- Add skillsDir field to AIToolOption interface
- Create command-generation module with tool-specific adapters
- Each adapter handles tool-specific file paths and frontmatter formats
- Add CommandAdapterRegistry for adapter lookup
- Update artifact-experimental-setup to use dynamic paths

* feat(config): add skillsDir for all supported AI tools

Add skillsDir mappings for tools that were missing:
- Amazon Q Developer (.amazonq)
- Antigravity (.agent)
- Auggie (.augment)
- Cline (.cline)
- CodeBuddy Code (.codebuddy)
- Continue (.continue)
- CoStrict (.cospec)
- Crush (.crush)
- iFlow (.iflow)
- Qoder (.qoder)
- Qwen Code (.qwen)

Fix RooCode path: .roocode → .roo

* feat(adapters): add command adapters for all supported AI tools

Add 18 new command adapters covering all supported AI tools in the
multi-provider skill generation system. Each adapter implements the
correct file path and frontmatter format for its respective tool.

New adapters: amazon-q, antigravity, auggie, cline, codex, codebuddy,
continue, costrict, crush, factory, gemini, github-copilot, iflow,
kilocode, opencode, qoder, qwen, roocode.

* fix(adapters): address PR review feedback

- Change .requiredOption to .option for custom error handling with tool list
- Add YAML escaping for special characters in all command adapters
- Normalize path separators in tests for cross-platform compatibility
- Update docs: --tool flag is required, not optional with default
- Add missing Windsurf adapter scenario to spec
- Fix spec headers and language specifiers
2026-01-21 23:32:06 -08:00
Tabish Bidiwale 54bd3f1ccd fix(docs): update invalid Discord link in experimental workflow (#555) 2026-01-21 15:04:09 -08:00
QraffaandTabish Bidiwale 675e870bf1 style: remove unnecessary whitespace (#554)
Co-authored-by: Tabish Bidiwale <30385142+TabishB@users.noreply.github.com>
2026-01-21 14:52:27 -08:00
Alexey StepanovandTabish Bidiwale 07eaf7b691 fix(claude): replace colon with dash in slash command frontmatter names (#553)
Claude Code's YAML parser fails when the `name` field contains a colon (e.g., `name: OpenSpec: Proposal`), causing it to fall back to the first content line as the description, showing `<!-- OPENSPEC:START -->`.

Changes:
  - Replace `OpenSpec: Proposal` with `OpenSpec - Proposal` in claude.ts
  - Add `shouldRefreshFrontmatter()` hook to allow updating existing files
  - Extract `buildContent()` helper method for DRY
  - Update test expectations for Claude configurator

Co-authored-by: Tabish Bidiwale <30385142+TabishB@users.noreply.github.com>
2026-01-21 14:36:16 -08:00
153721d14a Add /opsx:bulk-archive to experimental workflow setup command output (#551)
* Add /opsx:bulk-archive to experimental workflow setup command

* Update src/commands/artifact-workflow.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Tabish Bidiwale <30385142+TabishB@users.noreply.github.com>
2026-01-21 13:03:24 -08:00
mingcao-devandpengjiahan.pjh 70c2e17525 chore: rename "Qoder (CLI)" to "Qoder" (#552)
- Update tool name in config.ts
- Update README.md documentation and docs link

Co-authored-by: pengjiahan.pjh <pengjiahan.pjh@antgroup.com>
2026-01-21 13:00:43 -08:00
Tabish Bidiwale e2c333e493 fix(instructions): separate context and rules from template in JSON output (#547)
* fix(instructions): separate context and rules from template in JSON output

Previously, project config context and rules were prepended to the template
field in artifact instructions. This caused AI assistants to literally copy
these constraint blocks into generated artifact files, rather than treating
them as instructions.

Changes:
- Add `context` and `rules` as separate fields in ArtifactInstructions interface
- Update generateInstructions() to populate these as distinct fields
- Update CLI output to display context/rules with clear "do not include" comments
- Rename <context> (dependencies) to <dependencies> to avoid confusion
- Update tests for new structure

The JSON output from `openspec instructions` now clearly separates:
- `context`: Project background (constraints for AI)
- `rules`: Artifact-specific rules (constraints for AI)
- `template`: The actual structure for the output file

* fix(skills): update skill templates with separate context/rules documentation

Update generated skill templates (continue-change, ff-change) to document
that context and rules are separate JSON fields that should NOT be copied
into artifact output files.

Users running `openspec update` will get the updated skill instructions.
2026-01-20 20:52:07 -08:00
Tabish Bidiwale e137dd3981 fix(ci): use repository_dispatch for polish release notes (#545)
The GitHub App token doesn't have actions:write permission, which is
required for workflow_dispatch. Switch to repository_dispatch which
works with existing contents:write permission.

Changes:
- release-prepare.yml: Use gh api to trigger repository_dispatch
- polish-release-notes.yml: Add repository_dispatch trigger type
- Delete test workflow (validation complete)

Tested via PR #542 - both workflow_dispatch and repository_dispatch
triggers work correctly with claude-code-action.
2026-01-20 20:03:56 -08:00
Tabish Bidiwale 2beb8e77e8 test: add temporary workflow to validate repository_dispatch (#542)
This is a dry-run workflow to test repository_dispatch before modifying
the release pipeline. Will be deleted after validation.
2026-01-20 19:01:23 -08:00
openspec-release-bot[bot]andgithub-actions[bot] 3b16b13613 Version Packages (#541)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-20 17:21:29 -08:00
Tabish Bidiwale c4cfdc7c49 Add changeset for bulk-archive skill and setup simplification (#540) 2026-01-20 17:17:31 -08:00
Tabish Bidiwale e0736807b4 refactor(setup): simplify config creation and fix test hanging (#537)
* refactor(setup): simplify config creation and fix test hanging

- Replace interactive config prompts with automatic config creation using
  default schema. The generated config includes helpful comments explaining
  context and rules options.
- Remove unused promptForConfig, promptForArtifactRules, and isExitPromptError
  functions from config-prompts.ts
- Add forceExit: true to vitest config to prevent worker processes from hanging
  after tests complete

* docs: add schema-alias-support change proposal

Proposal to add schema alias support so `openspec-default` and `spec-driven`
can be used interchangeably, enabling a rename without breaking existing configs.

* fix(test): remove invalid forceExit config and add proper teardown

- Remove `forceExit: true` from vitest.config.ts (Jest option, not Vitest)
- Add actual teardown logic in vitest.setup.ts that forces exit after 1s
  grace period if processes are still hanging
2026-01-20 14:32:56 -08:00
Tabish Bidiwale fdb05a723e feat(skills): add bulk-archive skill for archiving multiple changes (#527)
Add `/opsx:bulk-archive` skill that allows archiving multiple completed
changes in a single operation. Features include:

- Multi-select change selection via AskUserQuestion
- Batch validation of artifacts, tasks, and delta specs
- Spec conflict detection when multiple changes touch same capability
- Agentic conflict resolution by checking codebase for implementation
- Consolidated status table before confirmation
- Single confirmation for entire batch operation
- Comprehensive summary showing archived/skipped/failed changes

This is useful when working on multiple changes in parallel and wanting
to archive them together after implementation is complete.
2026-01-20 11:37:33 -08:00
Tabish Bidiwale 8332a09811 fix(ci): use workflow_dispatch for polish release notes (#533)
* fix(ci): use workflow_dispatch for polish release notes

The claude-code-action doesn't support the `release` event type.
Switch to workflow_dispatch which is supported, and have
release-prepare trigger it after publishing.

* fix: get tag from package.json instead of gh release list
2026-01-20 00:36:24 -08:00
Tabish Bidiwale d61a49f6d5 fix(changelog): convert markdown headers to bold text for proper formatting (#532)
Changeset descriptions that use markdown headers (### New Features) get
nested inside list items, causing poor rendering. This converts all
affected entries to use **Bold Text** instead, which renders correctly.

Also adds a brief summary line to each entry for better readability.
2026-01-20 00:09:49 -08:00
openspec-release-bot[bot]andgithub-actions[bot] 7d1237f00d Version Packages (#531)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-20 00:01:09 -08:00
Tabish Bidiwale 33466b1e2a Add changeset for project config and schema commands (#530) 2026-01-19 23:58:09 -08:00
Tabish Bidiwale 6c8c778043 fix(config): handle null rules field in project config (#529)
* feat(config): add project-level configuration via openspec/config.yaml

Adds openspec/config.yaml support for project-level customization without
forking schemas. Teams can now:

- Set a default schema (used when --schema flag not provided)
- Inject project context into all artifact instructions
- Add per-artifact rules (e.g., proposal rules, specs rules)

Key changes:
- New `src/core/project-config.ts` with Zod schema and resilient parsing
- New `src/core/config-prompts.ts` for interactive config creation
- Updated schema resolution order: CLI → change metadata → config → default
- Updated instruction generation to inject <context> and <rules> XML sections
- Integrated config creation prompts into `artifact-experimental-setup`

Schema resolution precedence:
1. --schema CLI flag (explicit override)
2. .openspec.yaml in change directory (change-specific)
3. openspec/config.yaml schema field (project default)
4. "spec-driven" (hardcoded fallback)

* test(config): add e2e tests and performance benchmarks for project config

- Add project config integration tests in artifact-workflow.test.ts:
  - Test new change uses schema from config
  - Test CLI schema overrides config schema
  - Test context and rules injection in instructions
  - Test backwards compatibility without config file
  - Test immediate reflection of config changes

- Add performance benchmark tests in project-config.test.ts:
  - Typical config (1KB): <20ms target
  - Large config (50KB): <50ms target
  - Repeated reads consistency check
  - Missing config fast-path test

- Add project configuration documentation in experimental-workflow.md:
  - Config fields reference (schema, context, rules)
  - Schema precedence explanation
  - Artifact IDs by schema
  - Troubleshooting guide

- Mark all tasks complete in tasks.md

* refactor(config): remove benchmark tests, document decision in source

Remove performance benchmark tests from project-config.test.ts and
document the results directly in the source code instead. Benchmarks
showed config reads are fast enough (~0.5ms typical) that caching
is unnecessary.

* fix(config): resolve three issues in project config feature

- Remove nested <template> tags: generateInstructions() no longer wraps
  template content since printInstructionsText() already handles XML
  structure
- Fix schema message accuracy: newChangeCommand() now uses the resolved
  schema returned from createChange() instead of hardcoded fallback
- Add TTY check: artifact-experimental-setup skips interactive prompts
  in non-TTY environments (CI, automation) to prevent hangs

* fix(config): handle null rules field in project config

Add null check when parsing rules field since YAML `rules:` with no
value parses to null, and `typeof null === 'object'` in JavaScript.
Without this check, Object.entries(null) would throw an error.
2026-01-19 23:49:11 -08:00
Tabish Bidiwale 43b01ad374 docs: update workflow docs and mark schema commands as experimental (#526)
* docs: update workflow docs for schema management CLI

Update documentation to reflect implemented schema management features:
- Document schema CLI commands (which, validate, fork, init)
- Update gap summary to show completed phases (PR #522, #525)
- Improve custom schema examples with actual CLI usage
- Update resolution order documentation

* feat(cli): mark schema commands as experimental

Add [experimental] tag to help description and runtime warning
for schema management commands to indicate they may change.
2026-01-19 20:41:15 -08:00
Tabish Bidiwale 3cdcdfca8e feat(cli): add schema management commands (#525)
Add `openspec schema` command group with subcommands for managing
workflow schemas:

- `schema which [name]` - Show where a schema resolves from with
  shadow detection across project/user/package locations
- `schema validate [name]` - Validate schema structure, templates,
  and dependency graph
- `schema fork <source> [name]` - Copy an existing schema to project
  for customization
- `schema init <name>` - Create a new project-local schema with
  interactive or CLI-driven configuration

All commands support `--json` output for scripting. The init command
supports interactive prompts for description and artifact selection.

Implements the schema-management-cli change proposal.
2026-01-19 19:42:37 -08:00
Tabish Bidiwale 32fc19a60d fix: Windows path compatibility in resolver tests (#524)
- Use path.join() in test expectations instead of hardcoded forward slashes
- Add openspec/config.yaml with cross-platform requirements to prevent
  similar issues in future proposals

The tests were failing on Windows because path.join() uses backslashes
on Windows, but the test expectations hardcoded forward slashes.
2026-01-19 18:42:00 -08:00
Tabish Bidiwale 84f372517f change(schema-management-cli): proposal for schema management commands (#523)
* change(schema-management-cli): add proposal for schema management commands

Propose new CLI commands to improve the UX of creating and managing project schemas:

- `openspec schema init <name>` - Interactive wizard to scaffold new schemas
- `openspec schema fork <source> [name]` - Copy existing schema for customization
- `openspec schema validate [name]` - Validate schema structure before runtime
- `openspec schema which <name>` - Debug schema resolution path

* change(schema-management-cli): add design, tasks, and specs artifacts

Add implementation artifacts for the schema management CLI feature:
- Design document with decisions and rationale
- Task breakdown for implementation
- Specs for schema init, fork, validate, and which commands
2026-01-19 18:22:44 -08:00
Tabish Bidiwale adda63e17a feat(resolver): add project-local schema support (#522)
Add 3-level schema resolution: project-local → user override → package built-in.

- Add `getProjectSchemasDir(projectRoot)` to resolve project schemas at `./openspec/schemas/<name>/`
- Extend `SchemaInfo.source` type to include `'project'`
- Update `getSchemaDir()`, `resolveSchema()`, `listSchemas()`, `listSchemasWithInfo()` with optional `projectRoot` parameter
- Update CLI commands to display schema source labels (project/user/package)
- Add `projectRoot` to `ChangeContext` interface for proper resolution throughout workflow
- Add 17 new tests covering project-local schema resolution

This enables projects to define custom schemas that override user and package schemas,
while maintaining backward compatibility when projectRoot is not provided.
2026-01-19 18:01:35 -08:00
Tabish Bidiwale 90d05b7115 docs: add project-config demo guide (#521)
Add a quick-reference demo guide for the project-config feature
(openspec/config.yaml). This consolidates the demo walkthrough
into a standalone document that's easier to use when presenting
the feature.

Includes:
- Summary of what project config does
- 6 numbered demo scenarios
- Quick all-in-one demo script
- Key points to emphasize
2026-01-19 16:56:12 -08:00
Tabish Bidiwale 20714c1c28 feat(config): add project-level configuration via openspec/config.yaml (#499)
* feat(config): add project-level configuration via openspec/config.yaml

Adds openspec/config.yaml support for project-level customization without
forking schemas. Teams can now:

- Set a default schema (used when --schema flag not provided)
- Inject project context into all artifact instructions
- Add per-artifact rules (e.g., proposal rules, specs rules)

Key changes:
- New `src/core/project-config.ts` with Zod schema and resilient parsing
- New `src/core/config-prompts.ts` for interactive config creation
- Updated schema resolution order: CLI → change metadata → config → default
- Updated instruction generation to inject <context> and <rules> XML sections
- Integrated config creation prompts into `artifact-experimental-setup`

Schema resolution precedence:
1. --schema CLI flag (explicit override)
2. .openspec.yaml in change directory (change-specific)
3. openspec/config.yaml schema field (project default)
4. "spec-driven" (hardcoded fallback)

* test(config): add e2e tests and performance benchmarks for project config

- Add project config integration tests in artifact-workflow.test.ts:
  - Test new change uses schema from config
  - Test CLI schema overrides config schema
  - Test context and rules injection in instructions
  - Test backwards compatibility without config file
  - Test immediate reflection of config changes

- Add performance benchmark tests in project-config.test.ts:
  - Typical config (1KB): <20ms target
  - Large config (50KB): <50ms target
  - Repeated reads consistency check
  - Missing config fast-path test

- Add project configuration documentation in experimental-workflow.md:
  - Config fields reference (schema, context, rules)
  - Schema precedence explanation
  - Artifact IDs by schema
  - Troubleshooting guide

- Mark all tasks complete in tasks.md

* refactor(config): remove benchmark tests, document decision in source

Remove performance benchmark tests from project-config.test.ts and
document the results directly in the source code instead. Benchmarks
showed config reads are fast enough (~0.5ms typical) that caching
is unnecessary.

* fix(config): resolve three issues in project config feature

- Remove nested <template> tags: generateInstructions() no longer wraps
  template content since printInstructionsText() already handles XML
  structure
- Fix schema message accuracy: newChangeCommand() now uses the resolved
  schema returned from createChange() instead of hardcoded fallback
- Add TTY check: artifact-experimental-setup skips interactive prompts
  in non-TTY environments (CI, automation) to prevent hangs
2026-01-19 16:39:43 -08:00
Tabish Bidiwale 2e51ae26d3 fix: auto-trigger polish release notes on release publish (#519)
* perf: add path filtering to Nix validation CI job

Skip Nix flake validation when no Nix-related files change. This speeds
up CI for PRs that only touch source code, docs, or tests.

Files that trigger Nix validation:
- flake.nix, flake.lock
- package.json, pnpm-lock.yaml
- scripts/update-flake.sh
- .github/workflows/ci.yml

Uses dorny/paths-filter@v3 for change detection. Required-checks jobs
updated to handle skipped status correctly.

* fix: auto-trigger polish release notes on release publish

The workflow was only set up for manual dispatch, requiring someone to
remember to run it after each release. This change adds an automatic
trigger on `release: [published]` events while keeping the manual
trigger as a fallback.

The Claude Code Action supports any GitHub event when using the `prompt`
parameter for custom automations.
2026-01-19 01:28:45 -08:00
Tabish Bidiwale dbd4ed7bfb perf: add path filtering to Nix validation CI job (#518)
Skip Nix flake validation when no Nix-related files change. This speeds
up CI for PRs that only touch source code, docs, or tests.

Files that trigger Nix validation:
- flake.nix, flake.lock
- package.json, pnpm-lock.yaml
- scripts/update-flake.sh
- .github/workflows/ci.yml

Uses dorny/paths-filter@v3 for change detection. Required-checks jobs
updated to handle skipped status correctly.
2026-01-19 01:22:33 -08:00
openspec-release-bot[bot]andgithub-actions[bot] 473093f885 Version Packages (#517)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-19 00:59:49 -08:00
Tabish Bidiwale b5a884748b Add changeset for v0.21 release (#516) 2026-01-19 00:56:17 -08:00
Tabish Bidiwale 690c75225c fix: prevent implementation during explore mode (#515)
Add explicit instructions to both the explore skill and slash command:
- Add IMPORTANT notice clarifying explore is for thinking, not implementing
- Add "Open threads, not interrogations" stance for user-driven exploration
- Add "Don't implement" guardrail to both templates

Creating OpenSpec artifacts is still allowed since that captures thinking.
2026-01-19 00:28:45 -08:00
Tabish Bidiwale dd53fb7736 OPSX apply: infer target change (#513)
* opsx: infer change for apply

* opsx: prompt when apply ambiguous

* opsx: use AskUserQuestion when ambiguous

* Simplify opsx:apply change selection instructions

Update all change selection instructions across all opsx commands
(apply, continue, sync, archive, verify, ff) to use consistent wording:

"If omitted, check if it can be inferred from conversation context.
If vague or ambiguous you MUST prompt for available changes."

For apply specifically, also simplifies Step 1 from ~180 to ~60 words
while preserving the same behavior: infer from conversation, auto-select
if single change, prompt via AskUserQuestion if ambiguous, always announce.

Removes micromanagement details (validation commands, recommendation
markers, presentation specifics) and trusts the LLM to figure out
reasonable defaults.
2026-01-18 19:44:56 -08:00
Tabish Bidiwale 2a441c472d Refine opsx archive sync assessment (#514)
* Refine opsx archive sync assessment

* Simplify opsx archive sync instructions

Replace verbose algorithmic instructions with intent-focused guidance.
Trust the agent to figure out comparison logic rather than specifying
the exact algorithm for each delta type (ADDED, MODIFIED, etc).

Reduces section 4 from ~31 lines to ~13 lines per template.

* Add explicit paths for delta spec locations

Make it clear where agents should look for delta specs and main specs:
- Delta specs: openspec/changes/<name>/specs/
- Main specs: openspec/specs/<capability>/spec.md
2026-01-18 19:35:04 -08:00
Pim SnelandTabish Bidiwale ed4d965208 feat: add nix flake support (sorry for this duplicate) (#459)
* add nix flake support

* feat: add Nix flake maintenance automation

* Add Nix Flake CI Validation

* fix updatescript, update flake

* make update-script compatible with macos

---------

Co-authored-by: Tabish Bidiwale <30385142+TabishB@users.noreply.github.com>
2026-01-16 13:00:11 -08:00
Tabish Bidiwale c86985d6ec feat: add feedback command for submitting user feedback (#509)
* feat: add feedback command for submitting user feedback

Implement `openspec feedback` command that creates GitHub Issues using the gh CLI.
Includes graceful fallback to manual submission when gh is not available or
not authenticated.

Features:
- Automatic gh CLI detection and authentication check
- Graceful fallback with pre-filled issue URLs for manual submission
- Automatic metadata inclusion (version, platform, timestamp)
- /feedback skill for agent-assisted feedback with context enrichment
- Comprehensive test coverage with mocked gh CLI calls

* fix: address PR review comments for feedback command

- Add Windows compatibility: use 'where gh' on Windows, 'which gh' on Unix/macOS
- Fix shell injection vulnerability: replace execSync with execFileSync and argument arrays
- Fix British English phrasing: "in future" → "in the future"
- Reduce code duplication: extract formatTitle/formatBody to single location
- Update tests to verify execFileSync usage and cross-platform command detection
- Add spec scenarios for safe command execution and cross-platform support
2026-01-14 15:48:29 -08:00
Tabish Bidiwale bf4bc2426f fix: add auto-approval for file writes in polish-release-notes workflow (#505)
The workflow was failing because Claude Code requested permission to write files
(release-title.txt and polished-notes.md) but there was no interactive user to approve.

Added claude_args: "--allowedTools Write,Read" to pre-approve file operations
in automation mode.
2026-01-13 23:09:37 -08:00
Tabish Bidiwale c57e421cc2 fix: update polish-release-notes workflow to use correct Claude Code action parameters (#504)
The workflow was failing due to two issues:
1. Using unsupported 'release' event trigger - Claude Code action doesn't support this event type
2. Using deprecated 'direct_prompt' parameter instead of 'prompt'

Changes:
- Switch from 'release' trigger to 'workflow_dispatch' for manual triggering
- Replace 'direct_prompt' with 'prompt' parameter (v1.0 breaking change)
- Update all tag references from github.event.release.tag_name to inputs.tag_name

The workflow now needs to be manually triggered from GitHub Actions UI after a release is published.
2026-01-13 23:02:22 -08:00
openspec-release-bot[bot]andgithub-actions[bot] ed2e832066 Version Packages (#503)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-13 22:50:11 -08:00
Tabish Bidiwale 9db74aa5ac chore: add changeset for opsx:verify skill and bug fixes (#502)
* Add changeset for opsx:verify skill and bug fixes

* Update changeset to follow template format
2026-01-13 22:45:23 -08:00
Tabish Bidiwale b5b7248610 feat(skills): implement /opsx:verify skill for validating change implementations (#501)
Add comprehensive verification skill that checks implementation completeness, correctness, and coherence against change artifacts. The skill validates task completion, spec coverage, requirement implementation, and design adherence.
2026-01-13 22:38:49 -08:00
Tabish Bidiwale 322bfd455a fix(vitest): cap worker parallelism to prevent process storms (#500)
Vitest v3 defaults to `pool: "forks"` and scales worker processes with
CPU count. This repo's tests spawn many Node processes (CLI invocations,
temp FS operations), which can cause runaway CPU/memory usage when
combined with high parallelism.

Changes:
- Explicitly set pool to 'forks' (tests rely on process isolation)
- Add resolveMaxWorkers() to cap workers at min(4, availableCPUs)
- Allow VITEST_MAX_WORKERS env override for CI/automation tuning
2026-01-13 21:41:25 -08:00
Tabish Bidiwale 08c349369a docs: add MAINTAINERS.md with core maintainers and advisors (#495) 2026-01-13 21:05:28 -08:00
Tabish Bidiwale 40afee643e chore(openspec): add feedback command change proposal (#496)
Add change proposal for `openspec feedback` CLI command that enables
users and agents to submit feedback via GitHub Issues.

Key features:
- Simple `openspec feedback <message>` command
- GitHub Device OAuth for authentication
- `/feedback` skill for agent-assisted feedback with context enrichment
- Anonymization of sensitive data before submission
- User confirmation required before submitting
2026-01-13 15:39:55 -08:00
Tabish Bidiwale 05023dab43 feat(skills): add /opsx:verify change proposal (#497)
Add change proposal for a new verification skill that validates
implementation matches change artifacts (specs, tasks, design).

The skill verifies three dimensions:
- Completeness: all tasks done, all specs addressed
- Correctness: implementation matches specs and scenarios
- Coherence: follows design decisions and project patterns

Produces prioritized report with actionable fix suggestions.
2026-01-13 15:26:31 -08:00
Tabish Bidiwale d7a928b4e9 fix(agents): add --no-interactive to validate commands in agent workflows (#494)
AI agents following OpenSpec workflows would hit interactive prompts when
running `openspec validate` because the commands in AGENTS.md and slash
command templates didn't include the --no-interactive flag.

This caused hangs in LLM tool execution since agents run in pseudo-TTY
environments where process.stdin.isTTY returns true, triggering
interactive mode.

Fixes #492
2026-01-13 14:50:43 -08:00
Nob ShinjoandTabish Bidiwale 07dd634986 fix(powershell-generator): remove trailing comma from last entry (#485)
Co-authored-by: Tabish Bidiwale <30385142+TabishB@users.noreply.github.com>
2026-01-13 13:50:11 -08:00
Tabish Bidiwale 36078b1947 chore: improve release notes pipeline (#481)
* chore: improve changelog generation with GitHub integration

- Switch to @changesets/changelog-github for PR/author links in CHANGELOG.md
- Add comprehensive changeset README with template and contributor guidance
- Remove release:local script (CI-only releases)

* ci: add AI-powered release notes polishing

Transforms raw changelog into user-friendly release notes when a
GitHub Release is published. Uses Claude Code Action to:
- Generate concise release title (e.g., "v0.18.0 - OPSX Workflow")
- Rewrite changelog as developer-friendly release notes
- Remove noise (commit hashes, PR numbers, internal changes)

Requires CLAUDE_CODE_OAUTH_TOKEN secret (from claude setup-token).
2026-01-10 22:33:43 -08:00
Tabish Bidiwale d0e1b076c2 chore: trigger release workflow for v0.19.0 (#480) 2026-01-10 18:20:30 -08:00
Tabish Bidiwale 2fbda520de ci: remove auto-merge to fix release triggering (#479)
GitHub's auto-merge feature uses an internal token that cannot trigger
workflows. Even with manual approval, the merge performed by auto-merge
doesn't trigger the release workflow.

Removing auto-merge means:
1. Version PR is created with CI running
2. User manually merges the PR (clicking "Merge")
3. The merge triggers the release workflow
4. Package is published

This aligns with industry standard practice for changesets.
2026-01-10 18:18:09 -08:00
github-actions[bot] 5633556b6d Version Packages (#475)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-10 18:04:55 -08:00
Tabish Bidiwale 2bb0ed36c5 ci: pass GitHub App token to checkout for CI triggering (#478)
Move the GitHub App token generation before checkout and pass it
to actions/checkout. This configures git to use the App's identity
for all operations, allowing pushes to trigger CI workflows.

Removes commitMode: github-api which doesn't support executable files.
2026-01-10 18:01:05 -08:00
Tabish Bidiwale 06097f9cb7 ci: add commitMode github-api to trigger CI on version PR (#477)
* ci: use GitHub App token to trigger CI on version PR

Replace GITHUB_TOKEN with a GitHub App token so that the version PR
can trigger CI workflows. GITHUB_TOKEN cannot trigger workflows by
design (to prevent infinite loops).

Requires APP_ID variable and APP_PRIVATE_KEY secret to be configured.

* ci: upgrade create-github-app-token to v2

* ci: add commitMode github-api to trigger CI on version PR
2026-01-10 17:50:59 -08:00
Tabish Bidiwale 8f5a526396 ci: use GitHub App token to trigger CI on version PR (#476)
* ci: use GitHub App token to trigger CI on version PR

Replace GITHUB_TOKEN with a GitHub App token so that the version PR
can trigger CI workflows. GITHUB_TOKEN cannot trigger workflows by
design (to prevent infinite loops).

Requires APP_ID variable and APP_PRIVATE_KEY secret to be configured.

* ci: upgrade create-github-app-token to v2
2026-01-10 17:38:57 -08:00
Tabish Bidiwale eb152eb2ca ci: auto-merge version PR to streamline releases (#474)
* Add changeset for Continue support, shell completions, and explore command

* ci: auto-merge version PR to streamline releases

Enable auto-merge on the changesets version PR so it merges
automatically once CI passes. This reduces the release process
from 2 manual PR merges to effectively 1.

Requires enabling "Allow auto-merge" in repository settings
and branch protection rules on main.
2026-01-10 16:22:25 -08:00
e987a5a327 Add Continue support (#402)
* OpenSpec 支持 Continue 插件

* add update

* Update spec.md

* fix: correct Continue frontmatter format and README ordering

- Fix Continue frontmatter to use proper YAML format with opening `---`
  and required `invokable: true` field for slash command availability
- Fix README table ordering: Continue should be after Codex alphabetically
- Remove unused TemplateManager import from continue.ts
- Fix trailing whitespace in update.test.ts
- Fix double blank line in init.test.ts
- Fix extra blank line in cli-init/spec.md
- Update tests to verify correct frontmatter format

---------

Co-authored-by: ajuanli <ajuanli@tencent.com>
Co-authored-by: Tabish Bidiwale <30385142+TabishB@users.noreply.github.com>
Co-authored-by: Tabish Bidiwale <tabishbidiwale@gmail.com>
2026-01-10 15:54:09 -08:00
Tabish Bidiwale 4971cda812 fix: use actionCommand for telemetry command tracking (#472)
The preAction hook receives (thisCommand, actionCommand) where thisCommand
is the root program and actionCommand is the actual subcommand being run.
Using thisCommand was incorrectly tracking the root command instead of
the actual subcommand executed by the user.
2026-01-10 15:10:12 -08:00
Tabish Bidiwale 4715138927 fix: set USERPROFILE for Windows compatibility in telemetry tests (#469)
On Windows, os.homedir() uses the USERPROFILE environment variable
instead of HOME. The telemetry config tests were only mocking HOME,
causing them to fail on Windows CI.
2026-01-09 23:36:54 -08:00
Tabish Bidiwale 940898c1c5 Add optional anonymous usage statistics (#468)
* chore: add proposal for PostHog analytics integration

Introduces the proposal artifact for adding opt-in telemetry to OpenSpec
using PostHog. Covers command tracking, feature adoption metrics, and
privacy-respecting consent management.

* feat: add optional anonymous usage statistics

Introduces privacy-first usage analytics to help understand how OpenSpec
is being used. Key privacy protections:

- Only tracks command names and version (no arguments, paths, or content)
- Opt-out via OPENSPEC_TELEMETRY=0 or DO_NOT_TRACK=1
- Auto-disabled in CI environments
- No IP address collection (explicitly disabled)
- Anonymous ID is a random UUID with no PII

Uses PostHog with a reverse proxy to avoid ad blockers. First-run shows
a one-line notice informing users about the collection.

* docs: make telemetry section collapsible and concise
2026-01-09 23:10:31 -08:00
Tabish Bidiwale d49a88c3bb feat: add /opsx:explore command for exploratory thinking (#467)
Wire up the explore skill and slash command templates to the
artifact-experimental-setup command. This adds /opsx:explore as
a thinking partner mode for exploring ideas, investigating problems,
and clarifying requirements before committing to a change.

Changes:
- Add imports for getExploreSkillTemplate and getOpsxExploreCommandTemplate
- Add explore skill to skills array (generates openspec-explore/SKILL.md)
- Add explore command to commands array (generates opsx/explore.md)
- Add /opsx:explore to CLI usage message
- Update docs/experimental-workflow.md with explore command
2026-01-09 20:15:26 -08:00
Tabish Bidiwale bb9f6ce0ea docs: add OPSX experimental workflow visibility to README (#460)
* docs: add OPSX experimental workflow visibility to README

Add a subtle banner near the top and an Experimental Features section
before Contributing to draw attention to the new OPSX workflow.

* docs: reframe OPSX messaging around fluid iteration

Update messaging to emphasize the core value proposition:
- No phases, just actions
- Dependencies are enablers, not gates
- Update artifacts as you learn during implementation

The previous "step-by-step artifact creation" framing incorrectly
suggested more bureaucracy. OPSX is about less rigidity, not more.

* docs: add architecture deep dive with ASCII diagrams

Add comprehensive comparison of standard vs OPSX workflow architecture:
- Philosophy: phases vs actions
- Component architecture diagrams
- Dependency graph model
- Information flow comparison
- Iteration model comparison
- Custom schema example

* docs: emphasize hackability and experimentation rationale

Add "Why We Built This" section explaining the meta-level motivation:
- Instructions were hardcoded, hard to improve
- Needed granular, testable artifacts
- Wanted to experiment with different workflows without code changes
- OPSX makes the instruction system itself hackable

Update README banner and experimental features section to highlight
schema-driven, hackable nature alongside fluid iteration benefits.

* docs: reframe hackability as user benefit, not just internal

OPSX isn't just for OpenSpec devs to experiment - it's for everyone:
- Teams can create workflows that match how they work
- Power users can tweak prompts to get better AI outputs
- Contributors can experiment without releases

Updated framing from "we needed" to "now anyone can".

* docs: add guidance on when to update vs. start fresh

Addresses a common question: when does "update as you learn" become
"this is different work"? Adds heuristics based on intent, scope
overlap, and completability to help users make the judgment call.
2026-01-09 20:09:53 -08:00
Tabish Bidiwale ae85a7229d fix: offer parent flags in Bash and PowerShell completions when subcommands exist (#466)
When a command has both flags and subcommands, the Bash and PowerShell
completion generators now check if the user is typing a flag (input
starts with `-`) before offering subcommand completions. This fixes the
issue where parent-level flags were never suggested.

Before: `openspec config --<TAB>` → Only showed subcommands
After: `openspec config --<TAB>` → Shows parent flags when input starts with `-`

Fixes #463
2026-01-09 19:40:13 -08:00
Tabish Bidiwale 504c93bdf1 fix: skip additional Windows-specific tests (#465)
* fix: skip additional Windows-specific tests

- fish-installer: skip uninstall permission test (chmod on directory)
- powershell-installer: skip "skip configuration when script line exists"
  test (Windows has dual profile paths so the second profile gets configured)

* refactor: use ENOTDIR approach for cross-platform install error tests

Instead of platform-specific invalid paths (Z:\ or /root), create a
temporary file and use it as homeDir. This guarantees deterministic
ENOTDIR failures when trying to create subdirectories on all platforms.
2026-01-09 16:26:26 -08:00
Tabish Bidiwale c4a54a8d54 fix: skip Windows-specific permission tests that rely on chmod() (#464)
fs.chmod() on directories doesn't restrict write access on Windows since
Windows uses ACLs that Node.js doesn't control. Additionally, admin users
and CI runners can bypass read-only attributes. Skip these tests on Windows
and use platform-specific invalid paths in cross-platform tests.

Fixes #401 (bash/pwsh completion commit breaking Windows e2e tests).
2026-01-09 16:06:15 -08:00
38d2356836 feature/bash_fish_power_shells_completions (#401)
* added CLI completions support for: bash, fish and powershell

* Add bash/fish/powershell completions

* Archive extend-shell-completions

* Archive extend-shell-completions

* Fix canWriteFile control flow and add tests

* Fix bash completion fallback and security escaping

  - Add _init_completion fallback for systems without bash-completion
  - Fix command injection escaping in Fish/PowerShell generators
  - Add Bash command name escaping for security
  - Add comprehensive security tests for all generators
  - Fix test placement issues in bash/powershell test files

* refactor: extract completion templates and standardize naming

Extract static template literals from generators into separate template files.
Standardize naming to {SHELL}_STATIC_HELPERS and {SHELL}_DYNAMIC_HELPERS.

- Create bash/fish/powershell/zsh template files
- Rename constants: BASH_HELPERS → BASH_DYNAMIC_HELPERS,
  FISH_HELPER_FUNCTIONS → FISH_STATIC_HELPERS,
  POWERSHELL_HELPERS → POWERSHELL_DYNAMIC_HELPERS
- Update generator imports
- Remove ~99 lines of boilerplate from generators

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* docs: update spec to reflect multi-shell support

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: add all shells to zsh completion suggestions

* feat: add --yes flag to completion uninstall

* fix: remove bash-completion dependency from fallback

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: use printf instead of echo for Fish tab output

Fish's echo doesn't interpret escape sequences, so \t outputs
literally instead of as a tab character. Use printf for proper
tab-separated completion output.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: make UX messages shell-aware

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: add Homebrew paths for bash-completion detection

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: support both PowerShell Core and Windows PS 5.1

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: preserve colon handling in bash completion

Add -n : option to _init_completion to prevent colons from being
treated as word separators. This is important for spec/change IDs
that may contain colons.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: update completion tests to match implementation changes

Updated bash-generator test to expect `-n :` flag in _init_completion call.
Updated powershell-installer tests to match refactored implementation that
supports both PowerShell Core and Windows PowerShell 5.1.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Tabish Bidiwale <30385142+TabishB@users.noreply.github.com>
2026-01-09 15:49:50 -08:00
Neroyangandneroyang 3f67debf65 feat: change the frontmatter of the Codebuddy Slash Commands (#462)
* feat: change the frontmatter of the Codebuddy Slash Commands

* fix: fix the issue mentioned by coderabbitai

* feat: change the init.test

* feat: change the init.test

---------

Co-authored-by: neroyang <neroyang@tencent.com>
2026-01-09 10:08:59 -08:00
github-actions[bot]andTabish Bidiwale 533cb0fa87 chore(release): version packages (#458)
* Version Packages

* chore: trigger CI

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tabish Bidiwale <tabishbidiwale@gmail.com>
2026-01-07 00:38:00 -08:00
Tabish Bidiwale 8dfd824477 Add changeset for OPSX experimental workflow commands (#457) 2026-01-07 00:35:04 -08:00
Tabish Bidiwale 3ed1270316 docs: add experimental workflow (OPSX) user guide (#456)
Adds documentation for the experimental artifact-based workflow:
- Setup instructions (Claude Code only for now)
- Command reference for all /opsx:* commands
- Usage examples and tips
- Comparison with standard workflow
- Feedback links to Discord and GitHub
2026-01-07 00:24:14 -08:00
271 changed files with 34250 additions and 10950 deletions
+1
View File
@@ -0,0 +1 @@
-P ubuntu-latest=catthehacker/ubuntu:act-latest
+93 -4
View File
@@ -1,6 +1,95 @@
This directory is managed by Changesets.
# Changesets
- Add a changeset locally with `pnpm changeset`.
- The CI "Release (prepare)" workflow opens/updates a Version Packages PR.
- Publishing happens from a GitHub Release via the "Publish to npm" workflow.
This directory is managed by [Changesets](https://github.com/changesets/changesets).
## Quick Start
```bash
pnpm changeset
```
Follow the prompts to select version bump type and describe your changes.
## Workflow
1. **Add a changeset** — Run `pnpm changeset` locally before or after your PR
2. **Version PR** — CI opens/updates a "Version Packages" PR when changesets merge to main
3. **Release** — Merging the Version PR triggers npm publish and GitHub Release
> **Note:** Contributors only need to run `pnpm changeset`. Versioning (`changeset version`) and publishing happen automatically in CI.
## Template
Use this structure for your changeset content:
```markdown
---
"@fission-ai/openspec": patch
---
### New Features
- **Feature name** — What users can now do
### Bug Fixes
- Fixed issue where X happened when Y
### Breaking Changes
- `oldMethod()` has been removed, use `newMethod()` instead
### Deprecations
- `legacyOption` is deprecated and will be removed in v2.0
### Other
- Internal refactoring of X for better performance
```
Include only the sections relevant to your change.
## Version Bump Guide
| Type | When to use | Example |
|------|-------------|---------|
| `patch` | Bug fixes, small improvements | Fixed crash when config missing |
| `minor` | New features, non-breaking additions | Added `--verbose` flag |
| `major` | Breaking changes, removed features | Renamed `init` to `setup` |
## When to Create a Changeset
**Create one for:**
- New features or commands
- Bug fixes that affect users
- Breaking changes or deprecations
- Performance improvements users would notice
**Skip for:**
- Documentation-only changes
- Test additions/fixes
- Internal refactoring with no user impact
- CI/tooling changes
## Writing Good Descriptions
**Do:** Write for users, not developers
```markdown
- **Shell completions** — Tab completion now available for Bash, Fish, and PowerShell
```
**Don't:** Write implementation details
```markdown
- Added ShellCompletionGenerator class with Bash/Fish/PowerShell subclasses
```
**Do:** Explain the impact
```markdown
- Fixed config loading to respect `XDG_CONFIG_HOME` on Linux
```
**Don't:** Just reference the fix
```markdown
- Fixed #123
```
+4 -1
View File
@@ -1,6 +1,9 @@
{
"$schema": "https://unpkg.com/@changesets/config/schema.json",
"changelog": "@changesets/cli/changelog",
"changelog": [
"@changesets/changelog-github",
{ "repo": "Fission-AI/OpenSpec" }
],
"commit": false,
"fixed": [],
"linked": [],
+20
View File
@@ -0,0 +1,20 @@
# Github Workflows
## Testing CI Locally
Test GitHub Actions workflows locally using [act](https://nektosact.com/):
```bash
# Test all PR checks
act pull_request
# Test specific job
act pull_request -j nix-flake-validate
# Dry run to see what would execute
act pull_request --dryrun
```
The `.actrc` file configures act to use the appropriate Docker image.
+101 -2
View File
@@ -15,6 +15,29 @@ concurrency:
cancel-in-progress: true
jobs:
# Detect which files changed to enable path-based filtering
changes:
name: Detect changes
runs-on: ubuntu-latest
outputs:
nix: ${{ steps.filter.outputs.nix }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check for Nix-related changes
uses: dorny/paths-filter@v3
id: filter
with:
filters: |
nix:
- 'flake.nix'
- 'flake.lock'
- 'package.json'
- 'pnpm-lock.yaml'
- 'scripts/update-flake.sh'
- '.github/workflows/ci.yml'
test_pr:
name: Test
runs-on: ubuntu-latest
@@ -156,6 +179,66 @@ jobs:
exit 1
fi
nix-flake-validate:
name: Nix Flake Validation
runs-on: ubuntu-latest
timeout-minutes: 10
needs: changes
if: needs.changes.outputs.nix == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v13
- name: Setup Nix cache
uses: DeterminateSystems/magic-nix-cache-action@v8
- name: Build with Nix
run: nix build
- name: Verify build output
run: |
if [ ! -e "result" ]; then
echo "Error: Nix build output 'result' symlink not found"
exit 1
fi
if [ ! -f "result/bin/openspec" ]; then
echo "Error: openspec binary not found in build output"
exit 1
fi
echo "✅ Build output verified"
- name: Test binary execution
run: |
VERSION=$(nix run . -- --version)
echo "OpenSpec version: $VERSION"
if [ -z "$VERSION" ]; then
echo "Error: Version command returned empty output"
exit 1
fi
echo "✅ Binary execution successful"
- name: Validate update script
run: |
echo "Testing update-flake.sh script..."
bash scripts/update-flake.sh
echo "✅ Update script executed successfully"
- name: Check flake.nix modifications
run: |
if git diff --quiet flake.nix; then
echo "⚠️ Warning: flake.nix was not modified by update script"
else
echo "✅ flake.nix was updated by script"
git diff flake.nix
fi
- name: Restore flake.nix
if: always()
run: git checkout -- flake.nix || true
validate-changesets:
name: Validate Changesets
runs-on: ubuntu-latest
@@ -191,7 +274,7 @@ jobs:
required-checks-pr:
name: All checks passed
runs-on: ubuntu-latest
needs: [test_pr, lint]
needs: [test_pr, lint, nix-flake-validate]
if: always() && github.event_name == 'pull_request'
steps:
- name: Verify all checks passed
@@ -204,12 +287,20 @@ jobs:
echo "Lint job failed"
exit 1
fi
# Nix validation may be skipped if no Nix-related files changed
if [[ "${{ needs.nix-flake-validate.result }}" != "success" && "${{ needs.nix-flake-validate.result }}" != "skipped" ]]; then
echo "Nix flake validation job failed"
exit 1
fi
if [[ "${{ needs.nix-flake-validate.result }}" == "skipped" ]]; then
echo "Nix flake validation skipped (no Nix-related changes)"
fi
echo "All required checks passed!"
required-checks-main:
name: All checks passed
runs-on: ubuntu-latest
needs: [test_matrix, lint]
needs: [test_matrix, lint, nix-flake-validate]
if: always() && github.event_name != 'pull_request'
steps:
- name: Verify all checks passed
@@ -222,4 +313,12 @@ jobs:
echo "Lint job failed"
exit 1
fi
# Nix validation may be skipped if no Nix-related files changed
if [[ "${{ needs.nix-flake-validate.result }}" != "success" && "${{ needs.nix-flake-validate.result }}" != "skipped" ]]; then
echo "Nix flake validation job failed"
exit 1
fi
if [[ "${{ needs.nix-flake-validate.result }}" == "skipped" ]]; then
echo "Nix flake validation skipped (no Nix-related changes)"
fi
echo "All required checks passed!"
+13 -1
View File
@@ -18,9 +18,20 @@ jobs:
if: github.repository == 'Fission-AI/OpenSpec'
runs-on: ubuntu-latest
steps:
# Generate GitHub App token first - used for checkout and changesets
# This allows git operations to trigger CI workflows on the version PR
# (GITHUB_TOKEN cannot trigger workflows by design)
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- uses: pnpm/action-setup@v4
with:
@@ -36,6 +47,7 @@ jobs:
# Opens/updates the Version Packages PR; publishes when the Version PR merges
- name: Create/Update Version PR
id: changesets
uses: changesets/action@v1
with:
title: 'chore(release): version packages'
@@ -44,5 +56,5 @@ jobs:
# so package.json already contains the bumped version.
publish: pnpm run release:ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
# npm authentication handled via OIDC trusted publishing (no token needed)
+1
View File
@@ -148,3 +148,4 @@ CLAUDE.md
# Pnpm
.pnpm-store/
result
-18
View File
@@ -1,18 +0,0 @@
<!-- OPENSPEC:START -->
# OpenSpec Instructions
These instructions are for AI assistants working in this project.
Always open `@/openspec/AGENTS.md` when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding
Use `@/openspec/AGENTS.md` to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines
Keep this managed block so 'openspec update' can refresh the instructions.
<!-- OPENSPEC:END -->
+227 -3
View File
@@ -1,5 +1,225 @@
# @fission-ai/openspec
## 1.0.2
### Patch Changes
- [#596](https://github.com/Fission-AI/OpenSpec/pull/596) [`e91568d`](https://github.com/Fission-AI/OpenSpec/commit/e91568deb948073f3e9d9bb2d2ab5bf8080d6cf4) Thanks [@TabishB](https://github.com/TabishB)! - ### Bug Fixes
- Clarified spec naming convention — Specs should be named after capabilities (`specs/<capability>/spec.md`), not changes
- Fixed task checkbox format guidance — Tasks now clearly require `- [ ]` checkbox format for apply phase tracking
## 1.0.1
### Patch Changes
- [#587](https://github.com/Fission-AI/OpenSpec/pull/587) [`943e0d4`](https://github.com/Fission-AI/OpenSpec/commit/943e0d41026d034de66b9442d1276c01b293eb2b) Thanks [@TabishB](https://github.com/TabishB)! - ### Bug Fixes
- Fixed incorrect archive path in onboarding documentation — the template now shows the correct path `openspec/changes/archive/YYYY-MM-DD-<name>/` instead of the incorrect `openspec/archive/YYYY-MM-DD--<name>/`
## 1.0.0
### Major Changes
- [#578](https://github.com/Fission-AI/OpenSpec/pull/578) [`0cc9d90`](https://github.com/Fission-AI/OpenSpec/commit/0cc9d9025af367faa1688a7b2606a2549053cd3f) Thanks [@TabishB](https://github.com/TabishB)! - ## OpenSpec 1.0 — The OPSX Release
The workflow has been rebuilt from the ground up. OPSX replaces the old phase-locked `/openspec:*` commands with an action-based system where AI understands what artifacts exist, what's ready to create, and what each action unlocks.
### Breaking Changes
- **Old commands removed** — `/openspec:proposal`, `/openspec:apply`, and `/openspec:archive` no longer exist
- **Config files removed** — Tool-specific instruction files (`CLAUDE.md`, `.cursorrules`, `AGENTS.md`, `project.md`) are no longer generated
- **Migration** — Run `openspec init` to upgrade. Legacy artifacts are detected and cleaned up with confirmation.
### From Static Prompts to Dynamic Instructions
**Before:** AI received the same static instructions every time, regardless of project state.
**Now:** Instructions are dynamically assembled from three layers:
1. **Context** — Project background from `config.yaml` (tech stack, conventions)
2. **Rules** — Artifact-specific constraints (e.g., "propose spike tasks for unknowns")
3. **Template** — The actual structure for the output file
AI queries the CLI for real-time state: which artifacts exist, what's ready to create, what dependencies are satisfied, and what each action unlocks.
### From Phase-Locked to Action-Based
**Before:** Linear workflow — proposal → apply → archive. Couldn't easily go back or iterate.
**Now:** Flexible actions on a change. Edit any artifact anytime. The artifact graph tracks state automatically.
| Command | What it does |
| -------------------- | ---------------------------------------------------- |
| `/opsx:explore` | Think through ideas before committing to a change |
| `/opsx:new` | Start a new change |
| `/opsx:continue` | Create one artifact at a time (step-through) |
| `/opsx:ff` | Create all planning artifacts at once (fast-forward) |
| `/opsx:apply` | Implement tasks |
| `/opsx:verify` | Validate implementation matches artifacts |
| `/opsx:sync` | Sync delta specs to main specs |
| `/opsx:archive` | Archive completed change |
| `/opsx:bulk-archive` | Archive multiple changes with conflict detection |
| `/opsx:onboard` | Guided 15-minute walkthrough of complete workflow |
### From Text Merging to Semantic Spec Syncing
**Before:** Spec updates required manual merging or wholesale file replacement.
**Now:** Delta specs use semantic markers that AI understands:
- `## ADDED Requirements` — New requirements to add
- `## MODIFIED Requirements` — Partial updates (add scenario without copying existing ones)
- `## REMOVED Requirements` — Delete with reason and migration notes
- `## RENAMED Requirements` — Rename preserving content
Archive parses these at the requirement level, not brittle header matching.
### From Scattered Files to Agent Skills
**Before:** 8+ config files at project root + slash commands scattered across 21 tool-specific locations with different formats.
**Now:** Single `.claude/skills/` directory with YAML-fronted markdown files. Auto-detected by Claude Code, Cursor, Windsurf. Cross-editor compatible.
### New Features
- **Onboarding skill** — `/opsx:onboard` walks new users through their first complete change with codebase-aware task suggestions and step-by-step narration (11 phases, ~15 minutes)
- **21 AI tools supported** — Claude Code, Cursor, Windsurf, Continue, Gemini CLI, GitHub Copilot, Amazon Q, Cline, RooCode, Kilo Code, Auggie, CodeBuddy, Qoder, Qwen, CoStrict, Crush, Factory, OpenCode, Antigravity, iFlow, and Codex
- **Interactive setup** — `openspec init` shows animated welcome screen and searchable multi-select for choosing tools. Pre-selects already-configured tools for easy refresh.
- **Customizable schemas** — Define custom artifact workflows in `openspec/schemas/` without touching package code. Teams can share workflows via version control.
### Bug Fixes
- Fixed Claude Code YAML parsing failure when command names contained colons
- Fixed task file parsing to handle trailing whitespace on checkbox lines
- Fixed JSON instruction output to separate context/rules from template — AI was copying constraint blocks into artifact files
### Documentation
- New getting-started guide, CLI reference, concepts documentation
- Removed misleading "edit mid-flight and continue" claims that weren't implemented
- Added migration guide for upgrading from pre-OPSX versions
## 0.23.0
### Minor Changes
- [#540](https://github.com/Fission-AI/OpenSpec/pull/540) [`c4cfdc7`](https://github.com/Fission-AI/OpenSpec/commit/c4cfdc7c499daef30d8a218f5f59b8d9e5adb754) Thanks [@TabishB](https://github.com/TabishB)! - ### New Features
- **Bulk archive skill** — Archive multiple completed changes in a single operation with `/opsx:bulk-archive`. Includes batch validation, spec conflict detection, and consolidated confirmation
### Other
- **Simplified setup** — Config creation now uses sensible defaults with helpful comments instead of interactive prompts
## 0.22.0
### Minor Changes
- [#530](https://github.com/Fission-AI/OpenSpec/pull/530) [`33466b1`](https://github.com/Fission-AI/OpenSpec/commit/33466b1e2a6798bdd6d0e19149173585b0612e6f) Thanks [@TabishB](https://github.com/TabishB)! - Add project-level configuration, project-local schemas, and schema management commands
**New Features**
- **Project-level configuration** — Configure OpenSpec behavior per-project via `openspec/config.yaml`, including custom rules injection, context files, and schema resolution settings
- **Project-local schemas** — Define custom artifact schemas within your project's `openspec/schemas/` directory for project-specific workflows
- **Schema management commands** — New `openspec schema` commands (`list`, `show`, `export`, `validate`) for inspecting and managing artifact schemas (experimental)
**Bug Fixes**
- Fixed config loading to handle null `rules` field in project configuration
## 0.21.0
### Minor Changes
- [#516](https://github.com/Fission-AI/OpenSpec/pull/516) [`b5a8847`](https://github.com/Fission-AI/OpenSpec/commit/b5a884748be6156a7bb140b4941cfec4f20a9fc8) Thanks [@TabishB](https://github.com/TabishB)! - Add feedback command and Nix flake support
**New Features**
- **Feedback command** — Submit feedback directly from the CLI with `openspec feedback`, which creates GitHub Issues with automatic metadata inclusion and graceful fallback for manual submission
- **Nix flake support** — Install and develop openspec using Nix with the new `flake.nix`, including automated flake maintenance and CI validation
**Bug Fixes**
- **Explore mode guardrails** — Explore mode now explicitly prevents implementation, keeping the focus on thinking and discovery while still allowing artifact creation
**Other**
- Improved change inference in `opsx apply` — automatically detects the target change from conversation context or prompts when ambiguous
- Streamlined archive sync assessment with clearer delta spec location guidance
## 0.20.0
### Minor Changes
- [#502](https://github.com/Fission-AI/OpenSpec/pull/502) [`9db74aa`](https://github.com/Fission-AI/OpenSpec/commit/9db74aa5ac6547efadaed795217cfa17444f2004) Thanks [@TabishB](https://github.com/TabishB)! - Add `/opsx:verify` command and fix vitest process storms
**New Features**
- **`/opsx:verify` command** — Validate that change implementations match their specifications
**Bug Fixes**
- Fixed vitest process storms by capping worker parallelism
- Fixed agent workflows to use non-interactive mode for validation commands
- Fixed PowerShell completions generator to remove trailing commas
## 0.19.0
### Minor Changes
- eb152eb: Add Continue IDE support, shell completions, and `/opsx:explore` command
**New Features**
- **Continue IDE support** – OpenSpec now generates slash commands for [Continue](https://continue.dev/), expanding editor integration options alongside Cursor, Windsurf, Claude Code, and others
- **Shell completions for Bash, Fish, and PowerShell** – Run `openspec completion install` to set up tab completion in your preferred shell
- **`/opsx:explore` command** – A new thinking partner mode for exploring ideas and investigating problems before committing to changes
- **Codebuddy slash command improvements** – Updated frontmatter format for better compatibility
**Bug Fixes**
- Shell completions now correctly offer parent-level flags (like `--help`) when a command has subcommands
- Fixed Windows compatibility issues in tests
**Other**
- Added optional anonymous usage statistics to help understand how OpenSpec is used. This is **opt-out** by default – set `OPENSPEC_TELEMETRY=0` or `DO_NOT_TRACK=1` to disable. Only command names and version are collected; no arguments, file paths, or content. Automatically disabled in CI environments.
## 0.18.0
### Minor Changes
- 8dfd824: Add OPSX experimental workflow commands and enhanced artifact system
**New Commands:**
- `/opsx:ff` - Fast-forward through artifact creation, generating all needed artifacts in one go
- `/opsx:sync` - Sync delta specs from a change to main specs
- `/opsx:archive` - Archive completed changes with smart sync check
**Artifact Workflow Enhancements:**
- Schema-aware apply instructions with inline guidance and XML output
- Agent schema selection for experimental artifact workflow
- Per-change schema metadata via `.openspec.yaml` files
- Agent Skills for experimental artifact workflow
- Instruction loader for template loading and change context
- Restructured schemas as directories with templates
**Improvements:**
- Enhanced list command with last modified timestamps and sorting
- Change creation utilities for better workflow support
**Fixes:**
- Normalize paths for cross-platform glob compatibility
- Allow REMOVED requirements when creating new spec files
## 0.17.2
### Patch Changes
@@ -20,13 +240,15 @@
### Minor Changes
- 2e71835: ### New Features
- 2e71835: Add `openspec config` command and Oh-my-zsh completions
**New Features**
- Add `openspec config` command for managing global configuration settings
- Implement global config directory with XDG Base Directory specification support
- Add Oh-my-zsh shell completions support for enhanced CLI experience
### Bug Fixes
**Bug Fixes**
- Fix hang in pre-commit hooks by using dynamic imports
- Respect XDG_CONFIG_HOME environment variable on all platforms
@@ -34,7 +256,7 @@
- Align cli-completion spec with implementation
- Remove hardcoded agent field from slash commands
### Documentation
**Documentation**
- Alphabetize AI tools list in README and make it collapsible
@@ -55,6 +277,8 @@
### Minor Changes
- Add Continue slash command support so `openspec init` can generate `.continue/prompts/openspec-*.prompt` files with MARKDOWN frontmatter and `$ARGUMENTS` placeholder, and refresh them on `openspec update`.
- Add Antigravity slash command support so `openspec init` can generate `.agent/workflows/openspec-*.md` files with description-only frontmatter and `openspec update` refreshes existing workflows alongside Windsurf.
## 0.15.0
+17
View File
@@ -0,0 +1,17 @@
# Maintainers
People who maintain and guide OpenSpec.
## Core Maintainers
| Name | GitHub | Role |
|------|--------|------|
| Tabish Bidiwale | [@TabishB](https://github.com/TabishB) | Lead maintainer |
## Advisors
Advisors help shape technical direction and provide guidance to the project.
| Name | GitHub | Focus |
|------|--------|-------|
| Hari Krishnan | [@harikrishnan83](https://github.com/harikrishnan83) | Technical direction |
+138 -315
View File
@@ -1,381 +1,204 @@
<p align="center">
<a href="https://github.com/Fission-AI/OpenSpec">
<picture>
<source srcset="assets/openspec_pixel_dark.svg" media="(prefers-color-scheme: dark)">
<source srcset="assets/openspec_pixel_light.svg" media="(prefers-color-scheme: light)">
<img src="assets/openspec_pixel_light.svg" alt="OpenSpec logo" height="64">
<source srcset="assets/openspec_bg.png">
<img src="assets/openspec_bg.png" alt="OpenSpec logo">
</picture>
</a>
</p>
<p align="center">Spec-driven development for AI coding assistants.</p>
<p align="center">
<a href="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml/badge.svg" /></a>
<a href="https://www.npmjs.com/package/@fission-ai/openspec"><img alt="npm version" src="https://img.shields.io/npm/v/@fission-ai/openspec?style=flat-square" /></a>
<a href="https://nodejs.org/"><img alt="node version" src="https://img.shields.io/node/v/@fission-ai/openspec?style=flat-square" /></a>
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
<a href="https://conventionalcommits.org"><img alt="Conventional Commits" src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=flat-square" /></a>
<a href="https://discord.gg/YctCnvvshC"><img alt="Discord" src="https://img.shields.io/badge/Discord-Join%20the%20community-5865F2?logo=discord&logoColor=white&style=flat-square" /></a>
</p>
<p align="center">
<img src="assets/openspec_dashboard.png" alt="OpenSpec dashboard preview" width="90%">
<a href="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml/badge.svg" /></a>
<a href="https://www.npmjs.com/package/@fission-ai/openspec"><img alt="npm version" src="https://img.shields.io/npm/v/@fission-ai/openspec?style=flat-square" /></a>
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
<a href="https://discord.gg/YctCnvvshC"><img alt="Discord" src="https://img.shields.io/discord/1411657095639601154?style=flat-square&logo=discord&logoColor=white&label=Discord&suffix=%20online" /></a>
</p>
<details>
<summary><strong>The most loved spec framework.</strong></summary>
[![Stars](https://img.shields.io/github/stars/Fission-AI/OpenSpec?style=flat-square&label=Stars)](https://github.com/Fission-AI/OpenSpec/stargazers)
[![Downloads](https://img.shields.io/npm/dm/@fission-ai/openspec?style=flat-square&label=Downloads/mo)](https://www.npmjs.com/package/@fission-ai/openspec)
[![Contributors](https://img.shields.io/github/contributors/Fission-AI/OpenSpec?style=flat-square&label=Contributors)](https://github.com/Fission-AI/OpenSpec/graphs/contributors)
</details>
<p></p>
Our philosophy:
```text
→ fluid not rigid
→ iterative not waterfall
→ easy not complex
→ built for brownfield not just greenfield
→ scalable from personal projects to enterprises
```
> [!TIP]
> **New workflow now available!** We've rebuilt OpenSpec with a new artifact-guided workflow.
>
> Run `/opsx:onboard` to get started. → [Learn more here](docs/opsx.md)
<p align="center">
Follow <a href="https://x.com/0xTab">@0xTab on X</a> for updates · Join the <a href="https://discord.gg/YctCnvvshC">OpenSpec Discord</a> for help and questions.
</p>
# OpenSpec
### Teams
OpenSpec aligns humans and AI coding assistants with spec-driven development so you agree on what to build before any code is written. **No API keys required.**
Using OpenSpec in a team? [Email here](mailto:teams@openspec.dev) for access to our Slack channel.
## Why OpenSpec?
<!-- TODO: Add GIF demo of /opsx:new → /opsx:archive workflow -->
AI coding assistants are powerful but unpredictable when requirements live in chat history. OpenSpec adds a lightweight specification workflow that locks intent before implementation, giving you deterministic, reviewable outputs.
## See it in action
Key outcomes:
- Human and AI stakeholders agree on specs before work begins.
- Structured change folders (proposals, tasks, and spec updates) keep scope explicit and auditable.
- Shared visibility into what's proposed, active, or archived.
- Works with the AI tools you already use: custom slash commands where supported, context rules everywhere else.
```text
You: /opsx:new add-dark-mode
AI: Created openspec/changes/add-dark-mode/
Ready to create: proposal
## How OpenSpec compares (at a glance)
You: /opsx:ff # "fast-forward" - generate all planning docs
AI: ✓ proposal.md — why we're doing this, what's changing
✓ specs/ — requirements and scenarios
✓ design.md — technical approach
✓ tasks.md — implementation checklist
Ready for implementation!
- **Lightweight**: simple workflow, no API keys, minimal setup.
- **Brownfield-first**: works great beyond 0→1. OpenSpec separates the source of truth from proposals: `openspec/specs/` (current truth) and `openspec/changes/` (proposed updates). This keeps diffs explicit and manageable across features.
- **Change tracking**: proposals, tasks, and spec deltas live together; archiving merges the approved updates back into specs.
- **Compared to spec-kit & Kiro**: those shine for brand-new features (0→1). OpenSpec also excels when modifying existing behavior (1→n), especially when updates span multiple specs.
See the full comparison in [How OpenSpec Compares](#how-openspec-compares).
## How It Works
You: /opsx:apply
AI: Implementing tasks...
✓ 1.1 Add theme context provider
✓ 1.2 Create toggle component
✓ 2.1 Add CSS variables
✓ 2.2 Wire up localStorage
All tasks complete!
You: /opsx:archive
AI: Archived to openspec/changes/archive/2025-01-23-add-dark-mode/
Specs updated. Ready for the next feature.
```
┌────────────────────┐
│ Draft Change │
│ Proposal │
└────────┬───────────┘
│ share intent with your AI
▼
┌────────────────────┐
│ Review & Align │
│ (edit specs/tasks) │◀──── feedback loop ──────┐
└────────┬───────────┘ │
│ approved plan │
▼ │
┌────────────────────┐ │
│ Implement Tasks │──────────────────────────┘
│ (AI writes code) │
└────────┬───────────┘
│ ship the change
▼
┌────────────────────┐
│ Archive & Update │
│ Specs (source) │
└────────────────────┘
1. Draft a change proposal that captures the spec updates you want.
2. Review the proposal with your AI assistant until everyone agrees.
3. Implement tasks that reference the agreed specs.
4. Archive the change to merge the approved updates back into the source-of-truth specs.
```
## Getting Started
### Supported AI Tools
<details>
<summary><strong>Native Slash Commands</strong> (click to expand)</summary>
<summary><strong>OpenSpec Dashboard</strong></summary>
These tools have built-in OpenSpec commands. Select the OpenSpec integration when prompted.
| Tool | Commands |
|------|----------|
| **Amazon Q Developer** | `@openspec-proposal`, `@openspec-apply`, `@openspec-archive` (`.amazonq/prompts/`) |
| **Antigravity** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.agent/workflows/`) |
| **Auggie (Augment CLI)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.augment/commands/`) |
| **Claude Code** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` |
| **Cline** | Workflows in `.clinerules/workflows/` directory (`.clinerules/workflows/openspec-*.md`) |
| **CodeBuddy Code (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.codebuddy/commands/`) — see [docs](https://www.codebuddy.ai/cli) |
| **Codex** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (global: `~/.codex/prompts`, auto-installed) |
| **CoStrict** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.cospec/openspec/commands/`) — see [docs](https://costrict.ai)|
| **Crush** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.crush/commands/openspec/`) |
| **Cursor** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
| **Factory Droid** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.factory/commands/`) |
| **Gemini CLI** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.gemini/commands/openspec/`) |
| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.github/prompts/`) |
| **iFlow (iflow-cli)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.iflow/commands/`) |
| **Kilo Code** | `/openspec-proposal.md`, `/openspec-apply.md`, `/openspec-archive.md` (`.kilocode/workflows/`) |
| **OpenCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
| **Qoder (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.qoder/commands/openspec/`) — see [docs](https://qoder.com/cli) |
| **Qwen Code** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.qwen/commands/`) |
| **RooCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.roo/commands/`) |
| **Windsurf** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.windsurf/workflows/`) |
Kilo Code discovers team workflows automatically. Save the generated files under `.kilocode/workflows/` and trigger them from the command palette with `/openspec-proposal.md`, `/openspec-apply.md`, or `/openspec-archive.md`.
<p align="center">
<img src="assets/openspec_dashboard.png" alt="OpenSpec dashboard preview" width="90%">
</p>
</details>
<details>
<summary><strong>AGENTS.md Compatible</strong> (click to expand)</summary>
## Quick Start
These tools automatically read workflow instructions from `openspec/AGENTS.md`. Ask them to follow the OpenSpec workflow if they need a reminder. Learn more about the [AGENTS.md convention](https://agents.md/).
**Requires Node.js 20.19.0 or higher.**
| Tools |
|-------|
| Amp • Jules • Others |
</details>
### Install & Initialize
#### Prerequisites
- **Node.js >= 20.19.0** - Check your version with `node --version`
#### Step 1: Install the CLI globally
Install OpenSpec globally:
```bash
npm install -g @fission-ai/openspec@latest
```
Verify installation:
```bash
openspec --version
```
Then navigate to your project directory and initialize:
#### Step 2: Initialize OpenSpec in your project
Navigate to your project directory:
```bash
cd my-project
```
Run the initialization:
```bash
cd your-project
openspec init
```
**What happens during initialization:**
- You'll be prompted to pick any natively supported AI tools (Claude Code, CodeBuddy, Cursor, OpenCode, Qoder,etc.); other assistants always rely on the shared `AGENTS.md` stub
- OpenSpec automatically configures slash commands for the tools you choose and always writes a managed `AGENTS.md` hand-off at the project root
- A new `openspec/` directory structure is created in your project
Now tell your AI: `/opsx:new <what-you-want-to-build>`
**After setup:**
- Primary AI tools can trigger `/openspec` workflows without additional configuration
- Run `openspec list` to verify the setup and view any active changes
- If your coding assistant doesn't surface the new slash commands right away, restart it. Slash commands are loaded at startup,
so a fresh launch ensures they appear
> [!NOTE]
> Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 20+ tools and growing.
>
> Also works with pnpm, yarn, bun, and nix. [See installation options](docs/installation.md).
### Optional: Populate Project Context
## Docs
After `openspec init` completes, you'll receive a suggested prompt to help populate your project context:
→ **[Getting Started](docs/getting-started.md)**: first steps<br>
→ **[Workflows](docs/workflows.md)**: combos and patterns<br>
→ **[Commands](docs/commands.md)**: slash commands & skills<br>
→ **[CLI](docs/cli.md)**: terminal reference<br>
→ **[Supported Tools](docs/supported-tools.md)**: tool integrations & install paths<br>
→ **[Concepts](docs/concepts.md)**: how it all fits<br>
→ **[Multi-Language](docs/multi-language.md)**: multi-language support<br>
→ **[Customization](docs/customization.md)**: make it yours
```text
Populate your project context:
"Please read openspec/project.md and help me fill it out with details about my project, tech stack, and conventions"
```
Use `openspec/project.md` to define project-level conventions, standards, architectural patterns, and other guidelines that should be followed across all changes.
## Why OpenSpec?
### Create Your First Change
AI coding assistants are powerful but unpredictable when requirements live only in chat history. OpenSpec adds a lightweight spec layer so you agree on what to build before any code is written.
Here's a real example showing the complete OpenSpec workflow. This works with any AI tool. Those with native slash commands will recognize the shortcuts automatically.
- **Agree before you build** — human and AI align on specs before code gets written
- **Stay organized** — each change gets its own folder with proposal, specs, design, and tasks
- **Work fluidly** — update any artifact anytime, no rigid phase gates
- **Use your tools** — works with 20+ AI assistants via slash commands
#### 1. Draft the Proposal
Start by asking your AI to create a change proposal:
### How we compare
```text
You: Create an OpenSpec change proposal for adding profile search filters by role and team
(Shortcut for tools with slash commands: /openspec:proposal Add profile search filters)
**vs. [Spec Kit](https://github.com/github/spec-kit)** (GitHub) — Thorough but heavyweight. Rigid phase gates, lots of Markdown, Python setup. OpenSpec is lighter and lets you iterate freely.
AI: I'll create an OpenSpec change proposal for profile filters.
*Scaffolds openspec/changes/add-profile-filters/ with proposal.md, tasks.md, spec deltas.*
```
**vs. [Kiro](https://kiro.dev)** (AWS) — Powerful but you're locked into their IDE and limited to Claude models. OpenSpec works with the tools you already use.
#### 2. Verify & Review
Check that the change was created correctly and review the proposal:
```bash
$ openspec list # Confirm the change folder exists
$ openspec validate add-profile-filters # Validate spec formatting
$ openspec show add-profile-filters # Review proposal, tasks, and spec delta
```
#### 3. Refine the Specs
Iterate on the specifications until they match your needs:
```text
You: Can you add acceptance criteria for the role and team filters?
AI: I'll update the spec delta with scenarios for role and team filters.
*Edits openspec/changes/add-profile-filters/specs/profile/spec.md and tasks.md.*
```
#### 4. Implement the Change
Once specs look good, start implementation:
```text
You: The specs look good. Let's implement this change.
(Shortcut for tools with slash commands: /openspec:apply add-profile-filters)
AI: I'll work through the tasks in the add-profile-filters change.
*Implements tasks from openspec/changes/add-profile-filters/tasks.md*
*Marks tasks complete: Task 1.1 ✓, Task 1.2 ✓, Task 2.1 ✓...*
```
#### 5. Archive the Completed Change
After implementation is complete, archive the change:
```text
AI: All tasks are complete. The implementation is ready.
You: Please archive the change
(Shortcut for tools with slash commands: /openspec:archive add-profile-filters)
AI: I'll archive the add-profile-filters change.
*Runs: openspec archive add-profile-filters --yes*
✓ Change archived successfully. Specs updated. Ready for the next feature!
```
Or run the command yourself in terminal:
```bash
$ openspec archive add-profile-filters --yes # Archive the completed change without prompts
```
**Note:** Tools with native slash commands (Claude Code, CodeBuddy, Cursor, Codex, Qoder, RooCode) can use the shortcuts shown. All other tools work with natural language requests to "create an OpenSpec proposal", "apply the OpenSpec change", or "archive the change".
## Command Reference
```bash
openspec list # View active change folders
openspec view # Interactive dashboard of specs and changes
openspec show <change> # Display change details (proposal, tasks, spec updates)
openspec validate <change> # Check spec formatting and structure
openspec archive <change> [--yes|-y] # Move a completed change into archive/ (non-interactive with --yes)
```
## Example: How AI Creates OpenSpec Files
When you ask your AI assistant to "add two-factor authentication", it creates:
```
openspec/
├── specs/
│ └── auth/
│ └── spec.md # Current auth spec (if exists)
└── changes/
└── add-2fa/ # AI creates this entire structure
├── proposal.md # Why and what changes
├── tasks.md # Implementation checklist
├── design.md # Technical decisions (optional)
└── specs/
└── auth/
└── spec.md # Delta showing additions
```
### AI-Generated Spec (created in `openspec/specs/auth/spec.md`):
```markdown
# Auth Specification
## Purpose
Authentication and session management.
## Requirements
### Requirement: User Authentication
The system SHALL issue a JWT on successful login.
#### Scenario: Valid credentials
- WHEN a user submits valid credentials
- THEN a JWT is returned
```
### AI-Generated Change Delta (created in `openspec/changes/add-2fa/specs/auth/spec.md`):
```markdown
# Delta for Auth
## ADDED Requirements
### Requirement: Two-Factor Authentication
The system MUST require a second factor during login.
#### Scenario: OTP required
- WHEN a user submits valid credentials
- THEN an OTP challenge is required
```
### AI-Generated Tasks (created in `openspec/changes/add-2fa/tasks.md`):
```markdown
## 1. Database Setup
- [ ] 1.1 Add OTP secret column to users table
- [ ] 1.2 Create OTP verification logs table
## 2. Backend Implementation
- [ ] 2.1 Add OTP generation endpoint
- [ ] 2.2 Modify login flow to require OTP
- [ ] 2.3 Add OTP verification endpoint
## 3. Frontend Updates
- [ ] 3.1 Create OTP input component
- [ ] 3.2 Update login flow UI
```
**Important:** You don't create these files manually. Your AI assistant generates them based on your requirements and the existing codebase.
## Understanding OpenSpec Files
### Delta Format
Deltas are "patches" that show how specs change:
- **`## ADDED Requirements`** - New capabilities
- **`## MODIFIED Requirements`** - Changed behavior (include complete updated text)
- **`## REMOVED Requirements`** - Deprecated features
**Format requirements:**
- Use `### Requirement: <name>` for headers
- Every requirement needs at least one `#### Scenario:` block
- Use SHALL/MUST in requirement text
## How OpenSpec Compares
### vs. spec-kit
OpenSpec’s two-folder model (`openspec/specs/` for the current truth, `openspec/changes/` for proposed updates) keeps state and diffs separate. This scales when you modify existing features or touch multiple specs. spec-kit is strong for greenfield/0→1 but provides less structure for cross-spec updates and evolving features.
### vs. Kiro.dev
OpenSpec groups every change for a feature in one folder (`openspec/changes/feature-name/`), making it easy to track related specs, tasks, and designs together. Kiro spreads updates across multiple spec folders, which can make feature tracking harder.
### vs. No Specs
Without specs, AI coding assistants generate code from vague prompts, often missing requirements or adding unwanted features. OpenSpec brings predictability by agreeing on the desired behavior before any code is written.
## Team Adoption
1. **Initialize OpenSpec** – Run `openspec init` in your repo.
2. **Start with new features** – Ask your AI to capture upcoming work as change proposals.
3. **Grow incrementally** – Each change archives into living specs that document your system.
4. **Stay flexible** – Different teammates can use Claude Code, CodeBuddy, Cursor, or any AGENTS.md-compatible tool while sharing the same specs.
Run `openspec update` whenever someone switches tools so your agents pick up the latest instructions and slash-command bindings.
**vs. nothing** — AI coding without specs means vague prompts and unpredictable results. OpenSpec brings predictability without the ceremony.
## Updating OpenSpec
1. **Upgrade the package**
```bash
npm install -g @fission-ai/openspec@latest
```
2. **Refresh agent instructions**
- Run `openspec update` inside each project to regenerate AI guidance and ensure the latest slash commands are active.
**Upgrade the package**
```bash
npm install -g @fission-ai/openspec@latest
```
**Refresh agent instructions**
Run this inside each project to regenerate AI guidance and ensure the latest slash commands are active:
```bash
openspec update
```
## Usage Notes
**Model selection**: OpenSpec works best with high-reasoning models. We recommend Opus 4.5 and GPT 5.2 for both planning and implementation.
**Context hygiene**: OpenSpec benefits from a clean context window. Clear your context before starting implementation and maintain good context hygiene throughout your session.
## Contributing
**Small fixes** — Bug fixes, typo corrections, and minor improvements can be submitted directly as PRs.
**Larger changes** — For new features, significant refactors, or architectural changes, please submit an OpenSpec change proposal first so we can align on intent and goals before implementation begins.
When writing proposals, keep the OpenSpec philosophy in mind: we serve a wide variety of users across different coding agents, models, and use cases. Changes should work well for everyone.
**AI-generated code is welcome** — as long as it's been tested and verified. PRs containing AI-generated code should mention the coding agent and model used (e.g., "Generated with Claude Code using claude-opus-4-5-20251101").
### Development
- Install dependencies: `pnpm install`
- Build: `pnpm run build`
- Test: `pnpm test`
- Develop CLI locally: `pnpm run dev` or `pnpm run dev:cli`
- Conventional commits (one-line): `type(scope): subject`
## Other
<details>
<summary><strong>Telemetry</strong></summary>
OpenSpec collects anonymous usage stats.
We collect only command names and version to understand usage patterns. No arguments, paths, content, or PII. Automatically disabled in CI.
**Opt-out:** `export OPENSPEC_TELEMETRY=0` or `export DO_NOT_TRACK=1`
</details>
<details>
<summary><strong>Maintainers & Advisors</strong></summary>
See [MAINTAINERS.md](MAINTAINERS.md) for the list of core maintainers and advisors who help guide the project.
</details>
## License
MIT
+475
View File
@@ -0,0 +1,475 @@
<p align="center">
<a href="https://github.com/Fission-AI/OpenSpec">
<picture>
<source srcset="assets/openspec_pixel_dark.svg" media="(prefers-color-scheme: dark)">
<source srcset="assets/openspec_pixel_light.svg" media="(prefers-color-scheme: light)">
<img src="assets/openspec_pixel_light.svg" alt="OpenSpec logo" height="64">
</picture>
</a>
</p>
<p align="center">Spec-driven development for AI coding assistants.</p>
<p align="center">
<a href="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml/badge.svg" /></a>
<a href="https://www.npmjs.com/package/@fission-ai/openspec"><img alt="npm version" src="https://img.shields.io/npm/v/@fission-ai/openspec?style=flat-square" /></a>
<a href="https://nodejs.org/"><img alt="node version" src="https://img.shields.io/node/v/@fission-ai/openspec?style=flat-square" /></a>
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
<a href="https://conventionalcommits.org"><img alt="Conventional Commits" src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=flat-square" /></a>
<a href="https://discord.gg/YctCnvvshC"><img alt="Discord" src="https://img.shields.io/badge/Discord-Join%20the%20community-5865F2?logo=discord&logoColor=white&style=flat-square" /></a>
</p>
<p align="center">
<img src="assets/openspec_dashboard.png" alt="OpenSpec dashboard preview" width="90%">
</p>
<p align="center">
Follow <a href="https://x.com/0xTab">@0xTab on X</a> for updates · Join the <a href="https://discord.gg/YctCnvvshC">OpenSpec Discord</a> for help and questions.
</p>
<p align="center">
<sub>🧪 <strong>New:</strong> <a href="docs/opsx.md">OPSX Workflow</a> — schema-driven, hackable, fluid. Iterate on workflows without code changes.</sub>
</p>
# OpenSpec
OpenSpec aligns humans and AI coding assistants with spec-driven development so you agree on what to build before any code is written. **No API keys required.**
## Why OpenSpec?
AI coding assistants are powerful but unpredictable when requirements live in chat history. OpenSpec adds a lightweight specification workflow that locks intent before implementation, giving you deterministic, reviewable outputs.
Key outcomes:
- Human and AI stakeholders agree on specs before work begins.
- Structured change folders (proposals, tasks, and spec updates) keep scope explicit and auditable.
- Shared visibility into what's proposed, active, or archived.
- Works with the AI tools you already use: custom slash commands where supported, context rules everywhere else.
## How OpenSpec compares (at a glance)
- **Lightweight**: simple workflow, no API keys, minimal setup.
- **Brownfield-first**: works great beyond 0→1. OpenSpec separates the source of truth from proposals: `openspec/specs/` (current truth) and `openspec/changes/` (proposed updates). This keeps diffs explicit and manageable across features.
- **Change tracking**: proposals, tasks, and spec deltas live together; archiving merges the approved updates back into specs.
- **Compared to spec-kit & Kiro**: those shine for brand-new features (0→1). OpenSpec also excels when modifying existing behavior (1→n), especially when updates span multiple specs.
See the full comparison in [How OpenSpec Compares](#how-openspec-compares).
## How It Works
```
┌────────────────────┐
│ Draft Change │
│ Proposal │
└────────┬───────────┘
│ share intent with your AI
▼
┌────────────────────┐
│ Review & Align │
│ (edit specs/tasks) │◀──── feedback loop ──────┐
└────────┬───────────┘ │
│ approved plan │
▼ │
┌────────────────────┐ │
│ Implement Tasks │──────────────────────────┘
│ (AI writes code) │
└────────┬───────────┘
│ ship the change
▼
┌────────────────────┐
│ Archive & Update │
│ Specs (source) │
└────────────────────┘
1. Draft a change proposal that captures the spec updates you want.
2. Review the proposal with your AI assistant until everyone agrees.
3. Implement tasks that reference the agreed specs.
4. Archive the change to merge the approved updates back into the source-of-truth specs.
```
## Getting Started
### Supported AI Tools
<details>
<summary><strong>Native Slash Commands</strong> (click to expand)</summary>
These tools have built-in OpenSpec commands. Select the OpenSpec integration when prompted.
| Tool | Commands |
|------|----------|
| **Amazon Q Developer** | `@openspec-proposal`, `@openspec-apply`, `@openspec-archive` (`.amazonq/prompts/`) |
| **Antigravity** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.agent/workflows/`) |
| **Auggie (Augment CLI)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.augment/commands/`) |
| **Claude Code** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` |
| **Cline** | Workflows in `.clinerules/workflows/` directory (`.clinerules/workflows/openspec-*.md`) |
| **CodeBuddy Code (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.codebuddy/commands/`) — see [docs](https://www.codebuddy.ai/cli) |
| **Codex** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (global: `~/.codex/prompts`, auto-installed) |
| **Continue** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.continue/prompts/`) |
| **CoStrict** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.cospec/openspec/commands/`) — see [docs](https://costrict.ai)|
| **Crush** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.crush/commands/openspec/`) |
| **Cursor** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
| **Factory Droid** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.factory/commands/`) |
| **Gemini CLI** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.gemini/commands/openspec/`) |
| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.github/prompts/`) |
| **iFlow (iflow-cli)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.iflow/commands/`) |
| **Kilo Code** | `/openspec-proposal.md`, `/openspec-apply.md`, `/openspec-archive.md` (`.kilocode/workflows/`) |
| **OpenCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
| **Qoder** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.qoder/commands/openspec/`) — see [docs](https://qoder.com) |
| **Qwen Code** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.qwen/commands/`) |
| **RooCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.roo/commands/`) |
| **Windsurf** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.windsurf/workflows/`) |
Kilo Code discovers team workflows automatically. Save the generated files under `.kilocode/workflows/` and trigger them from the command palette with `/openspec-proposal.md`, `/openspec-apply.md`, or `/openspec-archive.md`.
</details>
<details>
<summary><strong>AGENTS.md Compatible</strong> (click to expand)</summary>
These tools automatically read workflow instructions from `openspec/AGENTS.md`. Ask them to follow the OpenSpec workflow if they need a reminder. Learn more about the [AGENTS.md convention](https://agents.md/).
| Tools |
|-------|
| Amp • Jules • Others |
</details>
### Install & Initialize
#### Prerequisites
- **Node.js >= 20.19.0** - Check your version with `node --version`
#### Step 1: Install the CLI globally
**Option A: Using npm**
```bash
npm install -g @fission-ai/openspec@latest
```
Verify installation:
```bash
openspec --version
```
**Option B: Using Nix (NixOS and Nix package manager)**
Run OpenSpec directly without installation:
```bash
nix run github:Fission-AI/OpenSpec -- init
```
Or install to your profile:
```bash
nix profile install github:Fission-AI/OpenSpec
```
Or add to your development environment in `flake.nix`:
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
openspec.url = "github:Fission-AI/OpenSpec";
};
outputs = { nixpkgs, openspec, ... }: {
devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
buildInputs = [ openspec.packages.x86_64-linux.default ];
};
};
}
```
Verify installation:
```bash
openspec --version
```
#### Step 2: Initialize OpenSpec in your project
Navigate to your project directory:
```bash
cd my-project
```
Run the initialization:
```bash
openspec init
```
**What happens during initialization:**
- You'll be prompted to pick any natively supported AI tools (Claude Code, CodeBuddy, Cursor, OpenCode, Qoder,etc.); other assistants always rely on the shared `AGENTS.md` stub
- OpenSpec automatically configures slash commands for the tools you choose and always writes a managed `AGENTS.md` hand-off at the project root
- A new `openspec/` directory structure is created in your project
**After setup:**
- Primary AI tools can trigger `/openspec` workflows without additional configuration
- Run `openspec list` to verify the setup and view any active changes
- If your coding assistant doesn't surface the new slash commands right away, restart it. Slash commands are loaded at startup,
so a fresh launch ensures they appear
### Optional: Populate Project Context
After `openspec init` completes, you'll receive a suggested prompt to help populate your project context:
```text
Populate your project context:
"Please read openspec/project.md and help me fill it out with details about my project, tech stack, and conventions"
```
Use `openspec/project.md` to define project-level conventions, standards, architectural patterns, and other guidelines that should be followed across all changes.
### Create Your First Change
Here's a real example showing the complete OpenSpec workflow. This works with any AI tool. Those with native slash commands will recognize the shortcuts automatically.
#### 1. Draft the Proposal
Start by asking your AI to create a change proposal:
```text
You: Create an OpenSpec change proposal for adding profile search filters by role and team
(Shortcut for tools with slash commands: /openspec:proposal Add profile search filters)
AI: I'll create an OpenSpec change proposal for profile filters.
*Scaffolds openspec/changes/add-profile-filters/ with proposal.md, tasks.md, spec deltas.*
```
#### 2. Verify & Review
Check that the change was created correctly and review the proposal:
```bash
$ openspec list # Confirm the change folder exists
$ openspec validate add-profile-filters # Validate spec formatting
$ openspec show add-profile-filters # Review proposal, tasks, and spec delta
```
#### 3. Refine the Specs
Iterate on the specifications until they match your needs:
```text
You: Can you add acceptance criteria for the role and team filters?
AI: I'll update the spec delta with scenarios for role and team filters.
*Edits openspec/changes/add-profile-filters/specs/profile/spec.md and tasks.md.*
```
#### 4. Implement the Change
Once specs look good, start implementation:
```text
You: The specs look good. Let's implement this change.
(Shortcut for tools with slash commands: /openspec:apply add-profile-filters)
AI: I'll work through the tasks in the add-profile-filters change.
*Implements tasks from openspec/changes/add-profile-filters/tasks.md*
*Marks tasks complete: Task 1.1 ✓, Task 1.2 ✓, Task 2.1 ✓...*
```
#### 5. Archive the Completed Change
After implementation is complete, archive the change:
```text
AI: All tasks are complete. The implementation is ready.
You: Please archive the change
(Shortcut for tools with slash commands: /openspec:archive add-profile-filters)
AI: I'll archive the add-profile-filters change.
*Runs: openspec archive add-profile-filters --yes*
✓ Change archived successfully. Specs updated. Ready for the next feature!
```
Or run the command yourself in terminal:
```bash
$ openspec archive add-profile-filters --yes # Archive the completed change without prompts
```
**Note:** Tools with native slash commands (Claude Code, CodeBuddy, Cursor, Codex, Qoder, RooCode) can use the shortcuts shown. All other tools work with natural language requests to "create an OpenSpec proposal", "apply the OpenSpec change", or "archive the change".
## Command Reference
```bash
openspec list # View active change folders
openspec view # Interactive dashboard of specs and changes
openspec show <change> # Display change details (proposal, tasks, spec updates)
openspec validate <change> # Check spec formatting and structure
openspec archive <change> [--yes|-y] # Move a completed change into archive/ (non-interactive with --yes)
```
## Example: How AI Creates OpenSpec Files
When you ask your AI assistant to "add two-factor authentication", it creates:
```
openspec/
├── specs/
│ └── auth/
│ └── spec.md # Current auth spec (if exists)
└── changes/
└── add-2fa/ # AI creates this entire structure
├── proposal.md # Why and what changes
├── tasks.md # Implementation checklist
├── design.md # Technical decisions (optional)
└── specs/
└── auth/
└── spec.md # Delta showing additions
```
### AI-Generated Spec (created in `openspec/specs/auth/spec.md`):
```markdown
# Auth Specification
## Purpose
Authentication and session management.
## Requirements
### Requirement: User Authentication
The system SHALL issue a JWT on successful login.
#### Scenario: Valid credentials
- WHEN a user submits valid credentials
- THEN a JWT is returned
```
### AI-Generated Change Delta (created in `openspec/changes/add-2fa/specs/auth/spec.md`):
```markdown
# Delta for Auth
## ADDED Requirements
### Requirement: Two-Factor Authentication
The system MUST require a second factor during login.
#### Scenario: OTP required
- WHEN a user submits valid credentials
- THEN an OTP challenge is required
```
### AI-Generated Tasks (created in `openspec/changes/add-2fa/tasks.md`):
```markdown
## 1. Database Setup
- [ ] 1.1 Add OTP secret column to users table
- [ ] 1.2 Create OTP verification logs table
## 2. Backend Implementation
- [ ] 2.1 Add OTP generation endpoint
- [ ] 2.2 Modify login flow to require OTP
- [ ] 2.3 Add OTP verification endpoint
## 3. Frontend Updates
- [ ] 3.1 Create OTP input component
- [ ] 3.2 Update login flow UI
```
**Important:** You don't create these files manually. Your AI assistant generates them based on your requirements and the existing codebase.
## Understanding OpenSpec Files
### Delta Format
Deltas are "patches" that show how specs change:
- **`## ADDED Requirements`** - New capabilities
- **`## MODIFIED Requirements`** - Changed behavior (include complete updated text)
- **`## REMOVED Requirements`** - Deprecated features
**Format requirements:**
- Use `### Requirement: <name>` for headers
- Every requirement needs at least one `#### Scenario:` block
- Use SHALL/MUST in requirement text
## How OpenSpec Compares
### vs. spec-kit
OpenSpec’s two-folder model (`openspec/specs/` for the current truth, `openspec/changes/` for proposed updates) keeps state and diffs separate. This scales when you modify existing features or touch multiple specs. spec-kit is strong for greenfield/0→1 but provides less structure for cross-spec updates and evolving features.
### vs. Kiro.dev
OpenSpec groups every change for a feature in one folder (`openspec/changes/feature-name/`), making it easy to track related specs, tasks, and designs together. Kiro spreads updates across multiple spec folders, which can make feature tracking harder.
### vs. No Specs
Without specs, AI coding assistants generate code from vague prompts, often missing requirements or adding unwanted features. OpenSpec brings predictability by agreeing on the desired behavior before any code is written.
## Team Adoption
1. **Initialize OpenSpec** – Run `openspec init` in your repo.
2. **Start with new features** – Ask your AI to capture upcoming work as change proposals.
3. **Grow incrementally** – Each change archives into living specs that document your system.
4. **Stay flexible** – Different teammates can use Claude Code, CodeBuddy, Cursor, or any AGENTS.md-compatible tool while sharing the same specs.
Run `openspec update` whenever someone switches tools so your agents pick up the latest instructions and slash-command bindings.
## Updating OpenSpec
1. **Upgrade the package**
```bash
npm install -g @fission-ai/openspec@latest
```
2. **Refresh agent instructions**
- Run `openspec update` inside each project to regenerate AI guidance and ensure the latest slash commands are active.
## Experimental Features
<details>
<summary><strong>🧪 OPSX: Fluid, Iterative Workflow</strong> (Claude Code only)</summary>
**Why this exists:**
- Standard workflow is locked down — you can't tweak instructions or customize
- When AI output is bad, you can't improve the prompts yourself
- Same workflow for everyone, no way to match how your team works
**What's different:**
- **Hackable** — edit templates and schemas yourself, test immediately, no rebuild
- **Granular** — each artifact has its own instructions, test and tweak individually
- **Customizable** — define your own workflows, artifacts, and dependencies
- **Fluid** — no phase gates, update any artifact anytime
```
You can always go back:
proposal ──→ specs ──→ design ──→ tasks ──→ implement
▲ ▲ ▲ │
└───────────┴──────────┴────────────────────┘
```
| Command | What it does |
|---------|--------------|
| `/opsx:new` | Start a new change |
| `/opsx:continue` | Create the next artifact (based on what's ready) |
| `/opsx:ff` | Fast-forward (all planning artifacts at once) |
| `/opsx:apply` | Implement tasks, updating artifacts as needed |
| `/opsx:archive` | Archive when done |
**Setup:** `openspec experimental`
[Full documentation →](docs/opsx.md)
</details>
<details>
<summary><strong>Telemetry</strong> – OpenSpec collects anonymous usage stats (opt-out: <code>OPENSPEC_TELEMETRY=0</code>)</summary>
We collect only command names and version to understand usage patterns. No arguments, paths, content, or PII. Automatically disabled in CI.
**Opt-out:** `export OPENSPEC_TELEMETRY=0` or `export DO_NOT_TRACK=1`
</details>
## Contributing
- Install dependencies: `pnpm install`
- Build: `pnpm run build`
- Test: `pnpm test`
- Develop CLI locally: `pnpm run dev` or `pnpm run dev:cli`
- Conventional commits (one-line): `type(scope): subject`
<details>
<summary><strong>Maintainers & Advisors</strong></summary>
See [MAINTAINERS.md](MAINTAINERS.md) for the list of core maintainers and advisors who help guide the project.
</details>
## License
MIT
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

-597
View File
@@ -1,597 +0,0 @@
# POC-OpenSpec-Core Analysis
---
## Design Decisions & Terminology
### Philosophy: Not a Workflow System
This system is **not** a workflow engine. It's an **artifact tracker with dependency awareness**.
| What it's NOT | What it IS |
|---------------|------------|
| Linear step-by-step progression | Exploratory, iterative planning |
| Bureaucratic checkpoints | Enablers that unlock possibilities |
| "You must complete step 1 first" | "Here's what you could create now" |
| Form-filling | Fluid document creation |
**Key insight:** Dependencies are *enablers*, not *gates*. You can't meaningfully write a design document if there's no proposal to design from - that's not bureaucracy, it's logic.
### Terminology
| Term | Definition | Example |
|------|------------|---------|
| **Change** | A unit of work being planned (feature, refactor, migration) | `openspec/changes/add-auth/` |
| **Schema** | An artifact graph definition (what artifacts exist, their dependencies) | `spec-driven.yaml` |
| **Artifact** | A node in the graph (a document to create) | `proposal`, `design`, `specs` |
| **Template** | Instructions/guidance for creating an artifact | `templates/proposal.md` |
### Hierarchy
```
Schema (defines) ──→ Artifacts (guided by) ──→ Templates
```
- **Schema** = the artifact graph (what exists, dependencies)
- **Artifact** = a document to produce
- **Template** = instructions for creating that artifact
### Schema Variations
Schemas can vary across multiple dimensions:
| Dimension | Examples |
|-----------|----------|
| Philosophy | `spec-driven`, `tdd`, `prototype-first` |
| Version | `v1`, `v2`, `v3` |
| Language | `en`, `zh`, `es` |
| Custom | `team-alpha`, `experimental` |
### Schema Resolution (XDG Standard)
Schemas follow the XDG Base Directory Specification with a 2-level resolution:
```
1. ${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml # Global user override
2. <package>/schemas/<name>/schema.yaml # Built-in defaults
```
**Platform-specific paths:**
- Unix/macOS: `~/.local/share/openspec/schemas/`
- Windows: `%LOCALAPPDATA%/openspec/schemas/`
- All platforms: `$XDG_DATA_HOME/openspec/schemas/` (when set)
**Why XDG?**
- Schemas are workflow definitions (data), not user preferences (config)
- Built-ins baked into package, never auto-copied
- Users customize by creating files in global data dir
- Consistent with modern CLI tooling standards
### Template Inheritance (2 Levels Max)
Templates are co-located with schemas in a `templates/` subdirectory:
```
1. ${XDG_DATA_HOME}/openspec/schemas/<schema>/templates/<artifact>.md # User override
2. <package>/schemas/<schema>/templates/<artifact>.md # Built-in
```
**Rules:**
- User overrides take precedence over package built-ins
- A CLI command shows resolved paths (no guessing)
- No inheritance between schemas (copy if you need to diverge)
- Templates are always co-located with their schema
**Why this matters:**
- Avoids "where does this come from?" debugging
- No implicit magic that works until it doesn't
- Schema + templates form a cohesive unit
---
## Executive Summary
This is an **artifact tracker with dependency awareness** that guides iterative development through a structured artifact pipeline. The core innovation is using the **filesystem as a database** - artifact completion is detected by file existence, making the system stateless and version-control friendly.
The system answers:
- "What artifacts exist for this change?"
- "What could I create next?" (not "what must I create")
- "What's blocking X?" (informational, not prescriptive)
---
## Core Components
### 1. ArtifactGraph (Slice 1 - COMPLETE)
The dependency graph engine with XDG-compliant schema resolution.
| Responsibility | Approach |
|----------------|----------|
| Model artifacts as a DAG | Artifact with `requires: string[]` |
| Track completion state | `Set<string>` for completed artifacts |
| Calculate build order | Kahn's algorithm (topological sort) |
| Find ready artifacts | Check if all dependencies are in `completed` set |
| Resolve schemas | XDG global → package built-ins |
**Key Data Structures (Zod-validated):**
```typescript
// Zod schemas define types + validation
const ArtifactSchema = z.object({
id: z.string().min(1),
generates: z.string().min(1), // e.g., "proposal.md" or "specs/*.md"
description: z.string(),
template: z.string(), // path to template file
requires: z.array(z.string()).default([]),
});
const SchemaYamlSchema = z.object({
name: z.string().min(1),
version: z.number().int().positive(),
description: z.string().optional(),
artifacts: z.array(ArtifactSchema).min(1),
});
// Derived types
type Artifact = z.infer<typeof ArtifactSchema>;
type SchemaYaml = z.infer<typeof SchemaYamlSchema>;
```
**Key Methods:**
- `resolveSchema(name)` - Load schema with XDG fallback
- `ArtifactGraph.fromSchema(schema)` - Build graph from schema
- `detectState(graph, changeDir)` - Scan filesystem for completion
- `getNextArtifacts(graph, completed)` - Find artifacts ready to create
- `getBuildOrder(graph)` - Topological sort of all artifacts
- `getBlocked(graph, completed)` - Artifacts with unmet dependencies
---
### 2. Change Utilities (Slice 2)
Simple utility functions for programmatic change creation. No class, no abstraction layer.
| Responsibility | Approach |
|----------------|----------|
| Create changes | Create dirs under `openspec/changes/<name>/` with README |
| Name validation | Enforce kebab-case naming |
**Key Paths:**
```
openspec/changes/<name>/ → Change instances with artifacts (project-level)
```
**Key Functions** (`src/utils/change-utils.ts`):
- `createChange(projectRoot, name, description?)` - Create new change directory + README
- `validateChangeName(name)` - Validate kebab-case naming, returns `{ valid, error? }`
**Note:** Existing CLI commands (`ListCommand`, `ChangeCommand`) already handle listing, path resolution, and existence checks. No need to extract that logic - it works fine as-is.
---
### 3. InstructionLoader (Slice 3)
Template resolution and instruction enrichment.
| Responsibility | Approach |
|----------------|----------|
| Resolve templates | XDG 2-level fallback (schema-specific → shared → built-in) |
| Build dynamic context | Gather dependency status, change info |
| Enrich templates | Inject context into base templates |
| Generate status reports | Formatted markdown with progress |
**Key Class - ChangeState:**
```
ChangeState {
changeName: string
changeDir: string
graph: ArtifactGraph
completed: Set<string>
// Methods
getNextSteps(): string[]
getStatus(artifactId): ArtifactStatus
isComplete(): boolean
}
```
**Key Functions:**
- `getTemplatePath(artifactId, schemaName?)` - Resolve with 2-level fallback
- `getEnrichedInstructions(artifactId, projectRoot, changeName?)` - Main entry point
- `getChangeStatus(projectRoot, changeName?)` - Formatted status report
---
### 4. CLI (Slice 4)
User interface layer. **All commands are deterministic** - require explicit `--change` parameter.
| Command | Function | Status |
|---------|----------|--------|
| `status --change <id>` | Show change progress (artifact graph) | **NEW** |
| `next --change <id>` | Show artifacts ready to create | **NEW** |
| `instructions <artifact> --change <id>` | Get enriched instructions for artifact | **NEW** |
| `list` | List all changes | EXISTS (`openspec change list`) |
| `new <name>` | Create change | **NEW** (uses `createChange()`) |
| `init` | Initialize structure | EXISTS (`openspec init`) |
| `templates --change <id>` | Show resolved template paths | **NEW** |
**Note:** Commands that operate on a change require `--change`. Missing parameter → error with list of available changes. Agent infers the change from conversation and passes it explicitly.
**Existing CLI commands** (not part of this slice):
- `openspec change list` / `openspec change show <id>` / `openspec change validate <id>`
- `openspec list --changes` / `openspec list --specs`
- `openspec view` (dashboard)
- `openspec init` / `openspec archive <change>`
---
### 5. Claude Commands
Integration layer for Claude Code. **Operational commands only** - artifact creation via natural language.
| Command | Purpose |
|---------|---------|
| `/status` | Show change progress |
| `/next` | Show what's ready to create |
| `/run [artifact]` | Execute a specific step (power users) |
| `/list` | List all changes |
| `/new <name>` | Create a new change |
| `/init` | Initialize structure |
**Artifact creation:** Users say "create the proposal" or "write the tests" in natural language. The agent:
1. Infers change from conversation (confirms if uncertain)
2. Infers artifact from request
3. Calls CLI with explicit `--change` parameter
4. Creates artifact following instructions
This works for ANY artifact in ANY schema - no new slash commands needed when schemas change.
**Note:** Legacy commands (`/openspec-proposal`, `/openspec-apply`, `/openspec-archive`) exist in the main project for backward compatibility but are separate from this architecture.
---
## Component Dependency Graph
```
┌─────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ ┌──────────────┐ ┌────────────────────┐ │
│ │ CLI │ ←─shell exec───────│ Claude Commands │ │
│ └──────┬───────┘ └────────────────────┘ │
└─────────┼───────────────────────────────────────────────────┘
│ imports
▼
┌─────────────────────────────────────────────────────────────┐
│ ORCHESTRATION LAYER │
│ ┌────────────────────┐ ┌──────────────────────────┐ │
│ │ InstructionLoader │ │ change-utils (Slice 2) │ │
│ │ (Slice 3) │ │ createChange() │ │
│ └─────────┬──────────┘ │ validateChangeName() │ │
│ │ └──────────────────────────┘ │
└────────────┼────────────────────────────────────────────────┘
│ uses
▼
┌─────────────────────────────────────────────────────────────┐
│ CORE LAYER │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ArtifactGraph (Slice 1) │ │
│ │ │ │
│ │ Schema Resolution (XDG) ──→ Graph ──→ State Detection│ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
▲
│ reads from
▼
┌─────────────────────────────────────────────────────────────┐
│ PERSISTENCE LAYER │
│ ┌──────────────────┐ ┌────────────────────────────────┐ │
│ │ XDG Schemas │ │ Project Artifacts │ │
│ │ ~/.local/share/ │ │ openspec/changes/<name>/ │ │
│ │ openspec/ │ │ - proposal.md, design.md │ │
│ │ schemas/ │ │ - specs/*.md, tasks.md │ │
│ └──────────────────┘ └────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## Key Design Patterns
### 1. Filesystem as Database
No SQLite, no JSON state files. The existence of `proposal.md` means proposal is complete.
```
// State detection is just file existence checking
if (exists(artifactPath)) {
completed.add(artifactId)
}
```
### 2. Deterministic CLI, Inferring Agent
**CLI layer:** Always deterministic - requires explicit `--change` parameter.
```
openspec status --change add-auth # explicit, works
openspec status # error: "No change specified"
```
**Agent layer:** Infers from conversation, confirms if uncertain, passes explicit `--change`.
This separation means:
- CLI is pure, testable, no state to corrupt
- Agent handles all "smartness"
- No config.yaml tracking of "active change"
### 3. XDG-Compliant Schema Resolution
```
${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml # User override
↓ (not found)
<package>/schemas/<name>/schema.yaml # Built-in
↓ (not found)
Error (schema not found)
```
### 4. Two-Level Template Fallback
```
${XDG_DATA_HOME}/openspec/schemas/<schema>/templates/<artifact>.md # User override
↓ (not found)
<package>/schemas/<schema>/templates/<artifact>.md # Built-in
↓ (not found)
Error (no silent fallback to avoid confusion)
```
### 5. Glob Pattern Support
`specs/*.md` allows multiple files to satisfy a single artifact:
```
if (artifact.generates.includes("*")) {
const parentDir = changeDir / patternParts[0]
if (exists(parentDir) && hasFiles(parentDir)) {
completed.add(artifactId)
}
}
```
### 6. Stateless State Detection
Every command re-scans the filesystem. No cached state to corrupt.
---
## Artifact Pipeline (Default Schema)
The default `spec-driven` schema:
```
┌──────────┐
│ proposal │ (no dependencies)
└────┬─────┘
│
▼
┌──────────┐
│ specs │ (requires: proposal)
└────┬─────┘
│
├──────────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ design │ │ │
│ │◄──┤ proposal │
└────┬─────┘ └──────────┘
│ (requires: proposal, specs)
▼
┌──────────┐
│ tasks │ (requires: design)
└──────────┘
```
Other schemas (TDD, prototype-first) would have different graphs.
---
## Implementation Order
Structured as **vertical slices** - each slice is independently testable.
---
### Slice 1: "What's Ready?" (Core Query) ✅ COMPLETE
**Delivers:** Types + Graph + State Detection + Schema Resolution
**Implementation:** `src/core/artifact-graph/`
- `types.ts` - Zod schemas and derived TypeScript types
- `schema.ts` - YAML parsing with Zod validation
- `graph.ts` - ArtifactGraph class with topological sort
- `state.ts` - Filesystem-based state detection
- `resolver.ts` - XDG-compliant schema resolution
- `builtin-schemas.ts` - Package-bundled default schemas
**Key decisions made:**
- Zod for schema validation (consistent with project)
- XDG for global schema overrides
- `Set<string>` for completion state (immutable, functional)
- `inProgress` and `failed` states deferred (require external tracking)
---
### Slice 2: "Change Creation Utilities"
**Delivers:** Utility functions for programmatic change creation
**Scope:**
- `createChange(projectRoot, name, description?)` → creates directory + README
- `validateChangeName(name)` → kebab-case pattern enforcement
**Not in scope (already exists in CLI commands):**
- `listChanges()` → exists in `ListCommand` and `ChangeCommand.getActiveChanges()`
- `getChangePath()` → simple `path.join()` inline
- `changeExists()` → simple `fs.access()` inline
- `isInitialized()` → simple directory check inline
**Why simplified:** Extracting existing CLI logic into a class would require similar refactoring of `SpecCommand` for consistency. The existing code works fine (~15 lines each). Only truly new functionality is `createChange()` + name validation.
---
### Slice 3: "Get Instructions" (Enrichment)
**Delivers:** Template resolution + context injection
**Testable behaviors:**
- Template fallback: schema-specific → shared → built-in → error
- Context injection: completed deps show ✓, missing show ✗
- Output path shown correctly based on change directory
---
### Slice 4: "CLI + Integration"
**Delivers:** New artifact graph commands (builds on existing CLI)
**New commands:**
- `status --change <id>` - Show artifact completion state
- `next --change <id>` - Show ready-to-create artifacts
- `instructions <artifact> --change <id>` - Get enriched template
- `templates --change <id>` - Show resolved paths
- `new <name>` - Create change (wrapper for `createChange()`)
**Already exists (not in scope):**
- `openspec change list/show/validate` - change management
- `openspec list --changes/--specs` - listing
- `openspec view` - dashboard
- `openspec init` - initialization
**Testable behaviors:**
- Each new command produces expected output
- Commands compose correctly (status → next → instructions flow)
- Error handling for missing changes, invalid artifacts, etc.
---
## Directory Structure
```
# Global (XDG paths - user overrides)
~/.local/share/openspec/ # Unix/macOS ($XDG_DATA_HOME/openspec/)
%LOCALAPPDATA%/openspec/ # Windows
└── schemas/ # Schema overrides
└── custom-workflow/ # User-defined schema directory
├── schema.yaml # Schema definition
└── templates/ # Co-located templates
└── proposal.md
# Package (built-in defaults)
<package>/
└── schemas/ # Built-in schema definitions
├── spec-driven/ # Default: proposal → specs → design → tasks
│ ├── schema.yaml
│ └── templates/
│ ├── proposal.md
│ ├── design.md
│ ├── spec.md
│ └── tasks.md
└── tdd/ # TDD: tests → implementation → docs
├── schema.yaml
└── templates/
├── test.md
├── implementation.md
├── spec.md
└── docs.md
# Project (change instances)
openspec/
└── changes/ # Change instances
├── add-auth/
│ ├── README.md # Auto-generated on creation
│ ├── proposal.md # Created artifacts
│ ├── design.md
│ └── specs/
│ └── *.md
├── refactor-db/
│ └── ...
└── archive/ # Completed changes
└── 2025-01-01-add-auth/
.claude/
├── settings.local.json # Permissions
└── commands/ # Slash commands
└── *.md
```
---
## Schema YAML Format
```yaml
# Built-in: <package>/schemas/spec-driven/schema.yaml
# Or user override: ~/.local/share/openspec/schemas/spec-driven/schema.yaml
name: spec-driven
version: 1
description: Specification-driven development
artifacts:
- id: proposal
generates: "proposal.md"
description: "Create project proposal document"
template: "proposal.md" # resolves from co-located templates/ directory
requires: []
- id: specs
generates: "specs/*.md" # glob pattern
description: "Create technical specification documents"
template: "specs.md"
requires:
- proposal
- id: design
generates: "design.md"
description: "Create design document"
template: "design.md"
requires:
- proposal
- specs
- id: tasks
generates: "tasks.md"
description: "Create tasks breakdown document"
template: "tasks.md"
requires:
- design
```
---
## Summary
| Layer | Component | Responsibility | Status |
|-------|-----------|----------------|--------|
| Core | ArtifactGraph | Pure dependency logic + XDG schema resolution | ✅ Slice 1 COMPLETE |
| Utils | change-utils | Change creation + name validation only | Slice 2 (new functionality only) |
| Core | InstructionLoader | Template resolution + enrichment | Slice 3 (all new) |
| Presentation | CLI | New artifact graph commands | Slice 4 (new commands only) |
| Integration | Claude Commands | AI assistant glue | Slice 4 |
**What already exists (not in this proposal):**
- `getActiveChangeIds()` in `src/utils/item-discovery.ts` - list changes
- `ChangeCommand.list/show/validate()` in `src/commands/change.ts`
- `ListCommand.execute()` in `src/core/list.ts`
- `ViewCommand.execute()` in `src/core/view.ts` - dashboard
- `src/core/init.ts` - initialization
- `src/core/archive.ts` - archiving
**Key Principles:**
- **Filesystem IS the database** - stateless, version-control friendly
- **Dependencies are enablers** - show what's possible, don't force order
- **Deterministic CLI, inferring agent** - CLI requires explicit `--change`, agent infers from context
- **XDG-compliant paths** - schemas and templates use standard user data directories
- **2-level inheritance** - user override → package built-in (no deeper)
- **Schemas are versioned** - support variations by philosophy, version, language
+894
View File
@@ -0,0 +1,894 @@
# CLI Reference
The OpenSpec CLI (`openspec`) provides terminal commands for project setup, validation, status inspection, and management. These commands complement the AI slash commands (like `/opsx:new`) documented in [Commands](commands.md).
## Summary
| Category | Commands | Purpose |
|----------|----------|---------|
| **Setup** | `init`, `update` | Initialize and update OpenSpec in your project |
| **Browsing** | `list`, `view`, `show` | Explore changes and specs |
| **Validation** | `validate` | Check changes and specs for issues |
| **Lifecycle** | `archive` | Finalize completed changes |
| **Workflow** | `status`, `instructions`, `templates`, `schemas` | Artifact-driven workflow support |
| **Schemas** | `schema init`, `schema fork`, `schema validate`, `schema which` | Create and manage custom workflows |
| **Config** | `config` | View and modify settings |
| **Utility** | `feedback`, `completion` | Feedback and shell integration |
---
## Human vs Agent Commands
Most CLI commands are designed for **human use** in a terminal. Some commands also support **agent/script use** via JSON output.
### Human-Only Commands
These commands are interactive and designed for terminal use:
| Command | Purpose |
|---------|---------|
| `openspec init` | Initialize project (interactive prompts) |
| `openspec view` | Interactive dashboard |
| `openspec config edit` | Open config in editor |
| `openspec feedback` | Submit feedback via GitHub |
| `openspec completion install` | Install shell completions |
### Agent-Compatible Commands
These commands support `--json` output for programmatic use by AI agents and scripts:
| Command | Human Use | Agent Use |
|---------|-----------|-----------|
| `openspec list` | Browse changes/specs | `--json` for structured data |
| `openspec show <item>` | Read content | `--json` for parsing |
| `openspec validate` | Check for issues | `--all --json` for bulk validation |
| `openspec status` | See artifact progress | `--json` for structured status |
| `openspec instructions` | Get next steps | `--json` for agent instructions |
| `openspec templates` | Find template paths | `--json` for path resolution |
| `openspec schemas` | List available schemas | `--json` for schema discovery |
---
## Global Options
These options work with all commands:
| Option | Description |
|--------|-------------|
| `--version`, `-V` | Show version number |
| `--no-color` | Disable color output |
| `--help`, `-h` | Display help for command |
---
## Setup Commands
### `openspec init`
Initialize OpenSpec in your project. Creates the folder structure and configures AI tool integrations.
```
openspec init [path] [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `path` | No | Target directory (default: current directory) |
**Options:**
| Option | Description |
|--------|-------------|
| `--tools <list>` | Configure AI tools non-interactively. Use `all`, `none`, or comma-separated list |
| `--force` | Auto-cleanup legacy files without prompting |
**Supported tools:** `amazon-q`, `antigravity`, `auggie`, `claude`, `cline`, `codex`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `kilocode`, `opencode`, `qoder`, `qwen`, `roocode`, `windsurf`
**Examples:**
```bash
# Interactive initialization
openspec init
# Initialize in a specific directory
openspec init ./my-project
# Non-interactive: configure for Claude and Cursor
openspec init --tools claude,cursor
# Configure for all supported tools
openspec init --tools all
# Skip prompts and auto-cleanup legacy files
openspec init --force
```
**What it creates:**
```
openspec/
├── specs/ # Your specifications (source of truth)
├── changes/ # Proposed changes
└── config.yaml # Project configuration
.claude/skills/ # Claude Code skill files (if claude selected)
.cursor/rules/ # Cursor rules (if cursor selected)
... (other tool configs)
```
---
### `openspec update`
Update OpenSpec instruction files after upgrading the CLI. Re-generates AI tool configuration files.
```
openspec update [path] [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `path` | No | Target directory (default: current directory) |
**Options:**
| Option | Description |
|--------|-------------|
| `--force` | Force update even when files are up to date |
**Example:**
```bash
# Update instruction files after npm upgrade
npm update @fission-ai/openspec
openspec update
```
---
## Browsing Commands
### `openspec list`
List changes or specs in your project.
```
openspec list [options]
```
**Options:**
| Option | Description |
|--------|-------------|
| `--specs` | List specs instead of changes |
| `--changes` | List changes (default) |
| `--sort <order>` | Sort by `recent` (default) or `name` |
| `--json` | Output as JSON |
**Examples:**
```bash
# List all active changes
openspec list
# List all specs
openspec list --specs
# JSON output for scripts
openspec list --json
```
**Output (text):**
```
Active changes:
add-dark-mode UI theme switching support
fix-login-bug Session timeout handling
```
---
### `openspec view`
Display an interactive dashboard for exploring specs and changes.
```
openspec view
```
Opens a terminal-based interface for navigating your project's specifications and changes.
---
### `openspec show`
Display details of a change or spec.
```
openspec show [item-name] [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `item-name` | No | Name of change or spec (prompts if omitted) |
**Options:**
| Option | Description |
|--------|-------------|
| `--type <type>` | Specify type: `change` or `spec` (auto-detected if unambiguous) |
| `--json` | Output as JSON |
| `--no-interactive` | Disable prompts |
**Change-specific options:**
| Option | Description |
|--------|-------------|
| `--deltas-only` | Show only delta specs (JSON mode) |
**Spec-specific options:**
| Option | Description |
|--------|-------------|
| `--requirements` | Show only requirements, exclude scenarios (JSON mode) |
| `--no-scenarios` | Exclude scenario content (JSON mode) |
| `-r, --requirement <id>` | Show specific requirement by 1-based index (JSON mode) |
**Examples:**
```bash
# Interactive selection
openspec show
# Show a specific change
openspec show add-dark-mode
# Show a specific spec
openspec show auth --type spec
# JSON output for parsing
openspec show add-dark-mode --json
```
---
## Validation Commands
### `openspec validate`
Validate changes and specs for structural issues.
```
openspec validate [item-name] [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `item-name` | No | Specific item to validate (prompts if omitted) |
**Options:**
| Option | Description |
|--------|-------------|
| `--all` | Validate all changes and specs |
| `--changes` | Validate all changes |
| `--specs` | Validate all specs |
| `--type <type>` | Specify type when name is ambiguous: `change` or `spec` |
| `--strict` | Enable strict validation mode |
| `--json` | Output as JSON |
| `--concurrency <n>` | Max parallel validations (default: 6, or `OPENSPEC_CONCURRENCY` env) |
| `--no-interactive` | Disable prompts |
**Examples:**
```bash
# Interactive validation
openspec validate
# Validate a specific change
openspec validate add-dark-mode
# Validate all changes
openspec validate --changes
# Validate everything with JSON output (for CI/scripts)
openspec validate --all --json
# Strict validation with increased parallelism
openspec validate --all --strict --concurrency 12
```
**Output (text):**
```
Validating add-dark-mode...
✓ proposal.md valid
✓ specs/ui/spec.md valid
⚠ design.md: missing "Technical Approach" section
1 warning found
```
**Output (JSON):**
```json
{
"version": "1.0.0",
"results": {
"changes": [
{
"name": "add-dark-mode",
"valid": true,
"warnings": ["design.md: missing 'Technical Approach' section"]
}
]
},
"summary": {
"total": 1,
"valid": 1,
"invalid": 0
}
}
```
---
## Lifecycle Commands
### `openspec archive`
Archive a completed change and merge delta specs into main specs.
```
openspec archive [change-name] [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `change-name` | No | Change to archive (prompts if omitted) |
**Options:**
| Option | Description |
|--------|-------------|
| `-y, --yes` | Skip confirmation prompts |
| `--skip-specs` | Skip spec updates (for infrastructure/tooling/doc-only changes) |
| `--no-validate` | Skip validation (requires confirmation) |
**Examples:**
```bash
# Interactive archive
openspec archive
# Archive specific change
openspec archive add-dark-mode
# Archive without prompts (CI/scripts)
openspec archive add-dark-mode --yes
# Archive a tooling change that doesn't affect specs
openspec archive update-ci-config --skip-specs
```
**What it does:**
1. Validates the change (unless `--no-validate`)
2. Prompts for confirmation (unless `--yes`)
3. Merges delta specs into `openspec/specs/`
4. Moves change folder to `openspec/changes/archive/YYYY-MM-DD-<name>/`
---
## Workflow Commands
These commands support the artifact-driven OPSX workflow. They're useful for both humans checking progress and agents determining next steps.
### `openspec status`
Display artifact completion status for a change.
```
openspec status [options]
```
**Options:**
| Option | Description |
|--------|-------------|
| `--change <id>` | Change name (prompts if omitted) |
| `--schema <name>` | Schema override (auto-detected from change's config) |
| `--json` | Output as JSON |
**Examples:**
```bash
# Interactive status check
openspec status
# Status for specific change
openspec status --change add-dark-mode
# JSON for agent use
openspec status --change add-dark-mode --json
```
**Output (text):**
```
Change: add-dark-mode
Schema: spec-driven
Artifacts:
✓ proposal proposal.md exists
✓ specs specs/ exists
◆ design ready (requires: specs)
○ tasks blocked (requires: design)
Next: Create design using /opsx:continue
```
**Output (JSON):**
```json
{
"change": "add-dark-mode",
"schema": "spec-driven",
"artifacts": [
{"id": "proposal", "status": "complete", "path": "proposal.md"},
{"id": "specs", "status": "complete", "path": "specs/"},
{"id": "design", "status": "ready", "requires": ["specs"]},
{"id": "tasks", "status": "blocked", "requires": ["design"]}
],
"next": "design"
}
```
---
### `openspec instructions`
Get enriched instructions for creating an artifact or applying tasks. Used by AI agents to understand what to create next.
```
openspec instructions [artifact] [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `artifact` | No | Artifact ID: `proposal`, `specs`, `design`, `tasks`, or `apply` |
**Options:**
| Option | Description |
|--------|-------------|
| `--change <id>` | Change name (required in non-interactive mode) |
| `--schema <name>` | Schema override |
| `--json` | Output as JSON |
**Special case:** Use `apply` as the artifact to get task implementation instructions.
**Examples:**
```bash
# Get instructions for next artifact
openspec instructions --change add-dark-mode
# Get specific artifact instructions
openspec instructions design --change add-dark-mode
# Get apply/implementation instructions
openspec instructions apply --change add-dark-mode
# JSON for agent consumption
openspec instructions design --change add-dark-mode --json
```
**Output includes:**
- Template content for the artifact
- Project context from config
- Content from dependency artifacts
- Per-artifact rules from config
---
### `openspec templates`
Show resolved template paths for all artifacts in a schema.
```
openspec templates [options]
```
**Options:**
| Option | Description |
|--------|-------------|
| `--schema <name>` | Schema to inspect (default: `spec-driven`) |
| `--json` | Output as JSON |
**Examples:**
```bash
# Show template paths for default schema
openspec templates
# Show templates for custom schema
openspec templates --schema my-workflow
# JSON for programmatic use
openspec templates --json
```
**Output (text):**
```
Schema: spec-driven
Templates:
proposal → ~/.openspec/schemas/spec-driven/templates/proposal.md
specs → ~/.openspec/schemas/spec-driven/templates/specs.md
design → ~/.openspec/schemas/spec-driven/templates/design.md
tasks → ~/.openspec/schemas/spec-driven/templates/tasks.md
```
---
### `openspec schemas`
List available workflow schemas with their descriptions and artifact flows.
```
openspec schemas [options]
```
**Options:**
| Option | Description |
|--------|-------------|
| `--json` | Output as JSON |
**Example:**
```bash
openspec schemas
```
**Output:**
```
Available schemas:
spec-driven (package)
The default spec-driven development workflow
Flow: proposal → specs → design → tasks
my-custom (project)
Custom workflow for this project
Flow: research → proposal → tasks
```
---
## Schema Commands
Commands for creating and managing custom workflow schemas.
### `openspec schema init`
Create a new project-local schema.
```
openspec schema init <name> [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `name` | Yes | Schema name (kebab-case) |
**Options:**
| Option | Description |
|--------|-------------|
| `--description <text>` | Schema description |
| `--artifacts <list>` | Comma-separated artifact IDs (default: `proposal,specs,design,tasks`) |
| `--default` | Set as project default schema |
| `--no-default` | Don't prompt to set as default |
| `--force` | Overwrite existing schema |
| `--json` | Output as JSON |
**Examples:**
```bash
# Interactive schema creation
openspec schema init research-first
# Non-interactive with specific artifacts
openspec schema init rapid \
--description "Rapid iteration workflow" \
--artifacts "proposal,tasks" \
--default
```
**What it creates:**
```
openspec/schemas/<name>/
├── schema.yaml # Schema definition
└── templates/
├── proposal.md # Template for each artifact
├── specs.md
├── design.md
└── tasks.md
```
---
### `openspec schema fork`
Copy an existing schema to your project for customization.
```
openspec schema fork <source> [name] [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `source` | Yes | Schema to copy |
| `name` | No | New schema name (default: `<source>-custom`) |
**Options:**
| Option | Description |
|--------|-------------|
| `--force` | Overwrite existing destination |
| `--json` | Output as JSON |
**Example:**
```bash
# Fork the built-in spec-driven schema
openspec schema fork spec-driven my-workflow
```
---
### `openspec schema validate`
Validate a schema's structure and templates.
```
openspec schema validate [name] [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `name` | No | Schema to validate (validates all if omitted) |
**Options:**
| Option | Description |
|--------|-------------|
| `--verbose` | Show detailed validation steps |
| `--json` | Output as JSON |
**Example:**
```bash
# Validate a specific schema
openspec schema validate my-workflow
# Validate all schemas
openspec schema validate
```
---
### `openspec schema which`
Show where a schema resolves from (useful for debugging precedence).
```
openspec schema which [name] [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `name` | No | Schema name |
**Options:**
| Option | Description |
|--------|-------------|
| `--all` | List all schemas with their sources |
| `--json` | Output as JSON |
**Example:**
```bash
# Check where a schema comes from
openspec schema which spec-driven
```
**Output:**
```
spec-driven resolves from: package
Source: /usr/local/lib/node_modules/@fission-ai/openspec/schemas/spec-driven
```
**Schema precedence:**
1. Project: `openspec/schemas/<name>/`
2. User: `~/.local/share/openspec/schemas/<name>/`
3. Package: Built-in schemas
---
## Configuration Commands
### `openspec config`
View and modify global OpenSpec configuration.
```
openspec config <subcommand> [options]
```
**Subcommands:**
| Subcommand | Description |
|------------|-------------|
| `path` | Show config file location |
| `list` | Show all current settings |
| `get <key>` | Get a specific value |
| `set <key> <value>` | Set a value |
| `unset <key>` | Remove a key |
| `reset` | Reset to defaults |
| `edit` | Open in `$EDITOR` |
**Examples:**
```bash
# Show config file path
openspec config path
# List all settings
openspec config list
# Get a specific value
openspec config get telemetry.enabled
# Set a value
openspec config set telemetry.enabled false
# Set a string value explicitly
openspec config set user.name "My Name" --string
# Remove a custom setting
openspec config unset user.name
# Reset all configuration
openspec config reset --all --yes
# Edit config in your editor
openspec config edit
```
---
## Utility Commands
### `openspec feedback`
Submit feedback about OpenSpec. Creates a GitHub issue.
```
openspec feedback <message> [options]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `message` | Yes | Feedback message |
**Options:**
| Option | Description |
|--------|-------------|
| `--body <text>` | Detailed description |
**Requirements:** GitHub CLI (`gh`) must be installed and authenticated.
**Example:**
```bash
openspec feedback "Add support for custom artifact types" \
--body "I'd like to define my own artifact types beyond the built-in ones."
```
---
### `openspec completion`
Manage shell completions for the OpenSpec CLI.
```
openspec completion <subcommand> [shell]
```
**Subcommands:**
| Subcommand | Description |
|------------|-------------|
| `generate [shell]` | Output completion script to stdout |
| `install [shell]` | Install completion for your shell |
| `uninstall [shell]` | Remove installed completions |
**Supported shells:** `bash`, `zsh`, `fish`, `powershell`
**Examples:**
```bash
# Install completions (auto-detects shell)
openspec completion install
# Install for specific shell
openspec completion install zsh
# Generate script for manual installation
openspec completion generate bash > ~/.bash_completion.d/openspec
# Uninstall
openspec completion uninstall
```
---
## Exit Codes
| Code | Meaning |
|------|---------|
| `0` | Success |
| `1` | Error (validation failure, missing files, etc.) |
---
## Environment Variables
| Variable | Description |
|----------|-------------|
| `OPENSPEC_CONCURRENCY` | Default concurrency for bulk validation (default: 6) |
| `EDITOR` or `VISUAL` | Editor for `openspec config edit` |
| `NO_COLOR` | Disable color output when set |
---
## Related Documentation
- [Commands](commands.md) - AI slash commands (`/opsx:new`, `/opsx:apply`, etc.)
- [Workflows](workflows.md) - Common patterns and when to use each command
- [Customization](customization.md) - Create custom schemas and templates
- [Getting Started](getting-started.md) - First-time setup guide
+652
View File
@@ -0,0 +1,652 @@
# Commands
This is the reference for OpenSpec's slash commands. These commands are invoked in your AI coding assistant's chat interface (e.g., Claude Code, Cursor, Windsurf).
For workflow patterns and when to use each command, see [Workflows](workflows.md). For CLI commands, see [CLI](cli.md).
## Quick Reference
| Command | Purpose |
|---------|---------|
| `/opsx:explore` | Think through ideas before committing to a change |
| `/opsx:new` | Start a new change |
| `/opsx:continue` | Create the next artifact based on dependencies |
| `/opsx:ff` | Fast-forward: create all planning artifacts at once |
| `/opsx:apply` | Implement tasks from the change |
| `/opsx:verify` | Validate implementation matches artifacts |
| `/opsx:sync` | Merge delta specs into main specs |
| `/opsx:archive` | Archive a completed change |
| `/opsx:bulk-archive` | Archive multiple changes at once |
| `/opsx:onboard` | Guided tutorial through the complete workflow |
---
## Command Reference
### `/opsx:explore`
Think through ideas, investigate problems, and clarify requirements before committing to a change.
**Syntax:**
```
/opsx:explore [topic]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `topic` | No | What you want to explore or investigate |
**What it does:**
- Opens an exploratory conversation with no structure required
- Investigates the codebase to answer questions
- Compares options and approaches
- Creates visual diagrams to clarify thinking
- Can transition to `/opsx:new` when insights crystallize
**Example:**
```text
You: /opsx:explore
AI: What would you like to explore?
You: How should we handle authentication for the mobile app?
AI: Let me investigate your current auth setup...
[Analyzes codebase]
I see you're using session-based auth for the web app.
For mobile, we have three main options:
1. JWT tokens (stateless, works offline)
2. OAuth2 (if you need third-party login)
3. Extend current sessions (simpler, but requires connectivity)
Your API already has CORS configured. Which direction interests you?
You: Let's go with JWT. Can we start a change for that?
AI: Ready when you are. Run /opsx:new add-jwt-auth to begin.
```
**Tips:**
- Use when requirements are unclear or you need to investigate
- No artifacts are created during exploration
- Good for comparing multiple approaches before deciding
- Can read files and search the codebase
---
### `/opsx:new`
Start a new change. Creates the change folder structure and scaffolds it with the selected schema.
**Syntax:**
```
/opsx:new [change-name] [--schema <schema-name>]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `change-name` | No | Name for the change folder (prompted if not provided) |
| `--schema` | No | Workflow schema to use (default: from config or `spec-driven`) |
**What it does:**
- Creates `openspec/changes/<change-name>/` directory
- Creates `.openspec.yaml` metadata file in the change folder
- Shows the first artifact template ready for creation
- Prompts for change name and schema if not provided
**What it creates:**
```
openspec/changes/<change-name>/
└── .openspec.yaml # Change metadata (schema, created date)
```
**Example:**
```
You: /opsx:new add-dark-mode
AI: Created openspec/changes/add-dark-mode/
Schema: spec-driven
Ready to create: proposal
Use /opsx:continue to create it, or /opsx:ff to create all artifacts.
```
**Tips:**
- Use descriptive names: `add-feature`, `fix-bug`, `refactor-module`
- Avoid generic names like `update`, `changes`, `wip`
- Schema can also be set in project config (`openspec/config.yaml`)
---
### `/opsx:continue`
Create the next artifact in the dependency chain. Creates one artifact at a time for incremental progress.
**Syntax:**
```
/opsx:continue [change-name]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `change-name` | No | Which change to continue (inferred from context if not provided) |
**What it does:**
- Queries the artifact dependency graph
- Shows which artifacts are ready vs blocked
- Creates the first ready artifact
- Reads dependency files for context
- Shows what becomes available after creation
**Example:**
```
You: /opsx:continue
AI: Change: add-dark-mode
Artifact status:
✓ proposal (done)
◆ specs (ready)
◆ design (ready)
○ tasks (blocked - needs: specs)
Creating specs...
[Reads proposal.md, creates specs based on requirements]
✓ Created openspec/changes/add-dark-mode/specs/ui/spec.md
Now available: tasks
Run /opsx:continue to create the next artifact.
```
**Tips:**
- Use when you want to review each artifact before proceeding
- Good for complex changes where you want control
- Multiple artifacts may become ready simultaneously
- You can edit created artifacts before continuing
---
### `/opsx:ff`
Fast-forward through artifact creation. Creates all planning artifacts at once.
**Syntax:**
```
/opsx:ff [change-name]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `change-name` | No | Which change to fast-forward (inferred from context if not provided) |
**What it does:**
- Creates all artifacts in dependency order
- Tracks progress via todo list
- Stops when all `apply-required` artifacts are complete
- Reads each dependency before creating the next artifact
**Example:**
```
You: /opsx:ff add-dark-mode
AI: Fast-forwarding add-dark-mode...
✓ Creating proposal.md
✓ Creating specs/ui/spec.md
✓ Creating design.md
✓ Creating tasks.md
All planning artifacts complete!
Ready for implementation. Run /opsx:apply to begin.
```
**Tips:**
- Use when you have a clear picture of what you're building
- Faster than `/opsx:continue` for straightforward changes
- You can still edit artifacts afterward
- Good for small to medium features
---
### `/opsx:apply`
Implement tasks from the change. Works through the task list, writing code and checking off items.
**Syntax:**
```
/opsx:apply [change-name]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `change-name` | No | Which change to implement (inferred from context if not provided) |
**What it does:**
- Reads `tasks.md` and identifies incomplete tasks
- Works through tasks one by one
- Writes code, creates files, runs tests as needed
- Marks tasks complete with checkboxes `[x]`
**Example:**
```
You: /opsx:apply
AI: Implementing add-dark-mode...
Reading tasks.md:
- [ ] 1.1 Create ThemeContext
- [ ] 1.2 Add CSS custom properties
- [ ] 1.3 Implement localStorage persistence
Working on 1.1: Create ThemeContext...
[Creates src/context/ThemeContext.tsx]
✓ 1.1 Complete
Working on 1.2: Add CSS custom properties...
[Updates src/styles/globals.css]
✓ 1.2 Complete
...
```
**Tips:**
- Can resume where you left off if interrupted
- Use for parallel changes by specifying the change name
- Completion state is tracked in `tasks.md` checkboxes
---
### `/opsx:verify`
Validate that implementation matches your change artifacts. Checks completeness, correctness, and coherence.
**Syntax:**
```
/opsx:verify [change-name]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `change-name` | No | Which change to verify (inferred from context if not provided) |
**What it does:**
- Checks three dimensions of implementation quality
- Searches codebase for implementation evidence
- Reports issues categorized as CRITICAL, WARNING, or SUGGESTION
- Does not block archive, but surfaces issues
**Verification dimensions:**
| Dimension | What it validates |
|-----------|-------------------|
| **Completeness** | All tasks done, all requirements implemented, scenarios covered |
| **Correctness** | Implementation matches spec intent, edge cases handled |
| **Coherence** | Design decisions reflected in code, patterns consistent |
**Example:**
```
You: /opsx:verify
AI: Verifying add-dark-mode...
COMPLETENESS
✓ All 8 tasks in tasks.md are checked
✓ All requirements in specs have corresponding code
⚠ Scenario "System preference detection" has no test coverage
CORRECTNESS
✓ Implementation matches spec intent
✓ Edge cases from scenarios are handled
✓ Error states match spec definitions
COHERENCE
✓ Design decisions reflected in code structure
✓ Naming conventions consistent with design.md
⚠ Design mentions "CSS variables" but implementation uses Tailwind classes
SUMMARY
─────────────────────────────
Critical issues: 0
Warnings: 2
Ready to archive: Yes (with warnings)
Recommendations:
1. Add test for system preference detection
2. Update design.md to reflect Tailwind usage, or refactor to use CSS variables
```
**Tips:**
- Run before archiving to catch mismatches early
- Warnings don't block archive but indicate potential issues
- Good for reviewing AI's work before committing
- Can reveal drift between artifacts and implementation
---
### `/opsx:sync`
**Optional command.** Merge delta specs from a change into main specs. Archive will prompt to sync if needed, so you typically don't need to run this manually.
**Syntax:**
```
/opsx:sync [change-name]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `change-name` | No | Which change to sync (inferred from context if not provided) |
**What it does:**
- Reads delta specs from change folder
- Parses ADDED/MODIFIED/REMOVED/RENAMED sections
- Merges changes into main `openspec/specs/` directory
- Preserves existing content not mentioned in delta
- Does not archive the change (remains active)
**Example:**
```text
You: /opsx:sync
AI: Syncing add-dark-mode delta specs...
Reading: openspec/changes/add-dark-mode/specs/ui/spec.md
Target: openspec/specs/ui/spec.md
Changes to apply:
✓ ADDED: Theme Selection requirement (2 scenarios)
✓ ADDED: Accessibility requirement (1 scenario)
Merging...
✓ openspec/specs/ui/spec.md updated
Change remains active. Run /opsx:archive when ready to complete.
```
**When to use manually:**
| Scenario | Use sync? |
|----------|-----------|
| Long-running change, want specs in main before archiving | Yes |
| Multiple parallel changes need the updated base specs | Yes |
| Want to preview/review the merge separately | Yes |
| Quick change, going straight to archive | No (archive handles it) |
**Tips:**
- Sync is intelligent, not copy-paste
- Can add scenarios to existing requirements without duplicating
- Change stays active after sync (not archived)
- Most users will never need to call this directly—archive prompts if needed
---
### `/opsx:archive`
Archive a completed change. Finalizes the change and moves it to the archive folder.
**Syntax:**
```
/opsx:archive [change-name]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `change-name` | No | Which change to archive (inferred from context if not provided) |
**What it does:**
- Checks artifact completion status
- Checks task completion (warns if incomplete)
- Offers to sync delta specs if not already synced
- Moves change folder to `openspec/changes/archive/YYYY-MM-DD-<name>/`
- Preserves all artifacts for audit trail
**Example:**
```text
You: /opsx:archive
AI: Archiving add-dark-mode...
Artifact status:
✓ proposal.md exists
✓ specs/ exists
✓ design.md exists
✓ tasks.md exists (8/8 tasks complete)
Delta specs: Not yet synced
→ Sync now? (recommended)
You: Yes
AI: ✓ Synced specs to openspec/specs/ui/spec.md
✓ Moved to openspec/changes/archive/2025-01-24-add-dark-mode/
Change archived successfully.
```
**Tips:**
- Archive won't block on incomplete tasks, but will warn
- Delta specs can be synced during archive or beforehand
- Archived changes are preserved for history
- Use `/opsx:verify` first to catch issues
---
### `/opsx:bulk-archive`
Archive multiple completed changes at once. Handles spec conflicts between changes.
**Syntax:**
```
/opsx:bulk-archive [change-names...]
```
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `change-names` | No | Specific changes to archive (prompts to select if not provided) |
**What it does:**
- Lists all completed changes
- Validates each change before archiving
- Detects spec conflicts across changes
- Resolves conflicts by checking what's actually implemented
- Archives in chronological order
**Example:**
```
You: /opsx:bulk-archive
AI: Found 3 completed changes:
- add-dark-mode (8/8 tasks complete)
- fix-login-bug (3/3 tasks complete)
- update-footer (2/2 tasks complete)
Checking for spec conflicts...
⚠ add-dark-mode and update-footer both touch specs/ui/
Inspecting codebase to resolve...
Both changes are implemented. Will merge in chronological order:
1. add-dark-mode (created Jan 20)
2. update-footer (created Jan 22)
Archive all 3 changes?
You: Yes
AI: ✓ Archived add-dark-mode
✓ Archived fix-login-bug
✓ Archived update-footer
Specs merged: add-dark-mode → update-footer
```
**Tips:**
- Good for parallel work streams
- Conflict resolution is agentic (checks codebase)
- Changes are archived in order of creation
- Prompts before overwriting spec content
---
### `/opsx:onboard`
Guided onboarding through the complete OpenSpec workflow. An interactive tutorial using your actual codebase.
**Syntax:**
```
/opsx:onboard
```
**What it does:**
- Walks through a complete workflow cycle with narration
- Scans your codebase for real improvement opportunities
- Creates an actual change with real artifacts
- Implements actual work (small, safe changes)
- Archives the completed change
- Explains each step as it happens
**Phases:**
1. Welcome and codebase analysis
2. Finding an improvement opportunity
3. Creating a change (`/opsx:new`)
4. Writing the proposal
5. Creating specs
6. Writing the design
7. Creating tasks
8. Implementing tasks (`/opsx:apply`)
9. Verifying implementation
10. Archiving the change
11. Summary and next steps
**Example:**
```
You: /opsx:onboard
AI: Welcome to OpenSpec!
I'll walk you through the complete workflow using your actual codebase.
We'll find something small to improve, create a proper change for it,
implement it, and archive it.
Let me scan your codebase for opportunities...
[Analyzes codebase]
I found a few things we could work on:
1. Add input validation to the contact form
2. Improve error messages in the auth flow
3. Add loading states to async buttons
Which interests you? (or suggest something else)
```
**Tips:**
- Best for new users learning the workflow
- Uses real code, not toy examples
- Creates a real change you can keep or discard
- Takes 15-30 minutes to complete
---
## Command Syntax by AI Tool
Different AI tools use slightly different command syntax. Use the format that matches your tool:
| Tool | Syntax Example |
|------|----------------|
| Claude Code | `/opsx:new`, `/opsx:apply` |
| Cursor | `/opsx-new`, `/opsx-apply` |
| Windsurf | `/opsx-new`, `/opsx-apply` |
| Copilot | `/opsx-new`, `/opsx-apply` |
The functionality is identical regardless of syntax.
---
## Legacy Commands
These commands use the older "all-at-once" workflow. They still work but OPSX commands are recommended.
| Command | What it does |
|---------|--------------|
| `/openspec:proposal` | Create all artifacts at once (proposal, specs, design, tasks) |
| `/openspec:apply` | Implement the change |
| `/openspec:archive` | Archive the change |
**When to use legacy commands:**
- Existing projects using the old workflow
- Simple changes where you don't need incremental artifact creation
- Preference for the all-or-nothing approach
**Migrating to OPSX:**
Legacy changes can be continued with OPSX commands. The artifact structure is compatible.
---
## Troubleshooting
### "Change not found"
The command couldn't identify which change to work on.
**Solutions:**
- Specify the change name explicitly: `/opsx:apply add-dark-mode`
- Check that the change folder exists: `openspec list`
- Verify you're in the right project directory
### "No artifacts ready"
All artifacts are either complete or blocked by missing dependencies.
**Solutions:**
- Run `openspec status --change <name>` to see what's blocking
- Check if required artifacts exist
- Create missing dependency artifacts first
### "Schema not found"
The specified schema doesn't exist.
**Solutions:**
- List available schemas: `openspec schemas`
- Check spelling of schema name
- Create the schema if it's custom: `openspec schema init <name>`
### Commands not recognized
The AI tool doesn't recognize OpenSpec commands.
**Solutions:**
- Ensure OpenSpec is initialized: `openspec init`
- Regenerate skills: `openspec update`
- Check that `.claude/skills/` directory exists (for Claude Code)
- Restart your AI tool to pick up new skills
### Artifacts not generating properly
The AI creates incomplete or incorrect artifacts.
**Solutions:**
- Add project context in `openspec/config.yaml`
- Add per-artifact rules for specific guidance
- Provide more detail in your change description
- Use `/opsx:continue` instead of `/opsx:ff` for more control
---
## Next Steps
- [Workflows](workflows.md) - Common patterns and when to use each command
- [CLI](cli.md) - Terminal commands for management and validation
- [Customization](customization.md) - Create custom schemas and workflows
+582
View File
@@ -0,0 +1,582 @@
# Concepts
This guide explains the core ideas behind OpenSpec and how they fit together. For practical usage, see [Getting Started](getting-started.md) and [Workflows](workflows.md).
## Philosophy
OpenSpec is built around four principles:
```
fluid not rigid — no phase gates, work on what makes sense
iterative not waterfall — learn as you build, refine as you go
easy not complex — lightweight setup, minimal ceremony
brownfield-first — works with existing codebases, not just greenfield
```
### Why These Principles Matter
**Fluid not rigid.** Traditional spec systems lock you into phases: first you plan, then you implement, then you're done. OpenSpec is more flexible — you can create artifacts in any order that makes sense for your work.
**Iterative not waterfall.** Requirements change. Understanding deepens. What seemed like a good approach at the start might not hold up after you see the codebase. OpenSpec embraces this reality.
**Easy not complex.** Some spec frameworks require extensive setup, rigid formats, or heavyweight processes. OpenSpec stays out of your way. Initialize in seconds, start working immediately, customize only if you need to.
**Brownfield-first.** Most software work isn't building from scratch — it's modifying existing systems. OpenSpec's delta-based approach makes it easy to specify changes to existing behavior, not just describe new systems.
## The Big Picture
OpenSpec organizes your work into two main areas:
```
┌─────────────────────────────────────────────────────────────────┐
│ openspec/ │
│ │
│ ┌─────────────────────┐ ┌──────────────────────────────┐ │
│ │ specs/ │ │ changes/ │ │
│ │ │ │ │ │
│ │ Source of truth │◄─────│ Proposed modifications │ │
│ │ How your system │ merge│ Each change = one folder │ │
│ │ currently works │ │ Contains artifacts + deltas │ │
│ │ │ │ │ │
│ └─────────────────────┘ └──────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
**Specs** are the source of truth — they describe how your system currently behaves.
**Changes** are proposed modifications — they live in separate folders until you're ready to merge them.
This separation is key. You can work on multiple changes in parallel without conflicts. You can review a change before it affects the main specs. And when you archive a change, its deltas merge cleanly into the source of truth.
## Specs
Specs describe your system's behavior using structured requirements and scenarios.
### Structure
```
openspec/specs/
├── auth/
│ └── spec.md # Authentication behavior
├── payments/
│ └── spec.md # Payment processing
├── notifications/
│ └── spec.md # Notification system
└── ui/
└── spec.md # UI behavior and themes
```
Organize specs by domain — logical groupings that make sense for your system. Common patterns:
- **By feature area**: `auth/`, `payments/`, `search/`
- **By component**: `api/`, `frontend/`, `workers/`
- **By bounded context**: `ordering/`, `fulfillment/`, `inventory/`
### Spec Format
A spec contains requirements, and each requirement has scenarios:
```markdown
# Auth Specification
## Purpose
Authentication and session management for the application.
## Requirements
### Requirement: User Authentication
The system SHALL issue a JWT token upon successful login.
#### Scenario: Valid credentials
- GIVEN a user with valid credentials
- WHEN the user submits login form
- THEN a JWT token is returned
- AND the user is redirected to dashboard
#### Scenario: Invalid credentials
- GIVEN invalid credentials
- WHEN the user submits login form
- THEN an error message is displayed
- AND no token is issued
### Requirement: Session Expiration
The system MUST expire sessions after 30 minutes of inactivity.
#### Scenario: Idle timeout
- GIVEN an authenticated session
- WHEN 30 minutes pass without activity
- THEN the session is invalidated
- AND the user must re-authenticate
```
**Key elements:**
| Element | Purpose |
|---------|---------|
| `## Purpose` | High-level description of this spec's domain |
| `### Requirement:` | A specific behavior the system must have |
| `#### Scenario:` | A concrete example of the requirement in action |
| SHALL/MUST/SHOULD | RFC 2119 keywords indicating requirement strength |
### Why Structure Specs This Way
**Requirements are the "what"** — they state what the system should do without specifying implementation.
**Scenarios are the "when"** — they provide concrete examples that can be verified. Good scenarios:
- Are testable (you could write an automated test for them)
- Cover both happy path and edge cases
- Use Given/When/Then or similar structured format
**RFC 2119 keywords** (SHALL, MUST, SHOULD, MAY) communicate intent:
- **MUST/SHALL** — absolute requirement
- **SHOULD** — recommended, but exceptions exist
- **MAY** — optional
## Changes
A change is a proposed modification to your system, packaged as a folder with everything needed to understand and implement it.
### Change Structure
```
openspec/changes/add-dark-mode/
├── proposal.md # Why and what
├── design.md # How (technical approach)
├── tasks.md # Implementation checklist
├── .openspec.yaml # Change metadata (optional)
└── specs/ # Delta specs
└── ui/
└── spec.md # What's changing in ui/spec.md
```
Each change is self-contained. It has:
- **Artifacts** — documents that capture intent, design, and tasks
- **Delta specs** — specifications for what's being added, modified, or removed
- **Metadata** — optional configuration for this specific change
### Why Changes Are Folders
Packaging a change as a folder has several benefits:
1. **Everything together.** Proposal, design, tasks, and specs live in one place. No hunting through different locations.
2. **Parallel work.** Multiple changes can exist simultaneously without conflicting. Work on `add-dark-mode` while `fix-auth-bug` is also in progress.
3. **Clean history.** When archived, changes move to `changes/archive/` with their full context preserved. You can look back and understand not just what changed, but why.
4. **Review-friendly.** A change folder is easy to review — open it, read the proposal, check the design, see the spec deltas.
## Artifacts
Artifacts are the documents within a change that guide the work.
### The Artifact Flow
```
proposal ──────► specs ──────► design ──────► tasks ──────► implement
│ │ │ │
why what how steps
+ scope changes approach to take
```
Artifacts build on each other. Each artifact provides context for the next.
### Artifact Types
#### Proposal (`proposal.md`)
The proposal captures **intent**, **scope**, and **approach** at a high level.
```markdown
# Proposal: Add Dark Mode
## Intent
Users have requested a dark mode option to reduce eye strain
during nighttime usage and match system preferences.
## Scope
In scope:
- Theme toggle in settings
- System preference detection
- Persist preference in localStorage
Out of scope:
- Custom color themes (future work)
- Per-page theme overrides
## Approach
Use CSS custom properties for theming with a React context
for state management. Detect system preference on first load,
allow manual override.
```
**When to update the proposal:**
- Scope changes (narrowing or expanding)
- Intent clarifies (better understanding of the problem)
- Approach fundamentally shifts
#### Specs (delta specs in `specs/`)
Delta specs describe **what's changing** relative to the current specs. See [Delta Specs](#delta-specs) below.
#### Design (`design.md`)
The design captures **technical approach** and **architecture decisions**.
```markdown
# Design: Add Dark Mode
## Technical Approach
Theme state managed via React Context to avoid prop drilling.
CSS custom properties enable runtime switching without class toggling.
## Architecture Decisions
### Decision: Context over Redux
Using React Context for theme state because:
- Simple binary state (light/dark)
- No complex state transitions
- Avoids adding Redux dependency
### Decision: CSS Custom Properties
Using CSS variables instead of CSS-in-JS because:
- Works with existing stylesheet
- No runtime overhead
- Browser-native solution
## Data Flow
```
ThemeProvider (context)
│
▼
ThemeToggle ◄──► localStorage
│
▼
CSS Variables (applied to :root)
```
## File Changes
- `src/contexts/ThemeContext.tsx` (new)
- `src/components/ThemeToggle.tsx` (new)
- `src/styles/globals.css` (modified)
```
**When to update the design:**
- Implementation reveals the approach won't work
- Better solution discovered
- Dependencies or constraints change
#### Tasks (`tasks.md`)
Tasks are the **implementation checklist** — concrete steps with checkboxes.
```markdown
# Tasks
## 1. Theme Infrastructure
- [ ] 1.1 Create ThemeContext with light/dark state
- [ ] 1.2 Add CSS custom properties for colors
- [ ] 1.3 Implement localStorage persistence
- [ ] 1.4 Add system preference detection
## 2. UI Components
- [ ] 2.1 Create ThemeToggle component
- [ ] 2.2 Add toggle to settings page
- [ ] 2.3 Update Header to include quick toggle
## 3. Styling
- [ ] 3.1 Define dark theme color palette
- [ ] 3.2 Update components to use CSS variables
- [ ] 3.3 Test contrast ratios for accessibility
```
**Task best practices:**
- Group related tasks under headings
- Use hierarchical numbering (1.1, 1.2, etc.)
- Keep tasks small enough to complete in one session
- Check tasks off as you complete them
## Delta Specs
Delta specs are the key concept that makes OpenSpec work for brownfield development. They describe **what's changing** rather than restating the entire spec.
### The Format
```markdown
# Delta for Auth
## ADDED Requirements
### Requirement: Two-Factor Authentication
The system MUST support TOTP-based two-factor authentication.
#### Scenario: 2FA enrollment
- GIVEN a user without 2FA enabled
- WHEN the user enables 2FA in settings
- THEN a QR code is displayed for authenticator app setup
- AND the user must verify with a code before activation
#### Scenario: 2FA login
- GIVEN a user with 2FA enabled
- WHEN the user submits valid credentials
- THEN an OTP challenge is presented
- AND login completes only after valid OTP
## MODIFIED Requirements
### Requirement: Session Expiration
The system MUST expire sessions after 15 minutes of inactivity.
(Previously: 30 minutes)
#### Scenario: Idle timeout
- GIVEN an authenticated session
- WHEN 15 minutes pass without activity
- THEN the session is invalidated
## REMOVED Requirements
### Requirement: Remember Me
(Deprecated in favor of 2FA. Users should re-authenticate each session.)
```
### Delta Sections
| Section | Meaning | What Happens on Archive |
|---------|---------|------------------------|
| `## ADDED Requirements` | New behavior | Appended to main spec |
| `## MODIFIED Requirements` | Changed behavior | Replaces existing requirement |
| `## REMOVED Requirements` | Deprecated behavior | Deleted from main spec |
### Why Deltas Instead of Full Specs
**Clarity.** A delta shows exactly what's changing. Reading a full spec, you'd have to diff it mentally against the current version.
**Conflict avoidance.** Two changes can touch the same spec file without conflicting, as long as they modify different requirements.
**Review efficiency.** Reviewers see the change, not the unchanged context. Focus on what matters.
**Brownfield fit.** Most work modifies existing behavior. Deltas make modifications first-class, not an afterthought.
## Schemas
Schemas define the artifact types and their dependencies for a workflow.
### How Schemas Work
```yaml
# openspec/schemas/spec-driven/schema.yaml
name: spec-driven
artifacts:
- id: proposal
generates: proposal.md
requires: [] # No dependencies, can create first
- id: specs
generates: specs/**/*.md
requires: [proposal] # Needs proposal before creating
- id: design
generates: design.md
requires: [proposal] # Can create in parallel with specs
- id: tasks
generates: tasks.md
requires: [specs, design] # Needs both specs and design first
```
**Artifacts form a dependency graph:**
```
proposal
(root node)
│
┌─────────────┴─────────────┐
│ │
▼ ▼
specs design
(requires: (requires:
proposal) proposal)
│ │
└─────────────┬─────────────┘
│
▼
tasks
(requires:
specs, design)
```
**Dependencies are enablers, not gates.** They show what's possible to create, not what you must create next. You can skip design if you don't need it. You can create specs before or after design — both depend only on proposal.
### Built-in Schemas
**spec-driven** (default)
The standard workflow for spec-driven development:
```
proposal → specs → design → tasks → implement
```
Best for: Most feature work where you want to agree on specs before implementation.
### Custom Schemas
Create custom schemas for your team's workflow:
```bash
# Create from scratch
openspec schema init research-first
# Or fork an existing one
openspec schema fork spec-driven research-first
```
**Example custom schema:**
```yaml
# openspec/schemas/research-first/schema.yaml
name: research-first
artifacts:
- id: research
generates: research.md
requires: [] # Do research first
- id: proposal
generates: proposal.md
requires: [research] # Proposal informed by research
- id: tasks
generates: tasks.md
requires: [proposal] # Skip specs/design, go straight to tasks
```
See [Customization](customization.md) for full details on creating and using custom schemas.
## Archive
Archiving completes a change by merging its delta specs into the main specs and preserving the change for history.
### What Happens When You Archive
```
Before archive:
openspec/
├── specs/
│ └── auth/
│ └── spec.md ◄────────────────┐
└── changes/ │
└── add-2fa/ │
├── proposal.md │
├── design.md │ merge
├── tasks.md │
└── specs/ │
└── auth/ │
└── spec.md ─────────┘
After archive:
openspec/
├── specs/
│ └── auth/
│ └── spec.md # Now includes 2FA requirements
└── changes/
└── archive/
└── 2025-01-24-add-2fa/ # Preserved for history
├── proposal.md
├── design.md
├── tasks.md
└── specs/
└── auth/
└── spec.md
```
### The Archive Process
1. **Merge deltas.** Each delta spec section (ADDED/MODIFIED/REMOVED) is applied to the corresponding main spec.
2. **Move to archive.** The change folder moves to `changes/archive/` with a date prefix for chronological ordering.
3. **Preserve context.** All artifacts remain intact in the archive. You can always look back to understand why a change was made.
### Why Archive Matters
**Clean state.** Active changes (`changes/`) shows only work in progress. Completed work moves out of the way.
**Audit trail.** The archive preserves the full context of every change — not just what changed, but the proposal explaining why, the design explaining how, and the tasks showing the work done.
**Spec evolution.** Specs grow organically as changes are archived. Each archive merges its deltas, building up a comprehensive specification over time.
## How It All Fits Together
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ OPENSPEC FLOW │
│ │
│ ┌────────────────┐ │
│ │ 1. START │ /opsx:new creates a change folder │
│ │ CHANGE │ │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ 2. CREATE │ /opsx:ff or /opsx:continue │
│ │ ARTIFACTS │ Creates proposal → specs → design → tasks │
│ │ │ (based on schema dependencies) │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ 3. IMPLEMENT │ /opsx:apply │
│ │ TASKS │ Work through tasks, checking them off │
│ │ │◄──── Update artifacts as you learn │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ 4. VERIFY │ /opsx:verify (optional) │
│ │ WORK │ Check implementation matches specs │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ ┌──────────────────────────────────────────────┐ │
│ │ 5. ARCHIVE │────►│ Delta specs merge into main specs │ │
│ │ CHANGE │ │ Change folder moves to archive/ │ │
│ └────────────────┘ │ Specs are now the updated source of truth │ │
│ └──────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
**The virtuous cycle:**
1. Specs describe current behavior
2. Changes propose modifications (as deltas)
3. Implementation makes the changes real
4. Archive merges deltas into specs
5. Specs now describe the new behavior
6. Next change builds on updated specs
## Glossary
| Term | Definition |
|------|------------|
| **Artifact** | A document within a change (proposal, design, tasks, or delta specs) |
| **Archive** | The process of completing a change and merging its deltas into main specs |
| **Change** | A proposed modification to the system, packaged as a folder with artifacts |
| **Delta spec** | A spec that describes changes (ADDED/MODIFIED/REMOVED) relative to current specs |
| **Domain** | A logical grouping for specs (e.g., `auth/`, `payments/`) |
| **Requirement** | A specific behavior the system must have |
| **Scenario** | A concrete example of a requirement, typically in Given/When/Then format |
| **Schema** | A definition of artifact types and their dependencies |
| **Spec** | A specification describing system behavior, containing requirements and scenarios |
| **Source of truth** | The `openspec/specs/` directory, containing the current agreed-upon behavior |
## Next Steps
- [Getting Started](getting-started.md) - Practical first steps
- [Workflows](workflows.md) - Common patterns and when to use each
- [Commands](commands.md) - Full command reference
- [Customization](customization.md) - Create custom schemas and configure your project
+342
View File
@@ -0,0 +1,342 @@
# Customization
OpenSpec provides three levels of customization:
| Level | What it does | Best for |
|-------|--------------|----------|
| **Project Config** | Set defaults, inject context/rules | Most teams |
| **Custom Schemas** | Define your own workflow artifacts | Teams with unique processes |
| **Global Overrides** | Share schemas across all projects | Power users |
---
## Project Configuration
The `openspec/config.yaml` file is the easiest way to customize OpenSpec for your team. It lets you:
- **Set a default schema** - Skip `--schema` on every command
- **Inject project context** - AI sees your tech stack, conventions, etc.
- **Add per-artifact rules** - Custom rules for specific artifacts
### Quick Setup
```bash
openspec init
```
This walks you through creating a config interactively. Or create one manually:
```yaml
# openspec/config.yaml
schema: spec-driven
context: |
Tech stack: TypeScript, React, Node.js, PostgreSQL
API style: RESTful, documented in docs/api.md
Testing: Jest + React Testing Library
We value backwards compatibility for all public APIs
rules:
proposal:
- Include rollback plan
- Identify affected teams
specs:
- Use Given/When/Then format
- Reference existing patterns before inventing new ones
```
### How It Works
**Default schema:**
```bash
# Without config
openspec new change my-feature --schema spec-driven
# With config - schema is automatic
openspec new change my-feature
```
**Context and rules injection:**
When generating any artifact, your context and rules are injected into the AI prompt:
```xml
<context>
Tech stack: TypeScript, React, Node.js, PostgreSQL
...
</context>
<rules>
- Include rollback plan
- Identify affected teams
</rules>
<template>
[Schema's built-in template]
</template>
```
- **Context** appears in ALL artifacts
- **Rules** ONLY appear for the matching artifact
### Schema Resolution Order
When OpenSpec needs a schema, it checks in this order:
1. CLI flag: `--schema <name>`
2. Change metadata (`.openspec.yaml` in the change folder)
3. Project config (`openspec/config.yaml`)
4. Default (`spec-driven`)
---
## Custom Schemas
When project config isn't enough, create your own schema with a completely custom workflow. Custom schemas live in your project's `openspec/schemas/` directory and are version-controlled with your code.
```text
your-project/
├── openspec/
│ ├── config.yaml # Project config
│ ├── schemas/ # Custom schemas live here
│ │ └── my-workflow/
│ │ ├── schema.yaml
│ │ └── templates/
│ └── changes/ # Your changes
└── src/
```
### Fork an Existing Schema
The fastest way to customize is to fork a built-in schema:
```bash
openspec schema fork spec-driven my-workflow
```
This copies the entire `spec-driven` schema to `openspec/schemas/my-workflow/` where you can edit it freely.
**What you get:**
```text
openspec/schemas/my-workflow/
├── schema.yaml # Workflow definition
└── templates/
├── proposal.md # Template for proposal artifact
├── spec.md # Template for specs
├── design.md # Template for design
└── tasks.md # Template for tasks
```
Now edit `schema.yaml` to change the workflow, or edit templates to change what AI generates.
### Create a Schema from Scratch
For a completely fresh workflow:
```bash
# Interactive
openspec schema init research-first
# Non-interactive
openspec schema init rapid \
--description "Rapid iteration workflow" \
--artifacts "proposal,tasks" \
--default
```
### Schema Structure
A schema defines the artifacts in your workflow and how they depend on each other:
```yaml
# openspec/schemas/my-workflow/schema.yaml
name: my-workflow
version: 1
description: My team's custom workflow
artifacts:
- id: proposal
generates: proposal.md
description: Initial proposal document
template: proposal.md
instruction: |
Create a proposal that explains WHY this change is needed.
Focus on the problem, not the solution.
requires: []
- id: design
generates: design.md
description: Technical design
template: design.md
instruction: |
Create a design document explaining HOW to implement.
requires:
- proposal # Can't create design until proposal exists
- id: tasks
generates: tasks.md
description: Implementation checklist
template: tasks.md
requires:
- design
apply:
requires: [tasks]
tracks: tasks.md
```
**Key fields:**
| Field | Purpose |
|-------|---------|
| `id` | Unique identifier, used in commands and rules |
| `generates` | Output filename (supports globs like `specs/**/*.md`) |
| `template` | Template file in `templates/` directory |
| `instruction` | AI instructions for creating this artifact |
| `requires` | Dependencies - which artifacts must exist first |
### Templates
Templates are markdown files that guide the AI. They're injected into the prompt when creating that artifact.
```markdown
<!-- templates/proposal.md -->
## Why
<!-- Explain the motivation for this change. What problem does this solve? -->
## What Changes
<!-- Describe what will change. Be specific about new capabilities or modifications. -->
## Impact
<!-- Affected code, APIs, dependencies, systems -->
```
Templates can include:
- Section headers the AI should fill in
- HTML comments with guidance for the AI
- Example formats showing expected structure
### Validate Your Schema
Before using a custom schema, validate it:
```bash
openspec schema validate my-workflow
```
This checks:
- `schema.yaml` syntax is correct
- All referenced templates exist
- No circular dependencies
- Artifact IDs are valid
### Use Your Custom Schema
Once created, use your schema with:
```bash
# Specify on command
openspec new change feature --schema my-workflow
# Or set as default in config.yaml
schema: my-workflow
```
### Debug Schema Resolution
Not sure which schema is being used? Check with:
```bash
# See where a specific schema resolves from
openspec schema which my-workflow
# List all available schemas
openspec schema which --all
```
Output shows whether it's from your project, user directory, or the package:
```text
Schema: my-workflow
Source: project
Path: /path/to/project/openspec/schemas/my-workflow
```
---
> **Note:** OpenSpec also supports user-level schemas at `~/.local/share/openspec/schemas/` for sharing across projects, but project-level schemas in `openspec/schemas/` are recommended since they're version-controlled with your code.
---
## Examples
### Rapid Iteration Workflow
A minimal workflow for quick iterations:
```yaml
# openspec/schemas/rapid/schema.yaml
name: rapid
version: 1
description: Fast iteration with minimal overhead
artifacts:
- id: proposal
generates: proposal.md
description: Quick proposal
template: proposal.md
instruction: |
Create a brief proposal for this change.
Focus on what and why, skip detailed specs.
requires: []
- id: tasks
generates: tasks.md
description: Implementation checklist
template: tasks.md
requires: [proposal]
apply:
requires: [tasks]
tracks: tasks.md
```
### Adding a Review Artifact
Fork the default and add a review step:
```bash
openspec schema fork spec-driven with-review
```
Then edit `schema.yaml` to add:
```yaml
- id: review
generates: review.md
description: Pre-implementation review checklist
template: review.md
instruction: |
Create a review checklist based on the design.
Include security, performance, and testing considerations.
requires:
- design
- id: tasks
# ... existing tasks config ...
requires:
- specs
- design
- review # Now tasks require review too
```
---
## See Also
- [CLI Reference: Schema Commands](cli.md#schema-commands) - Full command documentation
-926
View File
@@ -1,926 +0,0 @@
# OpenSpec Experimental Release Plan
This document outlines the plan to release the experimental artifact workflow system for user testing.
## Overview
The goal is to allow users to test the new artifact-driven workflow system alongside the existing OpenSpec commands. This experimental system (`opsx`) provides a more granular, step-by-step approach to creating change artifacts.
## Three Workflow Modes
### 1. Old Workflow (Current Production)
- **Commands**: `/openspec:proposal`, `/openspec:apply`, `/openspec:archive`
- **Behavior**: Hardcoded slash commands that generate all artifacts in one command
- **Status**: Production, unchanged
### 2. New Artifact System - Batch Mode (Future)
- **Commands**: Refactored `/openspec:proposal` using schemas
- **Behavior**: Schema-driven but generates all artifacts at once (like legacy)
- **Status**: Not in scope for this experimental release
- **Note**: This is a future refactor to unify the old system with schemas
### 3. New Artifact System - Granular Mode (Experimental)
- **Commands**: `/opsx:new`, `/opsx:continue`
- **Behavior**: One artifact at a time, dependency-driven, iterative
- **Status**: Target for this experimental release
---
## Work Items
### 1. Rename AWF to OPSX
**Current State:**
- Commands: `/awf:start`, `/awf:continue`
- Files: `.claude/commands/awf/start.md`, `.claude/commands/awf/continue.md`
**Target State:**
- Commands: `/opsx:new`, `/opsx:continue`
- Files: `.claude/commands/opsx/new.md`, `.claude/commands/opsx/continue.md`
**Tasks:**
- [x] Create `.claude/commands/opsx/` directory
- [x] Rename `start.md` → `new.md` and update content
- [x] Copy `continue.md` with updated references
- [x] Update all references from "awf" to "opsx" in command content
- [x] Update frontmatter (name, description) to use "opsx" naming
- [x] Remove `.claude/commands/awf/` directory
**CLI Commands:**
The underlying CLI commands (`openspec status`, `openspec instructions`, etc.) remain unchanged. Only the slash command names change.
---
### 2. Remove WF Skill Files
**Current State:**
- `.claude/commands/wf/start.md` - References non-existent `openspec wf` commands
- `.claude/commands/wf/continue.md` - References non-existent `openspec wf` commands
**Target State:**
- Directory and files removed
**Tasks:**
- [x] Delete `.claude/commands/wf/start.md`
- [x] Delete `.claude/commands/wf/continue.md`
- [x] Delete `.claude/commands/wf/` directory
---
### 3. Add Agent Skills for Experimental Workflow
**Purpose:**
Generate experimental workflow skills using the [Agent Skills](https://agentskills.io/specification) open standard.
**Why Skills Instead of Slash Commands:**
- **Cross-editor compatibility**: Skills work in Claude Code, Cursor, Windsurf, and other compatible editors automatically
- **Simpler implementation**: Single directory (`.claude/skills/`) instead of 18+ editor-specific configurators
- **Standard format**: Open standard with simple YAML frontmatter + markdown
- **User invocation**: Users explicitly invoke skills when they want to use them
**Behavior:**
1. Create `.claude/skills/` directory if it doesn't exist
2. Generate two skills using the Agent Skills specification:
- `openspec-new-change/SKILL.md` - Start a new change with artifact workflow
- `openspec-continue-change/SKILL.md` - Continue working on a change (create next artifact)
3. Skills are added **alongside** existing `/openspec:*` commands (not replacing)
**Supported Editors:**
- Claude Code (native support)
- Cursor (native support via Settings → Rules → Import Settings)
- Windsurf (imports `.claude` configs)
- Cline, Codex, and other Agent Skills-compatible editors
**Tasks:**
- [x] Create skill template content for `openspec-new-change` (based on current opsx:new)
- [x] Create skill template content for `openspec-continue-change` (based on current opsx:continue)
- [x] Add temporary `artifact-experimental-setup` command to CLI
- [x] Implement skill file generation (YAML frontmatter + markdown body)
- [x] Add success message with usage instructions
**Note:** The `artifact-experimental-setup` command is temporary and will be merged into `openspec init` once the experimental workflow is promoted to stable.
**Skill Format:**
Each skill is a directory with a `SKILL.md` file:
```
.claude/skills/
├── openspec-new-change/
│ └── SKILL.md # name, description, instructions
├── openspec-continue-change/
│ └── SKILL.md # name, description, instructions
└── openspec-apply-change/
└── SKILL.md # name, description, instructions
```
**CLI Interface:**
```bash
openspec artifact-experimental-setup
# Output:
# 🧪 Experimental Artifact Workflow Skills Created
#
# ✓ .claude/skills/openspec-new-change/SKILL.md
# ✓ .claude/skills/openspec-continue-change/SKILL.md
# ✓ .claude/skills/openspec-apply-change/SKILL.md
#
# 📖 Usage:
#
# Skills work automatically in compatible editors:
# • Claude Code - Auto-detected, ready to use
# • Cursor - Enable in Settings → Rules → Import Settings
# • Windsurf - Auto-imports from .claude directory
#
# Ask Claude naturally:
# • "I want to start a new OpenSpec change to add <feature>"
# • "Continue working on this change"
#
# Claude will automatically use the appropriate skill.
#
# 💡 This is an experimental feature.
# Feedback welcome at: https://github.com/Fission-AI/OpenSpec/issues
```
**Implementation Notes:**
- Simple file writing: Create directories and write templated `SKILL.md` files (no complex logic)
- Use existing `FileSystemUtils.writeFile()` pattern like slash command configurators
- Template structure: YAML frontmatter + markdown body
- Keep existing `/opsx:*` slash commands for now (manual cleanup later)
- Skills use invocation model (user explicitly asks Claude to use them)
- Skill `description` field guides when Claude suggests using the skill
- Each `SKILL.md` has required fields: `name` (matches directory) and `description`
---
### 4. Update `/opsx:new` Command Content
**Current Behavior (awf:start):**
1. Ask user what they want to build (if no input)
2. Create change directory
3. Show artifact status
4. Show what's ready
5. Get instructions for proposal
6. STOP and wait
**New Behavior (opsx:new):**
Same flow but with updated naming:
- References to "awf" → "opsx"
- References to `/awf:continue` → `/opsx:continue`
- Update frontmatter name/description
**Tasks:**
- [x] Update all "awf" references to "opsx"
- [x] Update command references in prompt text
- [x] Verify CLI commands still work (they use `openspec`, not `awf`)
---
### 5. Update `/opsx:continue` Command Content
**Current Behavior (awf:continue):**
1. Prompt for change selection (if not provided)
2. Check current status
3. Create ONE artifact based on what's ready
4. Show progress and what's unlocked
5. STOP
**New Behavior (opsx:continue):**
Same flow with updated naming.
**Tasks:**
- [x] Update all "awf" references to "opsx"
- [x] Update command references in prompt text
---
### 6. End-to-End Testing
**Objective:**
Run through a complete workflow with Claude using the new skills to create a real feature, validating the entire flow works.
**Test Scenario:**
Use a real OpenSpec feature as the test case (dog-fooding).
**Test Flow:**
1. Run `openspec artifact-experimental-setup` to create skills
2. Verify `.claude/skills/openspec-new-change/SKILL.md` created
3. Verify `.claude/skills/openspec-continue-change/SKILL.md` created
4. Verify `.claude/skills/openspec-apply-change/SKILL.md` created
5. Ask Claude: "I want to start a new OpenSpec change to add feature X"
6. Verify Claude invokes the `openspec-new-change` skill
7. Verify change directory created at `openspec/changes/add-feature-x/`
8. Verify proposal template shown
9. Ask Claude: "Continue working on this change"
10. Verify Claude invokes the `openspec-continue-change` skill
11. Verify `proposal.md` created with content
12. Ask Claude: "Continue" (create specs)
13. Verify `specs/*.md` created
14. Ask Claude: "Continue" (create design)
15. Verify `design.md` created
16. Ask Claude: "Continue" (create tasks)
17. Verify `tasks.md` created
18. Verify status shows 4/4 complete
19. Implement the feature based on tasks
20. Run `/openspec:archive` to archive the change
**Validation Checklist:**
- [ ] `openspec artifact-experimental-setup` creates correct directory structure
- [ ] Skills are auto-detected in Claude Code
- [ ] Skill descriptions trigger appropriate invocations
- [ ] Skills create change directory and show proposal template
- [ ] Skills correctly identify ready artifacts
- [ ] Skills create artifacts with meaningful content
- [ ] Dependency detection works (specs requires proposal, etc.)
- [ ] Progress tracking is accurate
- [ ] Template content is useful and well-structured
- [ ] Error handling works (invalid names, missing changes, etc.)
- [ ] Works with different schemas (spec-driven, tdd)
- [ ] Test in Cursor (Settings → Rules → Import Settings)
**Document Results:**
- Create test log documenting what worked and what didn't
- Note any friction points or confusing UX
- Identify bugs or improvements needed before user release
---
### 7. Documentation for Users
**Create user-facing documentation explaining:**
1. **What is the experimental workflow?**
- A new way to create OpenSpec changes step-by-step using Agent Skills
- One artifact at a time with dependency tracking
- More interactive and iterative than the batch approach
- Works across Claude Code, Cursor, Windsurf, and other compatible editors
2. **How to set up experimental workflow**
```bash
openspec artifact-experimental-setup
```
Note: This is a temporary command that will be integrated into `openspec init` once promoted to stable.
3. **Available skills**
- `openspec-new-change` - Start a new change with artifact workflow
- `openspec-continue-change` - Continue working (create next artifact)
4. **How to use**
- **Claude Code**: Skills are auto-detected, just ask Claude naturally
- "I want to start a new OpenSpec change to add X"
- "Continue working on this change"
- **Cursor**: Enable in Settings → Rules → Import Settings
- **Windsurf**: Auto-imports `.claude` directory
5. **Example workflow**
- Step-by-step walkthrough with natural language interactions
- Show how Claude invokes skills based on user requests
6. **Feedback mechanism**
- GitHub issue template for feedback
- What to report (bugs, UX issues, suggestions)
**Tasks:**
- [ ] Create `docs/experimental-workflow.md` user guide
- [ ] Add GitHub issue template for experimental feedback
- [ ] Update README with mention of experimental features
---
## Dependency Graph
```
1. Remove WF skill files
└── (no dependencies)
2. Rename AWF to OPSX
└── (no dependencies)
3. Add Agent Skills
└── Depends on: Rename AWF to OPSX (uses opsx content as templates)
4. Update opsx:new content
└── Depends on: Rename AWF to OPSX
5. Update opsx:continue content
└── Depends on: Rename AWF to OPSX
6. E2E Testing
└── Depends on: Add Agent Skills (tests the skills workflow)
7. User Documentation
└── Depends on: E2E Testing (need to know final behavior)
```
---
## Out of Scope
The following are explicitly NOT part of this experimental release:
1. **Batch mode refactor** - Making legacy `/openspec:proposal` use schemas
2. **New schemas** - Only shipping with existing `spec-driven` and `tdd`
3. **Schema customization UI** - No `openspec schema list` or similar
4. **Multiple editor support in CLI** - Skills work cross-editor automatically via `.claude/skills/`
5. **Replacing existing commands** - Skills are additive, not replacing `/openspec:*` or `/opsx:*`
---
## Success Criteria
The experimental release is ready when:
1. `openspec-new-change`, `openspec-continue-change`, and `openspec-apply-change` skills work end-to-end
2. `openspec artifact-experimental-setup` creates skills in `.claude/skills/`
3. Skills work in Claude Code and are compatible with Cursor/Windsurf
4. At least one complete workflow has been tested manually
5. User documentation exists explaining how to generate and use skills
6. Feedback mechanism is in place
7. WF skill files are removed
8. No references to "awf" remain in user-facing content
---
## Open Questions
1. **Schema selection** - Should `opsx:new` allow selecting a schema, or always use `spec-driven`?
- Current: Always uses `spec-driven` as default
- Consider: Add `--schema tdd` option or prompt
2. **Namespace in CLI** - Should experimental CLI commands be namespaced?
- Current: `openspec status`, `openspec instructions` (no namespace)
- Alternative: `openspec opsx status` (explicit experimental namespace)
- Recommendation: Keep current, less typing for users
3. **Deprecation path** - If opsx becomes the default, how do we migrate?
- Not needed for experimental release
- Document that command names may change
---
## Estimated Work Breakdown
| Item | Complexity | Notes |
|------|------------|-------|
| Remove WF files | Trivial | Just delete 2 files + directory |
| Rename AWF → OPSX | Low | File renames + content updates |
| Add Agent Skills | **Low** | **Simple: 3-4 files, single output directory, standard format** |
| Update opsx:new content | Low | Text replacements |
| Update opsx:continue content | Low | Text replacements |
| E2E Testing | Medium | Manual testing, documenting results |
| User Documentation | Medium | New docs, issue template |
**Key Improvement:** Switching to Agent Skills reduces complexity significantly:
- **Before:** 20+ files (type definitions, 18+ editor configurators, editor selection UI)
- **After:** 3-4 files (skill templates, simple CLI command)
- **Cross-editor:** Works automatically in Claude Code, Cursor, Windsurf without extra code
---
## User Feedback from E2E Testing
### What Worked Well
1. **Clear dependency graph** ⭐ HIGH PRIORITY - KEEP
- The status command showing blocked/unblocked artifacts was intuitive:
```
[x] proposal
[ ] design
[-] tasks (blocked by: design, specs)
```
- Users always knew what they could work on next
- **Relevance**: Core UX strength to preserve
2. **Structured instructions output** ⭐ HIGH PRIORITY - KEEP
- `openspec instructions <artifact>` gave templates, output paths, and context in one call
- Very helpful for understanding what to create
- **Relevance**: Essential for agent-driven workflow
3. **Simple scaffolding** ✅ WORKS WELL
- `openspec new change "name"` just worked - created directory structure without fuss
- **Relevance**: Good baseline, room for improvement (see pain points)
---
### Pain Points & Confusion
1. **Redundant CLI calls** ⚠️ MEDIUM PRIORITY
- Users called both `status` AND `next` every time, but they overlap significantly
- `status` already shows what's blocked
- **Recommendation**: Consider merging or making `next` give actionable guidance beyond just listing names
- **Relevance**: Reduces friction in iterative workflow
2. **Specs directory structure was ambiguous** 🔥 HIGH PRIORITY - FIX
- Instructions said: `Write to: .../specs/**/*.md`
- Users had to guess: `specs/spec.md`? `specs/game/spec.md`? `specs/tic-tac-toe/spec.md`?
- Users ended up doing manual `mkdir -p .../specs/tic-tac-toe` then writing `spec.md` inside
- **Recommendation**: CLI should scaffold this directory structure automatically
- **Relevance**: Critical agent UX - ambiguous paths cause workflow friction
3. **Repetitive --change flag** ⚠️ MEDIUM PRIORITY
- Every command needed `--change "tic-tac-toe-game"`
- After 10+ calls, this felt verbose
- **Recommendation**: `openspec use "tic-tac-toe-game"` to set context, then subsequent commands assume that change
- **Relevance**: Quality of life improvement for iterative sessions
4. **No validation feedback** 🔥 HIGH PRIORITY - ADD
- After writing each artifact, users just ran `status` hoping it would show `[x]`
- Questions raised:
- How did it know the artifact was "done"? File existence?
- What if spec format was wrong (e.g., wrong heading levels)?
- **Recommendation**: Add `openspec validate --change "name"` to check content quality
- **Relevance**: Critical for user confidence and catching errors early
5. **Query-heavy, action-light CLI** 🔥 HIGH PRIORITY - ENHANCE
- Most commands retrieve info. The only "action" is `new change`
- Artifact creation is manual Write to guessed paths
- **Recommendation**: `openspec create proposal --change "name"` could scaffold the file with template pre-filled, then user just edits
- **Relevance**: Directly impacts agent productivity - reduce manual file writing
6. **Instructions output was verbose** ⚠️ LOW PRIORITY
- XML-style output (`<artifact>`, `<template>`, `<instruction>`) was parseable but long
- Key info (output path, template) was buried in ~50 lines
- **Recommendation**: Add compact mode or structured JSON output for agents
- **Relevance**: Nice-to-have for agent parsing efficiency
---
### Workflow Friction
1. **Mandatory "STOP and wait" after showing proposal template** ⚠️ MEDIUM PRIORITY
- The skill said "STOP and wait" after showing the proposal template
- This felt overly cautious when user had already provided enough context (e.g., "tic tac toe, single player vs AI, minimal aesthetics")
- **Recommendation**: Make the pause optional or conditional based on context clarity
- **Relevance**: Reduces unnecessary round-trips in agent conversations
2. **No connection to implementation** 🔥 HIGH PRIORITY - ROADMAP ITEM
- After 4/4 artifacts complete, then what? The workflow ends at planning
- No `openspec apply` or guidance on how to execute the tasks
- User asked "would you like me to implement?" but that's outside OpenSpec's scope currently
- **Recommendation**: Add implementation bridge - either:
- `openspec apply` command to start execution phase
- Clear handoff to existing `/openspec:apply` workflow
- Documentation on next steps after planning completes
- **Relevance**: Critical missing piece - users expect end-to-end workflow
---
### Priority Summary
**MUST FIX (High Priority):**
1. Specs directory structure ambiguity (#2)
2. Add validation feedback (#4)
3. Make CLI more action-oriented (#5)
4. Bridge to implementation phase (#2 in Workflow Friction)
5. Keep clear dependency graph (#1 in What Worked)
6. Keep structured instructions (#2 in What Worked)
**SHOULD FIX (Medium Priority):**
1. Reduce redundant CLI calls (#1)
2. Repetitive `--change` flag (#3)
3. Mandatory STOP behavior (#1 in Workflow Friction)
**NICE TO HAVE (Low Priority):**
1. Compact instructions output mode (#6)
---
## Design Decisions (from E2E Testing Feedback)
Based on dev testing and analysis of agent workflow friction, we identified three blockers for experimental release and made the following decisions.
### Blockers Identified
From the pain points in E2E testing, three issues are blocking the experimental release:
1. **Specs directory ambiguity** - Agents don't know where to write spec files or how to name capabilities
2. **CLI is query-heavy** - Most commands retrieve info, artifact creation is manual
3. **Apply integration missing** - After 4/4 artifacts complete, no guidance on implementation phase
### Decision 1: Capability Discovery in Proposal (RESOLVED)
**Problem:** The specs artifact instruction says "Create one spec file per capability in `specs/<name>/spec.md`" but:
- Agent doesn't know what `<name>` should be
- Capability identification requires research (existing specs, codebase)
- Proposal template asks for "Affected specs" but doesn't structure it
- Research happens implicitly, output isn't captured
**Decision:** Enrich the proposal template to explicitly capture capability discovery.
**Current proposal template:**
```markdown
## Why
## What Changes
## Impact
- Affected specs: List capabilities... ← vague, easy to skip
- Affected code: ...
```
**New proposal template:**
```markdown
## Why
## What Changes
## Capabilities
### New Capabilities
<!-- Capabilities being introduced (will create new specs/<name>/spec.md) -->
- `<name>`: <brief description of what this capability covers>
### Modified Capabilities
<!-- Existing capabilities being changed (will update existing specs) -->
- `<existing-name>`: <what's changing>
## Impact
<!-- Affected code, APIs, dependencies, systems -->
```
**Rationale:**
- Proposal already asks for capabilities (just poorly) - this makes it explicit
- Captured output is reviewable (vs implicit research that can't be verified)
- Creates clear contract between proposal and specs phases
- Distinguishes NEW vs MODIFIED upfront (critical for specs phase)
- Agent can't skip research - it's part of the deliverable
**Implementation:**
- Update `schemas/spec-driven/templates/proposal.md`
- Update proposal instruction in `schemas/spec-driven/schema.yaml`
- Update skill instructions to guide capability discovery
### Decision 2: CLI Action Commands (IN PROGRESS)
**Problem:** CLI is mostly query-oriented. Agents run `openspec status`, `openspec next`, `openspec instructions` but then must manually write files.
#### Decision 2a: Remove `openspec next` command (RESOLVED)
**Problem:** The `next` command is redundant. It only shows which artifacts are ready, but `status` already shows this information (artifacts with status "ready" vs "blocked" vs "done").
**Current behavior:**
```bash
openspec status --change "X" # Shows: proposal (done), specs (ready), design (blocked), tasks (blocked)
openspec next --change "X" # Shows: ["specs"] ← redundant
```
**Decision:** Remove the `next` command. Agents should use `status` which provides the same info plus more context.
**Implementation:**
- Remove `next` command from CLI
- Update skill instructions to use `status` instead of `next`
- Update AGENTS.md references
#### Decision 2b: CLI Scaffolding (RESOLVED - NO)
**Problem:** After getting instructions, agents manually write files. Should CLI scaffold artifacts instead?
**Options considered:**
- Add `openspec create <artifact>` commands that scaffold files with templates
- Keep current approach where agent writes files directly from instructions
- Hybrid: CLI can scaffold, agent can also write directly
**Decision:** Keep current flow. No scaffolding commands.
**Rationale (from agent ergonomics perspective):**
- One Write is better than multiple Edits - agent composes full content atomically
- `instructions` already provides template in context - scaffolding just moves it to a file
- Fewer tool calls: `instructions` + Write (2) vs `create` + `instructions` + Read + Edit×N (4+)
- Scaffolding doesn't solve the real problem (not knowing WHAT to write)
- Real problem solved by proposal template change (capability discovery)
**For multi-file artifacts (specs):** Scaffolding can't help because CLI doesn't know capability names until proposal is complete. The capability discovery in proposal solves this.
### Decision 3: Apply Integration (RESOLVED)
**Original problem:** After planning completes (4/4 artifacts), the experimental workflow ends. No guidance on implementation.
**Key insight: No phases, just actions.**
Through discussion, we realized phases (planning → implementation → archive) are an artificial constraint. Work is fluid:
- You might start implementing, realize the design is wrong → update design.md
- You're halfway through tasks, discover a new requirement → update specs
- You bounce between "planning" and "implementing" constantly
**The better model: Actions on a Change**
A change is a thing (with artifacts). Actions are verbs you perform on a change. Actions aren't phases - they're fluid operations you can perform anytime.
| Action | What it does | Skill | CLI Command |
|--------|--------------|-------|-------------|
| `new` | Create a change (scaffold directory) | `opsx:new` | `openspec new change` |
| `continue` | Create next artifact (dependency-aware) | `opsx:continue` | `openspec instructions` |
| `apply` | Implement tasks (execute, check off) | `opsx:apply` (NEW) | TBD |
| `update` | Refresh/update artifacts based on learnings | `opsx:update` (NEW) | TBD |
| `explore` | Research, ask questions, understand | `opsx:explore` (NEW) | TBD |
| `validate` | Check artifacts are correct/complete | TBD | `openspec validate` |
| `archive` | Finalize and move to archive | existing | `openspec archive` |
**Key principles:**
- Actions are modeled as skills (primary interface for agents)
- Some skills have matching CLI commands for convenience
- Skills and CLI commands are decoupled - not everything needs both
- Actions can be performed in any order (with soft prerequisites)
- No linear phase gates
**What the schema defines:**
- Artifacts (what they are, where they go)
- Dependencies (what must exist first)
- Required vs optional
- Templates + instructions
**What the schema does NOT define:**
- Phases
- When you can modify things
- Linear workflow
**Progress tracking:**
- tasks.md checkboxes = implementation progress
- Artifact existence = planning progress
- Archive readiness = user decides (or all tasks done)
**For experimental release:**
- Create `opsx:apply` skill (guidance for implementing tasks)
- Document the "actions on a change" model
- Other actions (update, explore) can come later
---
### Design: `openspec-apply-change` Skill
#### Overview
The apply skill guides agents through implementing tasks from a completed (or in-progress) change. Unlike the old `/openspec:apply` command, this skill:
- Is **fluid** - can be invoked anytime, not just after all artifacts are done
- Allows **artifact updates** - if implementation reveals issues, update design/specs
- Works **until done** - keeps going through tasks until complete or blocked
- Tracks **progress via checkboxes** - tasks.md is the source of truth
#### Skill Metadata
```yaml
name: openspec-apply-change
description: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.
```
#### When to Invoke
The skill should be invoked when:
- User says "implement this change" or "start implementing"
- User says "work on the tasks" or "do the next task"
- User says "apply this change"
- All artifacts are complete and user wants to proceed
- User wants to continue implementation after a break
#### Input
- Optionally: change name
- Optionally: specific task number to work on
- If omitted: prompt for change selection (same pattern as continue-change)
#### Steps
```markdown
**Steps**
1. **If no change name provided, prompt for selection**
Run `openspec list --json` to get available changes. Use **AskUserQuestion** to let user select.
Show changes that have tasks.md (implementation-ready).
Mark changes with incomplete tasks as "(In Progress)".
2. **Get apply instructions**
```bash
openspec instructions apply --change "<name>" --json
```
This returns:
- Context file paths (proposal, specs, design, tasks)
- Progress (total, complete, remaining)
- Task list with status
- Dynamic instruction based on current state
**Handle states:**
- If blocked (missing artifacts): show message, suggest `openspec-continue-change`
- If all done: congratulate, suggest archive
- Otherwise: proceed to implementation
3. **Read context files**
Read the files listed in the instructions:
- `proposal.md` - why and what
- `specs/*.md` - requirements and scenarios
- `design.md` - technical approach (if exists)
- `tasks.md` - the implementation checklist
4. **Show current progress**
Display:
- Progress: "N/M tasks complete"
- Remaining tasks overview
- Dynamic instruction from CLI
5. **Implement tasks (loop until done or blocked)**
For each pending task:
- Show which task is being worked on
- Make the code changes required
- Keep changes minimal and focused
- Mark task complete in tasks.md: `- [ ]` → `- [x]`
- Continue to next task
**Pause if:**
- Task is unclear → ask for clarification
- Implementation reveals a design issue → suggest updating artifacts
- Error or blocker encountered → report and wait for guidance
- User interrupts
6. **On completion or pause, show status**
Display:
- Tasks completed this session
- Overall progress: "N/M tasks complete"
- If all done: suggest archive
- If paused: explain why and wait for guidance
```
#### Output Format
**During implementation:**
```
## Implementing: add-user-auth
Working on task 3/7: Create UserAuth service class
[...implementation happening...]
✓ Task complete
Working on task 4/7: Add login endpoint to AuthController
[...implementation happening...]
✓ Task complete
Working on task 5/7: Add JWT token generation
[...implementation happening...]
```
**On completion:**
```
## Implementation Complete
**Change:** add-user-auth
**Progress:** 7/7 tasks complete ✓
### Completed This Session
- [x] Create UserAuth service class
- [x] Add login endpoint to AuthController
- [x] Add JWT token generation
- [x] Add logout endpoint
- [x] Add auth middleware
- [x] Write unit tests
- [x] Update API documentation
All tasks complete! Ready to archive this change.
```
**On pause (issue encountered):**
```
## Implementation Paused
**Change:** add-user-auth
**Progress:** 4/7 tasks complete
### Issue Encountered
Task 5 "Add JWT token generation" - the design specifies using RS256 but
the existing auth library only supports HS256.
**Options:**
1. Update design.md to use HS256 instead
2. Add a new JWT library that supports RS256
3. Other approach
What would you like to do?
```
#### Guardrails
- Keep going through tasks until done or blocked
- Always read context before starting (specs, design)
- If task is ambiguous, pause and ask before implementing
- If implementation reveals issues, pause and suggest artifact updates
- Keep code changes minimal and scoped to each task
- Update task checkbox immediately after completing each task
- Pause on errors, blockers, or unclear requirements - don't guess
#### Fluid Workflow Integration
The apply skill supports the "actions on a change" model:
**Can be invoked anytime:**
- Before all artifacts are done (if tasks.md exists)
- After partial implementation
- Interleaved with other actions (update, continue)
**Allows artifact updates:**
- If implementation reveals design issues → suggest `opsx:update` or manual edit
- If requirements need clarification → suggest updating specs
- Not phase-locked - work fluidly
**Example fluid workflow:**
```
User: "Implement add-user-auth"
→ openspec-apply-change: implements tasks 1, 2, 3, 4...
→ Pauses at task 5: "Design says RS256 but library only supports HS256"
User: "Let's use HS256 instead, update the design"
→ User edits design.md (or uses opsx:update in future)
User: "Continue implementing"
→ openspec-apply-change: implements tasks 5, 6, 7
→ "All tasks complete! Ready to archive."
```
#### CLI Commands Used
```bash
openspec list --json # List changes for selection
openspec status --change "<name>" # Check artifact completion
openspec instructions apply --change "<name>" # Get apply instructions (NEW)
# File reads via Read tool for proposal, specs, design, tasks
# File edits via Edit tool for checking off tasks
```
#### New CLI Command: `openspec instructions apply`
For consistency with artifact instructions.
**Usage:**
```bash
openspec instructions apply --change "<name>" [--json]
```
**Output (Markdown format):**
```markdown
## Apply: add-user-auth
### Context Files
- proposal: openspec/changes/add-user-auth/proposal.md
- specs: openspec/changes/add-user-auth/specs/**/*.md
- design: openspec/changes/add-user-auth/design.md
- tasks: openspec/changes/add-user-auth/tasks.md
### Progress
2/7 complete
### Tasks
- [x] Create UserAuth service class
- [x] Add login endpoint
- [ ] Add JWT token generation
- [ ] Add logout endpoint
- [ ] Add auth middleware
- [ ] Write unit tests
- [ ] Update API documentation
### Instruction
Read context files, work through pending tasks, mark complete as you go.
Pause if you hit blockers or need clarification.
```
**Benefits of CLI command:**
- **Consistency** - same pattern as `openspec instructions <artifact>`
- **Structured output** - progress, tasks, context paths in one call
- **Clean format** - markdown is readable and compact (vs verbose XML)
- **Extensibility** - can add more sections later if needed
- **JSON option** - `--json` flag available for programmatic use
#### Differences from Old `/openspec:apply`
| Aspect | Old `/openspec:apply` | New `openspec-apply-change` |
|--------|----------------------|----------------------------|
| Invocation | After all artifacts done | Anytime (if tasks.md exists) |
| Granularity | All tasks at once | All tasks, but pauses on issues |
| Artifact updates | Not mentioned | Encouraged when needed |
| Progress tracking | Update all at end | Update after each task |
| Flow control | Push through everything | Pause on blockers, resume after |
| Context loading | Read once at start | Read context, reference as needed |
| Issue handling | Not specified | Pause, present options, wait for guidance |
#### Implementation Notes
1. **Add CLI command**: Add `openspec instructions apply` to artifact-workflow.ts
- Parse tasks.md for progress (count done/pending)
- Return context paths, progress, task list, simple instruction
2. **Add to skill-templates.ts**: Create `getApplyChangeSkillTemplate()` function
3. **Update artifact-experimental-setup**: Generate this skill alongside new/continue
4. **Update skills list**: Add to `.claude/skills/` directory
5. **Test the flow**: Verify it works with existing changes that have tasks.md
---
## Next Steps
1. ~~Review this plan and confirm scope~~ (Done - blockers identified)
2. ~~Design decisions~~ (Done - all 3 blockers resolved)
3. ~~Design apply skill~~ (Done - documented above)
4. ~~Implement proposal template change (Decision 1 - capability discovery)~~ (Done)
5. ~~Remove `openspec next` command (Decision 2a)~~ (Done)
6. ~~Add `openspec instructions apply` CLI command~~ (Done)
7. ~~Create `openspec-apply-change` skill~~ (Done)
8. Conduct E2E testing with updated workflow
9. Write user docs (document "actions on a change" model)
10. Release to test users
+273
View File
@@ -0,0 +1,273 @@
# Getting Started
This guide explains how OpenSpec works after you've installed and initialized it. For installation instructions, see the [main README](../README.md#quick-start).
## How It Works
OpenSpec helps you and your AI coding assistant agree on what to build before any code is written. The workflow follows a simple pattern:
```
┌────────────────────┐
│ Start a Change │ /opsx:new
└────────┬───────────┘
│
▼
┌────────────────────┐
│ Create Artifacts │ /opsx:ff or /opsx:continue
│ (proposal, specs, │
│ design, tasks) │
└────────┬───────────┘
│
▼
┌────────────────────┐
│ Implement Tasks │ /opsx:apply
│ (AI writes code) │
└────────┬───────────┘
│
▼
┌────────────────────┐
│ Archive & Merge │ /opsx:archive
│ Specs │
└────────────────────┘
```
## What OpenSpec Creates
After running `openspec init`, your project has this structure:
```
openspec/
├── specs/ # Source of truth (your system's behavior)
│ └── <domain>/
│ └── spec.md
├── changes/ # Proposed updates (one folder per change)
│ └── <change-name>/
│ ├── proposal.md
│ ├── design.md
│ ├── tasks.md
│ └── specs/ # Delta specs (what's changing)
│ └── <domain>/
│ └── spec.md
└── config.yaml # Project configuration (optional)
```
**Two key directories:**
- **`specs/`** - The source of truth. These specs describe how your system currently behaves. Organized by domain (e.g., `specs/auth/`, `specs/payments/`).
- **`changes/`** - Proposed modifications. Each change gets its own folder with all related artifacts. When a change is complete, its specs merge into the main `specs/` directory.
## Understanding Artifacts
Each change folder contains artifacts that guide the work:
| Artifact | Purpose |
|----------|---------|
| `proposal.md` | The "why" and "what" - captures intent, scope, and approach |
| `specs/` | Delta specs showing ADDED/MODIFIED/REMOVED requirements |
| `design.md` | The "how" - technical approach and architecture decisions |
| `tasks.md` | Implementation checklist with checkboxes |
**Artifacts build on each other:**
```
proposal ──► specs ──► design ──► tasks ──► implement
▲ ▲ ▲ │
└───────────┴──────────┴────────────────────┘
update as you learn
```
You can always go back and refine earlier artifacts as you learn more during implementation.
## How Delta Specs Work
Delta specs are the key concept in OpenSpec. They show what's changing relative to your current specs.
### The Format
Delta specs use sections to indicate the type of change:
```markdown
# Delta for Auth
## ADDED Requirements
### Requirement: Two-Factor Authentication
The system MUST require a second factor during login.
#### Scenario: OTP required
- GIVEN a user with 2FA enabled
- WHEN the user submits valid credentials
- THEN an OTP challenge is presented
## MODIFIED Requirements
### Requirement: Session Timeout
The system SHALL expire sessions after 30 minutes of inactivity.
(Previously: 60 minutes)
#### Scenario: Idle timeout
- GIVEN an authenticated session
- WHEN 30 minutes pass without activity
- THEN the session is invalidated
## REMOVED Requirements
### Requirement: Remember Me
(Deprecated in favor of 2FA)
```
### What Happens on Archive
When you archive a change:
1. **ADDED** requirements are appended to the main spec
2. **MODIFIED** requirements replace the existing version
3. **REMOVED** requirements are deleted from the main spec
The change folder moves to `openspec/changes/archive/` for audit history.
## Example: Your First Change
Let's walk through adding dark mode to an application.
### 1. Start the Change
```
You: /opsx:new add-dark-mode
AI: Created openspec/changes/add-dark-mode/
Ready to create: proposal
```
### 2. Create Artifacts
Use `/opsx:ff` (fast-forward) to create all planning artifacts at once:
```
You: /opsx:ff
AI: Creating artifacts for add-dark-mode...
✓ proposal.md — why we're doing this, what's changing
✓ specs/ — requirements and scenarios
✓ design.md — technical approach
✓ tasks.md — implementation checklist
Ready for implementation!
```
### 3. What Gets Created
**proposal.md** - Captures the intent:
```markdown
# Proposal: Add Dark Mode
## Intent
Users have requested a dark mode option to reduce eye strain
during nighttime usage.
## Scope
- Add theme toggle in settings
- Support system preference detection
- Persist preference in localStorage
## Approach
Use CSS custom properties for theming with a React context
for state management.
```
**specs/ui/spec.md** - Delta showing new requirements:
```markdown
# Delta for UI
## ADDED Requirements
### Requirement: Theme Selection
The system SHALL allow users to choose between light and dark themes.
#### Scenario: Manual toggle
- GIVEN a user on any page
- WHEN the user clicks the theme toggle
- THEN the theme switches immediately
- AND the preference persists across sessions
#### Scenario: System preference
- GIVEN a user with no saved preference
- WHEN the application loads
- THEN the system's preferred color scheme is used
```
**tasks.md** - Implementation checklist:
```markdown
# Tasks
## 1. Theme Infrastructure
- [ ] 1.1 Create ThemeContext with light/dark state
- [ ] 1.2 Add CSS custom properties for colors
- [ ] 1.3 Implement localStorage persistence
## 2. UI Components
- [ ] 2.1 Create ThemeToggle component
- [ ] 2.2 Add toggle to settings page
- [ ] 2.3 Update Header to include quick toggle
## 3. Styling
- [ ] 3.1 Define dark theme color palette
- [ ] 3.2 Update components to use CSS variables
```
### 4. Implement
```
You: /opsx:apply
AI: Working through tasks...
✓ 1.1 Created ThemeContext with light/dark state
✓ 1.2 Added CSS custom properties to globals.css
✓ 1.3 Implemented localStorage persistence
✓ 2.1 Created ThemeToggle component
...
All tasks complete!
```
During implementation, if you discover the design needs adjustment, just update the artifact and continue.
### 5. Archive
```
You: /opsx:archive
AI: Archiving add-dark-mode...
✓ Merged specs into openspec/specs/ui/spec.md
✓ Moved to openspec/changes/archive/2025-01-24-add-dark-mode/
Done! Ready for the next feature.
```
Your delta specs are now part of the main specs, documenting how your system works.
## Verifying and Reviewing
Use the CLI to check on your changes:
```bash
# List active changes
openspec list
# View change details
openspec show add-dark-mode
# Validate spec formatting
openspec validate add-dark-mode
# Interactive dashboard
openspec view
```
## Next Steps
- [Workflows](workflows.md) - Common patterns and when to use each command
- [Commands](commands.md) - Full reference for all slash commands
- [Concepts](concepts.md) - Deeper understanding of specs, changes, and schemas
- [Customization](customization.md) - Make OpenSpec work your way
+79
View File
@@ -0,0 +1,79 @@
# Installation
## Prerequisites
- **Node.js 20.19.0 or higher** — Check your version: `node --version`
## Package Managers
### npm
```bash
npm install -g @fission-ai/openspec@latest
```
### pnpm
```bash
pnpm add -g @fission-ai/openspec@latest
```
### yarn
```bash
yarn global add @fission-ai/openspec@latest
```
### bun
```bash
bun add -g @fission-ai/openspec@latest
```
## Nix
Run OpenSpec directly without installation:
```bash
nix run github:Fission-AI/OpenSpec -- init
```
Or install to your profile:
```bash
nix profile install github:Fission-AI/OpenSpec
```
Or add to your development environment in `flake.nix`:
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
openspec.url = "github:Fission-AI/OpenSpec";
};
outputs = { nixpkgs, openspec, ... }: {
devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
buildInputs = [ openspec.packages.x86_64-linux.default ];
};
};
}
```
## Verify Installation
```bash
openspec --version
```
## Next Steps
After installing, initialize OpenSpec in your project:
```bash
cd your-project
openspec init
```
See [Getting Started](getting-started.md) for a full walkthrough.
+575
View File
@@ -0,0 +1,575 @@
# Migrating to OPSX
This guide helps you transition from the legacy OpenSpec workflow to OPSX. The migration is designed to be smooth—your existing work is preserved, and the new system offers more flexibility.
## What's Changing?
OPSX replaces the old phase-locked workflow with a fluid, action-based approach. Here's the key shift:
| Aspect | Legacy | OPSX |
|--------|--------|------|
| **Commands** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` | `/opsx:new`, `/opsx:continue`, `/opsx:apply`, and more |
| **Workflow** | Create all artifacts at once | Create incrementally or all at once—your choice |
| **Going back** | Awkward phase gates | Natural—update any artifact anytime |
| **Customization** | Fixed structure | Schema-driven, fully hackable |
| **Configuration** | `CLAUDE.md` with markers + `project.md` | Clean config in `openspec/config.yaml` |
**The philosophy change:** Work isn't linear. OPSX stops pretending it is.
---
## Before You Begin
### Your Existing Work Is Safe
The migration process is designed with preservation in mind:
- **Active changes in `openspec/changes/`** — Completely preserved. You can continue them with OPSX commands.
- **Archived changes** — Untouched. Your history remains intact.
- **Main specs in `openspec/specs/`** — Untouched. These are your source of truth.
- **Your content in CLAUDE.md, AGENTS.md, etc.** — Preserved. Only the OpenSpec marker blocks are removed; everything you wrote stays.
### What Gets Removed
Only OpenSpec-managed files that are being replaced:
| What | Why |
|------|-----|
| Legacy slash command directories/files | Replaced by the new skills system |
| `openspec/AGENTS.md` | Obsolete workflow trigger |
| OpenSpec markers in `CLAUDE.md`, `AGENTS.md`, etc. | No longer needed |
**Legacy command locations by tool** (examples—your tool may vary):
- Claude Code: `.claude/commands/openspec/`
- Cursor: `.cursor/commands/openspec-*.md`
- Windsurf: `.windsurf/workflows/openspec-*.md`
- Cline: `.clinerules/workflows/openspec-*.md`
- Roo: `.roo/commands/openspec-*.md`
- GitHub Copilot: `.github/prompts/openspec-*.prompt.md`
- And others (Augment, Continue, Amazon Q, etc.)
The migration detects whichever tools you have configured and cleans up their legacy files.
The removal list may seem long, but these are all files that OpenSpec originally created. Your own content is never deleted.
### What Needs Your Attention
One file requires manual migration:
**`openspec/project.md`** — This file isn't deleted automatically because it may contain project context you've written. You'll need to:
1. Review its contents
2. Move useful context to `openspec/config.yaml` (see guidance below)
3. Delete the file when ready
**Why we made this change:**
The old `project.md` was passive—agents might read it, might not, might forget what they read. We found reliability was inconsistent.
The new `config.yaml` context is **actively injected into every OpenSpec planning request**. This means your project conventions, tech stack, and rules are always present when the AI is creating artifacts. Higher reliability.
**The tradeoff:**
Because context is injected into every request, you'll want to be concise. Focus on what really matters:
- Tech stack and key conventions
- Non-obvious constraints the AI needs to know
- Rules that frequently got ignored before
Don't worry about getting it perfect. We're still learning what works best here, and we'll be improving how context injection works as we experiment.
---
## Running the Migration
Both `openspec init` and `openspec update` detect legacy files and guide you through the same cleanup process. Use whichever fits your situation:
### Using `openspec init`
Run this if you want to add new tools or reconfigure which tools are set up:
```bash
openspec init
```
The init command detects legacy files and guides you through cleanup:
```
Upgrading to the new OpenSpec
OpenSpec now uses agent skills, the emerging standard across coding
agents. This simplifies your setup while keeping everything working
as before.
Files to remove
No user content to preserve:
• .claude/commands/openspec/
• openspec/AGENTS.md
Files to update
OpenSpec markers will be removed, your content preserved:
• CLAUDE.md
• AGENTS.md
Needs your attention
• openspec/project.md
We won't delete this file. It may contain useful project context.
The new openspec/config.yaml has a "context:" section for planning
context. This is included in every OpenSpec request and works more
reliably than the old project.md approach.
Review project.md, move any useful content to config.yaml's context
section, then delete the file when ready.
? Upgrade and clean up legacy files? (Y/n)
```
**What happens when you say yes:**
1. Legacy slash command directories are removed
2. OpenSpec markers are stripped from `CLAUDE.md`, `AGENTS.md`, etc. (your content stays)
3. `openspec/AGENTS.md` is deleted
4. New skills are installed in `.claude/skills/`
5. `openspec/config.yaml` is created with a default schema
### Using `openspec update`
Run this if you just want to migrate and refresh your existing tools to the latest version:
```bash
openspec update
```
The update command also detects and cleans up legacy artifacts, then refreshes your skills to the latest version.
### Non-Interactive / CI Environments
For scripted migrations:
```bash
openspec init --force --tools claude
```
The `--force` flag skips prompts and auto-accepts cleanup.
---
## Migrating project.md to config.yaml
The old `openspec/project.md` was a freeform markdown file for project context. The new `openspec/config.yaml` is structured and—critically—**injected into every planning request** so your conventions are always present when the AI works.
### Before (project.md)
```markdown
# Project Context
This is a TypeScript monorepo using React and Node.js.
We use Jest for testing and follow strict ESLint rules.
Our API is RESTful and documented in docs/api.md.
## Conventions
- All public APIs must maintain backwards compatibility
- New features should include tests
- Use Given/When/Then format for specifications
```
### After (config.yaml)
```yaml
schema: spec-driven
context: |
Tech stack: TypeScript, React, Node.js
Testing: Jest with React Testing Library
API: RESTful, documented in docs/api.md
We maintain backwards compatibility for all public APIs
rules:
proposal:
- Include rollback plan for risky changes
specs:
- Use Given/When/Then format for scenarios
- Reference existing patterns before inventing new ones
design:
- Include sequence diagrams for complex flows
```
### Key Differences
| project.md | config.yaml |
|------------|-------------|
| Freeform markdown | Structured YAML |
| One blob of text | Separate context and per-artifact rules |
| Unclear when it's used | Context appears in ALL artifacts; rules appear in matching artifacts only |
| No schema selection | Explicit `schema:` field sets default workflow |
### What to Keep, What to Drop
When migrating, be selective. Ask yourself: "Does the AI need this for *every* planning request?"
**Good candidates for `context:`**
- Tech stack (languages, frameworks, databases)
- Key architectural patterns (monorepo, microservices, etc.)
- Non-obvious constraints ("we can't use library X because...")
- Critical conventions that often get ignored
**Move to `rules:` instead**
- Artifact-specific formatting ("use Given/When/Then in specs")
- Review criteria ("proposals must include rollback plans")
- These only appear for the matching artifact, keeping other requests lighter
**Leave out entirely**
- General best practices the AI already knows
- Verbose explanations that could be summarized
- Historical context that doesn't affect current work
### Migration Steps
1. **Create config.yaml** (if not already created by init):
```yaml
schema: spec-driven
```
2. **Add your context** (be concise—this goes into every request):
```yaml
context: |
Your project background goes here.
Focus on what the AI genuinely needs to know.
```
3. **Add per-artifact rules** (optional):
```yaml
rules:
proposal:
- Your proposal-specific guidance
specs:
- Your spec-writing rules
```
4. **Delete project.md** once you've moved everything useful.
**Don't overthink it.** Start with the essentials and iterate. If you notice the AI missing something important, add it. If context feels bloated, trim it. This is a living document.
### Need Help? Use This Prompt
If you're unsure how to distill your project.md, ask your AI assistant:
```
I'm migrating from OpenSpec's old project.md to the new config.yaml format.
Here's my current project.md:
[paste your project.md content]
Please help me create a config.yaml with:
1. A concise `context:` section (this gets injected into every planning request, so keep it tight—focus on tech stack, key constraints, and conventions that often get ignored)
2. `rules:` for specific artifacts if any content is artifact-specific (e.g., "use Given/When/Then" belongs in specs rules, not global context)
Leave out anything generic that AI models already know. Be ruthless about brevity.
```
The AI will help you identify what's essential vs. what can be trimmed.
---
## The New Commands
After migration, you have 9 OPSX commands instead of 3:
| Command | Purpose |
|---------|---------|
| `/opsx:explore` | Think through ideas with no structure |
| `/opsx:new` | Start a new change |
| `/opsx:continue` | Create the next artifact (one at a time) |
| `/opsx:ff` | Fast-forward—create all planning artifacts at once |
| `/opsx:apply` | Implement tasks from tasks.md |
| `/opsx:verify` | Validate implementation matches specs |
| `/opsx:sync` | Preview spec merge (optional—archive prompts if needed) |
| `/opsx:archive` | Finalize and archive the change |
| `/opsx:bulk-archive` | Archive multiple changes at once |
### Command Mapping from Legacy
| Legacy | OPSX Equivalent |
|--------|-----------------|
| `/openspec:proposal` | `/opsx:new` then `/opsx:ff` |
| `/openspec:apply` | `/opsx:apply` |
| `/openspec:archive` | `/opsx:archive` |
### New Capabilities
**Granular artifact creation:**
```
/opsx:continue
```
Creates one artifact at a time based on dependencies. Use this when you want to review each step.
**Exploration mode:**
```
/opsx:explore
```
Think through ideas with a partner before committing to a change.
---
## Understanding the New Architecture
### From Phase-Locked to Fluid
The legacy workflow forced linear progression:
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PLANNING │ ───► │ IMPLEMENTING │ ───► │ ARCHIVING │
│ PHASE │ │ PHASE │ │ PHASE │
└──────────────┘ └──────────────┘ └──────────────┘
If you're in implementation and realize the design is wrong?
Too bad. Phase gates don't let you go back easily.
```
OPSX uses actions, not phases:
```
┌────────────────────────────────────────┐
│ ACTIONS (not phases) │
│ │
│ new ◄──► continue ◄──► apply ◄──► archive │
│ │ │ │ │ │
│ └──────────┴───────────┴───────────┘ │
│ any order │
└────────────────────────────────────────┘
```
### Dependency Graph
Artifacts form a directed graph. Dependencies are enablers, not gates:
```
proposal
(root node)
│
┌─────────────┴─────────────┐
│ │
▼ ▼
specs design
(requires: (requires:
proposal) proposal)
│ │
└─────────────┬─────────────┘
│
▼
tasks
(requires:
specs, design)
```
When you run `/opsx:continue`, it checks what's ready and offers the next artifact. You can also create multiple ready artifacts in any order.
### Skills vs Commands
The legacy system used tool-specific command files:
```
.claude/commands/openspec/
├── proposal.md
├── apply.md
└── archive.md
```
OPSX uses the emerging **skills** standard:
```
.claude/skills/
├── openspec-explore/SKILL.md
├── openspec-new-change/SKILL.md
├── openspec-continue-change/SKILL.md
├── openspec-apply-change/SKILL.md
└── ...
```
Skills are recognized across multiple AI coding tools and provide richer metadata.
---
## Continuing Existing Changes
Your in-progress changes work seamlessly with OPSX commands.
**Have an active change from the legacy workflow?**
```
/opsx:apply add-my-feature
```
OPSX reads the existing artifacts and continues from where you left off.
**Want to add more artifacts to an existing change?**
```
/opsx:continue add-my-feature
```
Shows what's ready to create based on what already exists.
**Need to see status?**
```bash
openspec status --change add-my-feature
```
---
## The New Config System
### config.yaml Structure
```yaml
# Required: Default schema for new changes
schema: spec-driven
# Optional: Project context (max 50KB)
# Injected into ALL artifact instructions
context: |
Your project background, tech stack,
conventions, and constraints.
# Optional: Per-artifact rules
# Only injected into matching artifacts
rules:
proposal:
- Include rollback plan
specs:
- Use Given/When/Then format
design:
- Document fallback strategies
tasks:
- Break into 2-hour maximum chunks
```
### Schema Resolution
When determining which schema to use, OPSX checks in order:
1. **CLI flag**: `--schema <name>` (highest priority)
2. **Change metadata**: `.openspec.yaml` in the change directory
3. **Project config**: `openspec/config.yaml`
4. **Default**: `spec-driven`
### Available Schemas
| Schema | Artifacts | Best For |
|--------|-----------|----------|
| `spec-driven` | proposal → specs → design → tasks | Most projects |
List all available schemas:
```bash
openspec workflow schemas
```
### Custom Schemas
Create your own workflow:
```bash
openspec schema init my-workflow
```
Or fork an existing one:
```bash
openspec schema fork spec-driven my-workflow
```
See [Customization](customization.md) for details.
---
## Troubleshooting
### "Legacy files detected in non-interactive mode"
You're running in a CI or non-interactive environment. Use:
```bash
openspec init --force
```
### Commands not appearing after migration
Restart your IDE. Skills are detected at startup.
### "Unknown artifact ID in rules"
Check that your `rules:` keys match your schema's artifact IDs:
- **spec-driven**: `proposal`, `specs`, `design`, `tasks`
Run this to see valid artifact IDs:
```bash
openspec workflow schemas --json
```
### Config not being applied
1. Ensure the file is at `openspec/config.yaml` (not `.yml`)
2. Validate YAML syntax
3. Config changes take effect immediately—no restart needed
### project.md not migrated
The system intentionally preserves `project.md` because it may contain your custom content. Review it manually, move useful parts to `config.yaml`, then delete it.
### Want to see what would be cleaned up?
Run init and decline the cleanup prompt—you'll see the full detection summary without any changes being made.
---
## Quick Reference
### Files After Migration
```
project/
├── openspec/
│ ├── specs/ # Unchanged
│ ├── changes/ # Unchanged
│ │ └── archive/ # Unchanged
│ └── config.yaml # NEW: Project configuration
├── .claude/
│ └── skills/ # NEW: OPSX skills
│ ├── openspec-explore/
│ ├── openspec-new-change/
│ └── ...
├── CLAUDE.md # OpenSpec markers removed, your content preserved
└── AGENTS.md # OpenSpec markers removed, your content preserved
```
### What's Gone
- `.claude/commands/openspec/` — replaced by `.claude/skills/`
- `openspec/AGENTS.md` — obsolete
- `openspec/project.md` — migrate to `config.yaml`, then delete
- OpenSpec marker blocks in `CLAUDE.md`, `AGENTS.md`, etc.
### Command Cheatsheet
```
/opsx:new Start a change
/opsx:continue Create next artifact
/opsx:ff Create all planning artifacts
/opsx:apply Implement tasks
/opsx:archive Finish and archive
```
---
## Getting Help
- **Discord**: [discord.gg/YctCnvvshC](https://discord.gg/YctCnvvshC)
- **GitHub Issues**: [github.com/Fission-AI/OpenSpec/issues](https://github.com/Fission-AI/OpenSpec/issues)
- **Documentation**: [docs/opsx.md](opsx.md) for the full OPSX reference
+115
View File
@@ -0,0 +1,115 @@
# Multi-Language Guide
Configure OpenSpec to generate artifacts in languages other than English.
## Quick Setup
Add a language instruction to your `openspec/config.yaml`:
```yaml
schema: spec-driven
context: |
Language: Portuguese (pt-BR)
All artifacts must be written in Brazilian Portuguese.
# Your other project context below...
Tech stack: TypeScript, React, Node.js
```
That's it. All generated artifacts will now be in Portuguese.
## Language Examples
### Portuguese (Brazil)
```yaml
context: |
Language: Portuguese (pt-BR)
All artifacts must be written in Brazilian Portuguese.
```
### Spanish
```yaml
context: |
Idioma: Español
Todos los artefactos deben escribirse en español.
```
### Chinese (Simplified)
```yaml
context: |
语言:中文(简体)
所有产出物必须用简体中文撰写。
```
### Japanese
```yaml
context: |
言語:日本語
すべての成果物は日本語で作成してください。
```
### French
```yaml
context: |
Langue : Français
Tous les artefacts doivent être rédigés en français.
```
### German
```yaml
context: |
Sprache: Deutsch
Alle Artefakte müssen auf Deutsch verfasst werden.
```
## Tips
### Handle Technical Terms
Decide how to handle technical terminology:
```yaml
context: |
Language: Japanese
Write in Japanese, but:
- Keep technical terms like "API", "REST", "GraphQL" in English
- Code examples and file paths remain in English
```
### Combine with Other Context
Language settings work alongside your other project context:
```yaml
schema: spec-driven
context: |
Language: Portuguese (pt-BR)
All artifacts must be written in Brazilian Portuguese.
Tech stack: TypeScript, React 18, Node.js 20
Database: PostgreSQL with Prisma ORM
```
## Verification
To verify your language config is working:
```bash
# Check the instructions - should show your language context
openspec instructions proposal --change my-change
# Output will include your language context
```
## Related Documentation
- [Customization Guide](./customization.md) - Project configuration options
- [Workflows Guide](./workflows.md) - Full workflow documentation
+645
View File
@@ -0,0 +1,645 @@
# OPSX Workflow
> **Compatibility:** Claude Code only (for now). Feedback welcome on [Discord](https://discord.gg/YctCnvvshC).
## What Is It?
OPSX is a **fluid, iterative workflow** for OpenSpec changes. No more rigid phases — just actions you can take anytime.
## Why This Exists
The standard OpenSpec workflow works, but it's **locked down**:
- **Instructions are hardcoded** — buried in TypeScript, you can't change them
- **All-or-nothing** — one big command creates everything, can't test individual pieces
- **Fixed structure** — same workflow for everyone, no customization
- **Black box** — when AI output is bad, you can't tweak the prompts
**OPSX opens it up.** Now anyone can:
1. **Experiment with instructions** — edit a template, see if the AI does better
2. **Test granularly** — validate each artifact's instructions independently
3. **Customize workflows** — define your own artifacts and dependencies
4. **Iterate quickly** — change a template, test immediately, no rebuild
```
Standard workflow: OPSX:
┌────────────────────────┐ ┌────────────────────────┐
│ Hardcoded in package │ │ schema.yaml │◄── You edit this
│ (can't change) │ │ templates/*.md │◄── Or this
│ ↓ │ │ ↓ │
│ Wait for new release │ │ Instant effect │
│ ↓ │ │ ↓ │
│ Hope it's better │ │ Test it yourself │
└────────────────────────┘ └────────────────────────┘
```
**This is for everyone:**
- **Teams** — create workflows that match how you actually work
- **Power users** — tweak prompts to get better AI outputs for your codebase
- **OpenSpec contributors** — experiment with new approaches without releases
We're all still learning what works best. OPSX lets us learn together.
## The User Experience
**The problem with linear workflows:**
You're "in planning phase", then "in implementation phase", then "done". But real work doesn't work that way. You implement something, realize your design was wrong, need to update specs, continue implementing. Linear phases fight against how work actually happens.
**OPSX approach:**
- **Actions, not phases** — create, implement, update, archive — do any of them anytime
- **Dependencies are enablers** — they show what's possible, not what's required next
```
proposal ──→ specs ──→ design ──→ tasks ──→ implement
```
## Setup
```bash
# 1. Make sure you have openspec installed and initialized
openspec init
# 2. Generate the experimental skills
openspec experimental
```
This creates skills in `.claude/skills/` that Claude Code auto-detects.
During setup, you'll be prompted to create a **project config** (`openspec/config.yaml`). This is optional but recommended.
## Project Configuration
Project config lets you set defaults and inject project-specific context into all artifacts.
### Creating Config
Config is created during `experimental`, or manually:
```yaml
# openspec/config.yaml
schema: spec-driven
context: |
Tech stack: TypeScript, React, Node.js
API conventions: RESTful, JSON responses
Testing: Vitest for unit tests, Playwright for e2e
Style: ESLint with Prettier, strict TypeScript
rules:
proposal:
- Include rollback plan
- Identify affected teams
specs:
- Use Given/When/Then format for scenarios
design:
- Include sequence diagrams for complex flows
```
### Config Fields
| Field | Type | Description |
|-------|------|-------------|
| `schema` | string | Default schema for new changes (e.g., `spec-driven`) |
| `context` | string | Project context injected into all artifact instructions |
| `rules` | object | Per-artifact rules, keyed by artifact ID |
### How It Works
**Schema precedence** (highest to lowest):
1. CLI flag (`--schema <name>`)
2. Change metadata (`.openspec.yaml` in change directory)
3. Project config (`openspec/config.yaml`)
4. Default (`spec-driven`)
**Context injection:**
- Context is prepended to every artifact's instructions
- Wrapped in `<context>...</context>` tags
- Helps AI understand your project's conventions
**Rules injection:**
- Rules are only injected for matching artifacts
- Wrapped in `<rules>...</rules>` tags
- Appear after context, before the template
### Artifact IDs by Schema
**spec-driven** (default):
- `proposal` — Change proposal
- `specs` — Specifications
- `design` — Technical design
- `tasks` — Implementation tasks
### Config Validation
- Unknown artifact IDs in `rules` generate warnings
- Schema names are validated against available schemas
- Context has a 50KB size limit
- Invalid YAML is reported with line numbers
### Troubleshooting
**"Unknown artifact ID in rules: X"**
- Check artifact IDs match your schema (see list above)
- Run `openspec schemas --json` to see artifact IDs for each schema
**Config not being applied:**
- Ensure file is at `openspec/config.yaml` (not `.yml`)
- Check YAML syntax with a validator
- Config changes take effect immediately (no restart needed)
**Context too large:**
- Context is limited to 50KB
- Summarize or link to external docs instead
## Commands
| Command | What it does |
|---------|--------------|
| `/opsx:explore` | Think through ideas, investigate problems, clarify requirements |
| `/opsx:new` | Start a new change |
| `/opsx:continue` | Create the next artifact (based on what's ready) |
| `/opsx:ff` | Fast-forward — create all planning artifacts at once |
| `/opsx:apply` | Implement tasks, updating artifacts as needed |
| `/opsx:sync` | Sync delta specs to main (optional—archive prompts if needed) |
| `/opsx:archive` | Archive when done |
## Usage
### Explore an idea
```
/opsx:explore
```
Think through ideas, investigate problems, compare options. No structure required - just a thinking partner. When insights crystallize, transition to `/opsx:new` or `/opsx:ff`.
### Start a new change
```
/opsx:new
```
You'll be asked what you want to build and which workflow schema to use.
### Create artifacts
```
/opsx:continue
```
Shows what's ready to create based on dependencies, then creates one artifact. Use repeatedly to build up your change incrementally.
```
/opsx:ff add-dark-mode
```
Creates all planning artifacts at once. Use when you have a clear picture of what you're building.
### Implement (the fluid part)
```
/opsx:apply
```
Works through tasks, checking them off as you go. If you're juggling multiple changes, you can run `/opsx:apply <name>`; otherwise it should infer from the conversation and prompt you to choose if it can't tell.
### Finish up
```
/opsx:archive # Move to archive when done (prompts to sync specs if needed)
```
## When to Update vs. Start Fresh
You can always edit your proposal or specs before implementation. But when does refining become "this is different work"?
### What a Proposal Captures
A proposal defines three things:
1. **Intent** — What problem are you solving?
2. **Scope** — What's in/out of bounds?
3. **Approach** — How will you solve it?
The question is: which changed, and by how much?
### Update the Existing Change When:
**Same intent, refined execution**
- You discover edge cases you didn't consider
- The approach needs tweaking but the goal is unchanged
- Implementation reveals the design was slightly off
**Scope narrows**
- You realize full scope is too big, want to ship MVP first
- "Add dark mode" → "Add dark mode toggle (system preference in v2)"
**Learning-driven corrections**
- Codebase isn't structured how you thought
- A dependency doesn't work as expected
- "Use CSS variables" → "Use Tailwind's dark: prefix instead"
### Start a New Change When:
**Intent fundamentally changed**
- The problem itself is different now
- "Add dark mode" → "Add comprehensive theme system with custom colors, fonts, spacing"
**Scope exploded**
- Change grew so much it's essentially different work
- Original proposal would be unrecognizable after updates
- "Fix login bug" → "Rewrite auth system"
**Original is completable**
- The original change can be marked "done"
- New work stands alone, not a refinement
- Complete "Add dark mode MVP" → Archive → New change "Enhance dark mode"
### The Heuristics
```
┌─────────────────────────────────────┐
│ Is this the same work? │
└──────────────┬──────────────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
Same intent? >50% overlap? Can original
Same problem? Same scope? be "done" without
│ │ these changes?
│ │ │
┌────────┴────────┐ ┌──────┴──────┐ ┌───────┴───────┐
│ │ │ │ │ │
YES NO YES NO NO YES
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
UPDATE NEW UPDATE NEW UPDATE NEW
```
| Test | Update | New Change |
|------|--------|------------|
| **Identity** | "Same thing, refined" | "Different work" |
| **Scope overlap** | >50% overlaps | <50% overlaps |
| **Completion** | Can't be "done" without changes | Can finish original, new work stands alone |
| **Story** | Update chain tells coherent story | Patches would confuse more than clarify |
### The Principle
> **Update preserves context. New change provides clarity.**
>
> Choose update when the history of your thinking is valuable.
> Choose new when starting fresh would be clearer than patching.
Think of it like git branches:
- Keep committing while working on the same feature
- Start a new branch when it's genuinely new work
- Sometimes merge a partial feature and start fresh for phase 2
## What's Different?
| | Standard (`/openspec:proposal`) | Experimental (`/opsx:*`) |
|---|---|---|
| **Structure** | One big proposal document | Discrete artifacts with dependencies |
| **Workflow** | Linear phases: plan → implement → archive | Fluid actions — do anything anytime |
| **Iteration** | Awkward to go back | Update artifacts as you learn |
| **Customization** | Fixed structure | Schema-driven (define your own artifacts) |
**The key insight:** work isn't linear. OPSX stops pretending it is.
## Architecture Deep Dive
This section explains how OPSX works under the hood and how it compares to the standard workflow.
### Philosophy: Phases vs Actions
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ STANDARD WORKFLOW │
│ (Phase-Locked, All-or-Nothing) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ PLANNING │ ───► │ IMPLEMENTING │ ───► │ ARCHIVING │ │
│ │ PHASE │ │ PHASE │ │ PHASE │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ /openspec:proposal /openspec:apply /openspec:archive │
│ │
│ • Creates ALL artifacts at once │
│ • Can't go back to update specs during implementation │
│ • Phase gates enforce linear progression │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ OPSX WORKFLOW │
│ (Fluid Actions, Iterative) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ ACTIONS (not phases) │ │
│ │ │ │
│ │ new ◄──► continue ◄──► apply ◄──► archive │ │
│ │ │ │ │ │ │ │
│ │ └──────────┴───────────┴───────────┘ │ │
│ │ any order │ │
│ └────────────────────────────────────────────┘ │
│ │
│ • Create artifacts one at a time OR fast-forward │
│ • Update specs/design/tasks during implementation │
│ • Dependencies enable progress, phases don't exist │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Component Architecture
**Standard workflow** uses hardcoded templates in TypeScript:
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ STANDARD WORKFLOW COMPONENTS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Hardcoded Templates (TypeScript strings) │
│ │ │
│ ▼ │
│ Configurators (18+ classes, one per editor) │
│ │ │
│ ▼ │
│ Generated Command Files (.claude/commands/openspec/*.md) │
│ │
│ • Fixed structure, no artifact awareness │
│ • Change requires code modification + rebuild │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
**OPSX** uses external schemas and a dependency graph engine:
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ OPSX COMPONENTS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Schema Definitions (YAML) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ name: spec-driven │ │
│ │ artifacts: │ │
│ │ - id: proposal │ │
│ │ generates: proposal.md │ │
│ │ requires: [] ◄── Dependencies │ │
│ │ - id: specs │ │
│ │ generates: specs/**/*.md ◄── Glob patterns │ │
│ │ requires: [proposal] ◄── Enables after proposal │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Artifact Graph Engine │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ • Topological sort (dependency ordering) │ │
│ │ • State detection (filesystem existence) │ │
│ │ • Rich instruction generation (templates + context) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Skill Files (.claude/skills/openspec-*/SKILL.md) │
│ │
│ • Cross-editor compatible (Claude Code, Cursor, Windsurf) │
│ • Skills query CLI for structured data │
│ • Fully customizable via schema files │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### Dependency Graph Model
Artifacts form a directed acyclic graph (DAG). Dependencies are **enablers**, not gates:
```
proposal
(root node)
│
┌─────────────┴─────────────┐
│ │
▼ ▼
specs design
(requires: (requires:
proposal) proposal)
│ │
└─────────────┬─────────────┘
│
▼
tasks
(requires:
specs, design)
│
▼
┌──────────────┐
│ APPLY PHASE │
│ (requires: │
│ tasks) │
└──────────────┘
```
**State transitions:**
```
BLOCKED ────────────────► READY ────────────────► DONE
│ │ │
Missing All deps File exists
dependencies are DONE on filesystem
```
### Information Flow
**Standard workflow** — agent receives static instructions:
```
User: "/openspec:proposal"
│
▼
┌─────────────────────────────────────────┐
│ Static instructions: │
│ • Create proposal.md │
│ • Create tasks.md │
│ • Create design.md │
│ • Create specs/<capability>/spec.md │
│ │
│ No awareness of what exists or │
│ dependencies between artifacts │
└─────────────────────────────────────────┘
│
▼
Agent creates ALL artifacts in one go
```
**OPSX** — agent queries for rich context:
```
User: "/opsx:continue"
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Step 1: Query current state │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ $ openspec status --change "add-auth" --json │ │
│ │ │ │
│ │ { │ │
│ │ "artifacts": [ │ │
│ │ {"id": "proposal", "status": "done"}, │ │
│ │ {"id": "specs", "status": "ready"}, ◄── First ready │ │
│ │ {"id": "design", "status": "ready"}, │ │
│ │ {"id": "tasks", "status": "blocked", "missingDeps": ["specs"]}│ │
│ │ ] │ │
│ │ } │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ Step 2: Get rich instructions for ready artifact │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ $ openspec instructions specs --change "add-auth" --json │ │
│ │ │ │
│ │ { │ │
│ │ "template": "# Specification\n\n## ADDED Requirements...", │ │
│ │ "dependencies": [{"id": "proposal", "path": "...", "done": true}│ │
│ │ "unlocks": ["tasks"] │ │
│ │ } │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ Step 3: Read dependencies → Create ONE artifact → Show what's unlocked │
└──────────────────────────────────────────────────────────────────────────┘
```
### Iteration Model
**Standard workflow** — awkward to iterate:
```
┌─────────┐ ┌─────────┐ ┌─────────┐
│/proposal│ ──► │ /apply │ ──► │/archive │
└─────────┘ └─────────┘ └─────────┘
│ │
│ ├── "Wait, the design is wrong"
│ │
│ ├── Options:
│ │ • Edit files manually (breaks context)
│ │ • Abandon and start over
│ │ • Push through and fix later
│ │
│ └── No official "go back" mechanism
│
└── Creates ALL artifacts at once
```
**OPSX** — natural iteration:
```
/opsx:new ───► /opsx:continue ───► /opsx:apply ───► /opsx:archive
│ │ │
│ │ ├── "The design is wrong"
│ │ │
│ │ ▼
│ │ Just edit design.md
│ │ and continue!
│ │ │
│ │ ▼
│ │ /opsx:apply picks up
│ │ where you left off
│ │
│ └── Creates ONE artifact, shows what's unlocked
│
└── Scaffolds change, waits for direction
```
### Custom Schemas
Create custom workflows using the schema management commands:
```bash
# Create a new schema from scratch (interactive)
openspec schema init my-workflow
# Or fork an existing schema as a starting point
openspec schema fork spec-driven my-workflow
# Validate your schema structure
openspec schema validate my-workflow
# See where a schema resolves from (useful for debugging)
openspec schema which my-workflow
```
Schemas are stored in `openspec/schemas/` (project-local, version controlled) or `~/.local/share/openspec/schemas/` (user global).
**Schema structure:**
```
openspec/schemas/research-first/
├── schema.yaml
└── templates/
├── research.md
├── proposal.md
└── tasks.md
```
**Example schema.yaml:**
```yaml
name: research-first
artifacts:
- id: research # Added before proposal
generates: research.md
requires: []
- id: proposal
generates: proposal.md
requires: [research] # Now depends on research
- id: tasks
generates: tasks.md
requires: [proposal]
```
**Dependency Graph:**
```
research ──► proposal ──► tasks
```
### Summary
| Aspect | Standard | OPSX |
|--------|----------|------|
| **Templates** | Hardcoded TypeScript | External YAML + Markdown |
| **Dependencies** | None (all at once) | DAG with topological sort |
| **State** | Phase-based mental model | Filesystem existence |
| **Customization** | Edit source, rebuild | Create schema.yaml |
| **Iteration** | Phase-locked | Fluid, edit anything |
| **Editor Support** | 18+ configurator classes | Single skills directory |
## Schemas
Schemas define what artifacts exist and their dependencies. Currently available:
- **spec-driven** (default): proposal → specs → design → tasks
```bash
# List available schemas
openspec schemas
# See all schemas with their resolution sources
openspec schema which --all
# Create a new schema interactively
openspec schema init my-workflow
# Fork an existing schema for customization
openspec schema fork spec-driven my-workflow
# Validate schema structure before use
openspec schema validate my-workflow
```
## Tips
- Use `/opsx:explore` to think through an idea before committing to a change
- `/opsx:ff` when you know what you want, `/opsx:continue` when exploring
- During `/opsx:apply`, if something's wrong — fix the artifact, then continue
- Tasks track progress via checkboxes in `tasks.md`
- Check status anytime: `openspec status --change "name"`
## Feedback
This is rough. That's intentional — we're learning what works.
Found a bug? Have ideas? Join us on [Discord](https://discord.gg/YctCnvvshC) or open an issue on [GitHub](https://github.com/Fission-AI/openspec/issues).
-211
View File
@@ -1,211 +0,0 @@
# Schema Customization
This document describes how users can customize OpenSpec schemas and templates, the current manual process, and the gap that needs to be addressed.
---
## Overview
OpenSpec uses a 2-level schema resolution system following the XDG Base Directory Specification:
1. **User override**: `${XDG_DATA_HOME}/openspec/schemas/<name>/`
2. **Package built-in**: `<npm-package>/schemas/<name>/`
When a schema is requested (e.g., `spec-driven`), the resolver checks the user directory first. If found, that entire schema directory is used. Otherwise, it falls back to the package's built-in schema.
---
## Current Manual Process
To override the default `spec-driven` schema, a user must:
### 1. Determine the correct directory path
| Platform | Path |
|----------|------|
| macOS/Linux | `~/.local/share/openspec/schemas/` |
| Windows | `%LOCALAPPDATA%\openspec\schemas\` |
| All (if set) | `$XDG_DATA_HOME/openspec/schemas/` |
### 2. Create the directory structure
```bash
# macOS/Linux example
mkdir -p ~/.local/share/openspec/schemas/spec-driven/templates
```
### 3. Find and copy the default schema files
The user must locate the installed npm package to copy the defaults:
```bash
# Find the package location (varies by install method)
npm list -g openspec --parseable
# or
which openspec && readlink -f $(which openspec)
# Copy files from the package's schemas/ directory
cp <package-path>/schemas/spec-driven/schema.yaml ~/.local/share/openspec/schemas/spec-driven/
cp <package-path>/schemas/spec-driven/templates/*.md ~/.local/share/openspec/schemas/spec-driven/templates/
```
### 4. Modify the copied files
Edit `schema.yaml` to change the workflow structure:
```yaml
name: spec-driven
version: 1
description: My custom workflow
artifacts:
- id: proposal
generates: proposal.md
description: Initial proposal
template: proposal.md
requires: []
# Add, remove, or modify artifacts...
```
Edit templates in `templates/` to customize the content guidance.
### 5. Verify the override is active
Currently there's no command to verify which schema is being used. Users must trust that the file exists in the right location.
---
## Gap Analysis
The current process has several friction points:
| Issue | Impact |
|-------|--------|
| **Path discovery** | Users must know XDG conventions and platform-specific paths |
| **Package location** | Finding the npm package path varies by install method (global, local, pnpm, yarn, volta, etc.) |
| **No scaffolding** | Users must manually create directories and copy files |
| **No verification** | No way to confirm which schema is actually being resolved |
| **No diffing** | When upgrading openspec, users can't see what changed in built-in templates |
| **Full copy required** | Must copy entire schema even to change one template |
### User Stories Not Currently Supported
1. *"I want to add a `research` artifact before `proposal`"* — requires manual copy and edit
2. *"I want to customize just the proposal template"* — must copy entire schema
3. *"I want to see what the default schema looks like"* — must find package path
4. *"I want to revert to defaults"* — must delete files and hope paths are correct
5. *"I upgraded openspec, did the templates change?"* — no way to diff
---
## Proposed Solution: Schema Configurator
A CLI command (or set of commands) that handles path resolution and file operations for users.
### Option A: Single `openspec schema` command
```bash
# List available schemas (built-in and user overrides)
openspec schema list
# Show where a schema resolves from
openspec schema which spec-driven
# Output: /Users/me/.local/share/openspec/schemas/spec-driven/ (user override)
# Output: /usr/local/lib/node_modules/openspec/schemas/spec-driven/ (built-in)
# Copy a built-in schema to user directory for customization
openspec schema copy spec-driven
# Creates ~/.local/share/openspec/schemas/spec-driven/ with all files
# Show diff between user override and built-in
openspec schema diff spec-driven
# Remove user override (revert to built-in)
openspec schema reset spec-driven
# Validate a schema
openspec schema validate spec-driven
```
### Option B: Dedicated `openspec customize` command
```bash
# Interactive schema customization
openspec customize
# Prompts: Which schema? What do you want to change? etc.
# Copy and open for editing
openspec customize spec-driven
# Copies to user dir, prints path, optionally opens in $EDITOR
```
### Option C: Init-time schema selection
```bash
# During project init, offer schema customization
openspec init
# ? Select a workflow schema:
# > spec-driven (default)
# tdd
# minimal
# custom (copy and edit)
```
### Recommended Approach
**Option A** provides the most flexibility and follows Unix conventions (subcommands for discrete operations). Key commands in priority order:
1. `openspec schema list` — see what's available
2. `openspec schema which <name>` — debug resolution
3. `openspec schema copy <name>` — scaffold customization
4. `openspec schema diff <name>` — compare with built-in
5. `openspec schema reset <name>` — revert to defaults
---
## Implementation Considerations
### Path Resolution
The resolver already exists in `src/core/artifact-graph/resolver.ts`:
```typescript
export function getPackageSchemasDir(): string { ... }
export function getUserSchemasDir(): string { ... }
export function getSchemaDir(name: string): string | null { ... }
export function listSchemas(): string[] { ... }
```
New commands would leverage these existing functions.
### File Operations
- Copy should preserve file permissions
- Copy should not overwrite existing user files without `--force`
- Reset should prompt for confirmation
### Template-Only Overrides
A future enhancement could support overriding individual templates without copying the entire schema. This would require changes to the resolution logic:
```
Current: schema dir (user) OR schema dir (built-in)
Future: schema.yaml from user OR built-in
+ each template from user OR built-in (independent fallback)
```
This adds complexity but enables the "I just want to change one template" use case.
---
## Related Documents
- [Schema Workflow Gaps](./schema-workflow-gaps.md) — End-to-end workflow analysis and phased implementation plan
## Related Files
| File | Purpose |
|------|---------|
| `src/core/artifact-graph/resolver.ts` | Schema resolution logic |
| `src/core/artifact-graph/instruction-loader.ts` | Template loading |
| `src/core/global-config.ts` | XDG path helpers |
| `schemas/spec-driven/` | Default schema and templates |
-378
View File
@@ -1,378 +0,0 @@
# Schema Workflow: End-to-End Analysis
This document analyzes the complete user journey for working with schemas in OpenSpec, identifies gaps, and proposes a phased solution.
---
## Current State
### What Exists
| Component | Status |
|-----------|--------|
| Schema resolution (XDG) | 2-level: user override → package built-in |
| Built-in schemas | `spec-driven`, `tdd` |
| Artifact workflow commands | `status`, `next`, `instructions`, `templates` with `--schema` flag |
| Change creation | `openspec new change <name>` — no schema binding |
### What's Missing
| Component | Status |
|-----------|--------|
| Schema bound to change | Not stored — must pass `--schema` every time |
| Project-local schemas | Not supported — can't version control with repo |
| Schema management CLI | None — manual path discovery required |
| Project default schema | None — hardcoded to `spec-driven` |
---
## User Journey Analysis
### Scenario 1: Using a Non-Default Schema
**Goal:** User wants to use TDD workflow for a new feature.
**Today's experience:**
```bash
openspec new change add-auth
# Creates directory, no schema info stored
openspec status --change add-auth
# Shows spec-driven artifacts (WRONG - user wanted TDD)
# User realizes mistake...
openspec status --change add-auth --schema tdd
# Correct, but must remember --schema every time
# 6 months later...
openspec status --change add-auth
# Wrong again - nobody remembers this was TDD
```
**Problems:**
- Schema is a runtime argument, not persisted
- Easy to forget `--schema` and get wrong results
- No record of intended schema for future reference
---
### Scenario 2: Customizing a Schema
**Goal:** User wants to add a "research" artifact before "proposal".
**Today's experience:**
```bash
# Step 1: Figure out where to put overrides
# Must know XDG conventions:
# macOS/Linux: ~/.local/share/openspec/schemas/
# Windows: %LOCALAPPDATA%\openspec\schemas/
# Step 2: Create directory structure
mkdir -p ~/.local/share/openspec/schemas/my-workflow/templates
# Step 3: Find the npm package to copy defaults
npm list -g openspec --parseable
# Output varies by package manager:
# npm: /usr/local/lib/node_modules/openspec
# pnpm: ~/.local/share/pnpm/global/5/node_modules/openspec
# volta: ~/.volta/tools/image/packages/openspec/...
# yarn: ~/.config/yarn/global/node_modules/openspec
# Step 4: Copy files
cp -r <package-path>/schemas/spec-driven/* \
~/.local/share/openspec/schemas/my-workflow/
# Step 5: Edit schema.yaml and templates
# No way to verify override is active
# No way to diff against original
```
**Problems:**
- Must know XDG path conventions
- Finding npm package path varies by install method
- No tooling to scaffold or verify
- No diff capability when upgrading openspec
---
### Scenario 3: Team Sharing Custom Workflow
**Goal:** Team wants everyone to use the same custom schema.
**Today's options:**
1. Everyone manually sets up XDG override — error-prone, drift risk
2. Document setup in README — still manual, easy to miss
3. Publish separate npm package — overkill for most teams
4. Check schema into repo — **not supported** (no project-local resolution)
**Problems:**
- No project-local schema resolution
- Can't version control custom schemas with the codebase
- No single source of truth for team workflow
---
## Gap Summary
| Gap | Impact | Workaround |
|-----|--------|------------|
| Schema not bound to change | Wrong results, forgotten context | Remember to pass `--schema` |
| No project-local schemas | Can't share via repo | Manual XDG setup per machine |
| No schema management CLI | Manual path hunting | Know XDG + find npm package |
| No project default schema | Must specify every time | Always pass `--schema` |
| No init-time schema selection | Missed setup opportunity | Manual config |
---
## Proposed Architecture
### New File Structure
```
openspec/
├── config.yaml # Project config (NEW)
├── schemas/ # Project-local schemas (NEW)
│ └── my-workflow/
│ ├── schema.yaml
│ └── templates/
│ ├── research.md
│ ├── proposal.md
│ └── ...
└── changes/
└── add-auth/
├── change.yaml # Change metadata (NEW)
├── proposal.md
└── ...
```
### config.yaml (Project Config)
```yaml
# openspec/config.yaml
defaultSchema: spec-driven
```
Sets the project-wide default schema. Used when:
- Creating new changes without `--schema`
- Running commands on changes without `change.yaml`
### change.yaml (Change Metadata)
```yaml
# openspec/changes/add-auth/change.yaml
schema: tdd
created: 2025-01-15T10:30:00Z
description: Add user authentication system
```
Binds a specific schema to a change. Created automatically by `openspec new change`.
### Schema Resolution Order
```
1. ./openspec/schemas/<name>/ # Project-local
2. ~/.local/share/openspec/schemas/<name>/ # User global (XDG)
3. <npm-package>/schemas/<name>/ # Built-in
```
Project-local takes priority, enabling version-controlled custom schemas.
### Schema Selection Order (Per Command)
```
1. --schema CLI flag # Explicit override
2. change.yaml in change directory # Change-specific binding
3. openspec/config.yaml defaultSchema # Project default
4. "spec-driven" # Hardcoded fallback
```
---
## Ideal User Experience
### Creating a Change
```bash
# Uses project default (from config.yaml, or spec-driven)
openspec new change add-auth
# Creates openspec/changes/add-auth/change.yaml:
# schema: spec-driven
# created: 2025-01-15T10:30:00Z
# Explicit schema for this change
openspec new change add-auth --schema tdd
# Creates change.yaml with schema: tdd
```
### Working with Changes
```bash
# Auto-reads schema from change.yaml — no --schema needed
openspec status --change add-auth
# Output: "Change: add-auth (schema: tdd)"
# Shows which artifacts are ready/blocked/done
# Explicit override still works (with informational message)
openspec status --change add-auth --schema spec-driven
# "Note: change.yaml specifies 'tdd', using 'spec-driven' per --schema flag"
```
### Customizing Schemas
```bash
# See what's available
openspec schema list
# Built-in:
# spec-driven proposal → specs → design → tasks
# tdd spec → tests → implementation → docs
# Project: (none)
# User: (none)
# Copy to project for customization
openspec schema copy spec-driven my-workflow
# Created ./openspec/schemas/my-workflow/
# Edit schema.yaml and templates/ to customize
# Copy to global (user-level override)
openspec schema copy spec-driven --global
# Created ~/.local/share/openspec/schemas/spec-driven/
# See where a schema resolves from
openspec schema which spec-driven
# ./openspec/schemas/spec-driven/ (project)
# or: ~/.local/share/openspec/schemas/spec-driven/ (user)
# or: /usr/local/lib/node_modules/openspec/schemas/spec-driven/ (built-in)
# Compare override with built-in
openspec schema diff spec-driven
# Shows diff between user/project version and package built-in
# Remove override, revert to built-in
openspec schema reset spec-driven
# Removes ./openspec/schemas/spec-driven/ (or --global for user dir)
```
### Project Setup
```bash
openspec init
# ? Select default workflow schema:
# > spec-driven (proposal → specs → design → tasks)
# tdd (spec → tests → implementation → docs)
# (custom schemas if detected)
#
# Writes to openspec/config.yaml:
# defaultSchema: spec-driven
```
---
## Implementation Phases
### Phase 1: Change Metadata (change.yaml)
**Priority:** High
**Solves:** "Forgot --schema", lost context, wrong results
**Scope:**
- Create `change.yaml` when running `openspec new change`
- Store `schema`, `created` timestamp
- Modify workflow commands to read schema from `change.yaml`
- `--schema` flag overrides (with informational message)
- Backwards compatible: missing `change.yaml` → use default
**change.yaml format:**
```yaml
schema: tdd
created: 2025-01-15T10:30:00Z
```
**Migration:**
- Existing changes without `change.yaml` continue to work
- Default to `spec-driven` (current behavior)
- Optional: `openspec migrate` to add `change.yaml` to existing changes
---
### Phase 2: Project-Local Schemas
**Priority:** High
**Solves:** Team sharing, version control, no XDG knowledge needed
**Scope:**
- Add `./openspec/schemas/` to resolution order (first priority)
- `openspec schema copy <name> [new-name]` creates in project by default
- `--global` flag for user-level XDG directory
- Teams can commit `openspec/schemas/` to repo
**Resolution order:**
```
1. ./openspec/schemas/<name>/ # Project-local (NEW)
2. ~/.local/share/openspec/schemas/<name>/ # User global
3. <npm-package>/schemas/<name>/ # Built-in
```
---
### Phase 3: Schema Management CLI
**Priority:** Medium
**Solves:** Path discovery, scaffolding, debugging
**Commands:**
```bash
openspec schema list # Show available schemas with sources
openspec schema which <name> # Show resolution path
openspec schema copy <name> [to] # Copy for customization
openspec schema diff <name> # Compare with built-in
openspec schema reset <name> # Remove override
openspec schema validate <name> # Validate schema.yaml structure
```
---
### Phase 4: Project Config + Init Enhancement
**Priority:** Low
**Solves:** Project-wide defaults, streamlined setup
**Scope:**
- Add `openspec/config.yaml` with `defaultSchema` field
- `openspec init` prompts for schema selection
- Store selection in `config.yaml`
- Commands use as fallback when no `change.yaml` exists
**config.yaml format:**
```yaml
defaultSchema: spec-driven
```
---
## Backwards Compatibility
| Scenario | Behavior |
|----------|----------|
| Existing change without `change.yaml` | Uses `--schema` flag or project default or `spec-driven` |
| Existing project without `config.yaml` | Falls back to `spec-driven` |
| `--schema` flag provided | Overrides `change.yaml` (with info message) |
| No project-local schemas dir | Skipped in resolution, checks user/built-in |
All existing functionality continues to work. New features are additive.
---
## Related Documents
- [Schema Customization](./schema-customization.md) — Details on manual override process and CLI gaps
- [Artifact POC](./artifact_poc.md) — Core artifact graph architecture
## Related Code
| File | Purpose |
|------|---------|
| `src/core/artifact-graph/resolver.ts` | Schema resolution logic |
| `src/core/artifact-graph/instruction-loader.ts` | Template loading |
| `src/core/global-config.ts` | XDG path helpers |
| `src/commands/artifact-workflow.ts` | CLI commands |
| `src/utils/change-utils.ts` | Change creation utilities |
+84
View File
@@ -0,0 +1,84 @@
# Supported Tools
OpenSpec works with 20+ AI coding assistants. When you run `openspec init`, you'll be prompted to select which tools you use, and OpenSpec will configure the appropriate integrations.
## How It Works
For each tool you select, OpenSpec installs:
1. **Skills** — Reusable instruction files that power the `/opsx:*` workflow commands
2. **Commands** — Tool-specific slash command bindings
## Tool Directory Reference
| Tool | Skills Location | Commands Location |
|------|-----------------|-------------------|
| Amazon Q Developer | `.amazonq/skills/` | `.amazonq/prompts/` |
| Antigravity | `.agent/skills/` | `.agent/workflows/` |
| Auggie (Augment CLI) | `.augment/skills/` | `.augment/commands/` |
| Claude Code | `.claude/skills/` | `.claude/commands/opsx/` |
| Cline | `.cline/skills/` | `.clinerules/workflows/` |
| CodeBuddy | `.codebuddy/skills/` | `.codebuddy/commands/opsx/` |
| Codex | `.codex/skills/` | `.codex/prompts/` |
| Continue | `.continue/skills/` | `.continue/prompts/` |
| CoStrict | `.cospec/skills/` | `.cospec/openspec/commands/` |
| Crush | `.crush/skills/` | `.crush/commands/opsx/` |
| Cursor | `.cursor/skills/` | `.cursor/commands/` |
| Factory Droid | `.factory/skills/` | `.factory/commands/` |
| Gemini CLI | `.gemini/skills/` | `.gemini/commands/opsx/` |
| GitHub Copilot | `.github/skills/` | `.github/prompts/` |
| iFlow | `.iflow/skills/` | `.iflow/commands/` |
| Kilo Code | `.kilocode/skills/` | `.kilocode/workflows/` |
| OpenCode | `.opencode/skills/` | `.opencode/command/` |
| Qoder | `.qoder/skills/` | `.qoder/commands/opsx/` |
| Qwen Code | `.qwen/skills/` | `.qwen/commands/` |
| RooCode | `.roo/skills/` | `.roo/commands/` |
| Windsurf | `.windsurf/skills/` | `.windsurf/commands/opsx/` |
## Non-Interactive Setup
For CI/CD or scripted setup, use the `--tools` flag:
```bash
# Configure specific tools
openspec init --tools claude,cursor
# Configure all supported tools
openspec init --tools all
# Skip tool configuration
openspec init --tools none
```
**Available tool IDs:** `amazon-q`, `antigravity`, `auggie`, `claude`, `cline`, `codebuddy`, `codex`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `kilocode`, `opencode`, `qoder`, `qwen`, `roocode`, `windsurf`
## What Gets Installed
For each tool, OpenSpec generates 10 skill files that power the OPSX workflow:
| Skill | Purpose |
|-------|---------|
| `openspec-explore` | Thinking partner for exploring ideas |
| `openspec-new-change` | Start a new change |
| `openspec-continue-change` | Create the next artifact |
| `openspec-ff-change` | Fast-forward through all planning artifacts |
| `openspec-apply-change` | Implement tasks |
| `openspec-verify-change` | Verify implementation completeness |
| `openspec-sync-specs` | Sync delta specs to main (optional—archive prompts if needed) |
| `openspec-archive-change` | Archive a completed change |
| `openspec-bulk-archive-change` | Archive multiple changes at once |
| `openspec-onboard` | Guided onboarding through a complete workflow cycle |
These skills are invoked via slash commands like `/opsx:new`, `/opsx:apply`, etc. See [Commands](commands.md) for the full list.
## Adding a New Tool
Want to add support for another AI coding assistant? Check out the [command adapter pattern](../CONTRIBUTING.md) or open an issue on GitHub.
---
## Related
- [CLI Reference](cli.md) — Terminal commands
- [Commands](commands.md) — Slash commands and skills
- [Getting Started](getting-started.md) — First-time setup
+425
View File
@@ -0,0 +1,425 @@
# Workflows
This guide covers common workflow patterns for OpenSpec and when to use each one. For basic setup, see [Getting Started](getting-started.md). For command reference, see [Commands](commands.md).
## Philosophy: Actions, Not Phases
Traditional workflows force you through phases: planning, then implementation, then done. But real work doesn't fit neatly into boxes.
OPSX takes a different approach:
```text
Traditional (phase-locked):
PLANNING ────────► IMPLEMENTING ────────► DONE
│ │
│ "Can't go back" │
└────────────────────┘
OPSX (fluid actions):
proposal ──► specs ──► design ──► tasks ──► implement
```
**Key principles:**
- **Actions, not phases** - Commands are things you can do, not stages you're stuck in
- **Dependencies are enablers** - They show what's possible, not what's required next
> **Customization:** OPSX workflows are driven by schemas that define artifact sequences. See [Customization](customization.md) for details on creating custom schemas.
## Workflow Patterns
### Quick Feature
When you know what you want to build and just need to execute:
```text
/opsx:new ──► /opsx:ff ──► /opsx:apply ──► /opsx:verify ──► /opsx:archive
```
**Example conversation:**
```text
You: /opsx:new add-logout-button
AI: Created openspec/changes/add-logout-button/
Ready to create: proposal
You: /opsx:ff
AI: Creating all planning artifacts...
✓ proposal.md
✓ specs/
✓ design.md
✓ tasks.md
Ready for implementation!
You: /opsx:apply
AI: Working through tasks...
✓ Added logout button to header
✓ Implemented session clearing
✓ Added confirmation dialog
All tasks complete!
You: /opsx:archive
AI: ✓ Merged specs
✓ Archived change
```
**Best for:** Small to medium features, bug fixes, straightforward changes.
### Exploratory
When requirements are unclear or you need to investigate first:
```text
/opsx:explore ──► /opsx:new ──► /opsx:continue ──► ... ──► /opsx:apply
```
**Example conversation:**
```text
You: /opsx:explore
AI: What would you like to explore?
You: I want to improve page load performance but I'm not sure
where the bottlenecks are.
AI: Let me help investigate...
[Analyzes bundle sizes, identifies slow queries, reviews
component render patterns]
I found three main bottlenecks:
1. Large unoptimized images
2. Synchronous data fetching in ProductList
3. Re-renders from context changes
Which would you like to address first?
You: Let's tackle the data fetching issue.
You: /opsx:new optimize-product-list-fetching
AI: Created change. Ready to create: proposal
You: /opsx:continue
AI: Creating proposal based on our exploration...
[Writes proposal incorporating investigation insights]
Ready to create: specs, design
Which would you like next?
You: /opsx:continue
AI: Creating specs...
```
**Best for:** Performance optimization, debugging, architectural decisions, unclear requirements.
### Parallel Changes
Work on multiple changes at once:
```text
Change A: /opsx:new ──► /opsx:ff ──► /opsx:apply (in progress)
│
context switch
│
Change B: /opsx:new ──► /opsx:ff ──────► /opsx:apply
```
**Example conversation:**
```text
You: I'm working on add-dark-mode but got pulled into a bug fix
You: /opsx:new fix-login-redirect
AI: Created openspec/changes/fix-login-redirect/
You: /opsx:ff
AI: ✓ Created planning artifacts
You: /opsx:apply
AI: Working on fix-login-redirect...
[Implements the fix]
All tasks complete!
You: /opsx:archive
AI: ✓ Archived fix-login-redirect
You: Let's get back to dark mode
You: /opsx:apply add-dark-mode
AI: Resuming add-dark-mode...
Picking up at task 2.3: Update Header...
```
**Best for:** Parallel work streams, urgent interrupts, team collaboration.
When you have multiple completed changes, use `/opsx:bulk-archive`:
```text
You: /opsx:bulk-archive
AI: Found 3 completed changes:
- add-dark-mode (all tasks done)
- fix-login-redirect (all tasks done)
- update-footer (all tasks done)
Checking for spec conflicts...
⚠ add-dark-mode and update-footer both touch specs/ui/
Inspecting codebase to resolve...
Both changes are implemented. Will apply in chronological order.
Archive all 3 changes?
You: Yes
AI: ✓ Archived add-dark-mode
✓ Archived fix-login-redirect
✓ Archived update-footer
Specs merged in order: add-dark-mode → update-footer
```
Bulk archive detects when multiple changes touch the same specs and resolves conflicts by checking what's actually implemented.
### Completing a Change
The recommended completion flow:
```text
/opsx:apply ──► /opsx:verify ──► /opsx:archive
│ │
validates prompts to sync
implementation if needed
```
#### Verify: Check Your Work
`/opsx:verify` validates implementation against your artifacts across three dimensions:
```text
You: /opsx:verify
AI: Verifying add-auth...
COMPLETENESS
✓ All 12 tasks in tasks.md are checked
✓ All requirements in specs have corresponding code
⚠ Scenario "Session timeout after inactivity" not tested
CORRECTNESS
✓ Implementation matches spec intent
✓ Edge cases from scenarios are handled
✓ Error states match spec definitions
COHERENCE
✓ Design decisions reflected in code structure
✓ Naming conventions consistent with design.md
⚠ Design mentions "event-driven" but implementation uses polling
SUMMARY
─────────────────────────────
Critical issues: 0
Warnings: 2
Ready to archive: Yes (with warnings)
Recommendations:
1. Add test for session timeout scenario
2. Consider refactoring to event-driven as designed, or update design.md
```
**What verify checks:**
| Dimension | What it validates |
|-----------|------------------|
| Completeness | All tasks done, all requirements implemented, scenarios covered |
| Correctness | Implementation matches spec intent, edge cases handled |
| Coherence | Design decisions reflected in code, patterns consistent |
Verify won't block archive, but it surfaces issues you might want to address first.
#### Archive: Finalize the Change
`/opsx:archive` completes the change and moves it to the archive:
```text
You: /opsx:archive
AI: Archiving add-auth...
Artifact status:
✓ proposal.md exists
✓ specs/ exists
✓ design.md exists
✓ tasks.md exists (12/12 tasks complete)
Delta specs: Not yet synced to main
→ Would you like to sync now?
You: Yes
AI: ✓ Synced specs to openspec/specs/auth/spec.md
✓ Moved to openspec/changes/archive/2025-01-24-add-auth/
Change archived successfully.
```
Archive will prompt if specs aren't synced. It won't block on incomplete tasks, but it will warn you.
## When to Use What
### `/opsx:ff` vs `/opsx:continue`
| Situation | Use |
|-----------|-----|
| Clear requirements, ready to build | `/opsx:ff` |
| Exploring, want to review each step | `/opsx:continue` |
| Want to iterate on proposal before specs | `/opsx:continue` |
| Time pressure, need to move fast | `/opsx:ff` |
| Complex change, want control | `/opsx:continue` |
**Rule of thumb:** If you can describe the full scope upfront, use `/opsx:ff`. If you're figuring it out as you go, use `/opsx:continue`.
### When to Update vs Start Fresh
A common question: when is updating an existing change okay, and when should you start a new one?
**Update the existing change when:**
- Same intent, refined execution
- Scope narrows (MVP first, rest later)
- Learning-driven corrections (codebase isn't what you expected)
- Design tweaks based on implementation discoveries
**Start a new change when:**
- Intent fundamentally changed
- Scope exploded to different work entirely
- Original change can be marked "done" standalone
- Patches would confuse more than clarify
```text
┌─────────────────────────────────────┐
│ Is this the same work? │
└──────────────┬──────────────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
Same intent? >50% overlap? Can original
Same problem? Same scope? be "done" without
│ │ these changes?
│ │ │
┌────────┴────────┐ ┌──────┴──────┐ ┌───────┴───────┐
│ │ │ │ │ │
YES NO YES NO NO YES
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
UPDATE NEW UPDATE NEW UPDATE NEW
```
**Example: "Add dark mode"**
- "Need to also support custom themes" → New change (scope exploded)
- "System preference detection is harder than expected" → Update (same intent)
- "Let's ship toggle first, add preferences later" → Update then archive, then new change
## Best Practices
### Keep Changes Focused
One logical unit of work per change. If you're doing "add feature X and also refactor Y", consider two separate changes.
**Why it matters:**
- Easier to review and understand
- Cleaner archive history
- Can ship independently
- Simpler rollback if needed
### Use `/opsx:explore` for Unclear Requirements
Before committing to a change, explore the problem space:
```text
You: /opsx:explore
AI: What would you like to explore?
You: How should we handle rate limiting for our API?
AI: Let me investigate your current setup and options...
[Researches codebase, considers patterns]
I see three approaches we could take:
1. Middleware-based (simple, per-endpoint)
2. Redis-backed (scalable, shared state)
3. Token bucket per user (fair, complex)
Your current stack suggests #1 or #2. What's your scale?
```
Exploration clarifies thinking before you create artifacts.
### Verify Before Archiving
Use `/opsx:verify` to check implementation matches artifacts:
```text
You: /opsx:verify
AI: Verifying add-dark-mode...
✓ All tasks in tasks.md are checked
✓ Requirements in specs have corresponding code
✓ Design decisions are reflected in implementation
Ready to archive!
```
Catches mismatches before you close out the change.
### Name Changes Clearly
Good names make `openspec list` useful:
```text
Good: Avoid:
add-dark-mode feature-1
fix-login-redirect update
optimize-product-query changes
implement-2fa wip
```
## Command Quick Reference
For full command details and options, see [Commands](commands.md).
| Command | Purpose | When to Use |
|---------|---------|-------------|
| `/opsx:explore` | Think through ideas | Unclear requirements, investigation |
| `/opsx:new` | Start a change | Beginning any new work |
| `/opsx:continue` | Create next artifact | Step-by-step artifact creation |
| `/opsx:ff` | Create all planning artifacts | Clear scope, ready to build |
| `/opsx:apply` | Implement tasks | Ready to write code |
| `/opsx:verify` | Validate implementation | Before archiving, catch mismatches |
| `/opsx:sync` | Merge delta specs | Optional—archive prompts if needed |
| `/opsx:archive` | Complete the change | All work finished |
| `/opsx:bulk-archive` | Archive multiple changes | Parallel work, batch completion |
## Next Steps
- [Commands](commands.md) - Full command reference with options
- [Concepts](concepts.md) - Deep dive into specs, artifacts, and schemas
- [Customization](customization.md) - Create custom workflows
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1767640445,
"narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+87
View File
@@ -0,0 +1,87 @@
{
description = "OpenSpec - AI-native system for spec-driven development";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);
in
{
packages = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
default = pkgs.stdenv.mkDerivation (finalAttrs: {
pname = "openspec";
version = "0.23.0";
src = ./.;
pnpmDeps = pkgs.fetchPnpmDeps {
inherit (finalAttrs) pname version src;
pnpm = pkgs.pnpm_9;
fetcherVersion = 3;
hash = "sha256-9s2kdvd7svK4hofnD66HkDc86WTQeayfF5y7L2dmjNg=";
};
nativeBuildInputs = with pkgs; [
nodejs_20
npmHooks.npmInstallHook
pnpmConfigHook
pnpm_9
];
buildPhase = ''
runHook preBuild
pnpm run build
runHook postBuild
'';
dontNpmPrune = true;
meta = with pkgs.lib; {
description = "AI-native system for spec-driven development";
homepage = "https://github.com/Fission-AI/OpenSpec";
license = licenses.mit;
maintainers = [ ];
mainProgram = "openspec";
};
});
});
apps = forAllSystems (system: {
default = {
type = "app";
program = "${self.packages.${system}.default}/bin/openspec";
};
});
devShells = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs_20
pnpm_9
];
shellHook = ''
echo "OpenSpec development environment"
echo "Node version: $(node --version)"
echo "pnpm version: $(pnpm --version)"
echo "Run 'pnpm install' to install dependencies"
'';
};
});
};
}
-454
View File
@@ -1,454 +0,0 @@
# OpenSpec Instructions
Instructions for AI coding assistants using OpenSpec for spec-driven development.
## TL;DR Quick Checklist
- Search existing work: `openspec spec list --long`, `openspec list` (use `rg` only for full-text search)
- Decide scope: new capability vs modify existing capability
- Pick a unique `change-id`: kebab-case, verb-led (`add-`, `update-`, `remove-`, `refactor-`)
- Scaffold: `proposal.md`, `tasks.md`, `design.md` (only if needed), and delta specs per affected capability
- Write deltas: use `## ADDED|MODIFIED|REMOVED|RENAMED Requirements`; include at least one `#### Scenario:` per requirement
- Validate: `openspec validate [change-id] --strict` and fix issues
- Request approval: Do not start implementation until proposal is approved
## Three-Stage Workflow
### Stage 1: Creating Changes
Create proposal when you need to:
- Add features or functionality
- Make breaking changes (API, schema)
- Change architecture or patterns
- Optimize performance (changes behavior)
- Update security patterns
Triggers (examples):
- "Help me create a change proposal"
- "Help me plan a change"
- "Help me create a proposal"
- "I want to create a spec proposal"
- "I want to create a spec"
Loose matching guidance:
- Contains one of: `proposal`, `change`, `spec`
- With one of: `create`, `plan`, `make`, `start`, `help`
Skip proposal for:
- Bug fixes (restore intended behavior)
- Typos, formatting, comments
- Dependency updates (non-breaking)
- Configuration changes
- Tests for existing behavior
**Workflow**
1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context.
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes/<id>/`.
3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement.
4. Run `openspec validate <id> --strict` and resolve any issues before sharing the proposal.
### Stage 2: Implementing Changes
Track these steps as TODOs and complete them one by one.
1. **Read proposal.md** - Understand what's being built
2. **Read design.md** (if exists) - Review technical decisions
3. **Read tasks.md** - Get implementation checklist
4. **Implement tasks sequentially** - Complete in order
5. **Confirm completion** - Ensure every item in `tasks.md` is finished before updating statuses
6. **Update checklist** - After all work is done, set every task to `- [x]` so the list reflects reality
7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved
### Stage 3: Archiving Changes
After deployment, create separate PR to:
- Move `changes/[name]/` → `changes/archive/YYYY-MM-DD-[name]/`
- Update `specs/` if capabilities changed
- Use `openspec archive <change-id> --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly)
- Run `openspec validate --strict` to confirm the archived change passes checks
## Before Any Task
**Context Checklist:**
- [ ] Read relevant specs in `specs/[capability]/spec.md`
- [ ] Check pending changes in `changes/` for conflicts
- [ ] Read `openspec/project.md` for conventions
- [ ] Run `openspec list` to see active changes
- [ ] Run `openspec list --specs` to see existing capabilities
**Before Creating Specs:**
- Always check if capability already exists
- Prefer modifying existing specs over creating duplicates
- Use `openspec show [spec]` to review current state
- If request is ambiguous, ask 1–2 clarifying questions before scaffolding
### Search Guidance
- Enumerate specs: `openspec spec list --long` (or `--json` for scripts)
- Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available)
- Show details:
- Spec: `openspec show <spec-id> --type spec` (use `--json` for filters)
- Change: `openspec show <change-id> --json --deltas-only`
- Full-text search (use ripgrep): `rg -n "Requirement:|Scenario:" openspec/specs`
## Quick Start
### CLI Commands
```bash
# Essential commands
openspec list # List active changes
openspec list --specs # List specifications
openspec show [item] # Display change or spec
openspec validate [item] # Validate changes or specs
openspec archive <change-id> [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
# Project management
openspec init [path] # Initialize OpenSpec
openspec update [path] # Update instruction files
# Interactive mode
openspec show # Prompts for selection
openspec validate # Bulk validation mode
# Debugging
openspec show [change] --json --deltas-only
openspec validate [change] --strict
```
### Command Flags
- `--json` - Machine-readable output
- `--type change|spec` - Disambiguate items
- `--strict` - Comprehensive validation
- `--no-interactive` - Disable prompts
- `--skip-specs` - Archive without spec updates
- `--yes`/`-y` - Skip confirmation prompts (non-interactive archive)
## Directory Structure
```
openspec/
├── project.md # Project conventions
├── specs/ # Current truth - what IS built
│ └── [capability]/ # Single focused capability
│ ├── spec.md # Requirements and scenarios
│ └── design.md # Technical patterns
├── changes/ # Proposals - what SHOULD change
│ ├── [change-name]/
│ │ ├── proposal.md # Why, what, impact
│ │ ├── tasks.md # Implementation checklist
│ │ ├── design.md # Technical decisions (optional; see criteria)
│ │ └── specs/ # Delta changes
│ │ └── [capability]/
│ │ └── spec.md # ADDED/MODIFIED/REMOVED
│ └── archive/ # Completed changes
```
## Creating Change Proposals
### Decision Tree
```
New request?
├─ Bug fix restoring spec behavior? → Fix directly
├─ Typo/format/comment? → Fix directly
├─ New feature/capability? → Create proposal
├─ Breaking change? → Create proposal
├─ Architecture change? → Create proposal
└─ Unclear? → Create proposal (safer)
```
### Proposal Structure
1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique)
2. **Write proposal.md:**
```markdown
## Why
[1-2 sentences on problem/opportunity]
## What Changes
- [Bullet list of changes]
- [Mark breaking changes with **BREAKING**]
## Impact
- Affected specs: [list capabilities]
- Affected code: [key files/systems]
```
3. **Create spec deltas:** `specs/[capability]/spec.md`
```markdown
## ADDED Requirements
### Requirement: New Feature
The system SHALL provide...
#### Scenario: Success case
- **WHEN** user performs action
- **THEN** expected result
## MODIFIED Requirements
### Requirement: Existing Feature
[Complete modified requirement]
## REMOVED Requirements
### Requirement: Old Feature
**Reason**: [Why removing]
**Migration**: [How to handle]
```
If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs/<capability>/spec.md`—one per capability.
4. **Create tasks.md:**
```markdown
## 1. Implementation
- [ ] 1.1 Create database schema
- [ ] 1.2 Implement API endpoint
- [ ] 1.3 Add frontend component
- [ ] 1.4 Write tests
```
5. **Create design.md when needed:**
Create `design.md` if any of the following apply; otherwise omit it:
- Cross-cutting change (multiple services/modules) or a new architectural pattern
- New external dependency or significant data model changes
- Security, performance, or migration complexity
- Ambiguity that benefits from technical decisions before coding
Minimal `design.md` skeleton:
```markdown
## Context
[Background, constraints, stakeholders]
## Goals / Non-Goals
- Goals: [...]
- Non-Goals: [...]
## Decisions
- Decision: [What and why]
- Alternatives considered: [Options + rationale]
## Risks / Trade-offs
- [Risk] → Mitigation
## Migration Plan
[Steps, rollback]
## Open Questions
- [...]
```
## Spec File Format
### Critical: Scenario Formatting
**CORRECT** (use #### headers):
```markdown
#### Scenario: User login success
- **WHEN** valid credentials provided
- **THEN** return JWT token
```
**WRONG** (don't use bullets or bold):
```markdown
- **Scenario: User login** ❌
**Scenario**: User login ❌
### Scenario: User login ❌
```
Every requirement MUST have at least one scenario.
### Requirement Wording
- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)
### Delta Operations
- `## ADDED Requirements` - New capabilities
- `## MODIFIED Requirements` - Changed behavior
- `## REMOVED Requirements` - Deprecated features
- `## RENAMED Requirements` - Name changes
Headers matched with `trim(header)` - whitespace ignored.
#### When to use ADDED vs MODIFIED
- ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement.
- MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details.
- RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name.
Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you aren’t explicitly changing the existing requirement, add a new requirement under ADDED instead.
Authoring a MODIFIED requirement correctly:
1) Locate the existing requirement in `openspec/specs/<capability>/spec.md`.
2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios).
3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior.
4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`.
Example for RENAMED:
```markdown
## RENAMED Requirements
- FROM: `### Requirement: Login`
- TO: `### Requirement: User Authentication`
```
## Troubleshooting
### Common Errors
**"Change must have at least one delta"**
- Check `changes/[name]/specs/` exists with .md files
- Verify files have operation prefixes (## ADDED Requirements)
**"Requirement must have at least one scenario"**
- Check scenarios use `#### Scenario:` format (4 hashtags)
- Don't use bullet points or bold for scenario headers
**Silent scenario parsing failures**
- Exact format required: `#### Scenario: Name`
- Debug with: `openspec show [change] --json --deltas-only`
### Validation Tips
```bash
# Always use strict mode for comprehensive checks
openspec validate [change] --strict
# Debug delta parsing
openspec show [change] --json | jq '.deltas'
# Check specific requirement
openspec show [spec] --json -r 1
```
## Happy Path Script
```bash
# 1) Explore current state
openspec spec list --long
openspec list
# Optional full-text search:
# rg -n "Requirement:|Scenario:" openspec/specs
# rg -n "^#|Requirement:" openspec/changes
# 2) Choose change id and scaffold
CHANGE=add-two-factor-auth
mkdir -p openspec/changes/$CHANGE/{specs/auth}
printf "## Why\n...\n\n## What Changes\n- ...\n\n## Impact\n- ...\n" > openspec/changes/$CHANGE/proposal.md
printf "## 1. Implementation\n- [ ] 1.1 ...\n" > openspec/changes/$CHANGE/tasks.md
# 3) Add deltas (example)
cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF'
## ADDED Requirements
### Requirement: Two-Factor Authentication
Users MUST provide a second factor during login.
#### Scenario: OTP required
- **WHEN** valid credentials are provided
- **THEN** an OTP challenge is required
EOF
# 4) Validate
openspec validate $CHANGE --strict
```
## Multi-Capability Example
```
openspec/changes/add-2fa-notify/
├── proposal.md
├── tasks.md
└── specs/
├── auth/
│ └── spec.md # ADDED: Two-Factor Authentication
└── notifications/
└── spec.md # ADDED: OTP email notification
```
auth/spec.md
```markdown
## ADDED Requirements
### Requirement: Two-Factor Authentication
...
```
notifications/spec.md
```markdown
## ADDED Requirements
### Requirement: OTP Email Notification
...
```
## Best Practices
### Simplicity First
- Default to <100 lines of new code
- Single-file implementations until proven insufficient
- Avoid frameworks without clear justification
- Choose boring, proven patterns
### Complexity Triggers
Only add complexity with:
- Performance data showing current solution too slow
- Concrete scale requirements (>1000 users, >100MB data)
- Multiple proven use cases requiring abstraction
### Clear References
- Use `file.ts:42` format for code locations
- Reference specs as `specs/auth/spec.md`
- Link related changes and PRs
### Capability Naming
- Use verb-noun: `user-auth`, `payment-capture`
- Single purpose per capability
- 10-minute understandability rule
- Split if description needs "AND"
### Change ID Naming
- Use kebab-case, short and descriptive: `add-two-factor-auth`
- Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-`
- Ensure uniqueness; if taken, append `-2`, `-3`, etc.
## Tool Selection Guide
| Task | Tool | Why |
|------|------|-----|
| Find files by pattern | Glob | Fast pattern matching |
| Search code content | Grep | Optimized regex search |
| Read specific files | Read | Direct file access |
| Explore unknown scope | Task | Multi-step investigation |
## Error Recovery
### Change Conflicts
1. Run `openspec list` to see active changes
2. Check for overlapping specs
3. Coordinate with change owners
4. Consider combining proposals
### Validation Failures
1. Run with `--strict` flag
2. Check JSON output for details
3. Verify spec file format
4. Ensure scenarios properly formatted
### Missing Context
1. Read project.md first
2. Check related specs
3. Review recent archives
4. Ask for clarification
## Quick Reference
### Stage Indicators
- `changes/` - Proposed, not yet built
- `specs/` - Built and deployed
- `archive/` - Completed changes
### File Purposes
- `proposal.md` - Why and what
- `tasks.md` - Implementation steps
- `design.md` - Technical decisions
- `spec.md` - Requirements and behavior
### CLI Essentials
```bash
openspec list # What's in progress?
openspec show [item] # View details
openspec validate --strict # Is it correct?
openspec archive <change-id> [--yes|-y] # Mark complete (add --yes for automation)
```
Remember: Specs are truth. Changes are proposals. Keep them in sync.
@@ -0,0 +1,136 @@
# Add Artifact Regeneration Support
## Problem
Currently, there is **no way to regenerate artifacts** in the OPSX workflow:
- `/opsx:apply` just reads whatever's on disk
- `/opsx:continue` only creates the NEXT artifact - won't touch existing ones
If you edit `design.md` after `tasks.md` exists, your only options are:
1. Delete tasks.md manually, then run `/opsx:continue`
2. Edit tasks.md manually
The documentation claims you can "update artifacts mid-flight and continue" but there's no mechanism that actually supports this.
## Proposed Solution
Two parts:
### Part 1: Staleness Detection
Add artifact staleness detection to `/opsx:apply`:
1. **Track modification times**: When generating an artifact, record the mtime of its dependencies
2. **Detect staleness**: When `/opsx:apply` runs, check if upstream artifacts (design.md, specs) have been modified since tasks.md was generated
3. **Prompt user**: If stale, ask: "Design was modified after tasks were generated. Would you like to regenerate tasks with `/opsx:continue`?"
## User Experience
### Vision: Seamless Mid-Flight Correction
This is the workflow we want to enable (currently documented but not supported):
```
You: /opsx:apply
AI: Working through tasks...
✓ Task 1.1: Created caching layer
✓ Task 1.2: Added cache invalidation
Working on 1.3: Implement TTL...
I noticed the design assumes Redis, but your project uses
in-memory caching. Should I update the design?
You: Yes, update it to use the existing cache module.
AI: Updated design.md to use CacheManager from src/cache/
Updated tasks.md with revised implementation steps
Continuing implementation...
✓ Task 1.3: Implemented TTL using CacheManager
...
```
**No restart needed.** Just update the artifact and continue.
### Staleness Warning UX
When user manually edits an upstream artifact:
```
$ /opsx:apply
⚠️ Detected changes to upstream artifacts:
- design.md modified 5 minutes ago (after tasks.md was generated)
Options:
1. Regenerate tasks (recommended)
2. Continue anyway with current tasks
3. Cancel
>
```
### Part 2: Regeneration Capability
Add a way to regenerate specific artifacts:
```bash
# Option A: Flag on continue
/opsx:continue --regenerate tasks
# Option B: Separate command
/opsx:regenerate tasks
# Option C: Interactive prompt when staleness detected
/opsx:apply
# "Design changed. Regenerate tasks? [y/N]"
```
## Technical Approach
### Option A: Metadata File
Store `.openspec-meta.json` in change directory:
```json
{
"tasks.md": {
"generated_at": "2025-01-24T10:00:00Z",
"dependencies": {
"design.md": "2025-01-24T09:55:00Z",
"specs/feature/spec.md": "2025-01-24T09:50:00Z"
}
}
}
```
### Option B: Frontmatter
Add YAML frontmatter to generated artifacts:
```markdown
---
generated_at: 2025-01-24T10:00:00Z
depends_on:
- design.md@2025-01-24T09:55:00Z
---
# Tasks
...
```
### Option C: Git-based
Use git to detect if upstream files changed since downstream was last modified. No extra metadata needed but requires git.
## Non-Goals
- Automatic regeneration (user should always choose)
- Blocking apply entirely (just warn)
- Tracking code file changes (only artifact dependencies)
## Dependencies
- Should be implemented after `fix-midflight-update-docs` so docs are accurate first
- Could be combined with that change if desired
## Success Criteria
- User is warned when applying with stale artifacts
- Clear path to regenerate if needed
- No false positives (only warn when genuinely stale)
- Documentation claims become actually true
@@ -0,0 +1,20 @@
## Why
Users and agents need a simple way to submit feedback about OpenSpec directly from the CLI. Currently there's no mechanism to collect user feedback, feature requests, or bug reports in a way that enables follow-up conversation. Using GitHub Issues allows us to track feedback, prevent spam via GitHub auth, and enables outreach to users.
## What Changes
- Add `openspec feedback <message>` CLI command
- Leverage `gh` CLI for GitHub authentication and issue creation
- Add `/feedback` skill for agent-assisted feedback with context enrichment
- Ensure cross-platform compatibility (macOS, Linux, Windows)
## Impact
- Affected specs: New `cli-feedback` capability
- Affected code:
- `src/cli/index.ts` - Register feedback command
- `src/commands/feedback.ts` - Command implementation using `gh` CLI
- `src/core/templates/skill-templates.ts` - Feedback skill template
- `src/core/completions/command-registry.ts` - Shell completions
- External dependency: Requires `gh` CLI installed and authenticated
@@ -0,0 +1,188 @@
## ADDED Requirements
### Requirement: Feedback command
The system SHALL provide an `openspec feedback` command that creates a GitHub Issue in the openspec repository using the `gh` CLI. The system SHALL use `execFileSync` with argument arrays to prevent shell injection vulnerabilities.
#### Scenario: Simple feedback submission
- **WHEN** user executes `openspec feedback "Great tool!"`
- **THEN** the system executes `gh issue create` with title "Feedback: Great tool!"
- **AND** the issue is created in the openspec repository
- **AND** the issue has the `feedback` label
- **AND** the system displays the created issue URL
#### Scenario: Safe command execution
- **WHEN** submitting feedback via `gh` CLI
- **THEN** the system uses `execFileSync` with separate arguments array
- **AND** user input is NOT passed through a shell
- **AND** shell metacharacters (quotes, backticks, $(), etc.) are treated as literal text
#### Scenario: Feedback with body
- **WHEN** user executes `openspec feedback "Title here" --body "Detailed description..."`
- **THEN** the system creates a GitHub Issue with the specified title
- **AND** the issue body contains the detailed description
- **AND** the issue body includes metadata (OpenSpec version, platform, timestamp)
### Requirement: GitHub CLI dependency
The system SHALL use `gh` CLI for automatic feedback submission when available, and provide a manual submission fallback when `gh` is not installed or not authenticated. The system SHALL use platform-appropriate commands to detect `gh` CLI availability.
#### Scenario: Missing gh CLI with fallback
- **WHEN** user runs `openspec feedback "message"`
- **AND** `gh` CLI is not installed (not found in PATH)
- **THEN** the system displays warning: "GitHub CLI not found. Manual submission required."
- **AND** outputs structured feedback content with delimiters:
- "--- FORMATTED FEEDBACK ---"
- Title line
- Labels line
- Body content with metadata
- "--- END FEEDBACK ---"
- **AND** displays pre-filled GitHub issue URL for manual submission
- **AND** exits with zero code (successful fallback)
#### Scenario: Cross-platform gh CLI detection on Unix
- **WHEN** system is running on macOS or Linux (platform is 'darwin' or 'linux')
- **AND** checking if `gh` CLI is installed
- **THEN** the system executes `which gh` command
#### Scenario: Cross-platform gh CLI detection on Windows
- **WHEN** system is running on Windows (platform is 'win32')
- **AND** checking if `gh` CLI is installed
- **THEN** the system executes `where gh` command
#### Scenario: Unauthenticated gh CLI with fallback
- **WHEN** user runs `openspec feedback "message"`
- **AND** `gh` CLI is installed but not authenticated
- **THEN** the system displays warning: "GitHub authentication required. Manual submission required."
- **AND** outputs structured feedback content (same format as missing gh CLI scenario)
- **AND** displays pre-filled GitHub issue URL for manual submission
- **AND** displays authentication instructions: "To auto-submit in the future: gh auth login"
- **AND** exits with zero code (successful fallback)
#### Scenario: Authenticated gh CLI
- **WHEN** user runs `openspec feedback "message"`
- **AND** `gh auth status` returns success (authenticated)
- **THEN** the system proceeds with feedback submission
### Requirement: Issue metadata
The system SHALL include relevant metadata in the GitHub Issue body.
#### Scenario: Standard metadata
- **WHEN** creating a GitHub Issue for feedback
- **THEN** the issue body includes:
- OpenSpec CLI version
- Platform (darwin, linux, win32)
- Submission timestamp
- Separator line: "---\nSubmitted via OpenSpec CLI"
#### Scenario: Windows platform metadata
- **WHEN** creating a GitHub Issue for feedback on Windows
- **THEN** the issue body includes "Platform: win32"
- **AND** all platform detection uses Node.js `os.platform()` API
#### Scenario: No sensitive metadata
- **WHEN** creating a GitHub Issue for feedback
- **THEN** the issue body does NOT include:
- File paths from user's system
- Project names or directory names
- Environment variables
- IP addresses
### Requirement: Feedback always works
The system SHALL allow feedback submission regardless of telemetry settings.
#### Scenario: Feedback with telemetry disabled
- **WHEN** user has disabled telemetry via `OPENSPEC_TELEMETRY=0`
- **AND** user runs `openspec feedback "message"`
- **THEN** the feedback is still submitted via `gh` CLI
- **AND** telemetry events are not sent
#### Scenario: Feedback in CI environment
- **WHEN** `CI=true` is set in the environment
- **AND** user runs `openspec feedback "message"`
- **THEN** the feedback submission proceeds normally (if `gh` is available and authenticated)
### Requirement: Error handling
The system SHALL handle feedback submission errors gracefully.
#### Scenario: gh CLI execution failure
- **WHEN** `gh issue create` command fails
- **THEN** the system displays the error output from `gh` CLI
- **AND** exits with the same exit code as `gh`
#### Scenario: Network failure
- **WHEN** `gh` CLI reports network connectivity issues
- **THEN** the system displays the error message from `gh`
- **AND** suggests checking network connectivity
- **AND** exits with non-zero code
### Requirement: Feedback skill for agents
The system SHALL provide a `/feedback` skill that guides agents through collecting and submitting user feedback.
#### Scenario: Agent-initiated feedback
- **WHEN** user invokes `/feedback` in an agent conversation
- **THEN** the agent gathers context from the conversation
- **AND** drafts a feedback issue with enriched content
- **AND** anonymizes sensitive information
- **AND** presents the draft to the user for approval
- **AND** submits via `openspec feedback` command on user confirmation
#### Scenario: Context enrichment
- **WHEN** agent drafts feedback
- **THEN** the agent includes relevant context such as:
- What task was being performed
- What worked well or poorly
- Specific friction points or praise
#### Scenario: Anonymization
- **WHEN** agent drafts feedback
- **THEN** the agent removes or replaces:
- File paths with `<path>` or generic descriptions
- API keys, tokens, secrets with `<redacted>`
- Company/organization names with `<company>`
- Personal names with `<user>`
- Specific URLs with `<url>` unless public/relevant
#### Scenario: User confirmation required
- **WHEN** agent has drafted feedback
- **THEN** the agent MUST show the complete draft to the user
- **AND** ask for explicit approval before submitting
- **AND** allow the user to request modifications
- **AND** only submit after user confirms
### Requirement: Shell completions
The system SHALL provide shell completions for the feedback command.
#### Scenario: Command completion
- **WHEN** user types `openspec fee<TAB>`
- **THEN** the shell completes to `openspec feedback`
#### Scenario: Flag completion
- **WHEN** user types `openspec feedback "msg" --<TAB>`
- **THEN** the shell suggests available flags (`--body`)
@@ -0,0 +1,30 @@
## 1. Feedback Command
- [x] 1.1 Create `src/commands/feedback.ts` with command implementation
- [x] 1.2 Check `gh` CLI availability using platform-appropriate command (`which` on Unix/macOS, `where` on Windows)
- [x] 1.3 Check GitHub auth status with `gh auth status`
- [x] 1.4 Execute `gh issue create` with formatted title and body using `execFileSync` to prevent shell injection
- [x] 1.5 Display issue URL returned by `gh` CLI
- [x] 1.6 Register `feedback <message>` command in `src/cli/index.ts`
- [x] 1.7 Ensure cross-platform compatibility (macOS, Linux, Windows)
## 2. Shell Completions
- [x] 2.1 Add `feedback` command to command registry
- [x] 2.2 Regenerate completion scripts for all shells
## 3. Feedback Skill
- [x] 3.1 Create feedback skill template in `skill-templates.ts`
- [x] 3.2 Document context gathering workflow
- [x] 3.3 Document anonymization rules
- [x] 3.4 Document user confirmation flow
## 4. Testing
- [x] 4.1 Add unit tests for feedback command (mock `gh` subprocess calls)
- [x] 4.2 Add integration test for full feedback flow with mocked `gh` CLI
- [x] 4.3 Test error handling for missing `gh` CLI
- [x] 4.4 Test error handling for unauthenticated `gh` session
- [x] 4.5 Test cross-platform `gh` CLI detection (verify `which` on Unix, `where` on Windows)
- [x] 4.6 Test platform metadata includes correct value for Windows (win32)
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-24
@@ -0,0 +1,115 @@
## Context
OpenSpec has a complete skill and slash command generation system. Skills are defined in `src/core/templates/skill-templates.ts` as functions that return `SkillTemplate` objects (for Agent Skills) and `CommandTemplate` objects (for slash commands). These are registered in `src/core/shared/skill-generation.ts` and generated during `openspec init` and `openspec update`.
Existing skills follow a consistent pattern:
- `getXxxSkillTemplate()` returns the skill with name, description, instructions
- `getOpsxXxxCommandTemplate()` returns the slash command with name, description, category, tags, content
- Both are registered in their respective arrays in `skill-generation.ts`
## Goals / Non-Goals
**Goals:**
- Add `/opsx:onboard` skill that teaches the OpenSpec workflow through guided practice
- Follow existing patterns for skill/command template generation
- Provide comprehensive narration that explains each step
- Include codebase analysis to suggest real, appropriately-scoped tasks
**Non-Goals:**
- Creating a separate "demo mode" or simulated workflow (we do real work)
- Adding new CLI commands (this is purely agent instructions)
- Modifying the init/update flow (just adding to the template arrays)
## Decisions
### Decision 1: Single Monolithic Skill
The onboard skill will be a single comprehensive instruction set rather than composing existing skills with flags.
**Rationale:**
- Slash commands don't support flags (they're just prompts)
- A monolithic skill gives complete control over narration and pacing
- Easier to maintain a single cohesive experience
- Users learn the real commands by seeing them mentioned in narration
### Decision 2: Codebase Analysis Patterns
The skill instructions will direct the agent to look for specific patterns when suggesting starter tasks:
1. TODO/FIXME comments in code
2. Missing error handling (`catch` blocks that swallow errors, no try-catch around risky operations)
3. Functions without tests (cross-reference src/ with test files)
4. Type: `any` in TypeScript files
5. Console.log statements in non-debug code
6. Missing input validation on user-facing inputs
7. Recent git commits (for context on what user is working on)
**Rationale:** These are universally applicable, easy to detect, and produce well-scoped tasks.
### Decision 3: Narration Integration Style
Each phase will follow a pattern:
1. **EXPLAIN** what we're about to do and why (1-2 sentences)
2. **DO** the action (run command, create artifact)
3. **SHOW** what happened
4. **PAUSE** at key transitions (not every step)
Pauses occur at:
- After task selection (before creating change)
- After drafting proposal (before saving)
- After tasks are generated (before implementation)
- After archive (final recap)
**Rationale:** Too many pauses becomes tedious. Too few loses the teaching opportunity. These are the natural "chapter breaks."
### Decision 4: Scope Guardrail Approach
When user selects a task that's too large, the skill will:
1. Acknowledge the task is valuable
2. Explain why smaller is better for first time
3. Suggest a smaller slice or alternative
4. Let user override if they insist
**Rationale:** Soft guardrails teach without frustrating. Users learn scope calibration as part of the experience.
### Decision 5: Template Structure
The skill template will be ~400-600 lines of instruction text, structured as:
```
- Preflight checks (init status)
- Phase 1: Welcome & Setup
- Phase 2: Task Selection (with codebase analysis instructions)
- Phase 3: Explore Demo (brief)
- Phase 4: Change Creation
- Phase 5: Proposal
- Phase 6: Specs
- Phase 7: Design
- Phase 8: Tasks
- Phase 9: Apply (Implementation)
- Phase 10: Archive
- Phase 11: Recap & Next Steps
- Edge cases & graceful exits
```
The command template will be identical to the skill template (same content, different wrapper).
**Rationale:** Following the established pattern where skill and command share the same core instructions.
## Risks / Trade-offs
**Risk: Instruction length**
The skill will be significantly longer than existing skills (~500 lines vs ~100-200).
→ Mitigation: This is acceptable since onboarding is inherently comprehensive. Token cost is one-time per session.
**Risk: Codebase analysis may find nothing**
Some codebases (new projects, very clean code) may not have obvious improvement opportunities.
→ Mitigation: Fall back to asking user what they want to build. Include "add a new feature" as an option.
**Risk: Task suggestions may be inappropriate**
Agent might suggest tasks that touch sensitive code or have hidden complexity.
→ Mitigation: User always chooses; agent just suggests. Scope estimates help set expectations.
**Risk: User abandons mid-way**
Onboarding takes ~15 minutes; users may not complete it.
→ Mitigation: Graceful exit handling - note the change is saved, explain how to continue later.
@@ -0,0 +1,27 @@
## Why
Users who run `openspec init` are left with files but no clear path to actually using the system. There's a gap between "I have OpenSpec set up" and "I understand the workflow." An onboarding skill would guide users through their first complete change cycle on a real task in their codebase, teaching the workflow by doing it.
## What Changes
- Add new `/opsx:onboard` skill that guides users through their first OpenSpec change
- Add corresponding slash command template for editor integrations
- The skill will:
- Analyze the user's codebase to suggest appropriately-scoped starter tasks
- Walk through the full workflow (explore → new → proposal → specs → design → tasks → apply → archive)
- Provide narration explaining each step as it happens
- Result in a real, implemented change in the user's codebase
## Capabilities
### New Capabilities
- `opsx-onboard-skill`: The onboarding skill that guides users through their first complete OpenSpec workflow cycle with narration and codebase-aware task suggestions
### Modified Capabilities
<!-- No existing specs are being modified - this is purely additive -->
## Impact
- `src/core/templates/skill-templates.ts`: Add `getOnboardSkillTemplate()` and `getOpsxOnboardCommandTemplate()` functions
- `src/core/shared/skill-generation.ts`: Register the new skill and command templates in `getSkillTemplates()` and `getCommandTemplates()`
- Users running `openspec init` or `openspec update` will get the new skill/command files generated
@@ -0,0 +1,162 @@
## ADDED Requirements
### Requirement: OPSX Onboard Skill
The system SHALL provide an `/opsx:onboard` skill that guides users through their first complete OpenSpec workflow cycle with narration and real codebase work.
#### Scenario: Skill invocation
- **WHEN** user invokes `/opsx:onboard`
- **THEN** agent checks if OpenSpec is initialized
- **AND** if not initialized, prompts user to run `openspec init` first
- **AND** if initialized, proceeds with onboarding flow
#### Scenario: Welcome and expectations
- **WHEN** onboarding begins
- **THEN** agent displays welcome message explaining what will happen
- **AND** sets expectation of ~15 minute duration
- **AND** explains the workflow phases: explore → new → artifacts → apply → archive
### Requirement: Codebase Analysis for Task Suggestions
The skill SHALL analyze the user's codebase to suggest appropriately-scoped starter tasks.
#### Scenario: Codebase scanning
- **WHEN** onboarding reaches task selection phase
- **THEN** agent scans codebase for small improvement opportunities
- **AND** looks for: TODO/FIXME comments, missing error handling, functions without tests, outdated dependencies, type: any in TypeScript, console.log in production code, missing input validation
- **AND** checks recent git commits for context on current work
#### Scenario: Task suggestion presentation
- **WHEN** agent has analyzed codebase
- **THEN** agent presents 3-4 specific task suggestions with scope estimates
- **AND** each suggestion includes: task description, estimated scope (files/lines), why it's a good starter
- **AND** offers option for user to specify their own task
#### Scenario: Scope guardrail
- **WHEN** user selects or describes a task that is too large
- **THEN** agent gently redirects toward smaller scope
- **AND** suggests breaking down or deferring the large task
- **AND** offers appropriately-sized alternatives
### Requirement: Explore Phase Demo
The skill SHALL briefly demonstrate explore mode before creating a change.
#### Scenario: Brief explore demonstration
- **WHEN** task is selected
- **THEN** agent briefly demonstrates `/opsx:explore` by investigating relevant code
- **AND** explains explore mode is for thinking before doing
- **AND** keeps this phase short (not a full exploration session)
- **AND** transitions to change creation
### Requirement: Guided Artifact Creation
The skill SHALL guide users through each artifact with narration explaining the purpose.
#### Scenario: Change creation with narration
- **WHEN** creating the change directory
- **THEN** agent runs `openspec new change "<name>"` with derived kebab-case name
- **AND** explains what a "change" is (container for thinking and planning)
- **AND** shows the folder structure that was created
- **AND** pauses for user acknowledgment before proceeding
#### Scenario: Proposal creation with narration
- **WHEN** creating proposal.md
- **THEN** agent explains proposals capture WHY we're making this change
- **AND** drafts proposal based on selected task
- **AND** shows draft to user for approval before saving
- **AND** explains the sections (Why, What Changes, Capabilities, Impact)
#### Scenario: Specs creation with narration
- **WHEN** creating spec files
- **THEN** agent explains specs define WHAT we're building in detail
- **AND** explains the requirement/scenario format
- **AND** creates spec file(s) based on proposal capabilities
- **AND** notes that specs become documentation that stays in sync
#### Scenario: Design creation with narration
- **WHEN** creating design.md
- **THEN** agent explains design captures HOW we'll build it
- **AND** notes this is where technical decisions and tradeoffs live
- **AND** for small changes, acknowledges design may be brief
- **AND** creates design based on proposal and specs
#### Scenario: Tasks creation with narration
- **WHEN** creating tasks.md
- **THEN** agent explains tasks break work into checkboxes
- **AND** explains these drive the apply phase
- **AND** generates task list from design and specs
- **AND** shows tasks and asks if ready to implement
### Requirement: Guided Implementation
The skill SHALL implement tasks with narration connecting back to artifacts.
#### Scenario: Implementation with narration
- **WHEN** implementing tasks
- **THEN** agent announces each task before working on it
- **AND** implements the change in the codebase
- **AND** occasionally references how specs/design informed decisions
- **AND** marks each task complete as it finishes
- **AND** keeps narration light (not over-explaining)
#### Scenario: Implementation completion
- **WHEN** all tasks are complete
- **THEN** agent announces completion
- **AND** summarizes what was done
- **AND** transitions to archive phase
### Requirement: Archive with Explanation
The skill SHALL archive the completed change and explain what happened.
#### Scenario: Archive with narration
- **WHEN** archiving the change
- **THEN** agent explains archive moves change to dated folder
- **AND** runs archive process
- **AND** shows where archived change lives
- **AND** explains the long-term value (finding decisions later)
### Requirement: Recap and Next Steps
The skill SHALL conclude with a recap and command reference.
#### Scenario: Final recap
- **WHEN** onboarding is complete
- **THEN** agent summarizes the workflow phases completed
- **AND** emphasizes this rhythm works for any size change
- **AND** provides command reference table (/opsx:explore, /opsx:new, /opsx:ff, /opsx:continue, /opsx:apply, /opsx:verify, /opsx:archive)
- **AND** suggests next actions (try /opsx:new or /opsx:ff on something)
### Requirement: Graceful Exit Handling
The skill SHALL handle users who want to stop mid-way.
#### Scenario: User wants to stop
- **WHEN** user indicates they want to stop during onboarding
- **THEN** agent acknowledges gracefully
- **AND** notes that the in-progress change is saved
- **AND** explains how to continue later with `/opsx:continue <name>`
- **AND** exits without pressure
#### Scenario: User wants quick reference only
- **WHEN** user says they just want to see the commands
- **THEN** agent provides command cheat sheet
- **AND** exits gracefully with encouragement to try `/opsx:new`
@@ -0,0 +1,21 @@
## 1. Add Skill Template
- [x] 1.1 Add `getOnboardSkillTemplate()` function to `src/core/templates/skill-templates.ts` with full onboarding instruction text covering all phases (preflight, welcome, task selection, explore demo, change creation, proposal, specs, design, tasks, apply, archive, recap)
- [x] 1.2 Include codebase analysis instructions for suggesting starter tasks (TODO/FIXME, missing error handling, missing tests, type:any, console.log, missing validation)
- [x] 1.3 Include narration pattern instructions (EXPLAIN → DO → SHOW → PAUSE at key transitions)
- [x] 1.4 Include scope guardrail instructions for redirecting users away from overly large tasks
- [x] 1.5 Include graceful exit handling instructions (user stops mid-way, user just wants command reference)
## 2. Add Command Template
- [x] 2.1 Add `getOpsxOnboardCommandTemplate()` function to `src/core/templates/skill-templates.ts` returning CommandTemplate with same instruction content as skill
## 3. Register Templates
- [x] 3.1 Add onboard skill to `getSkillTemplates()` array in `src/core/shared/skill-generation.ts` with dirName `openspec-onboard`
- [x] 3.2 Add onboard command to `getCommandTemplates()` array in `src/core/shared/skill-generation.ts` with id `onboard`
## 4. Verify
- [x] 4.1 Run `pnpm run build` to ensure TypeScript compiles
- [x] 4.2 Test skill generation by running `openspec init` in a test directory and verifying onboard skill/command files are created
@@ -0,0 +1,96 @@
# Design: Add /opsx:verify Skill
## Architecture Decision: Dynamic Generation via Setup Command
### Context
All existing opsx experimental skills (explore, new, continue, apply, ff, sync, archive) are dynamically generated when users run `openspec artifact-experimental-setup`. They are not manually created files checked into the repository.
### Decision
**Integrate verify into the existing artifact-experimental-setup system rather than creating static skill files.**
### Rationale
1. **Consistency**: All 7 existing opsx skills follow this pattern. Adding verify as the 8th skill should follow the same architecture.
2. **Maintainability**: Template functions in `skill-templates.ts` are the single source of truth. Changes to skill definitions automatically propagate to all users when they re-run setup.
3. **Distribution**: Users get the verify skill automatically when running `openspec artifact-experimental-setup`, just like all other opsx skills. No special installation steps needed.
4. **Versioning**: Skills are generated from the installed npm package version, ensuring consistency between CLI version and skill behavior.
### Implementation Approach
#### 1. Template Functions
Add two template functions to `src/core/templates/skill-templates.ts`:
```typescript
export function getVerifyChangeSkillTemplate(): SkillTemplate
export function getOpsxVerifyCommandTemplate(): CommandTemplate
```
These return the skill definition (for Agent Skills) and slash command definition (for explicit invocation).
#### 2. Setup Integration
Update `artifactExperimentalSetupCommand()` in `src/commands/artifact-workflow.ts`:
- Import both template functions
- Add verify to the `skills` array (position 8)
- Add verify to the `commands` array (position 8)
- Update help text to list `/opsx:verify`
#### 3. Generated Artifacts
When users run `openspec artifact-experimental-setup`, the command creates:
- `.claude/skills/openspec-verify-change/SKILL.md` - Agent Skills format
- `.claude/commands/opsx/verify.md` - Slash command format
Both are generated from the template functions, with YAML frontmatter automatically added.
### Alternatives Considered
**Alternative 1: Static skill files in repository**
Create `.claude/skills/openspec-verify-change/SKILL.md` as a static file in the OpenSpec repository.
**Rejected because:**
- Inconsistent with all other opsx skills
- Requires users to manually copy/update files
- Versioning becomes complicated (repo version vs installed package version)
- Breaks the established pattern
**Alternative 2: Separate verify setup command**
Add `openspec setup-verify` as a separate command.
**Rejected because:**
- Fragments the setup experience
- Users would need to run multiple commands
- Doesn't scale if we add more skills in the future
- Goes against the "setup once, get everything" philosophy
### Trade-offs
**Advantages:**
- Consistent with existing architecture
- Zero additional setup burden for users
- Easy to update and maintain
- Automatic version compatibility
**Disadvantages:**
- Slightly more complex initial implementation (template functions + integration)
- Requires understanding the setup system (but that's already documented)
### Verification
The implementation correctly follows this design if:
1. Both template functions exist in `skill-templates.ts`
2. Verify appears in both skills and commands arrays in `artifact-workflow.ts`
3. Help text mentions `/opsx:verify`
4. Running `openspec artifact-experimental-setup` generates both skill and command files
5. Build succeeds with no TypeScript errors
@@ -0,0 +1,48 @@
# Change: Add /opsx:verify Skill
## Why
Users need a way to validate that their implementation actually matches what was requested before archiving a change. Currently, there's no systematic way to check:
- Whether all tasks are truly complete
- Whether the implementation covers all spec requirements and scenarios
- Whether the implementation follows the design decisions
- Whether the code is coherent and makes sense
A user requested: "Can we get a :verify that will ensure that the implementation matches what was requested?"
## What Changes
- Add `getVerifyChangeSkillTemplate()` function to `skill-templates.ts`
- Add `getOpsxVerifyCommandTemplate()` function to `skill-templates.ts`
- Integrate verify skill into `artifactExperimentalSetupCommand` in `artifact-workflow.ts`
- Add verify to the skills and commands arrays in the setup command
- Update help text to include `/opsx:verify` in the list of available commands
- Create `opsx-verify-skill` capability spec
## Verification Dimensions
The skill verifies across three dimensions:
1. **Completeness** - Are all tasks done? Are all specs addressed?
2. **Correctness** - Does the implementation match specs? Are scenarios covered?
3. **Coherence** - Does the implementation make sense? Does it follow design.md?
## Output Format
Produces a prioritized report with:
- Summary scorecard (tasks, specs, design adherence)
- Critical issues first (must fix before archive)
- Warnings second (should fix)
- Suggestions third (nice to have)
- Actionable fix recommendations for each issue
## Impact
- Affected specs: New `opsx-verify-skill` spec
- Affected code:
- `src/core/templates/skill-templates.ts` - Added 2 new template functions
- `src/commands/artifact-workflow.ts` - Integrated verify into experimental setup
- Generated artifacts: When users run `openspec artifact-experimental-setup`:
- Creates `.claude/skills/openspec-verify-change/SKILL.md`
- Creates `.claude/commands/opsx/verify.md`
- Related skills: Works alongside `/opsx:apply` and before `/opsx:archive`
@@ -0,0 +1,190 @@
# opsx-verify-skill Specification
## Purpose
Defines the agent skill for verifying that implementation matches change artifacts (specs, tasks, design).
## ADDED Requirements
### Requirement: Verify Skill Invocation
The system SHALL provide an `/opsx:verify` skill that validates implementation against change artifacts.
#### Scenario: Verify with change name provided
- **WHEN** agent executes `/opsx:verify <change-name>`
- **THEN** the agent verifies implementation for that specific change
- **AND** produces a verification report
#### Scenario: Verify without change name
- **WHEN** agent executes `/opsx:verify` without a change name
- **THEN** the agent prompts user to select from available changes
- **AND** shows only changes that have implementation tasks
#### Scenario: Change has no tasks
- **WHEN** selected change has no tasks.md or tasks are empty
- **THEN** the agent reports "No tasks to verify"
- **AND** suggests running `/opsx:continue` to create tasks
### Requirement: Completeness Verification
The agent SHALL verify that all required work has been completed.
#### Scenario: Task completion check
- **WHEN** verifying completeness
- **THEN** the agent reads tasks.md
- **AND** counts tasks marked `- [x]` (complete) vs `- [ ]` (incomplete)
- **AND** reports completion status with specific incomplete tasks listed
#### Scenario: Spec coverage check
- **WHEN** verifying completeness
- **AND** delta specs exist in `openspec/changes/<name>/specs/`
- **THEN** the agent extracts all requirements from delta specs
- **AND** searches codebase for implementation of each requirement
- **AND** reports which requirements appear to have implementation vs which are missing
#### Scenario: All tasks complete
- **WHEN** all tasks are marked complete
- **THEN** report "Tasks: N/N complete"
- **AND** mark completeness dimension as passed
#### Scenario: Incomplete tasks found
- **WHEN** some tasks are incomplete
- **THEN** report "Tasks: X/N complete"
- **AND** list each incomplete task
- **AND** mark as CRITICAL issue
- **AND** suggest: "Complete remaining tasks or mark as done if already implemented"
### Requirement: Correctness Verification
The agent SHALL verify that implementation matches the specifications.
#### Scenario: Requirement implementation mapping
- **WHEN** verifying correctness
- **THEN** for each requirement in delta specs:
- Search codebase for implementation
- Identify relevant files and line numbers
- Assess whether implementation satisfies the requirement
#### Scenario: Scenario coverage check
- **WHEN** verifying correctness
- **THEN** for each scenario in delta specs:
- Check if the scenario's conditions are handled in code
- Check if tests exist that cover the scenario
- Report coverage status
#### Scenario: Implementation matches spec
- **WHEN** implementation appears to satisfy a requirement
- **THEN** report which files/lines implement it
- **AND** mark requirement as covered
#### Scenario: Implementation diverges from spec
- **WHEN** implementation exists but doesn't match spec intent
- **THEN** report the divergence as WARNING
- **AND** explain what differs
- **AND** suggest: either update implementation or update spec to match reality
#### Scenario: Missing implementation
- **WHEN** no implementation found for a requirement
- **THEN** report as CRITICAL issue
- **AND** suggest: "Implement requirement X" with guidance on what's needed
### Requirement: Coherence Verification
The agent SHALL verify that implementation is sensible and follows design decisions.
#### Scenario: Design.md adherence check
- **WHEN** verifying coherence
- **AND** design.md exists for the change
- **THEN** extract key decisions from design.md
- **AND** verify implementation follows those decisions
- **AND** report any deviations
#### Scenario: No design.md
- **WHEN** verifying coherence
- **AND** no design.md exists
- **THEN** skip design adherence check
- **AND** note "No design.md to verify against"
#### Scenario: Design decision followed
- **WHEN** implementation follows a design decision
- **THEN** report as confirmed
- **AND** cite evidence from code
#### Scenario: Design decision violated
- **WHEN** implementation contradicts a design decision
- **THEN** report as WARNING
- **AND** explain the contradiction
- **AND** suggest: either update implementation or update design.md
#### Scenario: Code pattern consistency
- **WHEN** verifying coherence
- **THEN** check if new code follows existing project patterns
- **AND** flag any significant deviations as suggestions
### Requirement: Verification Report Format
The agent SHALL produce a structured, prioritized report.
#### Scenario: Report summary
- **WHEN** verification completes
- **THEN** display summary scorecard:
```
## Verification Report: <change-name>
### Summary
| Dimension | Status |
|--------------|----------|
| Completeness | X/Y |
| Correctness | X/Y |
| Coherence | Followed |
```
#### Scenario: Issue prioritization
- **WHEN** issues are found
- **THEN** group and display in priority order:
1. CRITICAL - Must fix before archive (missing implementation, incomplete tasks)
2. WARNING - Should fix (divergence from spec/design, missing tests)
3. SUGGESTION - Nice to fix (pattern inconsistencies, minor improvements)
#### Scenario: Actionable recommendations
- **WHEN** reporting an issue
- **THEN** include specific, actionable fix recommendation
- **AND** reference relevant files and line numbers where applicable
- **AND** avoid vague suggestions like "consider reviewing"
#### Scenario: All checks pass
- **WHEN** no issues found across all dimensions
- **THEN** display:
```
All checks passed. Ready for archive.
```
#### Scenario: Critical issues found
- **WHEN** CRITICAL issues exist
- **THEN** display:
```
X critical issue(s) found. Fix before archiving.
```
- **AND** do NOT suggest running archive
#### Scenario: Only warnings/suggestions
- **WHEN** no CRITICAL issues but warnings exist
- **THEN** display:
```
No critical issues. Y warning(s) to consider.
Ready for archive (with noted improvements).
```
### Requirement: Flexible Artifact Handling
The agent SHALL gracefully handle changes with varying artifact completeness.
#### Scenario: Minimal change (tasks only)
- **WHEN** change has only tasks.md
- **THEN** verify task completion only
- **AND** skip spec and design checks
- **AND** note which checks were skipped
#### Scenario: Change with specs but no design
- **WHEN** change has tasks.md and delta specs but no design.md
- **THEN** verify completeness and correctness
- **AND** skip design adherence
- **AND** still check code coherence against project patterns
#### Scenario: Full change (all artifacts)
- **WHEN** change has proposal, design, specs, and tasks
- **THEN** perform all verification checks
- **AND** cross-reference artifacts for consistency
@@ -0,0 +1,15 @@
# Tasks: Add /opsx:verify Skill
## 1. Skill Template Functions
- [x] 1.1 Add `getVerifyChangeSkillTemplate()` to skill-templates.ts
- [x] 1.2 Add `getOpsxVerifyCommandTemplate()` to skill-templates.ts
## 2. Integration with artifact-experimental-setup
- [x] 2.1 Import verify template functions in artifact-workflow.ts
- [x] 2.2 Add verify to skills array in artifactExperimentalSetupCommand
- [x] 2.3 Add verify to commands array in artifactExperimentalSetupCommand
- [x] 2.4 Add verify to help text output
## 3. Verification (Build & Test)
- [x] 3.1 Verify TypeScript compilation succeeds
- [x] 3.2 Verify all 8 skills are now included (was 7, now 8)
@@ -0,0 +1,15 @@
# Change Proposal: Extend Shell Completions
## Why
Zsh completions provide an excellent developer experience, but many developers use bash, fish, or PowerShell. Extending completion support to these shells removes friction for the majority of developers who don't use Zsh.
## What Changes
This change adds bash, fish, and PowerShell completion support following the same architectural patterns, documentation methodology, and testing rigor established for Zsh completions.
## Deltas
- **Spec:** `cli-completion`
- **Operation:** MODIFIED
- **Description:** Extend completion generation, installation, and testing requirements to support bash, fish, and PowerShell while maintaining the existing Zsh implementation and architectural patterns
@@ -0,0 +1,328 @@
# cli-completion Spec Delta
## MODIFIED Requirements
### Requirement: Native Shell Behavior Integration
The completion system SHALL respect and integrate with each supported shell's native completion patterns and user interaction model.
#### Scenario: Zsh native completion
- **WHEN** generating Zsh completion scripts
- **THEN** use Zsh completion system with `_arguments`, `_describe`, and `compadd`
- **AND** completions SHALL trigger on single TAB (standard Zsh behavior)
- **AND** display as an interactive menu that users navigate with TAB/arrow keys
- **AND** support Oh My Zsh's enhanced menu styling automatically
#### Scenario: Bash native completion
- **WHEN** generating Bash completion scripts
- **THEN** use Bash completion with `complete` builtin and `COMPREPLY` array
- **AND** completions SHALL trigger on double TAB (standard Bash behavior)
- **AND** display as space-separated list or column format
- **AND** support both bash-completion v1 and v2 patterns
#### Scenario: Fish native completion
- **WHEN** generating Fish completion scripts
- **THEN** use Fish's `complete` command with conditions
- **AND** completions SHALL trigger on single TAB with auto-suggestion preview
- **AND** display with Fish's native coloring and description alignment
- **AND** leverage Fish's built-in caching automatically
#### Scenario: PowerShell native completion
- **WHEN** generating PowerShell completion scripts
- **THEN** use `Register-ArgumentCompleter` with scriptblock
- **AND** completions SHALL trigger on TAB with cycling behavior
- **AND** display with PowerShell's native completion UI
- **AND** support both Windows PowerShell 5.1 and PowerShell Core 7+
#### Scenario: No custom UX patterns
- **WHEN** implementing completion for any shell
- **THEN** do NOT attempt to customize completion trigger behavior
- **AND** do NOT override shell-specific navigation patterns
- **AND** ensure completions feel native to experienced users of that shell
### Requirement: Shell Detection
The completion system SHALL automatically detect the user's current shell environment.
#### Scenario: Detecting Zsh from environment
- **WHEN** no shell is explicitly specified
- **THEN** read the `$SHELL` environment variable
- **AND** extract the shell name from the path (e.g., `/bin/zsh` → `zsh`)
- **AND** validate the shell is one of: `zsh`, `bash`, `fish`, `powershell`
- **AND** throw an error if the shell is not supported
#### Scenario: Detecting Bash from environment
- **WHEN** `$SHELL` contains `bash` in the path
- **THEN** detect shell as `bash`
- **AND** proceed with bash-specific completion logic
#### Scenario: Detecting Fish from environment
- **WHEN** `$SHELL` contains `fish` in the path
- **THEN** detect shell as `fish`
- **AND** proceed with fish-specific completion logic
#### Scenario: Detecting PowerShell from environment
- **WHEN** `$PSModulePath` environment variable is present
- **THEN** detect shell as `powershell`
- **AND** proceed with PowerShell-specific completion logic
#### Scenario: Unsupported shell detection
- **WHEN** shell path indicates an unsupported shell
- **THEN** throw error: "Shell '<name>' is not supported. Supported shells: zsh, bash, fish, powershell"
### Requirement: Completion Generation
The completion command SHALL generate completion scripts for all supported shells on demand.
#### Scenario: Generating Zsh completion
- **WHEN** user executes `openspec completion generate zsh`
- **THEN** output a complete Zsh completion script to stdout
- **AND** include completions for all commands: init, list, show, validate, archive, view, update, change, spec, completion
- **AND** include all command-specific flags and options
- **AND** use Zsh's `_arguments` and `_describe` built-in functions
- **AND** support dynamic completion for change and spec IDs
#### Scenario: Generating Bash completion
- **WHEN** user executes `openspec completion generate bash`
- **THEN** output a complete Bash completion script to stdout
- **AND** include completions for all commands and subcommands
- **AND** use `complete -F` with custom completion function
- **AND** populate `COMPREPLY` with appropriate suggestions
- **AND** support dynamic completion for change and spec IDs via `openspec __complete`
#### Scenario: Generating Fish completion
- **WHEN** user executes `openspec completion generate fish`
- **THEN** output a complete Fish completion script to stdout
- **AND** use `complete -c openspec` with conditions
- **AND** include command-specific completions with `--condition` predicates
- **AND** support dynamic completion for change and spec IDs via `openspec __complete`
- **AND** include descriptions for each completion option
#### Scenario: Generating PowerShell completion
- **WHEN** user executes `openspec completion generate powershell`
- **THEN** output a complete PowerShell completion script to stdout
- **AND** use `Register-ArgumentCompleter -CommandName openspec`
- **AND** implement scriptblock that handles command context
- **AND** support dynamic completion for change and spec IDs via `openspec __complete`
- **AND** return `[System.Management.Automation.CompletionResult]` objects
### Requirement: Installation Automation
The completion command SHALL automatically install completion scripts into shell configuration files for all supported shells.
#### Scenario: Installing for Oh My Zsh
- **WHEN** user executes `openspec completion install zsh`
- **THEN** detect if Oh My Zsh is installed by checking for `$ZSH` environment variable or `~/.oh-my-zsh/` directory
- **AND** create custom completions directory at `~/.oh-my-zsh/custom/completions/` if it doesn't exist
- **AND** write completion script to `~/.oh-my-zsh/custom/completions/_openspec`
- **AND** ensure `~/.oh-my-zsh/custom/completions` is in `$fpath` by updating `~/.zshrc` if needed
- **AND** display success message with instruction to run `exec zsh` or restart terminal
#### Scenario: Installing for standard Zsh
- **WHEN** user executes `openspec completion install zsh` and Oh My Zsh is not detected
- **THEN** create completions directory at `~/.zsh/completions/` if it doesn't exist
- **AND** write completion script to `~/.zsh/completions/_openspec`
- **AND** add `fpath=(~/.zsh/completions $fpath)` to `~/.zshrc` if not already present
- **AND** add `autoload -Uz compinit && compinit` to `~/.zshrc` if not already present
- **AND** display success message with instruction to run `exec zsh` or restart terminal
#### Scenario: Installing for Bash with bash-completion
- **WHEN** user executes `openspec completion install bash`
- **THEN** detect if bash-completion is installed by checking for `/usr/share/bash-completion` or `/etc/bash_completion.d`
- **AND** if bash-completion is available, write to `/etc/bash_completion.d/openspec` (with sudo) or `~/.local/share/bash-completion/completions/openspec`
- **AND** if bash-completion is not available, write to `~/.bash_completion.d/openspec` and source it from `~/.bashrc`
- **AND** add sourcing line to `~/.bashrc` using marker-based updates if needed
- **AND** display success message with instruction to run `exec bash` or restart terminal
#### Scenario: Installing for Fish
- **WHEN** user executes `openspec completion install fish`
- **THEN** create Fish completions directory at `~/.config/fish/completions/` if it doesn't exist
- **AND** write completion script to `~/.config/fish/completions/openspec.fish`
- **AND** Fish automatically loads completions from this directory (no config file modification needed)
- **AND** display success message indicating completions are immediately available
#### Scenario: Installing for PowerShell
- **WHEN** user executes `openspec completion install powershell`
- **THEN** detect PowerShell profile location via `$PROFILE` environment variable or default paths
- **AND** create profile directory if it doesn't exist
- **AND** add completion script import to profile using marker-based updates
- **AND** write completion script to PowerShell modules directory or alongside profile
- **AND** display success message with instruction to restart PowerShell or run `. $PROFILE`
#### Scenario: Auto-detecting shell for installation
- **WHEN** user executes `openspec completion install` without specifying a shell
- **THEN** detect current shell using shell detection logic
- **AND** install completion for the detected shell (zsh, bash, fish, or powershell)
- **AND** display which shell was detected
#### Scenario: Already installed
- **WHEN** completion is already installed for the target shell
- **THEN** display message indicating completion is already installed
- **AND** offer to reinstall/update by overwriting existing files
- **AND** exit with code 0
### Requirement: Uninstallation
The completion command SHALL remove installed completion scripts and configuration for all supported shells.
#### Scenario: Uninstalling Zsh completion
- **WHEN** user executes `openspec completion uninstall zsh`
- **THEN** prompt for confirmation before proceeding (unless `--yes` flag provided)
- **AND** if user declines, cancel uninstall and display "Uninstall cancelled."
- **AND** if user confirms, remove `~/.oh-my-zsh/custom/completions/_openspec` if Oh My Zsh is detected
- **AND** remove `~/.zsh/completions/_openspec` if standard Zsh setup is detected
- **AND** remove fpath modifications from `~/.zshrc` using marker-based removal
- **AND** display success message
#### Scenario: Uninstalling Bash completion
- **WHEN** user executes `openspec completion uninstall bash`
- **THEN** prompt for confirmation (unless `--yes` flag provided)
- **AND** if user confirms, remove completion file from bash-completion directory or `~/.bash_completion.d/`
- **AND** remove sourcing lines from `~/.bashrc` using marker-based removal
- **AND** display success message
#### Scenario: Uninstalling Fish completion
- **WHEN** user executes `openspec completion uninstall fish`
- **THEN** prompt for confirmation (unless `--yes` flag provided)
- **AND** if user confirms, remove `~/.config/fish/completions/openspec.fish`
- **AND** display success message (no config file modification needed)
#### Scenario: Uninstalling PowerShell completion
- **WHEN** user executes `openspec completion uninstall powershell`
- **THEN** prompt for confirmation (unless `--yes` flag provided)
- **AND** if user confirms, remove completion import from PowerShell profile using marker-based removal
- **AND** remove completion script file
- **AND** display success message
#### Scenario: Auto-detecting shell for uninstallation
- **WHEN** user executes `openspec completion uninstall` without specifying a shell
- **THEN** detect current shell and uninstall completion for that shell
#### Scenario: Not installed
- **WHEN** attempting to uninstall completion that isn't installed
- **THEN** display error message indicating completion is not installed
- **AND** exit with code 1
### Requirement: Architecture Patterns
The completion implementation SHALL follow clean architecture principles with TypeScript best practices, supporting multiple shells through a plugin-based pattern.
#### Scenario: Shell-specific generators
- **WHEN** implementing completion generators
- **THEN** create generator classes for each shell: `ZshGenerator`, `BashGenerator`, `FishGenerator`, `PowerShellGenerator`
- **AND** implement a common `CompletionGenerator` interface with method:
- `generate(commands: CommandDefinition[]): string` - Returns complete shell script
- **AND** each generator handles shell-specific syntax, escaping, and patterns
- **AND** all generators consume the same `CommandDefinition[]` from the command registry
#### Scenario: Shell-specific installers
- **WHEN** implementing completion installers
- **THEN** create installer classes for each shell: `ZshInstaller`, `BashInstaller`, `FishInstaller`, `PowerShellInstaller`
- **AND** implement a common `CompletionInstaller` interface with methods:
- `install(script: string): Promise<InstallationResult>` - Installs completion script
- `uninstall(): Promise<{ success: boolean; message: string }>` - Removes completion
- **AND** each installer handles shell-specific paths, config files, and installation patterns
#### Scenario: Factory pattern for shell selection
- **WHEN** selecting shell-specific implementation
- **THEN** use `CompletionFactory` class with static methods:
- `createGenerator(shell: SupportedShell): CompletionGenerator`
- `createInstaller(shell: SupportedShell): CompletionInstaller`
- **AND** factory uses switch statements with TypeScript exhaustiveness checking
- **AND** adding new shell requires updating `SupportedShell` type and factory cases
#### Scenario: Dynamic completion providers
- **WHEN** implementing dynamic completions
- **THEN** create a `CompletionProvider` class that encapsulates project discovery logic
- **AND** implement methods:
- `getChangeIds(): Promise<string[]>` - Discovers active change IDs
- `getSpecIds(): Promise<string[]>` - Discovers spec IDs
- `isOpenSpecProject(): boolean` - Checks if current directory is OpenSpec-enabled
- **AND** implement caching with 2-second TTL using class properties
#### Scenario: Command registry
- **WHEN** defining completable commands
- **THEN** create a centralized `CommandDefinition` type with properties:
- `name: string` - Command name
- `description: string` - Help text
- `flags: FlagDefinition[]` - Available flags
- `acceptsPositional: boolean` - Whether command takes positional arguments
- `positionalType: string` - Type of positional (change-id, spec-id, path, shell)
- `subcommands?: CommandDefinition[]` - Nested subcommands
- **AND** export a `COMMAND_REGISTRY` constant with all command definitions
- **AND** all generators consume this registry to ensure consistency across shells
#### Scenario: Type-safe shell detection
- **WHEN** implementing shell detection
- **THEN** define a `SupportedShell` type as literal type: `'zsh' | 'bash' | 'fish' | 'powershell'`
- **AND** implement `detectShell()` function in `src/utils/shell-detection.ts`
- **AND** return detected shell or throw error with supported shells list
### Requirement: Testing Support
The completion implementation SHALL be testable with unit and integration tests for all supported shells.
#### Scenario: Mock shell environment
- **WHEN** writing tests for shell detection
- **THEN** allow overriding `$SHELL` and `$PSModulePath` environment variables
- **AND** use dependency injection for file system operations
- **AND** test detection for all four shells independently
#### Scenario: Generator output verification
- **WHEN** testing completion generators
- **THEN** create test suite for each shell generator (zsh, bash, fish, powershell)
- **AND** verify generated scripts contain expected patterns for that shell
- **AND** test that command registry is properly consumed
- **AND** ensure dynamic completion placeholders are present
- **AND** verify shell-specific syntax and escaping
#### Scenario: Installer simulation
- **WHEN** testing installation logic
- **THEN** create test suite for each shell installer
- **AND** use temporary test directories instead of actual home directories
- **AND** verify file creation without modifying real shell configurations
- **AND** test path resolution logic independently
- **AND** mock file system operations to avoid side effects
#### Scenario: Cross-shell consistency
- **WHEN** testing completion behavior
- **THEN** verify all shells support the same commands and flags
- **AND** verify dynamic completions work consistently across shells
- **AND** ensure error messages are consistent across shells
@@ -0,0 +1,49 @@
# Implementation Tasks
## Phase 1: Foundation and Bash Support
- [x] Update `SupportedShell` type in `src/utils/shell-detection.ts` to include `'bash' | 'fish' | 'powershell'`
- [x] Extend shell detection logic to recognize bash, fish, and PowerShell from environment variables
- [x] Create `src/core/completions/generators/bash-generator.ts` implementing `CompletionGenerator` interface
- [x] Create `src/core/completions/installers/bash-installer.ts` implementing `CompletionInstaller` interface
- [x] Update `CompletionFactory.createGenerator()` to support bash
- [x] Update `CompletionFactory.createInstaller()` to support bash
- [x] Create test file `test/core/completions/generators/bash-generator.test.ts` mirroring zsh test structure
- [x] Create test file `test/core/completions/installers/bash-installer.test.ts` mirroring zsh test structure
- [x] Verify bash completions work manually: `openspec completion install bash && exec bash`
## Phase 2: Fish Support
- [x] Create `src/core/completions/generators/fish-generator.ts` implementing `CompletionGenerator` interface
- [x] Create `src/core/completions/installers/fish-installer.ts` implementing `CompletionInstaller` interface
- [x] Update `CompletionFactory.createGenerator()` to support fish
- [x] Update `CompletionFactory.createInstaller()` to support fish
- [x] Create test file `test/core/completions/generators/fish-generator.test.ts`
- [x] Create test file `test/core/completions/installers/fish-installer.test.ts`
- [x] Verify fish completions work manually: `openspec completion install fish`
## Phase 3: PowerShell Support
- [x] Create `src/core/completions/generators/powershell-generator.ts` implementing `CompletionGenerator` interface
- [x] Create `src/core/completions/installers/powershell-installer.ts` implementing `CompletionInstaller` interface
- [x] Update `CompletionFactory.createGenerator()` to support powershell
- [x] Update `CompletionFactory.createInstaller()` to support powershell
- [x] Create test file `test/core/completions/generators/powershell-generator.test.ts`
- [x] Create test file `test/core/completions/installers/powershell-installer.test.ts`
- [x] Verify PowerShell completions work manually on Windows or macOS PowerShell
## Phase 4: Documentation and Testing
- [x] Update `CLAUDE.md` or relevant documentation to mention all four supported shells
- [x] Add cross-shell consistency test verifying all shells support same commands
- [x] Run `pnpm test` to ensure all tests pass
- [x] Run `pnpm run build` to verify TypeScript compilation
- [x] Test all shells on different platforms (Linux for bash/fish/zsh, Windows/macOS for PowerShell)
## Phase 5: Validation and Cleanup
- [x] Run `openspec validate extend-shell-completions --strict` and resolve all issues
- [x] Update error messages to list all four supported shells
- [x] Verify `openspec completion --help` documentation is current
- [x] Test auto-detection works for all shells
- [x] Ensure uninstall works cleanly for all shells
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-07
@@ -0,0 +1,94 @@
## Context
OpenSpec is a TypeScript CLI tool using pnpm for dependency management. The project requires Node.js ≥20.19.0. Nix uses its own build system that needs to understand how to fetch dependencies and build the project reproducibly.
The Nix ecosystem has specific patterns for packaging Node.js/pnpm projects that differ from the traditional npm ecosystem.
## Goals
- Enable OpenSpec to be run directly via `nix run github:Fission-AI/OpenSpec`
- Support all major platforms (Linux x86/ARM, macOS x86/ARM)
- Use existing pnpm-lock.yaml for reproducible builds
- Provide development environment for Nix users
## Non-Goals
- Replace existing npm/pnpm publishing workflow
- Publish to nixpkgs (can be done later as separate effort)
- Support Windows (Nix doesn't run natively on Windows)
## Decisions
### Use stdenv.mkDerivation instead of buildNpmPackage
**Decision**: Package OpenSpec using `stdenv.mkDerivation` with pnpm hooks.
**Rationale**: The zigbee2mqtt package in nixpkgs demonstrates the current best practice for pnpm projects. Using `buildNpmPackage` with pnpm requires complex configuration, while `mkDerivation` with the right hooks is more straightforward and better supported.
**Alternative considered**: Using `buildNpmPackage` with `npmConfigHook = pkgs.pnpmConfigHook` - this is the older pattern and causes issues with dependency fetching.
### Use fetchPnpmDeps with explicit pnpm version
**Decision**: Use `pkgs.fetchPnpmDeps` with `pnpm = pkgs.pnpm_9` and `fetcherVersion = 3`.
**Rationale**:
- pnpm lockfile version 9.0 requires fetcherVersion 3
- Explicit pnpm_9 ensures consistency between fetch and build
- This is the documented way to handle pnpm projects in nixpkgs
### Multi-platform support without flake-utils
**Decision**: Implement multi-platform support using plain Nix with `nixpkgs.lib.genAttrs`.
**Rationale**: Per user request, avoid extra dependencies. The `genAttrs` pattern is simple and well-understood in the Nix community.
### Node.js 20 instead of latest
**Decision**: Pin to nodejs_20 to match package.json engines requirement.
**Rationale**: Ensures consistency with development environment and npm package requirements. Avoids potential compatibility issues with newer Node versions.
## Key Implementation Details
### Dependency Hash Management
The `pnpmDeps.hash` field must be updated whenever dependencies change. The workflow:
1. Set hash to fake value (all zeros)
2. Run `nix build`
3. Nix fails with actual hash
4. Update flake.nix with correct hash
This is standard Nix workflow for fixed-output derivations.
### Build Inputs
Required nativeBuildInputs:
- `nodejs_20` - runtime
- `npmHooks.npmInstallHook` - handles installation phase
- `pnpmConfigHook` - configures pnpm environment
- `pnpm_9` - pnpm executable
The `dontNpmPrune = true` is important to keep all dependencies after build.
## Risks / Trade-offs
**[Risk]** Hash needs updating when dependencies change → **Mitigation**: Document this clearly; error message from Nix provides correct hash
**[Risk]** Nix builds might lag behind npm releases → **Mitigation**: This is fine; Nix users can still use npm if they need bleeding edge
**[Trade-off]** Additional maintenance burden for hash updates → **Benefit**: Better experience for Nix ecosystem users
## Migration Plan
1. Add flake.nix to repository
2. Test builds on multiple platforms (can use GitHub Actions with Nix)
3. Update README with Nix installation instructions
4. Optionally add to CI pipeline to catch hash mismatches early
No breaking changes - this is purely additive.
## Open Questions
- Should we add automatic hash updating to CI? (Could use nix-update-script)
- Should we submit to nixpkgs after validation? (Separate decision)
- Do we want to support older Node versions in flake? (Probably no - stick to package.json requirement)
@@ -0,0 +1,25 @@
## Why
OpenSpec users on NixOS or using the Nix package manager cannot easily install or run OpenSpec without going through npm. Adding a Nix flake makes OpenSpec a first-class citizen in the Nix ecosystem, enabling users to run `nix run github:Fission-AI/OpenSpec -- init` or include OpenSpec in their development environments declaratively.
## What Changes
- Add `flake.nix` to repository root with multi-platform support (x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin)
- Package uses pnpm for dependency management (matching existing development workflow)
- Support both direct execution via `nix run` and installation via `nix profile install`
- Provide dev shell for contributors using Nix
## Capabilities
### New Capabilities
- `nix-flake-support`: Nix flake configuration for building and running OpenSpec
### Modified Capabilities
- None
## Impact
- **New files**: `flake.nix` in repository root
- **Documentation**: Should add installation instructions for Nix users
- **CI/CD**: Could add flake checking to CI pipeline (optional)
- **Maintenance**: Requires updating pnpmDeps hash when dependencies change
@@ -0,0 +1,79 @@
## ADDED Requirements
### Requirement: Multi-platform Nix flake
The system SHALL provide a Nix flake that builds OpenSpec for multiple platforms.
#### Scenario: Build on Linux x86_64
- **WHEN** user runs `nix build` on x86_64-linux system
- **THEN** system builds OpenSpec package successfully
- **AND** package includes the `openspec` binary
#### Scenario: Build on macOS ARM
- **WHEN** user runs `nix build` on aarch64-darwin system
- **THEN** system builds OpenSpec package successfully
- **AND** package includes the `openspec` binary
#### Scenario: Build on Linux ARM
- **WHEN** user runs `nix build` on aarch64-linux system
- **THEN** system builds OpenSpec package successfully
#### Scenario: Build on macOS x86_64
- **WHEN** user runs `nix build` on x86_64-darwin system
- **THEN** system builds OpenSpec package successfully
### Requirement: Direct execution via nix run
The system SHALL allow users to run OpenSpec directly from GitHub without installing.
#### Scenario: Run init command from GitHub
- **WHEN** user runs `nix run github:Fission-AI/OpenSpec -- init`
- **THEN** system downloads and builds OpenSpec
- **AND** executes `openspec init` command
#### Scenario: Run any OpenSpec command
- **WHEN** user runs `nix run github:Fission-AI/OpenSpec -- <command> <args>`
- **THEN** system executes `openspec <command> <args>`
### Requirement: pnpm dependency management
The system SHALL use pnpm for building OpenSpec in the Nix flake.
#### Scenario: Fetch dependencies with pnpm
- **WHEN** Nix builds the package
- **THEN** system uses `fetchPnpmDeps` to download dependencies
- **AND** uses pnpm-lock.yaml for reproducible builds
- **AND** uses fetcherVersion 3 for lockfile version 9.0
#### Scenario: Build with pnpm
- **WHEN** Nix runs the build phase
- **THEN** system executes `pnpm run build`
- **AND** produces dist directory with compiled TypeScript
### Requirement: Node.js version compatibility
The system SHALL use Node.js 20 as specified in package.json engines field.
#### Scenario: Build with correct Node version
- **WHEN** Nix builds OpenSpec
- **THEN** system uses nodejs_20 from nixpkgs
- **AND** build succeeds without version compatibility errors
### Requirement: Development shell
The system SHALL provide a Nix development shell for contributors.
#### Scenario: Enter dev shell
- **WHEN** user runs `nix develop` in OpenSpec repository
- **THEN** system provides shell with nodejs_20 and pnpm_9
- **AND** displays welcome message with versions
- **AND** provides instructions to run `pnpm install`
### Requirement: Proper binary installation
The system SHALL install the openspec binary correctly.
#### Scenario: Binary in PATH
- **WHEN** package is built or installed
- **THEN** `openspec` binary is available in `$out/bin/openspec`
- **AND** binary is executable
- **AND** binary can be invoked without full path when installed
#### Scenario: Binary executes correctly
- **WHEN** user runs the installed `openspec` command
- **THEN** system executes the CLI entry point
- **AND** all subcommands work correctly
@@ -0,0 +1,65 @@
## 1. Create Flake Structure
- [x] 1.1 Create flake.nix in repository root
- [x] 1.2 Define inputs (nixpkgs only, no flake-utils)
- [x] 1.3 Set up supportedSystems list (4 platforms)
- [x] 1.4 Create forAllSystems helper function
## 2. Configure Package Build
- [x] 2.1 Set up stdenv.mkDerivation with finalAttrs pattern
- [x] 2.2 Configure pnpmDeps with fetchPnpmDeps
- [x] 2.3 Set pnpm = pnpm_9 and fetcherVersion = 3
- [x] 2.4 Add placeholder hash (all zeros)
- [x] 2.5 Configure nativeBuildInputs (nodejs_20, hooks, pnpm_9)
- [x] 2.6 Set dontNpmPrune = true
## 3. Define Build Phase
- [x] 3.1 Add buildPhase with runHook preBuild
- [x] 3.2 Add pnpm run build command
- [x] 3.3 Add runHook postBuild
## 4. Configure Installation
- [x] 4.1 Let npmInstallHook handle installation automatically
- [x] 4.2 Verify binary ends up in $out/bin/openspec
## 5. Add Metadata
- [x] 5.1 Set meta.description
- [x] 5.2 Set meta.homepage
- [x] 5.3 Set meta.license (MIT)
- [x] 5.4 Set meta.mainProgram = "openspec"
## 6. Configure App Entry Point
- [x] 6.1 Add apps output with forAllSystems
- [x] 6.2 Set default app to openspec binary
- [x] 6.3 Test that nix run works
## 7. Add Development Shell
- [x] 7.1 Add devShells output with forAllSystems
- [x] 7.2 Include nodejs_20 and pnpm_9 in buildInputs
- [x] 7.3 Add shellHook with welcome message and instructions
## 8. Get Correct Dependency Hash
- [x] 8.1 Run nix build to trigger hash mismatch
- [x] 8.2 Copy correct hash from error message
- [x] 8.3 Update pnpmDeps.hash in flake.nix
- [x] 8.4 Verify build succeeds
## 9. Testing
- [x] 9.1 Test `nix build` on x86_64-linux
- [x] 9.2 Test `nix run . -- --version` works
- [x] 9.3 Test `nix develop` provides correct environment
- [ ] 9.4 Test on macOS if available
- [ ] 9.5 Test `nix run github:Fission-AI/OpenSpec -- init` after merge to main
## 10. Documentation
- [x] 10.1 Add Nix installation section to README
- [x] 10.2 Include example commands for common Nix workflows in README
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-09
@@ -0,0 +1,117 @@
## Context
The Nix flake added in the previous change requires manual maintenance when:
1. Package version changes (must update flake.nix version field)
2. Dependencies change (must update pnpmDeps hash)
Currently this requires maintainers to:
- Manually edit flake.nix version
- Set placeholder hash
- Run nix build to get error
- Copy hash from error message
- Update flake.nix again
- Verify build works
This is tedious and error-prone, especially for maintainers unfamiliar with Nix.
## Goals
- Automate version and hash updates for flake.nix
- Make script idempotent and safe to run multiple times
- Provide clear feedback during execution
- Integrate easily into release workflow
## Non-Goals
- Automatically commit changes (maintainer decides when to commit)
- Support non-pnpm package managers
- Handle complex Nix configurations beyond OpenSpec's use case
## Decisions
### Use Bash instead of Node.js script
**Decision**: Implement as bash script rather than Node.js.
**Rationale**:
- Needs to call Nix commands which are bash-native
- Parsing Nix output is simpler in bash with grep/sed
- Maintainers updating flake.nix likely have Nix installed (bash environment)
- Node.js would add unnecessary complexity for shell operations
**Alternative considered**: Node.js script with child_process - adds dependency on extra npm packages for shell operations, less natural for Nix tooling.
### Extract hash from build error output
**Decision**: Trigger intentional build failure with placeholder hash to get correct hash.
**Rationale**: This is the standard Nix workflow for updating fixed-output derivations. No API exists to compute the hash without building.
**Alternative considered**: Pre-compute hash from pnpm-lock.yaml - would require understanding Nix's hash algorithm and pnpm's lockfile structure, fragile and non-standard.
### Use sed for in-place file editing
**Decision**: Use `sed -i` for updating flake.nix in place.
**Rationale**: Simple, available on all Unix-like systems, handles the specific replacement patterns needed.
**Alternative considered**:
- Using Node.js to parse/modify: Overkill for simple string replacement
- Manual `sed` without `-i`: Requires temp files, more complex
### Verify build after hash update
**Decision**: Always run verification build after updating hash.
**Rationale**: Catches errors immediately, gives maintainer confidence the update worked.
**Trade-off**: Takes extra time (~30s) but prevents broken flake.nix commits.
## Key Implementation Details
### Path Resolution
Script calculates paths relative to its own location:
```bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
```
This allows running from any working directory.
### Error Handling
Uses `set -euo pipefail` for strict error handling:
- `-e`: Exit on any command failure
- `-u`: Exit on undefined variable access
- `-o pipefail`: Catch failures in pipes
### Hash Extraction Pattern
Uses grep with Perl regex to extract hash:
```bash
grep -oP 'got:\s+\Ksha256-[A-Za-z0-9+/=]+'
```
This reliably extracts the hash regardless of surrounding text.
## Risks / Trade-offs
**[Risk]** Script assumes standard Nix error message format → **Mitigation**: If extraction fails, script exits with error and shows full output
**[Risk]** Build might fail for reasons other than hash mismatch → **Mitigation**: Script checks for hash in output before proceeding
**[Trade-off]** Requires Nix installed to run → **Benefit**: Only maintainers updating flake need to run this, and they have Nix
## Migration Plan
1. Add script to scripts directory
2. Document in scripts/README.md
3. Use in next version bump to verify workflow
4. Update CONTRIBUTING.md if needed to mention script
No breaking changes - purely additive tooling.
## Open Questions
None - straightforward automation script.
@@ -0,0 +1,23 @@
## Why
Maintaining the Nix flake requires manual updates to version and dependency hash when releasing new versions or updating dependencies. This is error-prone and requires maintainers to understand Nix internals. Automating this process ensures consistency and reduces friction for releases.
## What Changes
- Add `scripts/update-flake.sh` to automatically update flake.nix version and dependency hash
- Add `scripts/README.md` documenting all maintenance scripts
- Script extracts version from package.json and determines correct pnpm dependency hash automatically
## Capabilities
### New Capabilities
- `flake-update-script`: Automation script for maintaining flake.nix
### Modified Capabilities
- None
## Impact
- **New files**: `scripts/update-flake.sh`, `scripts/README.md`
- **Maintainer workflow**: Version bumps now include running `./scripts/update-flake.sh`
- **Dependencies**: Script requires Node.js (already a dependency) and Nix (for maintainers using Nix)
@@ -0,0 +1,86 @@
## ADDED Requirements
### Requirement: Automatic Version Update
The script SHALL automatically update the version in flake.nix to match package.json.
#### Scenario: Version extraction from package.json
- **WHEN** script runs
- **THEN** version is read from package.json using Node.js
- **AND** version field in flake.nix is updated to match
#### Scenario: Version already up-to-date
- **WHEN** script runs and flake.nix version already matches package.json
- **THEN** script reports version is up-to-date
- **AND** continues to hash update
### Requirement: Automatic Hash Determination
The script SHALL automatically determine and update the correct pnpm dependency hash.
#### Scenario: Trigger build to get hash
- **WHEN** script needs to determine correct hash
- **THEN** script sets placeholder hash in flake.nix
- **AND** runs nix build which fails with correct hash
- **AND** extracts correct hash from build error output
#### Scenario: Hash extraction from build output
- **WHEN** nix build fails with hash mismatch
- **THEN** script parses "got: sha256-..." from error output
- **AND** updates flake.nix with correct hash
#### Scenario: Hash update failure
- **WHEN** script cannot extract hash from build output
- **THEN** script exits with error
- **AND** displays build output for debugging
### Requirement: Build Verification
The script SHALL verify that flake.nix builds successfully after updates.
#### Scenario: Successful verification
- **WHEN** hash has been updated
- **THEN** script runs nix build to verify
- **AND** reports success if build completes
#### Scenario: Dirty git tree warning
- **WHEN** build succeeds but git tree is dirty
- **THEN** script reports warning about dirty tree
- **AND** still indicates build success
### Requirement: User Feedback
The script SHALL provide clear progress information and next steps.
#### Scenario: Progress reporting
- **WHEN** script runs
- **THEN** each step is reported with descriptive message
- **AND** detected version and hash are displayed
#### Scenario: Success summary
- **WHEN** script completes successfully
- **THEN** summary shows updated version and hash
- **AND** next steps are displayed (test, commit, etc.)
### Requirement: Script Safety
The script SHALL fail fast on errors and use safe defaults.
#### Scenario: Bash error handling
- **WHEN** script encounters an error
- **THEN** script exits immediately (set -e)
- **AND** undefined variables cause exit (set -u)
- **AND** pipe failures are caught (set -o pipefail)
#### Scenario: File path resolution
- **WHEN** script determines file locations
- **THEN** paths are calculated relative to script location
- **AND** script works regardless of working directory
### Requirement: Documentation
The system SHALL provide documentation for the update script.
#### Scenario: Script usage documentation
- **WHEN** maintainer needs to use update script
- **THEN** scripts/README.md explains when and how to use it
- **AND** example workflow is provided
#### Scenario: Script listing
- **WHEN** maintainer views scripts/README.md
- **THEN** all maintenance scripts are documented
- **AND** purpose of each script is clear
@@ -0,0 +1,55 @@
## 1. Create Update Script
- [x] 1.1 Create scripts/update-flake.sh file
- [x] 1.2 Add shebang and error handling (set -euo pipefail)
- [x] 1.3 Add path resolution for project root and files
- [x] 1.4 Make script executable (chmod +x)
## 2. Implement Version Update Logic
- [x] 2.1 Extract version from package.json using Node.js
- [x] 2.2 Use sed to update version in flake.nix
- [x] 2.3 Report if version already up-to-date
- [x] 2.4 Display detected version to user
## 3. Implement Hash Update Logic
- [x] 3.1 Set placeholder hash in flake.nix
- [x] 3.2 Run nix build and capture output (allow failure)
- [x] 3.3 Extract correct hash from build error using grep
- [x] 3.4 Handle case where hash extraction fails
- [x] 3.5 Update flake.nix with correct hash
- [x] 3.6 Display detected hash to user
## 4. Add Build Verification
- [x] 4.1 Run nix build after hash update
- [x] 4.2 Check for dirty git tree warning
- [x] 4.3 Report success or failure clearly
## 5. Add User Feedback
- [x] 5.1 Add progress messages for each step
- [x] 5.2 Add success summary with version and hash
- [x] 5.3 Add next steps instructions (test, commit)
- [x] 5.4 Add error messages with context
## 6. Create Documentation
- [x] 6.1 Create scripts/README.md
- [x] 6.2 Document update-flake.sh purpose and usage
- [x] 6.3 Add example workflow
- [x] 6.4 Document other existing scripts
## 7. Testing
- [x] 7.1 Test script runs successfully
- [x] 7.2 Verify version is extracted correctly
- [x] 7.3 Verify hash is updated correctly
- [x] 7.4 Verify build succeeds after update
- [x] 7.5 Test idempotency (running twice works)
## 8. Integration
- [ ] 8.1 Add note to release process documentation
- [ ] 8.2 Use in next actual version bump to validate workflow
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-10
@@ -0,0 +1,175 @@
## Context
OpenSpec needs usage analytics to understand adoption and inform product decisions. PostHog provides a privacy-conscious analytics platform suitable for open source projects.
## Goals / Non-Goals
**Goals:**
- Track daily/weekly/monthly active usage
- Understand command usage patterns
- Keep implementation minimal and privacy-respecting
- Enable opt-out with minimal friction
**Non-Goals:**
- Detailed error tracking or diagnostics
- User identification or profiling
- Complex event hierarchies
- Full CLI command for telemetry management (env var sufficient for now)
## Decisions
### Opt-Out Model
**Decision:** Telemetry enabled by default, opt-out via environment variable.
```bash
OPENSPEC_TELEMETRY=0 # Disable telemetry
DO_NOT_TRACK=1 # Industry standard, also respected
```
Auto-disabled when `CI=true` is detected.
**Rationale:**
- Opt-in typically yields ~3% participation—not enough for meaningful data
- Understanding usage patterns requires statistically significant sample sizes
- Environment variable opt-out is simple and immediate
- Respecting `DO_NOT_TRACK` follows industry convention
**Alternatives considered:**
- Opt-in only - Insufficient data for product decisions
- Config file setting - More complex, env var sufficient for MVP
- Full `openspec telemetry` command - Can add later if users request
### Event Design
**Decision:** Single event type with minimal properties.
```typescript
{
event: 'command_executed',
properties: {
command: 'init', // Command name only
version: '1.2.3' // OpenSpec version
}
}
```
**Rationale:**
- Answers the core questions: how much usage, which commands are popular
- PostHog derives DAU/WAU/MAU from anonymous user counts over time
- No arguments, paths, or content—clean privacy story
- Easy to explain in disclosure notice
**Not tracked:**
- Command arguments
- File paths or contents
- Error messages or stack traces
- Project names or spec content
- IP addresses (`$ip: null` explicitly set)
### Anonymous ID
**Decision:** Random UUID, lazily generated on first telemetry send, stored in global config.
```typescript
// ~/.config/openspec/config.json
{
"telemetry": {
"anonymousId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
```
**Rationale:**
- Random UUID has no relation to the person—can't be reversed
- Stored in config so same user = same ID across sessions (needed for DAU/WAU/MAU)
- Lazy generation means no ID created if user opts out before first command
- User can delete config to reset identity
**Alternatives considered:**
- Machine-derived hash (hostname, MAC) - Feels invasive, fingerprint-like
- Per-session UUID - Breaks user counting metrics entirely
### SDK Configuration
**Decision:** PostHog Node SDK with immediate flush, shutdown on exit.
```typescript
const posthog = new PostHog(API_KEY, {
flushAt: 1, // Send immediately, don't batch
flushInterval: 0 // No timer-based flushing
});
// Before CLI exits
await posthog.shutdown();
```
**Rationale:**
- CLI processes are short-lived; batching would lose events
- `flushAt: 1` ensures each event sends immediately
- `shutdown()` guarantees flush before process exit
- Adds ~100-300ms to exit—negligible for typical CLI workflows
**Error handling:**
- Network failures silently ignored (telemetry shouldn't break CLI)
- `shutdown()` wrapped in try/catch
### Hook Location
**Decision:** Commander.js `preAction` and `postAction` hooks.
```typescript
program
.hook('preAction', (thisCommand) => {
maybeShowTelemetryNotice();
trackCommand(thisCommand.name(), VERSION);
})
.hook('postAction', async () => {
await shutdown();
});
```
**Rationale:**
- Centralized—one place for all telemetry logic
- Automatic—new commands get tracked without code changes
- Clean separation—command handlers don't know about telemetry
**Subcommand handling:**
- Track full command path for nested commands (e.g., `change:apply`)
### First-Run Notice
**Decision:** One-liner on first command ever, stored "seen" flag in config.
```
Note: OpenSpec collects anonymous usage stats. Opt out: OPENSPEC_TELEMETRY=0
```
**Rationale:**
- First command (not just `init`) ensures notice is always seen
- Non-blocking—no prompt, just informational
- One-liner is visible but not intrusive
- Storing "seen" in config prevents repeated display
**Config after first run:**
```json
{
"telemetry": {
"anonymousId": "...",
"noticeSeen": true
}
}
```
## Risks / Trade-offs
| Risk | Mitigation |
|------|------------|
| Users prefer opt-in | Clear disclosure, trivial opt-out, transparent about what's collected |
| GDPR concerns | No personal data, no IP, user can delete config |
| Slows CLI exit by ~200ms | Negligible for most workflows; can optimize if needed |
| PostHog outage affects CLI | Fire-and-forget with timeout; failures are silent |
## Open Questions
None—design is intentionally minimal. Future enhancements (dedicated command, workflow tracking) can be added based on user feedback.
@@ -0,0 +1,37 @@
## Why
OpenSpec currently has no visibility into how the tool is being used. Without analytics, we cannot:
- Understand which commands and features are most valuable to users
- Measure adoption and usage patterns
- Make data-driven decisions about product development
Adding PostHog analytics enables product insights while respecting user privacy through transparent, opt-out telemetry.
## What Changes
- Add PostHog Node.js SDK as a dependency
- Implement telemetry system with environment variable opt-out
- Track command usage (command name and version only)
- Show first-run notice informing users about telemetry
- Store anonymous ID in global config (`~/.config/openspec/config.json`)
- Respect `DO_NOT_TRACK` and `OPENSPEC_TELEMETRY=0` environment variables
- Auto-disable in CI environments
## Capabilities
### New Capabilities
- `telemetry`: Anonymous usage analytics using PostHog. Covers command tracking, opt-out controls, and first-run disclosure notice.
### Modified Capabilities
- `global-config`: Add telemetry state storage (anonymous ID, notice seen flag)
## Impact
- **Dependencies**: Add `posthog-node` package
- **Privacy**: Opt-out via env var, no personal data collected, clear disclosure
- **Configuration**: New global config fields for telemetry state
- **Network**: Async event sending with flush on exit (~100-300ms added)
- **CI/CD**: Telemetry auto-disabled when `CI=true`
- **Documentation**: Update README with telemetry disclosure
@@ -0,0 +1,21 @@
## MODIFIED Requirements
### Requirement: Global configuration storage
The system SHALL store global configuration in `~/.config/openspec/config.json`, including telemetry state with `anonymousId` and `noticeSeen` fields.
#### Scenario: Initial config creation
- **WHEN** no global config file exists
- **AND** the first telemetry event is about to be sent
- **THEN** the system creates `~/.config/openspec/config.json` with telemetry configuration
#### Scenario: Telemetry config structure
- **WHEN** reading or writing telemetry configuration
- **THEN** the config contains a `telemetry` object with `anonymousId` (string UUID) and `noticeSeen` (boolean) fields
#### Scenario: Config file format
- **WHEN** storing configuration
- **THEN** the system writes valid JSON that can be read and modified by users
#### Scenario: Existing config preservation
- **WHEN** adding telemetry fields to an existing config file
- **THEN** the system preserves all existing configuration fields
@@ -0,0 +1,116 @@
## ADDED Requirements
### Requirement: Command execution tracking
The system SHALL send a `command_executed` event to PostHog when any CLI command executes, including only the command name and OpenSpec version as properties.
#### Scenario: Standard command execution
- **WHEN** a user runs any openspec command
- **THEN** the system sends a `command_executed` event with `command` and `version` properties
#### Scenario: Subcommand execution
- **WHEN** a user runs a nested command like `openspec change apply`
- **THEN** the system sends a `command_executed` event with the full command path (e.g., `change:apply`)
### Requirement: Privacy-preserving event design
The system SHALL NOT include command arguments, file paths, project names, spec content, error messages, or IP addresses in telemetry events.
#### Scenario: Command with arguments
- **WHEN** a user runs `openspec init my-project --force`
- **THEN** the telemetry event contains only `command: "init"` and `version: "<version>"` without arguments
#### Scenario: IP address exclusion
- **WHEN** the system sends a telemetry event
- **THEN** the event explicitly sets `$ip: null` to prevent IP tracking
### Requirement: Environment variable opt-out
The system SHALL disable telemetry when `OPENSPEC_TELEMETRY=0` or `DO_NOT_TRACK=1` environment variables are set.
#### Scenario: OPENSPEC_TELEMETRY opt-out
- **WHEN** `OPENSPEC_TELEMETRY=0` is set in the environment
- **THEN** the system sends no telemetry events
#### Scenario: DO_NOT_TRACK opt-out
- **WHEN** `DO_NOT_TRACK=1` is set in the environment
- **THEN** the system sends no telemetry events
#### Scenario: Environment variable takes precedence
- **WHEN** the user has previously used the CLI (config exists)
- **AND** the user sets `OPENSPEC_TELEMETRY=0`
- **THEN** telemetry is disabled regardless of config state
### Requirement: CI environment auto-disable
The system SHALL automatically disable telemetry when `CI=true` environment variable is detected.
#### Scenario: CI environment detection
- **WHEN** `CI=true` is set in the environment
- **THEN** the system sends no telemetry events
#### Scenario: CI with explicit enable
- **WHEN** `CI=true` is set
- **AND** `OPENSPEC_TELEMETRY=1` is explicitly set
- **THEN** telemetry remains disabled (CI takes precedence for privacy)
### Requirement: First-run telemetry notice
The system SHALL display a one-line telemetry disclosure notice on the first command execution, before any telemetry is sent.
#### Scenario: First command execution
- **WHEN** a user runs their first openspec command
- **AND** telemetry is enabled
- **THEN** the system displays: "Note: OpenSpec collects anonymous usage stats. Opt out: OPENSPEC_TELEMETRY=0"
#### Scenario: Subsequent command execution
- **WHEN** a user has already seen the notice (noticeSeen: true in config)
- **THEN** the system does not display the notice
#### Scenario: Notice before telemetry
- **WHEN** displaying the first-run notice
- **THEN** the notice appears before any telemetry event is sent
### Requirement: Anonymous user identification
The system SHALL generate a random UUID as an anonymous identifier on first telemetry send, stored in global config.
#### Scenario: First telemetry event
- **WHEN** the first telemetry event is sent
- **AND** no anonymousId exists in config
- **THEN** the system generates a random UUID v4 and stores it in config
#### Scenario: Persistent identity
- **WHEN** a user runs multiple commands across sessions
- **THEN** the same anonymousId is used for all events
#### Scenario: Lazy generation with opt-out
- **WHEN** a user opts out before running any command
- **THEN** no anonymousId is ever generated or stored
### Requirement: Immediate event sending
The system SHALL send telemetry events immediately without batching, using `flushAt: 1` and `flushInterval: 0` configuration.
#### Scenario: Event transmission timing
- **WHEN** a command executes
- **THEN** the telemetry event is sent immediately, not queued for batch transmission
### Requirement: Graceful shutdown
The system SHALL call `posthog.shutdown()` before CLI exit to ensure pending events are flushed.
#### Scenario: Normal exit
- **WHEN** a command completes successfully
- **THEN** the system awaits `shutdown()` before exiting
#### Scenario: Error exit
- **WHEN** a command fails with an error
- **THEN** the system still awaits `shutdown()` before exiting
### Requirement: Silent failure handling
The system SHALL silently ignore telemetry failures without affecting CLI functionality.
#### Scenario: Network failure
- **WHEN** the telemetry request fails due to network error
- **THEN** the CLI command completes normally without error message
#### Scenario: PostHog outage
- **WHEN** PostHog service is unavailable
- **THEN** the CLI command completes normally without error message
#### Scenario: Shutdown failure
- **WHEN** `shutdown()` fails or times out
- **THEN** the CLI exits normally without error message
@@ -0,0 +1,47 @@
## 1. Setup
- [x] 1.1 Add `posthog-node` package as a dependency
- [x] 1.2 Create `src/telemetry/` module directory
- [x] 1.3 Add PostHog API key configuration (environment variable or embedded)
## 2. Global Config
- [x] 2.1 Create or extend global config module for `~/.config/openspec/config.json`
- [x] 2.2 Implement read/write functions that preserve existing config fields
- [x] 2.3 Define telemetry config structure (`anonymousId`, `noticeSeen`)
## 3. Core Telemetry Module
- [x] 3.1 Implement `isTelemetryEnabled()` checking `OPENSPEC_TELEMETRY`, `DO_NOT_TRACK`, and `CI` env vars
- [x] 3.2 Implement `getOrCreateAnonymousId()` with lazy UUID generation
- [x] 3.3 Initialize PostHog client with `flushAt: 1` and `flushInterval: 0`
- [x] 3.4 Implement `trackCommand(commandName, version)` with `$ip: null`
- [x] 3.5 Implement `shutdown()` with try/catch for silent failure handling
## 4. First-Run Notice
- [x] 4.1 Implement `maybeShowTelemetryNotice()` function
- [x] 4.2 Check `noticeSeen` flag before displaying notice
- [x] 4.3 Display notice text: "Note: OpenSpec collects anonymous usage stats. Opt out: OPENSPEC_TELEMETRY=0"
- [x] 4.4 Update `noticeSeen` in config after first display
## 5. CLI Integration
- [x] 5.1 Add Commander.js `preAction` hook to show notice and track command
- [x] 5.2 Add Commander.js `postAction` hook to call shutdown
- [x] 5.3 Handle subcommand path extraction (e.g., `change:apply`)
## 6. Testing
- [x] 6.1 Test opt-out via `OPENSPEC_TELEMETRY=0`
- [x] 6.2 Test opt-out via `DO_NOT_TRACK=1`
- [x] 6.3 Test auto-disable in CI environment
- [x] 6.4 Test first-run notice display and noticeSeen persistence
- [x] 6.5 Test anonymous ID generation and persistence
- [x] 6.6 Test silent failure on network error (mock PostHog)
## 7. Documentation
- [x] 7.1 Add telemetry disclosure section to README
- [x] 7.2 Document opt-out methods (`OPENSPEC_TELEMETRY=0`, `DO_NOT_TRACK=1`)
- [x] 7.3 Document what data is collected and not collected
@@ -0,0 +1,16 @@
## Why
CodeBuddy slash command configurator currently uses inconsistent frontmatter fields compared to other tools. It uses `category` and `tags` fields (like Crush) but should use `argument-hint` field (like Factory, Auggie, and Codex) for better consistency. Additionally, the `proposal` command is missing frontmatter fields entirely. After reviewing CodeBuddy's official documentation, the correct format should use `description` and `argument-hint` fields with square bracket parameter format.
## What Changes
- Replace `category` and `tags` fields with `argument-hint` field in CodeBuddy frontmatter
- Add missing frontmatter fields to the `proposal` command
- Use correct square bracket format for `argument-hint` parameters (e.g., `[change-id]`)
- Ensure consistency with CodeBuddy's official documentation
## Impact
- Affected specs: cli-init, cli-update
- Affected code: `src/core/configurators/slash/codebuddy.ts`
- CodeBuddy users will get proper argument hints in the correct format for slash commands
@@ -0,0 +1,75 @@
## MODIFIED Requirements
### Requirement: Slash Command Configuration
The init command SHALL generate slash command files for supported editors using shared templates.
#### Scenario: Generating slash commands for Antigravity
- **WHEN** the user selects Antigravity during initialization
- **THEN** create `.agent/workflows/openspec-proposal.md`, `.agent/workflows/openspec-apply.md`, and `.agent/workflows/openspec-archive.md`
- **AND** ensure each file begins with YAML frontmatter that contains only a `description: <stage summary>` field followed by the shared OpenSpec workflow instructions wrapped in managed markers
- **AND** populate the workflow body with the same proposal/apply/archive guidance used for other tools so Antigravity behaves like Windsurf while pointing to the `.agent/workflows/` directory
#### Scenario: Generating slash commands for Claude Code
- **WHEN** the user selects Claude Code during initialization
- **THEN** create `.claude/commands/openspec/proposal.md`, `.claude/commands/openspec/apply.md`, and `.claude/commands/openspec/archive.md`
- **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 CodeBuddy Code
- **WHEN** the user selects CodeBuddy Code during initialization
- **THEN** create `.codebuddy/commands/openspec/proposal.md`, `.codebuddy/commands/openspec/apply.md`, and `.codebuddy/commands/openspec/archive.md`
- **AND** populate each file from shared templates that include CodeBuddy-compatible YAML frontmatter for the `description` and `argument-hint` fields
- **AND** use square bracket format for `argument-hint` parameters (e.g., `[change-id]`)
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
#### Scenario: Generating slash commands for Cline
- **WHEN** the user selects Cline during initialization
- **THEN** create `.clinerules/workflows/openspec-proposal.md`, `.clinerules/workflows/openspec-apply.md`, and `.clinerules/workflows/openspec-archive.md`
- **AND** populate each file from shared templates so command text matches other tools
- **AND** include Cline-specific Markdown heading frontmatter
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
#### Scenario: Generating slash commands for Crush
- **WHEN** the user selects Crush during initialization
- **THEN** create `.crush/commands/openspec/proposal.md`, `.crush/commands/openspec/apply.md`, and `.crush/commands/openspec/archive.md`
- **AND** populate each file from shared templates so command text matches other tools
- **AND** include Crush-specific frontmatter with OpenSpec category and tags
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
#### Scenario: Generating slash commands for Cursor
- **WHEN** the user selects Cursor during initialization
- **THEN** create `.cursor/commands/openspec-proposal.md`, `.cursor/commands/openspec-apply.md`, and `.cursor/commands/openspec-archive.md`
- **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 Factory Droid
- **WHEN** the user selects Factory Droid during initialization
- **THEN** create `.factory/commands/openspec-proposal.md`, `.factory/commands/openspec-apply.md`, and `.factory/commands/openspec-archive.md`
- **AND** populate each file from shared templates that include Factory-compatible YAML frontmatter for the `description` and `argument-hint` fields
- **AND** include the `$ARGUMENTS` placeholder in the template body so droid receives any user-supplied input
- **AND** wrap the generated content in OpenSpec managed markers so `openspec update` can safely refresh the commands
#### Scenario: Generating slash commands for OpenCode
- **WHEN** the user selects OpenCode during initialization
- **THEN** create `.opencode/commands/openspec-proposal.md`, `.opencode/commands/openspec-apply.md`, and `.opencode/commands/openspec-archive.md`
- **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 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`
- **AND** populate each file from shared templates (wrapped in OpenSpec markers) so workflow text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
#### Scenario: Generating slash commands for Kilo Code
- **WHEN** the user selects Kilo Code during initialization
- **THEN** create `.kilocode/workflows/openspec-proposal.md`, `.kilocode/workflows/openspec-apply.md`, and `.kilocode/workflows/openspec-archive.md`
- **AND** populate each file from shared templates (wrapped in OpenSpec markers) so workflow text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
#### Scenario: Generating slash commands for Codex
- **WHEN** the user selects Codex during initialization
- **THEN** create global prompt files at `~/.codex/prompts/openspec-proposal.md`, `~/.codex/prompts/openspec-apply.md`, and `~/.codex/prompts/openspec-archive.md` (or under `$CODEX_HOME/prompts` if set)
- **AND** populate each file from shared templates that map the first numbered placeholder (`$1`) to the primary user input (e.g., change identifier or question text)
- **AND** wrap the generated content in OpenSpec markers so `openspec update` can refresh the prompts without touching surrounding custom notes
@@ -0,0 +1,56 @@
## MODIFIED Requirements
### Requirement: Slash Command Updates
The update command SHALL refresh existing slash command files for configured tools without creating new ones, and ensure the OpenCode archive command accepts change ID arguments.
#### Scenario: Updating slash commands for Antigravity
- **WHEN** `.agent/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh the OpenSpec-managed portion of each file so the workflow copy matches other tools while preserving the existing single-field `description` frontmatter
- **AND** skip creating any missing workflow files during update, mirroring the behavior for Windsurf and other IDEs
#### Scenario: Updating slash commands for Claude Code
- **WHEN** `.claude/commands/openspec/` contains `proposal.md`, `apply.md`, and `archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Updating slash commands for CodeBuddy Code
- **WHEN** `.codebuddy/commands/openspec/` contains `proposal.md`, `apply.md`, and `archive.md`
- **THEN** refresh each file using the shared CodeBuddy templates that include YAML frontmatter for the `description` and `argument-hint` fields
- **AND** use square bracket format for `argument-hint` parameters (e.g., `[change-id]`)
- **AND** preserve any user customizations outside the OpenSpec managed markers
#### Scenario: Updating slash commands for Cline
- **WHEN** `.clinerules/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates
- **AND** include Cline-specific Markdown heading frontmatter
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Updating slash commands for Crush
- **WHEN** `.crush/commands/` contains `openspec/proposal.md`, `openspec/apply.md`, and `openspec/archive.md`
- **THEN** refresh each file using shared templates
- **AND** include Crush-specific frontmatter with OpenSpec category and tags
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Updating slash commands for Cursor
- **WHEN** `.cursor/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Updating slash commands for Factory Droid
- **WHEN** `.factory/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using the shared Factory templates that include YAML frontmatter for the `description` and `argument-hint` fields
- **AND** ensure the template body retains the `$ARGUMENTS` placeholder so user input keeps flowing into droid
- **AND** update only the content inside the OpenSpec managed markers, leaving any unmanaged notes untouched
- **AND** skip creating missing files during update
#### Scenario: Updating slash commands for OpenCode
- **WHEN** `.opencode/command/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
- **AND** ensure the archive command includes `$ARGUMENTS` placeholder in frontmatter for accepting change ID arguments
#### Scenario: Updating slash commands for Windsurf
- **WHEN** `.windsurf/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
@@ -0,0 +1,6 @@
## 1. Implementation
- [x] 1.1 Update CodeBuddy frontmatter to use `argument-hint` instead of `category` and `tags`
- [x] 1.2 Add missing frontmatter fields to the `proposal` command
- [x] 1.3 Ensure all three commands (proposal, apply, archive) have consistent frontmatter structure
- [x] 1.4 Test the changes by running `openspec init` and `openspec update`
@@ -0,0 +1,206 @@
# Design: Nix CI Validation
## Context
OpenSpec recently added Nix flake support to enable Nix users to install the tool. This includes:
- `flake.nix`: Nix package definition with pnpm dependency fetching
- `scripts/update-flake.sh`: Automation script to update version and hash when releasing
Currently, there is no CI validation ensuring these Nix artifacts remain functional. The existing CI workflow (.github/workflows/ci.yml) validates Node.js builds, tests, and linting across multiple platforms (Linux, macOS, Windows) but does not validate Nix builds.
**Stakeholders**: Nix users, maintainers, contributors who need confidence that Nix support works.
**Constraints**:
- Must work in GitHub Actions Linux runners
- Should minimize CI runtime impact (<5 minutes added)
- Should support local testing with `act` for rapid iteration
- Must integrate with existing required checks
## Goals / Non-Goals
**Goals**:
- Validate `nix build` succeeds on every PR/push
- Validate `scripts/update-flake.sh` executes without errors
- Ensure Nix support doesn't regress silently
- Support local testing with `act`
- Optimize with caching to minimize CI time
**Non-Goals**:
- Testing on macOS (GitHub-hosted macOS runners are slower and more expensive; Nix flake already declares macOS support)
- Building for all declared systems (x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin) - focus on most common platform
- Validating Nix flake quality/style (nixpkgs-fmt, etc.) - can be added later if needed
- Running OpenSpec's full test suite through Nix build - existing CI already does this
## Decisions
### Decision 1: Use DeterminateSystems nix-installer-action
**What**: Use `determinatesystems/nix-installer-action` for installing Nix in CI.
**Why**:
- Official GitHub Action maintained by Determinate Systems (Nix experts)
- Handles GitHub Actions environment quirks automatically
- Includes automatic caching configuration
- More reliable than curl | sh installation script
- Better error messages and diagnostics
**Alternatives considered**:
- Official Nix installer (`curl -L https://nixos.org/nix/install | sh`): Works but requires manual setup of flakes, caching, and CI-specific configuration
- `cachix/install-nix-action`: Popular alternative but determinatesystems is more actively maintained and has better GHA integration
### Decision 2: Use Magic Nix Cache for performance
**What**: Use `determinatesystems/magic-nix-cache-action` for automatic binary caching.
**Why**:
- Zero-configuration caching for Nix store
- Significantly reduces CI time on subsequent runs (from ~5min to ~1-2min)
- Free for public repositories
- Handles cache keys automatically
**Alternatives considered**:
- Manual Nix store caching with GitHub Actions cache: More complex, requires manual cache key management
- Cachix: Excellent tool but requires account setup and token management
- No caching: Acceptable for initial implementation, but poor developer experience
### Decision 3: Separate job for Nix validation
**What**: Create a dedicated `nix-validate` job in .github/workflows/ci.yml that runs in parallel with other jobs.
**Why**:
- Keeps Nix validation isolated from Node.js validation
- Allows parallel execution for faster CI
- Easier to debug when Nix-specific issues occur
- Can be marked as required check independently
**Alternatives considered**:
- Add Nix steps to existing jobs: Creates coupling between Node.js and Nix validation, harder to maintain
- Separate workflow file: Overkill for a single job, harder to manage required checks
### Decision 4: Validate update script by executing it
**What**: Run `scripts/update-flake.sh` as part of CI validation.
**Why**:
- Ensures the script doesn't break due to changes in package.json format, nix build output, or dependencies
- Tests the full workflow users will follow when releasing
- Catches errors early
**Implementation approach**:
- Execute script in a way that doesn't modify git state (or discard changes after)
- Verify script exits with code 0
- Optionally validate that flake.nix contains expected patterns after execution
**Alternatives considered**:
- Mock/dry-run mode: Would require modifying the script significantly
- Skip validation: Risky - script could break and only be discovered at release time
- Only run on release branches: Misses issues early in development
### Decision 5: Run on pull_request and push to main
**What**: Configure Nix validation job to run on:
- `pull_request` events (any PR to main)
- `push` events (direct pushes to main)
- `workflow_dispatch` (manual trigger for testing)
**Why**:
- Catches issues before merge (pull_request)
- Validates main branch stays healthy (push)
- Allows manual testing without creating PRs (workflow_dispatch)
### Decision 6: Support act for local testing
**What**: Ensure workflow is compatible with `act` tool for local CI testing.
**Why**:
- Faster iteration when developing CI changes
- Allows testing without pushing to GitHub
- Reduces commit noise from CI debugging
**Requirements**:
- Use standard GitHub Actions syntax
- Document any act-specific configuration needed
- Test that Nix can be installed in act's Docker containers
**Limitations**:
- act may not perfectly replicate GitHub's runners, but close enough for validation
## Risks / Trade-offs
### Risk: CI runtime increase
**Impact**: Adding Nix validation will increase total CI time by 2-5 minutes per run.
**Mitigation**:
- Run Nix job in parallel with existing jobs (no blocking delay)
- Use magic-nix-cache for subsequent runs (~1-2 min with cache)
- Configure appropriate timeout (10 minutes max)
**Acceptance**: The benefit of preventing Nix regressions outweighs the cost.
### Risk: Nix installer failures in CI
**Impact**: Transient failures in Nix installation could block PRs.
**Mitigation**:
- Use determinatesystems action which has retry logic
- Monitor for flaky failures and adjust if needed
- Document troubleshooting steps
**Acceptance**: Nix installation is generally stable in GHA; this is low risk.
### Risk: Update script modifies git state
**Impact**: Running update-flake.sh modifies flake.nix, which could cause CI to fail if git state is checked.
**Mitigation**:
- Run script in isolation without committing changes
- Add `git checkout -- flake.nix` after validation
- Or accept dirty git state in CI (doesn't affect build validation)
**Acceptance**: Script validation is important enough to handle this carefully.
### Risk: act compatibility issues
**Impact**: Workflow might not work perfectly with act due to Docker environment differences.
**Mitigation**:
- Document known limitations
- Focus on GitHub Actions as primary validation target
- Use act as best-effort local testing
**Acceptance**: act support is nice-to-have, not required.
## Migration Plan
### Phase 1: Add Nix job (new, non-required)
1. Add `nix-validate` job to .github/workflows/ci.yml
2. Configure to run in parallel with existing jobs
3. Do NOT mark as required check initially
4. Monitor for ~1 week to ensure stability
### Phase 2: Make required
1. After validation is stable, add to required checks
2. Update branch protection rules in GitHub settings
3. Document in CONTRIBUTING.md or README
### Rollback Plan
If Nix validation causes issues:
1. Remove job from required checks in GitHub settings (immediate)
2. Comment out or remove job from workflow (permanent fix)
3. Investigate and fix issues
4. Re-enable following same phased approach
## Open Questions
- **Q**: Should we test update-flake.sh on every CI run, or only when package.json or pnpm-lock.yaml changes?
- **A**: Test on every run for simplicity. The script is fast (<30 seconds) and catching regressions is valuable.
- **Q**: Should we validate on macOS as well?
- **A**: No for initial implementation. Linux validation is sufficient and macOS runners are slower/more expensive. Can add later if users report macOS-specific issues.
- **Q**: Should we run full OpenSpec tests through the Nix build?
- **A**: No. The Nix build already runs `pnpm test` as part of its build phase. Existing CI jobs cover testing thoroughly. Nix validation focuses on build success.
- **Q**: What timeout should we use for the Nix validation job?
- **A**: Start with 10 minutes. With caching, jobs should complete in 1-3 minutes. Without cache (first run), 5-7 minutes is expected.
@@ -0,0 +1,21 @@
# Add Nix CI Validation
## Why
The project recently added Nix flake support (flake.nix) and an automated update script (scripts/update-flake.sh) to enable Nix users to install OpenSpec. However, there is no CI validation ensuring these Nix artifacts continue to work as the project evolves. This creates risk that breaking changes could be merged without detection.
## What Changes
- Add a new GitHub Actions workflow job to validate Nix flake builds successfully
- Add validation that the update-flake.sh script executes without errors
- Test on Linux (where Nix support is most common)
- Ensure CI fails if Nix build or update script breaks
- Enable local testing with `act` for developers
## Impact
- Affected specs: New capability `ci-nix-validation`
- Affected code: `.github/workflows/ci.yml` (add new job)
- Affected infrastructure: GitHub Actions runners with Nix installed
- Benefits: Prevents regressions in Nix support, gives confidence to Nix users
- Trade-offs: Adds ~2-3 minutes to CI runtime
@@ -0,0 +1,104 @@
# CI Nix Validation Specification
## ADDED Requirements
### Requirement: Nix Flake Build Validation
The CI system SHALL validate that the Nix flake builds successfully on every pull request and push to main.
#### Scenario: Successful flake build
- **WHEN** a pull request or push to main is made
- **THEN** the CI SHALL execute `nix build` and verify it completes with exit code 0
- **AND** the build output SHALL contain the openspec binary
#### Scenario: Flake build failure
- **WHEN** the Nix flake configuration is broken
- **THEN** the CI job SHALL fail with a non-zero exit code
- **AND** the CI SHALL prevent merging of the pull request
#### Scenario: Multi-platform support check
- **WHEN** the flake declares support for multiple systems
- **THEN** the CI SHALL validate the flake builds on at least Linux (x86_64-linux)
### Requirement: Update Script Validation
The CI system SHALL validate that the update-flake.sh script executes successfully and produces valid output.
#### Scenario: Update script execution
- **WHEN** the CI runs the update script validation
- **THEN** the script SHALL execute without errors
- **AND** the script SHALL correctly extract the version from package.json
- **AND** the script SHALL update flake.nix with the correct version
#### Scenario: Update script with mock hash
- **WHEN** validating the update script in CI
- **THEN** the script SHALL be able to detect and extract the correct pnpm dependency hash
- **AND** the flake.nix SHALL be updated with a valid sha256 hash
### Requirement: CI Job Integration
The Nix validation jobs SHALL be integrated into the existing GitHub Actions workflow and required for merge.
#### Scenario: PR merge requirements
- **WHEN** a pull request is created
- **THEN** the Nix validation job SHALL be included in required checks
- **AND** the PR SHALL NOT be mergeable until Nix validation passes
#### Scenario: Job execution triggers
- **WHEN** code is pushed to a pull request OR pushed to main OR manually triggered
- **THEN** the Nix validation job SHALL execute automatically
### Requirement: Local Testing Support
The CI workflow SHALL be testable locally using the `act` tool to enable rapid iteration.
#### Scenario: Local CI execution with act
- **WHEN** a developer runs `act` with the Nix validation workflow
- **THEN** the workflow SHALL execute in the local Docker environment
- **AND** the developer SHALL receive feedback on Nix build status without pushing to GitHub
#### Scenario: Act configuration compatibility
- **WHEN** the workflow is designed
- **THEN** it SHALL use standard GitHub Actions syntax compatible with `act`
- **AND** any Nix-specific setup SHALL work in the act Docker environment
### Requirement: Nix Installation in CI
The CI environment SHALL have Nix properly installed and configured before running validation.
#### Scenario: Nix installation step
- **WHEN** the Nix validation job starts
- **THEN** Nix SHALL be installed using the official Nix installer or determinatesystems/nix-installer-action
- **AND** the Nix installation SHALL be cached for subsequent runs to improve performance
#### Scenario: Nix configuration for CI
- **WHEN** Nix is installed in CI
- **THEN** it SHALL be configured to work in the GitHub Actions environment
- **AND** experimental features (flakes, nix-command) SHALL be enabled
### Requirement: CI Performance Optimization
The Nix validation SHALL be optimized to minimize CI runtime impact.
#### Scenario: Acceptable runtime
- **WHEN** the Nix validation job runs
- **THEN** it SHALL complete in under 5 minutes on a clean run
- **AND** with caching, it SHALL complete in under 3 minutes on subsequent runs
#### Scenario: Parallel execution
- **WHEN** multiple CI jobs are running
- **THEN** the Nix validation job SHALL run in parallel with other validation jobs (tests, lint)
- **AND** SHALL NOT block other independent checks
@@ -0,0 +1,49 @@
# Implementation Tasks
## 1. Add Nix Installation to CI
- [x] 1.1 Research Nix installation options for GitHub Actions (nix-installer-action vs manual install)
- [x] 1.2 Add Nix installation step to .github/workflows/ci.yml
- [x] 1.3 Configure Nix with experimental features enabled (flakes, nix-command)
- [x] 1.4 Add Nix store caching to improve CI performance
## 2. Create Nix Build Validation Job
- [x] 2.1 Add new `nix-flake-validate` job to .github/workflows/ci.yml
- [x] 2.2 Implement `nix build` step with proper error handling
- [x] 2.3 Add verification step to confirm binary exists in build output
- [x] 2.4 Add step to test binary execution (`nix run . -- --version`)
## 3. Add Update Script Validation
- [x] 3.1 Add job step to run scripts/update-flake.sh in dry-run or test mode
- [x] 3.2 Verify script executes without errors
- [x] 3.3 Add validation that version is correctly extracted from package.json
- [x] 3.4 Verify flake.nix is updated with correct format (version and hash)
## 4. Configure Job Dependencies and Requirements
- [x] 4.1 Configure Nix validation job to run on pull_request and push events
- [x] 4.2 Add Nix validation to required checks list
- [x] 4.3 Configure job to run in parallel with existing test/lint jobs
- [x] 4.4 Set appropriate timeout (5-10 minutes)
## 5. Test with act Locally
- [x] 5.1 Install act locally if not already available
- [x] 5.2 Test Nix validation job using `act pull_request`
- [x] 5.3 Verify act can run the workflow with Nix installed
- [x] 5.4 Document any act-specific configuration needed in .actrc or README
## 6. Documentation and Finalization
- [x] 6.1 Add documentation about Nix CI validation to README or CONTRIBUTING.md
- [x] 6.2 Document how to test CI locally with act
- [ ] 6.3 Update CI badge or status indicators if needed
- [ ] 6.4 Test end-to-end by creating a test PR
## 7. Archive Change
- [x] 7.1 After merge and verification, create new spec file at openspec/specs/ci-nix-validation/spec.md
- [x] 7.2 Move change directory to openspec/changes/archive/[date]-add-nix-ci-validation/
- [x] 7.3 Run `openspec validate --strict` to confirm archived change passes
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-23
@@ -0,0 +1,193 @@
## Context
Currently `openspec init` and `openspec experimental` are separate commands with distinct purposes:
- **init**: Creates `openspec/` directory, generates `AGENTS.md`/`project.md`, configures tool config files (`CLAUDE.md`, etc.), generates old slash commands (`/openspec:proposal`, etc.)
- **experimental**: Generates skills (9 per tool), generates opsx slash commands (`/opsx:new`, etc.), creates `config.yaml`
The skill-based workflow (experimental) is the direction we're going, so we're making it the default by merging into `init`.
## Goals / Non-Goals
**Goals:**
- Single `openspec init` command that sets up the complete skill-based workflow
- Clean migration path for existing users with legacy artifacts
- Remove all code related to config files and old slash commands
- Keep the polished UX from experimental (animated welcome, searchable multi-select)
**Non-Goals:**
- Supporting both workflows simultaneously
- Providing options to use the old workflow
- Backward compatibility for `/openspec:*` commands (breaking change)
## Decisions
### Decision 1: Merge into init, not into experimental
**Choice**: Rewrite `init` to do what `experimental` does, then delete `experimental`.
**Rationale**: `init` is the canonical setup command. Users expect `init` to set up their project. `experimental` was always meant to be temporary.
**Alternatives considered**:
- Keep `experimental` as the main command → confusing name for default behavior
- Create new command → unnecessary, `init` already exists
### Decision 2: Legacy cleanup with Y/N prompt
**Choice**: Detect legacy artifacts, show what was found, prompt `"Legacy files detected. Upgrade and clean up? [Y/n]"`, then remove if confirmed.
**Rationale**: Users should know what's being removed. A single Y/N is simple and decisive. No need for multiple options.
**Alternatives considered**:
- Multiple options (keep/remove/cancel) → overcomplicated
- Silent removal → users might be surprised
- Just warn without removing → leaves cruft
### Decision 3: Surgical removal of legacy content
**Choice**: For files with mixed content (OpenSpec markers + user content), only remove the OpenSpec marker block. For files that are 100% OpenSpec content, delete the entire file.
**Rationale**: Respects user customizations. CLAUDE.md might have other instructions beyond OpenSpec.
**Edge cases**:
- **Config files with mixed content**: Remove only `<!-- OPENSPEC:START -->` to `<!-- OPENSPEC:END -->` block
- **Config files that are 100% OpenSpec**: Delete file entirely (check if content outside markers is empty/whitespace)
- **Old slash command directories** (`.claude/commands/openspec/`): Delete entire directory (ours)
- **`openspec/AGENTS.md`**: Delete (ours)
- **Root `AGENTS.md`**: Only remove OpenSpec marker block, preserve rest
### Decision 6: Preserve project.md with migration hint
**Choice**: Do NOT auto-delete `openspec/project.md`. Preserve it and show a message directing users to manually migrate content to `config.yaml`'s `context:` field.
**Rationale**:
- `project.md` may contain valuable user-written project documentation
- The new workflow uses `config.yaml.context` for the same purpose (auto-injected into artifacts)
- Auto-deleting would lose user content; auto-migrating is complex (needs LLM to compress)
- Users can migrate manually or use `/opsx:explore` to get AI assistance
**Migration path**:
1. During legacy cleanup, detect `openspec/project.md` but do not delete
2. Show in output: "openspec/project.md still exists - migrate content to config.yaml's context: field, then delete"
3. User migrates manually or asks Claude in explore mode: "help me migrate project.md to config.yaml"
4. User deletes project.md when ready
**Why not auto-migrate?**
- `project.md` is verbose (sections, headers, placeholders)
- `config.yaml.context` should be concise and dense
- LLM compression would be ideal but adds complexity and non-determinism to init
- Manual migration lets users decide what's actually important
### Decision 4: Hidden alias for experimental
**Choice**: Keep `openspec experimental` as a hidden command that delegates to `init`.
**Rationale**: Users who learned `experimental` can still use it during transition. Hidden means it won't show in help.
### Decision 5: Reuse existing infrastructure
**Choice**: Reuse skill templates, command adapters, welcome screen, and multi-select from experimental.
**Rationale**: Already built and working. Just needs to be called from init instead of experimental.
## Risks / Trade-offs
| Risk | Mitigation |
|------|------------|
| Users with custom `/openspec:*` commands lose them | Document in release notes; old commands are in git history |
| Mixed-content detection might be imperfect | Conservative approach: if unsure, preserve the file and warn |
| Users confused by missing config files | Clear messaging in init output about what changed |
| `openspec update` might break | Review and update `update` command to work with new structure |
## Architecture
### What init creates (after merge)
```
openspec/
├── config.yaml # Schema settings (from experimental)
├── specs/ # Empty, for user's specs
└── changes/ # Empty, for user's changes
└── archive/
.<tool>/skills/ # 9 skills per selected tool
├── openspec-explore/SKILL.md
├── openspec-new-change/SKILL.md
├── openspec-continue-change/SKILL.md
├── openspec-apply-change/SKILL.md
├── openspec-ff-change/SKILL.md
├── openspec-verify-change/SKILL.md
├── openspec-sync-specs/SKILL.md
├── openspec-archive-change/SKILL.md
└── openspec-bulk-archive-change/SKILL.md
.<tool>/commands/opsx/ # 9 slash commands per selected tool
├── explore.md
├── new.md
├── continue.md
├── apply.md
├── ff.md
├── verify.md
├── sync.md
├── archive.md
└── bulk-archive.md
```
### What init no longer creates
- `CLAUDE.md`, `.cursorrules`, `.windsurfrules`, etc. (config files)
- `openspec/AGENTS.md`
- `openspec/project.md`
- Root `AGENTS.md` stub
- `.claude/commands/openspec/` (old slash commands)
### Legacy detection targets
| Artifact Type | Detection Method | Removal Method |
|--------------|------------------|----------------|
| Config files (CLAUDE.md, etc.) | File exists AND contains OpenSpec markers | Remove marker block; delete file if empty after |
| Old slash command dirs | Directory exists at `.<tool>/commands/openspec/` | Delete entire directory |
| openspec/AGENTS.md | File exists at `openspec/AGENTS.md` | Delete file |
| openspec/project.md | File exists at `openspec/project.md` | **Preserve** - show migration hint only |
| Root AGENTS.md | File exists at `AGENTS.md` AND contains OpenSpec markers | Remove marker block; delete file if empty after |
### Code to remove
- `src/core/configurators/` - entire directory (ToolRegistry, all config generators)
- `src/core/configurators/slash/` - entire directory (SlashCommandRegistry, old command generators)
- `src/core/templates/slash-command-templates.ts` - old `/openspec:*` content
- `src/core/templates/claude-template.ts`
- `src/core/templates/cline-template.ts`
- `src/core/templates/costrict-template.ts`
- `src/core/templates/agents-template.ts`
- `src/core/templates/agents-root-stub.ts`
- `src/core/templates/project-template.ts`
- `src/commands/experimental/` - entire directory (merged into init)
- Related test files
### Code to migrate into init
- Animated welcome screen (`src/ui/welcome-screen.ts`) - keep, call from init
- Searchable multi-select (`src/prompts/searchable-multi-select.ts`) - keep, call from init
- Skill templates (`src/core/templates/skill-templates.ts`) - keep
- Command generation (`src/core/command-generation/`) - keep
- Tool states detection (from `experimental/setup.ts`) - move to init
## Open Questions
1. **What happens to `openspec update`?** - RESOLVED
**Current behavior**: Updates `openspec/AGENTS.md`, config files (`CLAUDE.md`, etc.) via `ToolRegistry`, and old slash commands (`/openspec:*`) via `SlashCommandRegistry`.
**New behavior**: Rewrite to refresh skills and opsx commands instead:
- Detect which tools have skills installed (check for `.claude/skills/openspec-*/`, etc.)
- Refresh all 9 skill files per installed tool using `skill-templates.ts`
- Refresh all 9 opsx command files per installed tool using `command-generation/` adapters
- Remove imports of `ToolRegistry`, `SlashCommandRegistry`, `agentsTemplate`
- Update output messaging to reflect skills/commands instead of config files
**Key principle**: Same as current update - only refresh existing tools, don't add new ones.
2. **Should we keep `openspec schemas` and other experimental subcommands?** - RESOLVED
**Decision**: Yes, keep them. Remove "[Experimental]" label from all subcommands (status, instructions, schemas, etc.). See task 4.3.
@@ -0,0 +1,32 @@
## Why
The current setup has two separate commands (`openspec init` and `openspec experimental`) that configure different parts of the OpenSpec workflow. This creates confusion about which command to run, results in partial setups, and maintains two parallel systems (config files + old slash commands vs skills + opsx commands). Making the skill-based workflow the default simplifies onboarding and establishes a single, consistent way to use OpenSpec.
## What Changes
- **BREAKING**: `openspec init` now generates skills and `/opsx:*` commands instead of config files and `/openspec:*` commands
- **BREAKING**: Config files (`CLAUDE.md`, `.cursorrules`, etc.) are no longer generated
- **BREAKING**: Old slash commands (`/openspec:proposal`, `/openspec:apply`, `/openspec:archive`) are no longer generated
- **BREAKING**: `openspec/AGENTS.md` and `openspec/project.md` are no longer generated
- Merge `experimental` command functionality into `init`
- Add legacy detection and auto-cleanup with Y/N confirmation
- Keep `openspec experimental` as hidden alias for backward compatibility
- Use the animated welcome screen from experimental for the unified init
## Capabilities
### New Capabilities
- `legacy-cleanup`: Detect and remove legacy OpenSpec artifacts (config files, old slash commands, AGENTS.md) during init
### Modified Capabilities
- `cli-init`: Complete rewrite - generates skills and opsx commands instead of config files and old slash commands; removes AGENTS.md/project.md generation; adds legacy cleanup; uses experimental's animated welcome screen
## Impact
- **Code removal**: `ToolRegistry`, `SlashCommandRegistry`, config file generators, old slash command templates, AGENTS.md/project.md templates
- **Code migration**: Move skill generation and command adapter logic from `experimental/setup.ts` into `init.ts`
- **Commands affected**: `init` (rewritten), `experimental` (becomes hidden alias), `update` (may need adjustment)
- **User migration**: Existing users running `init` will be prompted to clean up legacy files
- **Breaking for**: Users relying on config files for passive triggering, users using `/openspec:*` commands
@@ -0,0 +1,176 @@
## MODIFIED Requirements
### Requirement: Directory Creation
The command SHALL create the OpenSpec directory structure with config file.
#### Scenario: Creating OpenSpec structure
- **WHEN** `openspec init` is executed
- **THEN** create the following directory structure:
```
openspec/
├── config.yaml
├── specs/
└── changes/
└── archive/
```
### Requirement: AI Tool Configuration
The command SHALL configure AI coding assistants with skills and slash commands using a searchable multi-select experience.
#### Scenario: Prompting for AI tool selection
- **WHEN** run interactively
- **THEN** display animated welcome screen with OpenSpec logo
- **AND** present a searchable multi-select that shows all available tools
- **AND** mark already configured tools with "(configured ✓)" indicator
- **AND** pre-select configured tools for easy refresh
- **AND** sort configured tools to appear first in the list
- **AND** allow filtering by typing to search
#### Scenario: Selecting tools to configure
- **WHEN** user selects tools and confirms
- **THEN** generate skills in `.<tool>/skills/` directory for each selected tool
- **AND** generate slash commands in `.<tool>/commands/opsx/` directory for each selected tool
- **AND** create `openspec/config.yaml` with default schema setting
### Requirement: Skill Generation
The command SHALL generate Agent Skills for selected AI tools.
#### Scenario: Generating skills for a tool
- **WHEN** a tool is selected during initialization
- **THEN** create 9 skill directories under `.<tool>/skills/`:
- `openspec-explore/SKILL.md`
- `openspec-new-change/SKILL.md`
- `openspec-continue-change/SKILL.md`
- `openspec-apply-change/SKILL.md`
- `openspec-ff-change/SKILL.md`
- `openspec-verify-change/SKILL.md`
- `openspec-sync-specs/SKILL.md`
- `openspec-archive-change/SKILL.md`
- `openspec-bulk-archive-change/SKILL.md`
- **AND** each SKILL.md SHALL contain YAML frontmatter with name and description
- **AND** each SKILL.md SHALL contain the skill instructions
### Requirement: Slash Command Generation
The command SHALL generate opsx slash commands for selected AI tools.
#### Scenario: Generating slash commands for a tool
- **WHEN** a tool is selected during initialization
- **THEN** create 9 slash command files using the tool's command adapter:
- `/opsx:explore`
- `/opsx:new`
- `/opsx:continue`
- `/opsx:apply`
- `/opsx:ff`
- `/opsx:verify`
- `/opsx:sync`
- `/opsx:archive`
- `/opsx:bulk-archive`
- **AND** use tool-specific path conventions (e.g., `.claude/commands/opsx/` for Claude)
- **AND** include tool-specific frontmatter format
### Requirement: Success Output
The command SHALL provide clear, actionable next steps upon successful initialization.
#### Scenario: Displaying success message
- **WHEN** initialization completes successfully
- **THEN** display categorized summary:
- "Created: <tools>" for newly configured tools
- "Refreshed: <tools>" for already-configured tools that were updated
- Count of skills and commands generated
- **AND** display getting started section with:
- `/opsx:new` - Start a new change
- `/opsx:continue` - Create the next artifact
- `/opsx:apply` - Implement tasks
- **AND** display links to documentation and feedback
#### Scenario: Displaying restart instruction
- **WHEN** initialization completes successfully and tools were created or refreshed
- **THEN** display instruction to restart IDE for slash commands to take effect
### Requirement: Config File Generation
The command SHALL create an OpenSpec config file with schema settings.
#### Scenario: Creating config.yaml
- **WHEN** initialization completes
- **AND** config.yaml does not exist
- **THEN** create `openspec/config.yaml` with default schema setting
- **AND** display config location in output
#### Scenario: Preserving existing config.yaml
- **WHEN** initialization runs in extend mode
- **AND** `openspec/config.yaml` already exists
- **THEN** preserve the existing config file
- **AND** display "(exists)" indicator in output
### Requirement: Non-Interactive Mode
The command SHALL support non-interactive operation through command-line options.
#### Scenario: Select all tools non-interactively
- **WHEN** run with `--tools all`
- **THEN** automatically select every available AI tool without prompting
- **AND** proceed with skill and command generation
#### Scenario: Select specific tools non-interactively
- **WHEN** run with `--tools claude,cursor`
- **THEN** parse the comma-separated tool IDs
- **AND** generate skills and commands for specified tools only
#### Scenario: Skip tool configuration non-interactively
- **WHEN** run with `--tools none`
- **THEN** create only the openspec directory structure and config.yaml
- **AND** skip skill and command generation
### Requirement: Experimental Command Alias
The command SHALL maintain backward compatibility with the experimental command.
#### Scenario: Running openspec experimental
- **WHEN** user runs `openspec experimental`
- **THEN** delegate to `openspec init`
- **AND** the command SHALL be hidden from help output
## REMOVED Requirements
### Requirement: File Generation
**Reason**: AGENTS.md and project.md are no longer generated. Skills contain all necessary instructions.
**Migration**: Skills in `.<tool>/skills/` provide all OpenSpec workflow instructions. No manual file needed.
### Requirement: AI Tool Configuration Details
**Reason**: Config files (CLAUDE.md, .cursorrules, etc.) are replaced by skills.
**Migration**: Use skills in `.<tool>/skills/` instead of config files. Skills provide richer, tool-specific instructions.
### Requirement: Slash Command Configuration
**Reason**: Old `/openspec:*` slash commands are replaced by `/opsx:*` commands with richer functionality.
**Migration**: Use `/opsx:new`, `/opsx:continue`, `/opsx:apply` instead of `/openspec:proposal`, `/openspec:apply`, `/openspec:archive`.
### Requirement: Root instruction stub
**Reason**: Root AGENTS.md stub is no longer needed. Skills provide tool-specific instructions.
**Migration**: Skills are loaded automatically by supporting tools. No root stub needed.
@@ -0,0 +1,158 @@
## ADDED Requirements
### Requirement: Legacy artifact detection
The system SHALL detect legacy OpenSpec artifacts from previous init versions.
#### Scenario: Detecting legacy config files
- **WHEN** running `openspec init` on an existing project
- **THEN** the system SHALL check for config files with OpenSpec markers:
- `CLAUDE.md`
- `.cursorrules`
- `.windsurfrules`
- `.clinerules`
- `.kilocode_rules`
- `.github/copilot-instructions.md`
- `.amazonq/instructions.md`
- `CODEBUDDY.md`
- `IFLOW.md`
- And all other tool config files from the legacy ToolRegistry
#### Scenario: Detecting legacy slash command directories
- **WHEN** running `openspec init` on an existing project
- **THEN** the system SHALL check for old slash command directories:
- `.claude/commands/openspec/`
- `.cursor/commands/openspec/` (note: old format used `openspec-*.md` in commands root)
- `.windsurf/workflows/openspec-*.md`
- And equivalent directories for all tools in the legacy SlashCommandRegistry
#### Scenario: Detecting legacy OpenSpec structure files
- **WHEN** running `openspec init` on an existing project
- **THEN** the system SHALL check for:
- `openspec/AGENTS.md`
- `openspec/project.md` (for migration messaging only, not deleted)
- Root `AGENTS.md` with OpenSpec markers
### Requirement: Legacy cleanup confirmation
The system SHALL prompt for confirmation before removing legacy artifacts.
#### Scenario: Prompting for cleanup when legacy detected
- **WHEN** legacy artifacts are detected
- **THEN** the system SHALL display what was found
- **AND** prompt: "Legacy files detected. Upgrade and clean up? [Y/n]"
- **AND** default to Yes if user presses Enter
#### Scenario: User confirms cleanup
- **WHEN** user responds Y or presses Enter
- **THEN** the system SHALL remove legacy artifacts
- **AND** proceed with skill-based setup
#### Scenario: User declines cleanup
- **WHEN** user responds N
- **THEN** the system SHALL abort initialization
- **AND** display message suggesting manual cleanup or using `--force` flag
#### Scenario: Non-interactive mode
- **WHEN** running with `--no-interactive` or in CI environment
- **AND** legacy artifacts are detected
- **THEN** the system SHALL abort with exit code 1
- **AND** display detected legacy artifacts
- **AND** suggest running interactively or using `--force` flag
### Requirement: Surgical removal of config file content
The system SHALL preserve user content when removing OpenSpec markers from config files.
#### Scenario: Config file with only OpenSpec content
- **WHEN** a config file contains only OpenSpec marker block (whitespace outside is acceptable)
- **THEN** the system SHALL remove the OpenSpec marker block
- **AND** preserve the file (even if empty or whitespace-only)
- **AND** NOT delete the file (config files belong to the user's project root)
#### Scenario: Config file with mixed content
- **WHEN** a config file contains content outside OpenSpec markers
- **THEN** the system SHALL remove only the `<!-- OPENSPEC:START -->` to `<!-- OPENSPEC:END -->` block
- **AND** preserve all content before and after the markers
- **AND** clean up any resulting double blank lines
#### Scenario: Root AGENTS.md with mixed content
- **WHEN** root `AGENTS.md` contains OpenSpec markers AND other content
- **THEN** the system SHALL remove only the OpenSpec marker block
- **AND** preserve the rest of the file
### Requirement: Legacy directory removal
The system SHALL remove legacy slash command directories entirely.
#### Scenario: Removing old slash command directory
- **WHEN** a legacy slash command directory exists (e.g., `.claude/commands/openspec/`)
- **THEN** the system SHALL delete the entire directory and its contents
- **AND** NOT delete the parent directory (e.g., `.claude/commands/` remains)
#### Scenario: Removing legacy AGENTS.md
- **WHEN** `openspec/AGENTS.md` exists
- **THEN** the system SHALL delete the file
- **AND** NOT delete the `openspec/` directory itself
### Requirement: project.md migration hint
The system SHALL preserve project.md and display a migration hint instead of deleting it.
#### Scenario: project.md exists during upgrade
- **WHEN** `openspec/project.md` exists during legacy cleanup
- **THEN** the system SHALL NOT delete the file
- **AND** the system SHALL display a migration hint in the output:
```
Manual migration needed:
→ openspec/project.md still exists
Move useful content to config.yaml's "context:" field, then delete
```
#### Scenario: project.md migration rationale
- **GIVEN** project.md may contain user-written project documentation
- **AND** config.yaml's context field serves the same purpose (auto-injected into artifacts)
- **WHEN** displaying the migration hint
- **THEN** users can migrate manually or use `/opsx:explore` to get AI assistance
### Requirement: Cleanup reporting
The system SHALL report what was cleaned up.
#### Scenario: Displaying cleanup summary
- **WHEN** legacy cleanup completes
- **THEN** the system SHALL display a summary section:
```
Cleaned up legacy files:
✓ Removed OpenSpec markers from CLAUDE.md
✓ Removed .claude/commands/openspec/ (replaced by /opsx:*)
✓ Removed openspec/AGENTS.md (no longer needed)
```
- **AND IF** `openspec/project.md` exists
- **THEN** the system SHALL display a separate migration section:
```
Manual migration needed:
→ openspec/project.md still exists
Move useful content to config.yaml's "context:" field, then delete
```
#### Scenario: No legacy detected
- **WHEN** no legacy artifacts are found
- **THEN** the system SHALL NOT display the cleanup section
- **AND** proceed directly with skill setup
@@ -0,0 +1,67 @@
## 1. Legacy Detection & Cleanup Module
- [x] 1.1 Create `src/core/legacy-cleanup.ts` with detection functions for all legacy artifact types
- [x] 1.2 Implement `detectLegacyConfigFiles()` - check for config files with OpenSpec markers
- [x] 1.3 Implement `detectLegacySlashCommands()` - check for old `/openspec:*` command directories
- [x] 1.4 Implement `detectLegacyStructureFiles()` - check for AGENTS.md (project.md detected separately for messaging)
- [x] 1.5 Implement `removeMarkerBlock()` - surgically remove OpenSpec marker blocks from files
- [x] 1.6 Implement `cleanupLegacyArtifacts()` - orchestrate removal with proper edge case handling (preserves project.md)
- [x] 1.7 Implement migration hint output for project.md - show message directing users to migrate to config.yaml
- [x] 1.8 Add unit tests for legacy detection and cleanup functions
## 2. Rewrite Init Command
- [x] 2.1 Replace `src/core/init.ts` with new implementation using experimental's approach
- [x] 2.2 Import and use animated welcome screen from `src/ui/welcome-screen.ts`
- [x] 2.3 Import and use searchable multi-select from `src/prompts/searchable-multi-select.ts`
- [x] 2.4 Integrate legacy detection at start of init flow
- [x] 2.5 Add Y/N prompt for legacy cleanup confirmation
- [x] 2.6 Generate skills using existing `skill-templates.ts`
- [x] 2.7 Generate slash commands using existing `command-generation/` adapters
- [x] 2.8 Create `openspec/config.yaml` with default schema
- [x] 2.9 Update success output to match new workflow (skills, /opsx:* commands)
- [x] 2.10 Add `--force` flag to skip legacy cleanup prompt in non-interactive mode
## 3. Remove Legacy Code
- [x] 3.1 Delete `src/core/configurators/` directory (ToolRegistry, all config generators)
- [x] 3.2 Delete `src/core/templates/slash-command-templates.ts`
- [x] 3.3 Delete `src/core/templates/claude-template.ts`
- [x] 3.4 Delete `src/core/templates/cline-template.ts`
- [x] 3.5 Delete `src/core/templates/costrict-template.ts`
- [x] 3.6 Delete `src/core/templates/agents-template.ts`
- [x] 3.7 Delete `src/core/templates/agents-root-stub.ts`
- [x] 3.8 Delete `src/core/templates/project-template.ts`
- [x] 3.9 Delete `src/commands/experimental/` directory
- [x] 3.10 Update `src/core/templates/index.ts` to remove deleted exports
- [x] 3.11 Delete related test files for removed modules (wizard.ts)
## 4. Update CLI Registration
- [x] 4.1 Update `src/cli/index.ts` to remove `registerArtifactWorkflowCommands()` call
- [x] 4.2 Keep experimental subcommands (status, instructions, schemas, etc.) but register directly
- [x] 4.3 Remove "[Experimental]" labels from kept subcommands
- [x] 4.4 Add hidden `experimental` command as alias to `init`
## 5. Update Related Commands
- [x] 5.1 Update `openspec update` command to refresh skills/commands instead of config files
- [x] 5.2 Remove config file refresh logic from update
- [x] 5.3 Add skill refresh logic to update
## 6. Testing & Verification
- [x] 6.1 Add integration tests for new init flow (fresh install)
- [x] 6.2 Add integration tests for legacy detection and cleanup
- [x] 6.3 Add integration tests for extend mode (re-running init)
- [x] 6.4 Test non-interactive mode with `--tools` flag
- [x] 6.5 Test `--force` flag for CI environments
- [x] 6.6 Verify cross-platform path handling (use path.join throughout)
- [x] 6.7 Run full test suite and fix any broken tests
## 7. Documentation & Cleanup
- [x] 7.1 Update README with new init behavior (skill-based workflow is self-documenting)
- [x] 7.2 Document breaking changes for release notes (in tasks file)
- [x] 7.3 Remove any orphaned imports/references to deleted modules (verified none exist)
- [x] 7.4 Run linter and fix any issues (passed)
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-22
@@ -0,0 +1,144 @@
## Context
The `artifact-experimental-setup` command generates skill files and opsx slash commands for AI coding assistants. Currently it hardcodes paths to `.claude/skills` and `.claude/commands/opsx`.
The existing `AI_TOOLS` array in `config.ts` lists 22 AI tools but lacks path information. There's also an existing `SlashCommandConfigurator` system for the old workflow commands, but it's tightly coupled to the old 3 commands (proposal, apply, archive) and can't be easily extended for the 9 opsx commands.
Each AI tool has:
- Different skill directory conventions (`.claude/skills/`, `.cursor/skills/`, etc.)
- Different command file paths (`.claude/commands/opsx/`, `.cursor/commands/`, etc.)
- Different frontmatter formats (YAML keys, structure varies by tool)
## Goals / Non-Goals
**Goals:**
- Support skill generation for any AI tool following the Agent Skills spec
- Support command generation with tool-specific formatting via adapters
- Require explicit tool selection (no defaults)
- Create a generic, extensible command generation system
**Non-Goals:**
- Global path installation (deferred to future work)
- Multi-tool generation in single command (future enhancement)
- Unifying with existing SlashCommandConfigurator (separate systems for now)
## Decisions
### 1. Add `skillsDir` to `AIToolOption` interface
**Decision**: Add single `skillsDir` field to existing interface. No `commandsDir` or `globalSkillsDir`.
```typescript
interface AIToolOption {
name: string;
value: string;
available: boolean;
successLabel?: string;
skillsDir?: string; // e.g., '.claude' - /skills suffix per Agent Skills spec
}
```
**Rationale**:
- Skills follow Agent Skills spec: `<toolDir>/skills/` - suffix is standard
- Commands need per-tool formatting, handled by adapters (not a simple path)
- Global paths deferred - can extend interface later
### 2. Strategy/Adapter pattern for command generation
**Decision**: Create generic command generation with tool-specific adapters.
```text
┌─────────────────────────────────────────────────────────────────┐
│ CommandContent │
│ (tool-agnostic: id, name, description, category, tags, body) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ generateCommand(content, adapter) │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Claude │ │ Cursor │ │ Windsurf │
│ Adapter │ │ Adapter │ │ Adapter │
└──────────┘ └──────────┘ └──────────┘
```
**Interfaces:**
```typescript
// Tool-agnostic command data
interface CommandContent {
id: string; // e.g., 'explore', 'new', 'apply'
name: string; // e.g., 'OpenSpec Explore'
description: string; // e.g., 'Enter explore mode...'
category: string; // e.g., 'OpenSpec'
tags: string[]; // e.g., ['openspec', 'explore']
body: string; // The command instructions
}
// Per-tool formatting strategy
interface ToolCommandAdapter {
toolId: string;
getFilePath(commandId: string): string;
formatFile(content: CommandContent): string;
}
```
**Rationale**:
- Separates "what to generate" from "how to format it"
- Each tool's frontmatter quirks encapsulated in its adapter
- Easy to add new tools by implementing adapter interface
- Body content shared across all tools
**Alternative considered**: Extend existing SlashCommandConfigurator
- Rejected: Tightly coupled to old 3 commands, significant refactor needed
### 3. Adapter registry pattern
**Decision**: Create `CommandAdapterRegistry` similar to existing `SlashCommandRegistry`.
```typescript
class CommandAdapterRegistry {
private static adapters: Map<string, ToolCommandAdapter> = new Map();
static get(toolId: string): ToolCommandAdapter | undefined;
static getAll(): ToolCommandAdapter[];
}
```
**Rationale**:
- Consistent with existing codebase patterns
- Easy lookup by tool ID
- Centralized registration
### 4. Required tool flag
**Decision**: Require `--tool` flag - error if omitted.
**Rationale**:
- Explicit tool selection avoids assumptions
- Consistent with project convention of not providing defaults
- Users must consciously choose their target tool
## Risks / Trade-offs
**[Risk] Adapter maintenance burden** → Each new tool needs an adapter. Mitigated by simple interface - most adapters are ~20 lines.
**[Risk] Frontmatter format drift** → Tools may change their formats. Mitigated by encapsulating format in adapter - single place to update.
**[Trade-off] Two command systems** → Old SlashCommandConfigurator and new CommandAdapterRegistry coexist. Acceptable for now - can unify later if needed.
**[Trade-off] skillsDir optional** → Tools without skillsDir configured will error. Acceptable - we add paths as tools are tested.
## Implementation Approach
1. Add `skillsDir` to `AIToolOption` and populate for known tools
2. Create `CommandContent` and `ToolCommandAdapter` interfaces
3. Implement adapters for Claude, Cursor, Windsurf (start with 3)
4. Create `CommandAdapterRegistry`
5. Create `generateCommand()` function
6. Update `artifact-experimental-setup` to use new system
7. Add `--tool` flag with validation
@@ -0,0 +1,36 @@
## Why
The `artifact-experimental-setup` command currently hardcodes skill output paths to `.claude/skills` and `.claude/commands/opsx`. This prevents users of other AI coding tools (Cursor, Windsurf, Codex, etc.) from using OpenSpec's skill generation. We need to support the diverse ecosystem of AI coding assistants, each with their own conventions for skill/instruction file locations and command frontmatter formats.
## What Changes
- Add `skillsDir` path configuration to the existing `AIToolOption` interface in `config.ts`
- Add required `--tool <tool-id>` flag to the `artifact-experimental-setup` command
- Create a generic command generation system using Strategy/Adapter pattern:
- `CommandContent`: tool-agnostic command data (id, name, description, body)
- `ToolCommandAdapter`: per-tool formatting (file paths, frontmatter format)
- `CommandGenerator`: orchestrates generation using content + adapter
- Require explicit tool selection (no default) for clarity
## Capabilities
### New Capabilities
- `ai-tool-paths`: Configuration mapping AI tool IDs to their project-local skill directory paths
- `command-generation`: Generic command generation system with tool adapters for formatting differences
### Modified Capabilities
- `cli-artifact-workflow`: Adding `--tool` flag to setup command for provider selection
## Impact
- **Files Modified**:
- `src/core/config.ts` - Extend `AIToolOption` interface with `skillsDir` field
- `src/commands/artifact-workflow.ts` - Add `--tool` flag, use provider paths and adapters
- **New Files**:
- `src/core/command-generation/types.ts` - CommandContent, ToolCommandAdapter interfaces
- `src/core/command-generation/generator.ts` - Generic command generator
- `src/core/command-generation/adapters/*.ts` - Per-tool adapters
- **Backward Compatibility**: Existing workflows unaffected - this is a new command setup feature
- **User-Facing**: Required `--tool` flag on `artifact-experimental-setup` command for explicit tool selection
@@ -0,0 +1,63 @@
# ai-tool-paths Specification
## Purpose
Define the path configuration for AI coding tool skill directories, enabling skill generation to target different tools following the Agent Skills spec.
## Requirements
## ADDED Requirements
### Requirement: AIToolOption skillsDir field
The `AIToolOption` interface SHALL include an optional `skillsDir` field for skill generation path configuration.
#### Scenario: Interface includes skillsDir field
- **WHEN** a tool entry is defined in `AI_TOOLS` that supports skill generation
- **THEN** it SHALL include a `skillsDir` field specifying the project-local base directory (e.g., `.claude`)
#### Scenario: Skills path follows Agent Skills spec
- **WHEN** generating skills for a tool with `skillsDir: '.claude'`
- **THEN** skills SHALL be written to `<projectRoot>/<skillsDir>/skills/`
- **AND** the `/skills` suffix is appended per Agent Skills specification
### Requirement: Path configuration for supported tools
The `AI_TOOLS` array SHALL include `skillsDir` for tools that support the Agent Skills specification.
#### Scenario: Claude Code paths defined
- **WHEN** looking up the `claude` tool
- **THEN** `skillsDir` SHALL be `.claude`
#### Scenario: Cursor paths defined
- **WHEN** looking up the `cursor` tool
- **THEN** `skillsDir` SHALL be `.cursor`
#### Scenario: Windsurf paths defined
- **WHEN** looking up the `windsurf` tool
- **THEN** `skillsDir` SHALL be `.windsurf`
#### Scenario: Tools without skillsDir
- **WHEN** a tool has no `skillsDir` defined
- **THEN** skill generation SHALL error with message indicating the tool is not supported
### Requirement: Cross-platform path handling
The system SHALL handle paths correctly across operating systems.
#### Scenario: Path construction on Windows
- **WHEN** constructing skill paths on Windows
- **THEN** the system SHALL use `path.join()` for all path construction
- **AND** SHALL NOT hardcode forward slashes
#### Scenario: Path construction on Unix
- **WHEN** constructing skill paths on macOS or Linux
- **THEN** the system SHALL use `path.join()` for consistency
@@ -0,0 +1,60 @@
# cli-artifact-workflow Delta Specification
## Purpose
Add `--tool` flag to the `artifact-experimental-setup` command for multi-provider support.
## ADDED Requirements
### Requirement: Tool selection flag
The `artifact-experimental-setup` command SHALL accept a `--tool <tool-id>` flag to specify the target AI tool.
#### Scenario: Specify tool via flag
- **WHEN** user runs `openspec artifact-experimental-setup --tool cursor`
- **THEN** skill files are generated in `.cursor/skills/`
- **AND** command files are generated using Cursor's frontmatter format
#### Scenario: Missing tool flag
- **WHEN** user runs `openspec artifact-experimental-setup` without `--tool`
- **THEN** the system displays an error requiring the `--tool` flag
- **AND** lists valid tool IDs in the error message
#### Scenario: Unknown tool ID
- **WHEN** user runs `openspec artifact-experimental-setup --tool unknown-tool`
- **AND** the tool ID is not in `AI_TOOLS`
- **THEN** the system displays an error listing valid tool IDs
#### Scenario: Tool without skillsDir
- **WHEN** user specifies a tool that has no `skillsDir` configured
- **THEN** the system displays an error indicating skill generation is not supported for that tool
#### Scenario: Tool without command adapter
- **WHEN** user specifies a tool that has `skillsDir` but no command adapter registered
- **THEN** skill files are generated successfully
- **AND** command generation is skipped with informational message
### Requirement: Output messaging
The setup command SHALL display clear output about what was generated.
#### Scenario: Show target tool in output
- **WHEN** setup command runs successfully
- **THEN** output includes the target tool name (e.g., "Setting up for Cursor...")
#### Scenario: Show generated paths
- **WHEN** setup command completes
- **THEN** output lists all generated skill file paths
- **AND** lists all generated command file paths (if applicable)
#### Scenario: Show skipped commands message
- **WHEN** command generation is skipped due to missing adapter
- **THEN** output includes message: "Command generation skipped - no adapter for <tool>"
@@ -0,0 +1,98 @@
# command-generation Specification
## Purpose
Define a generic command generation system that supports multiple AI tools through a Strategy/Adapter pattern, separating command content from tool-specific formatting.
## ADDED Requirements
### Requirement: CommandContent interface
The system SHALL define a tool-agnostic `CommandContent` interface for command data.
#### Scenario: CommandContent structure
- **WHEN** defining a command to generate
- **THEN** `CommandContent` SHALL include:
- `id`: string identifier (e.g., 'explore', 'apply')
- `name`: human-readable name (e.g., 'OpenSpec Explore')
- `description`: brief description of command purpose
- `category`: grouping category (e.g., 'OpenSpec')
- `tags`: array of tag strings
- `body`: the command instruction content
### Requirement: ToolCommandAdapter interface
The system SHALL define a `ToolCommandAdapter` interface for per-tool formatting.
#### Scenario: Adapter interface structure
- **WHEN** implementing a tool adapter
- **THEN** `ToolCommandAdapter` SHALL require:
- `toolId`: string identifier matching `AIToolOption.value`
- `getFilePath(commandId: string)`: returns relative file path for command
- `formatFile(content: CommandContent)`: returns complete file content with frontmatter
#### Scenario: Claude adapter formatting
- **WHEN** formatting a command for Claude Code
- **THEN** the adapter SHALL output YAML frontmatter with `name`, `description`, `category`, `tags` fields
- **AND** file path SHALL follow pattern `.claude/commands/opsx/<id>.md`
#### Scenario: Cursor adapter formatting
- **WHEN** formatting a command for Cursor
- **THEN** the adapter SHALL output YAML frontmatter with `name` as `/opsx-<id>`, `id`, `category`, `description` fields
- **AND** file path SHALL follow pattern `.cursor/commands/opsx-<id>.md`
#### Scenario: Windsurf adapter formatting
- **WHEN** formatting a command for Windsurf
- **THEN** the adapter SHALL output YAML frontmatter with `name`, `description`, `category`, `tags` fields
- **AND** file path SHALL follow pattern `.windsurf/commands/opsx/<id>.md`
### Requirement: Command generator function
The system SHALL provide a `generateCommand` function that combines content with adapter.
#### Scenario: Generate command file
- **WHEN** calling `generateCommand(content, adapter)`
- **THEN** it SHALL return an object with:
- `path`: the file path from `adapter.getFilePath(content.id)`
- `fileContent`: the formatted content from `adapter.formatFile(content)`
#### Scenario: Generate multiple commands
- **WHEN** generating all opsx commands for a tool
- **THEN** the system SHALL iterate over command contents and generate each using the tool's adapter
### Requirement: CommandAdapterRegistry
The system SHALL provide a registry for looking up tool adapters.
#### Scenario: Get adapter by tool ID
- **WHEN** calling `CommandAdapterRegistry.get('cursor')`
- **THEN** it SHALL return the Cursor adapter or undefined if not registered
#### Scenario: Get all adapters
- **WHEN** calling `CommandAdapterRegistry.getAll()`
- **THEN** it SHALL return array of all registered adapters
#### Scenario: Adapter not found
- **WHEN** looking up an adapter for unregistered tool
- **THEN** `CommandAdapterRegistry.get()` SHALL return undefined
- **AND** caller SHALL handle missing adapter appropriately
### Requirement: Shared command body content
The body content of commands SHALL be shared across all tools.
#### Scenario: Same instructions across tools
- **WHEN** generating the 'explore' command for Claude and Cursor
- **THEN** both SHALL use the same `body` content
- **AND** only the frontmatter and file path SHALL differ
@@ -0,0 +1,55 @@
## 1. Extend AIToolOption Interface
- [x] 1.1 Add `skillsDir?: string` field to `AIToolOption` interface in `src/core/config.ts`
## 2. Add skillsDir to AI_TOOLS
- [x] 2.1 Add `skillsDir: '.claude'` to Claude Code tool entry
- [x] 2.2 Add `skillsDir: '.cursor'` to Cursor tool entry
- [x] 2.3 Add `skillsDir: '.windsurf'` to Windsurf tool entry
- [x] 2.4 Add skillsDir for other tools with known Agent Skills spec support (codex, opencode, roocode, kilocode, gemini, factory, github-copilot)
## 3. Create Command Generation Types
- [x] 3.1 Create `src/core/command-generation/types.ts` with `CommandContent` interface
- [x] 3.2 Add `ToolCommandAdapter` interface to types.ts
- [x] 3.3 Export types from module index
## 4. Implement Tool Command Adapters
- [x] 4.1 Create `src/core/command-generation/adapters/claude.ts` with Claude frontmatter format
- [x] 4.2 Create `src/core/command-generation/adapters/cursor.ts` with Cursor frontmatter format
- [x] 4.3 Create `src/core/command-generation/adapters/windsurf.ts` with Windsurf frontmatter format
- [x] 4.4 Create base adapter or utility for shared YAML formatting logic (if applicable)
## 5. Create Command Adapter Registry
- [x] 5.1 Create `src/core/command-generation/registry.ts` with `CommandAdapterRegistry` class
- [x] 5.2 Register Claude, Cursor, Windsurf adapters in static initializer
- [x] 5.3 Add `get(toolId)` and `getAll()` methods
## 6. Create Command Generator
- [x] 6.1 Create `src/core/command-generation/generator.ts` with `generateCommand()` function
- [x] 6.2 Add `generateCommands()` function for batch generation
- [x] 6.3 Create module index `src/core/command-generation/index.ts` exporting public API
## 7. Update artifact-experimental-setup Command
- [x] 7.1 Add `--tool <tool-id>` option (required) to command in `src/commands/artifact-workflow.ts`
- [x] 7.2 Add validation: `--tool` flag is required (error if missing with list of valid tools)
- [x] 7.3 Add validation: tool exists in AI_TOOLS
- [x] 7.4 Add validation: tool has skillsDir configured
- [x] 7.5 Replace hardcoded `.claude` skill paths with `tool.skillsDir`
- [x] 7.6 Replace hardcoded command generation with `CommandAdapterRegistry.get()` + `generateCommands()`
- [x] 7.7 Handle missing adapter gracefully (skip commands with message)
- [x] 7.8 Update output messages to show target tool name and paths
## 8. Testing
- [x] 8.1 Add unit tests for `CommandContent` and `ToolCommandAdapter` contracts
- [x] 8.2 Add unit tests for Claude adapter (path + frontmatter format)
- [x] 8.3 Add unit tests for Cursor adapter (path + frontmatter format)
- [x] 8.4 Add unit tests for `CommandAdapterRegistry.get()` and missing adapter case
- [x] 8.5 Add integration test for `--tool` flag validation
- [x] 8.6 Verify cross-platform path handling uses `path.join()` throughout
@@ -0,0 +1,2 @@
schema: spec-driven
created: "2025-01-13"
+665
View File
@@ -0,0 +1,665 @@
# Design: Project Config
## Context
OpenSpec currently has a fixed schema resolution order:
1. `--schema` CLI flag
2. `.openspec.yaml` in change directory
3. Hardcoded default: `"spec-driven"`
This forces users who want project-level customization to fork entire schemas, even for simple additions like injecting tech stack context or adding artifact-specific rules.
The proposal introduces `openspec/config.yaml` as a lightweight customization layer that sits between preset schemas and full forking. It allows teams to:
- Set a default schema
- Inject project context into all artifacts
- Add per-artifact rules
**Constraints:**
- Must not break existing changes that lack config
- Must maintain clean separation between "configure" (this) and "fork" (project-local-schemas)
- Config is project-level only (no global/user-level config)
**Key stakeholders:**
- OpenSpec users who need light customization without forking
- Teams sharing workflow conventions via committed config
## Goals / Non-Goals
**Goals:**
- Load and parse `openspec/config.yaml` using Zod schema
- Use config's `schema` field as default in schema resolution
- Inject `context` into all artifact instructions
- Inject `rules` into matching artifact instructions only
- Gracefully handle missing or invalid config (fallback to defaults)
**Non-Goals:**
- Structural changes to schemas (`skip`, `add`, inheritance) - those belong in fork path
- File references for context (`context: ./file.md`) - start with strings
- Global user-level config (XDG dirs, etc.)
- Config management commands (`openspec config init`) - manual creation for now
- Migration from old setups (no existing config to migrate from)
## Decisions
### 1. Config File Format: YAML vs JSON
**Decision:** Use YAML (`.yaml` extension, support `.yml` alias)
**Rationale:**
- YAML supports multi-line strings naturally (`context: |`)
- More readable for documentation-heavy content
- Consistent with `.openspec.yaml` used in changes
- Easy to parse with existing `yaml` library
**Alternatives considered:**
- JSON: More strict, but poor multi-line string UX
- TOML: Less familiar to most users
### 2. Config Location: Project Root vs openspec/ Directory
**Decision:** `./openspec/config.yaml` (inside openspec directory)
**Rationale:**
- Co-located with `openspec/schemas/` (project-local-schemas)
- Keeps project root clean
- Natural namespace for OpenSpec configuration
- Mirrors structure used by other tools (e.g., `.github/`)
**Alternatives considered:**
- `./openspec.config.yaml` in root: Pollutes root, less clear ownership
- XDG config directories: Out of scope, no global config yet
### 3. Context Injection: XML Tags vs Markdown Sections
**Decision:** Use XML-style tags `<context>` and `<rules>`
**Rationale:**
- Clear delimiters that don't conflict with Markdown
- Agents can easily parse structure
- Matches existing patterns in the codebase for special sections
**Example:**
```xml
<context>
Tech stack: TypeScript, React
</context>
<rules>
- Include rollback plan
</rules>
<template>
## Summary
...
</template>
```
**Alternatives considered:**
- Markdown headers: Conflicts with template content
- Comments: Less visible to agents
### 4. Schema Resolution: Insert Position
**Decision:** Config's `schema` field goes between change metadata and hardcoded default
**New resolution order:**
1. `--schema` CLI flag (explicit override)
2. `.openspec.yaml` in change directory (change-specific binding)
3. **`openspec/config.yaml` schema field** (NEW - project default)
4. `"spec-driven"` (hardcoded fallback)
**Rationale:**
- Preserves CLI and change-level overrides (most specific wins)
- Makes config act as a "project default"
- Backwards compatible (no existing configs to conflict with)
### 5. Rules Validation: Strict vs Permissive
**Decision:** Warn on unknown artifact IDs, don't error
**Rationale:**
- Future-proof: If schema adds new artifacts, old configs don't break
- Dev experience: Typos show warnings, but don't halt workflow
- User can fix incrementally
**Example:**
```yaml
rules:
proposal: [...]
testplan: [...] # Schema doesn't have this artifact → WARN, not ERROR
```
### 6. Error Handling: Config Parse Failures
**Decision:** Log warning and fall back to defaults (don't halt commands)
**Rationale:**
- Syntax errors in config shouldn't break all of OpenSpec
- User can fix config incrementally
- Commands remain usable during config development
**Warning message:**
```
⚠️ Failed to parse openspec/config.yaml: [error details]
Falling back to default schema (spec-driven)
```
## Implementation Plan
### Phase 1: Core Types and Loading
**File: `src/core/project-config.ts` (NEW)**
```typescript
import { z } from 'zod';
import { readFileSync, existsSync } from 'fs';
import { parse as parseYaml } from 'yaml';
import { findProjectRoot } from '../utils/path-utils';
/**
* Zod schema for project configuration.
*
* Purpose:
* 1. Documentation - clearly defines the config file structure
* 2. Type safety - TypeScript infers ProjectConfig type from schema
* 3. Runtime validation - uses safeParse() for resilient field-by-field validation
*
* Why Zod over manual validation:
* - Helps understand OpenSpec's data interfaces at a glance
* - Single source of truth for type and validation
* - Consistent with other OpenSpec schemas
*/
export const ProjectConfigSchema = z.object({
schema: z.string().min(1).describe('The workflow schema to use (e.g., "spec-driven", "tdd")'),
context: z.string().optional().describe('Project context injected into all artifact instructions'),
rules: z.record(
z.string(),
z.array(z.string())
).optional().describe('Per-artifact rules, keyed by artifact ID'),
});
export type ProjectConfig = z.infer<typeof ProjectConfigSchema>;
const MAX_CONTEXT_SIZE = 50 * 1024; // 50KB hard limit
/**
* Read and parse openspec/config.yaml from project root.
* Uses resilient parsing - validates each field independently using Zod safeParse.
* Returns null if file doesn't exist.
* Returns partial config if some fields are invalid (with warnings).
*/
export function readProjectConfig(): ProjectConfig | null {
const projectRoot = findProjectRoot();
// Try both .yaml and .yml, prefer .yaml
let configPath = path.join(projectRoot, 'openspec', 'config.yaml');
if (!existsSync(configPath)) {
configPath = path.join(projectRoot, 'openspec', 'config.yml');
if (!existsSync(configPath)) {
return null; // No config is OK
}
}
try {
const content = readFileSync(configPath, 'utf-8');
const raw = parseYaml(content);
if (!raw || typeof raw !== 'object') {
console.warn(`⚠️ openspec/config.yaml is not a valid YAML object`);
return null;
}
const config: Partial<ProjectConfig> = {};
// Parse schema field using Zod
const schemaField = z.string().min(1);
const schemaResult = schemaField.safeParse(raw.schema);
if (schemaResult.success) {
config.schema = schemaResult.data;
} else if (raw.schema !== undefined) {
console.warn(`⚠️ Invalid 'schema' field in config (must be non-empty string)`);
}
// Parse context field with size limit
if (raw.context !== undefined) {
const contextField = z.string();
const contextResult = contextField.safeParse(raw.context);
if (contextResult.success) {
const contextSize = Buffer.byteLength(contextResult.data, 'utf-8');
if (contextSize > MAX_CONTEXT_SIZE) {
console.warn(
`⚠️ Context too large (${(contextSize / 1024).toFixed(1)}KB, limit: ${MAX_CONTEXT_SIZE / 1024}KB)`
);
console.warn(` Ignoring context field`);
} else {
config.context = contextResult.data;
}
} else {
console.warn(`⚠️ Invalid 'context' field in config (must be string)`);
}
}
// Parse rules field using Zod
if (raw.rules !== undefined) {
const rulesField = z.record(z.string(), z.array(z.string()));
// First check if it's an object structure
if (typeof raw.rules === 'object' && !Array.isArray(raw.rules)) {
const parsedRules: Record<string, string[]> = {};
let hasValidRules = false;
for (const [artifactId, rules] of Object.entries(raw.rules)) {
const rulesArrayResult = z.array(z.string()).safeParse(rules);
if (rulesArrayResult.success) {
// Filter out empty strings
const validRules = rulesArrayResult.data.filter(r => r.length > 0);
if (validRules.length > 0) {
parsedRules[artifactId] = validRules;
hasValidRules = true;
}
if (validRules.length < rulesArrayResult.data.length) {
console.warn(
`⚠️ Some rules for '${artifactId}' are empty strings, ignoring them`
);
}
} else {
console.warn(
`⚠️ Rules for '${artifactId}' must be an array of strings, ignoring this artifact's rules`
);
}
}
if (hasValidRules) {
config.rules = parsedRules;
}
} else {
console.warn(`⚠️ Invalid 'rules' field in config (must be object)`);
}
}
// Return partial config even if some fields failed
return Object.keys(config).length > 0 ? (config as ProjectConfig) : null;
} catch (error) {
console.warn(`⚠️ Failed to parse openspec/config.yaml:`, error);
return null;
}
}
/**
* Validate artifact IDs in rules against a schema's artifacts.
* Called during instruction loading (when schema is known).
* Returns warnings for unknown artifact IDs.
*/
export function validateConfigRules(
rules: Record<string, string[]>,
validArtifactIds: Set<string>,
schemaName: string
): string[] {
const warnings: string[] = [];
for (const artifactId of Object.keys(rules)) {
if (!validArtifactIds.has(artifactId)) {
const validIds = Array.from(validArtifactIds).sort().join(', ');
warnings.push(
`Unknown artifact ID in rules: "${artifactId}". ` +
`Valid IDs for schema "${schemaName}": ${validIds}`
);
}
}
return warnings;
}
/**
* Suggest valid schema names when user provides invalid schema.
* Uses fuzzy matching to find similar names.
*/
export function suggestSchemas(
invalidSchemaName: string,
availableSchemas: { name: string; isBuiltIn: boolean }[]
): string {
// Simple fuzzy match: Levenshtein distance
function levenshtein(a: string, b: string): number {
const matrix: number[][] = [];
for (let i = 0; i <= b.length; i++) {
matrix[i] = [i];
}
for (let j = 0; j <= a.length; j++) {
matrix[0][j] = j;
}
for (let i = 1; i <= b.length; i++) {
for (let j = 1; j <= a.length; j++) {
if (b.charAt(i - 1) === a.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j] + 1
);
}
}
}
return matrix[b.length][a.length];
}
// Find closest matches (distance <= 3)
const suggestions = availableSchemas
.map(s => ({ ...s, distance: levenshtein(invalidSchemaName, s.name) }))
.filter(s => s.distance <= 3)
.sort((a, b) => a.distance - b.distance)
.slice(0, 3);
const builtIn = availableSchemas.filter(s => s.isBuiltIn).map(s => s.name);
const projectLocal = availableSchemas.filter(s => !s.isBuiltIn).map(s => s.name);
let message = `❌ Schema '${invalidSchemaName}' not found in openspec/config.yaml\n\n`;
if (suggestions.length > 0) {
message += `Did you mean one of these?\n`;
suggestions.forEach(s => {
const type = s.isBuiltIn ? 'built-in' : 'project-local';
message += ` - ${s.name} (${type})\n`;
});
message += '\n';
}
message += `Available schemas:\n`;
if (builtIn.length > 0) {
message += ` Built-in: ${builtIn.join(', ')}\n`;
}
if (projectLocal.length > 0) {
message += ` Project-local: ${projectLocal.join(', ')}\n`;
} else {
message += ` Project-local: (none found)\n`;
}
message += `\nFix: Edit openspec/config.yaml and change 'schema: ${invalidSchemaName}' to a valid schema name`;
return message;
}
```
### Phase 2: Schema Resolution
**File: `src/utils/change-metadata.ts`**
Update `resolveSchemaForChange()` to check config:
```typescript
export function resolveSchemaForChange(
changeName: string,
cliSchema?: string
): string {
// 1. CLI flag wins
if (cliSchema) {
return cliSchema;
}
// 2. Change metadata (.openspec.yaml)
const metadata = readChangeMetadata(changeName);
if (metadata?.schema) {
return metadata.schema;
}
// 3. Project config (NEW)
const projectConfig = readProjectConfig();
if (projectConfig?.schema) {
return projectConfig.schema;
}
// 4. Hardcoded default
return 'spec-driven';
}
```
**File: `src/utils/change-utils.ts`**
Update `createNewChange()` to use config schema:
```typescript
export function createNewChange(
changeName: string,
schema?: string
): void {
// Use schema from config if not specified
const resolvedSchema = schema ?? readProjectConfig()?.schema ?? 'spec-driven';
// ... rest of change creation logic
}
```
### Phase 3: Instruction Injection and Validation
**File: `src/core/artifact-graph/instruction-loader.ts`**
Update `loadInstructions()` to inject context, rules, and validate artifact IDs:
```typescript
// Session-level cache for validation warnings (avoid repeating same warnings)
const shownWarnings = new Set<string>();
export function loadInstructions(
changeName: string,
artifactId: string
): InstructionOutput {
const projectConfig = readProjectConfig();
// Load base instructions from schema
const baseInstructions = loadSchemaInstructions(changeName, artifactId);
const schema = getSchemaForChange(changeName); // Assumes we have schema loaded
// Validate rules artifact IDs (only once per session)
if (projectConfig?.rules) {
const validArtifactIds = new Set(schema.artifacts.map(a => a.id));
const warnings = validateConfigRules(
projectConfig.rules,
validArtifactIds,
schema.name
);
// Show each unique warning only once per session
for (const warning of warnings) {
if (!shownWarnings.has(warning)) {
console.warn(`⚠️ ${warning}`);
shownWarnings.add(warning);
}
}
}
// Build enriched instruction with XML sections
let enrichedInstruction = '';
// Add context (all artifacts)
if (projectConfig?.context) {
enrichedInstruction += `<context>\n${projectConfig.context}\n</context>\n\n`;
}
// Add rules (only for matching artifact)
const rulesForArtifact = projectConfig?.rules?.[artifactId];
if (rulesForArtifact && rulesForArtifact.length > 0) {
enrichedInstruction += `<rules>\n`;
for (const rule of rulesForArtifact) {
enrichedInstruction += `- ${rule}\n`;
}
enrichedInstruction += `</rules>\n\n`;
}
// Add original template
enrichedInstruction += `<template>\n${baseInstructions.template}\n</template>`;
return {
...baseInstructions,
instruction: enrichedInstruction,
};
}
```
**Note on validation timing:** Rules are validated lazily during instruction loading (not at config load time) because:
1. Schema isn't known at config load time (circular dependency)
2. Warnings shown when user actually uses the feature (better UX)
3. Validation warnings cached per session to avoid spam
### Phase 4: Performance and Caching
**Why config is read multiple times:**
```typescript
// Example: "openspec instructions proposal --change my-feature"
// 1. Schema resolution (to know which schema to use)
resolveSchemaForChange('my-feature')
→ readProjectConfig() // Read #1
// 2. Instruction loading (to inject context and rules)
loadInstructions('my-feature', 'proposal')
→ readProjectConfig() // Read #2
// Result: Config read twice per command
// More complex commands may read 3-5 times
```
**Performance Strategy:**
V1 approach: No caching, read config fresh each time
- Simpler implementation
- No cache invalidation complexity
- Acceptable if config reads are fast enough
**Benchmark targets:**
- Typical config (1KB context, 5 artifact rules): **< 10ms** per read (imperceptible even 5x)
- Large config (50KB context limit): **< 50ms** per read (acceptable for rare case)
**If benchmarks fail:** Add simple caching:
```typescript
// Simple in-memory cache with no invalidation
let cachedConfig: { mtime: number; config: ProjectConfig | null } | null = null;
export function readProjectConfig(): ProjectConfig | null {
const projectRoot = findProjectRoot();
const configPath = path.join(projectRoot, 'openspec', 'config.yaml');
if (!existsSync(configPath)) {
return null;
}
const stats = statSync(configPath);
const mtime = stats.mtimeMs;
// Return cached config if file hasn't changed
if (cachedConfig && cachedConfig.mtime === mtime) {
return cachedConfig.config;
}
// Read and parse config
const config = parseConfigFile(configPath); // Extracted logic
// Cache result
cachedConfig = { mtime, config };
return config;
}
```
**Performance testing task:** Add to Phase 6 (Testing)
- Measure typical config read time (1KB context)
- Measure large config read time (50KB context limit)
- Measure repeated reads within single command
- Document results, add caching only if needed
## Data Flow
```
┌──────────────────────────────────────────────────────────────┐
│ │
│ User runs: openspec instructions proposal --change foo │
│ │
└────────────────────────────┬─────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ resolveSchemaForChange("foo") │
│ │
│ 1. Check CLI flag ✗ │
│ 2. Check .openspec.yaml ✗ │
│ 3. Check openspec/config.yaml ✓ → "spec-driven" │
│ │
└────────────────────────────┬─────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ loadInstructions("foo", "proposal") │
│ │
│ 1. Load spec-driven/artifacts/proposal.yaml │
│ 2. Read openspec/config.yaml │
│ 3. Build enriched instruction: │
│ - <context>...</context> │
│ - <rules>...</rules> (if rules.proposal exists) │
│ - <template>...</template> │
│ │
└────────────────────────────┬─────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Return InstructionOutput with enriched content │
│ │
│ Agent sees project context + rules + schema template │
│ │
└──────────────────────────────────────────────────────────────┘
```
## Risks / Trade-offs
**[Risk]** Config typos silently ignored (e.g., wrong artifact ID in rules)
→ **Mitigation:** Validate and warn on unknown artifact IDs during config load. Don't error to allow forward compatibility.
**[Risk]** Context grows too large, pollutes all artifact instructions
→ **Mitigation:** Document recommended size (< 500 chars). If this becomes an issue, add per-artifact context override later.
**[Risk]** YAML parsing errors break OpenSpec commands
→ **Mitigation:** Catch parse errors, log warning, fall back to defaults. Commands remain functional.
**[Risk]** Config cached incorrectly across commands
→ **Mitigation:** Read config fresh on each `readProjectConfig()` call. No caching layer for v1 (simplicity over perf).
**[Trade-off]** Context is injected into ALL artifacts
→ **Benefit:** Consistent project knowledge across workflow
→ **Cost:** Can't scope context to specific artifacts (yet)
→ **Future:** Add `context: { global: "...", proposal: "..." }` if needed
**[Trade-off]** Rules use artifact IDs, not human names
→ **Benefit:** Stable identifiers (IDs don't change)
→ **Cost:** User needs to know artifact IDs from schema
→ **Mitigation:** Document common artifact IDs, show in `openspec status` output
## Migration Plan
**No migration needed** - this is a new feature with no existing state.
**Rollout steps:**
1. Deploy with config loading behind feature flag (optional, for safety)
2. Test with internal project (this repo)
3. Document in README with examples
4. Remove feature flag if used
**Rollback strategy:**
- Config is additive only (doesn't break existing changes)
- If bugs found, config parsing can be disabled with env var
- Users can delete config file to restore old behavior
## Open Questions
**Q: Should context support file references (`context: ./CONTEXT.md`)?**
**A (deferred):** Start with string-only. Add file reference later if users request it. Keeps v1 simple.
**Q: Should we support `.yml` alias in addition to `.yaml`?**
**A:** Yes, check both extensions. Prefer `.yaml` in docs, but accept `.yml` for users who prefer it.
**Q: What if config's schema field references a non-existent schema?**
**A:** Schema resolution will fail downstream. Show error when trying to load schema, suggest valid schema names.
**Q: Should rules be validated against the resolved schema's artifact IDs?**
**A:** Yes, validate and warn, but don't halt. This allows forward compatibility if schema evolves.
+774
View File
@@ -0,0 +1,774 @@
# Project Config
## Summary
Add `openspec/config.yaml` support for project-level configuration. This enables teams to customize OpenSpec behavior without forking schemas, by providing context and rules that are injected into artifact generation.
## Motivation
Currently, customizing OpenSpec requires forking entire schemas:
- Must copy all files even to add one rule
- Lose updates when openspec upgrades
- High friction for simple customizations
Most users don't need different workflow structure. They need to:
- Provide project context (tech stack, conventions, constraints)
- Add rules for specific artifacts (requirements, formatting preferences)
## Design Decisions
### Two-Path Model
OpenSpec customization follows two distinct paths:
```
┌─────────────────────────────────────────────────────────────────┐
│ │
│ CONFIGURE (this change) FORK (project-local-schemas) │
│ ───────────────────── ──────────────────────────── │
│ │
│ Use a preset schema Define your own schema │
│ + add context from scratch │
│ + add rules │
│ │
│ openspec/config.yaml openspec/schemas/my-flow/ │
│ │
│ ✓ Simple ✓ Full control │
│ ✓ Get updates ✗ You maintain everything │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Config Schema
```yaml
# openspec/config.yaml
# Required: which workflow schema to use
schema: spec-driven
# Optional: project context injected into all artifact prompts
context: |
Tech stack: TypeScript, React, Node.js, PostgreSQL
API style: RESTful, documented in docs/api-conventions.md
Testing: Jest + React Testing Library
We value backwards compatibility for all public APIs
# Optional: per-artifact rules (additive)
rules:
proposal:
- Include rollback plan
- Identify affected teams and notify in #platform-changes
specs:
- Use Given/When/Then format
- Reference existing patterns before inventing new ones
tasks:
- Each task should be completable in < 2 hours
- Include acceptance criteria
```
### What's NOT in Config
The following were explicitly excluded to keep the model simple:
| Feature | Decision | Rationale |
|---------|----------|-----------|
| `skip: [artifact]` | Not supported | Structural changes belong in fork path |
| `add: [{...}]` | Not supported | Structural changes belong in fork path |
| `extends: base` | Not supported | No inheritance, fork is full copy |
| `context: ./file.md` | Not supported (yet) | Start with string, add file reference later if needed |
### Field Definitions
#### `schema` (required)
Which workflow schema to use. Can be:
- Built-in name: `spec-driven`, `tdd`
- Project-local schema name: `my-workflow` (requires project-local-schemas change)
This becomes the default schema for:
- New changes created without `--schema` flag
- Commands run on changes without `.openspec.yaml` metadata
#### `context` (optional)
A string containing project context. Injected into ALL artifact prompts.
Use cases:
- Tech stack description
- Link to conventions/style guides
- Team constraints or preferences
- Domain-specific context
#### `rules` (optional)
Per-artifact rules, keyed by artifact ID. Additive to schema's built-in guidance.
```yaml
rules:
<artifact-id>:
- Rule 1
- Rule 2
```
Rules are injected into the specific artifact's prompt, not all prompts.
### Injection Format
When generating instructions for an artifact:
```xml
<context>
Tech stack: TypeScript, React, Node.js, PostgreSQL
API style: RESTful, documented in docs/api-conventions.md
...
</context>
<rules>
- Include rollback plan
- Identify affected teams and notify in #platform-changes
</rules>
<template>
[Schema's built-in template content]
</template>
```
Context appears for all artifacts. Rules only appear for the matching artifact.
### Config Creation Strategy
**Why integrate with `artifact-experimental-setup`?**
This feature targets **experimental workflow users**. The decision to create config during experimental setup (rather than providing standalone commands) is intentional:
**Rationale:**
1. **Single entry point** - Users setting up experimental features are already in "configuration mode"
2. **Contextual timing** - Natural to configure project defaults when setting up workflow
3. **Avoids premature API surface** - No standalone `openspec config init` until feature graduates
4. **Experimental scope** - Keeps config as experimental feature, not stable API
5. **Progressive disclosure** - Users can skip and create manually later if needed
**Evolution path:**
```
Today (Experimental):
openspec artifact-experimental-setup
→ prompts for config creation
→ creates .claude/skills/
→ creates openspec/config.yaml
Future (When graduating):
openspec init
→ prompts for config creation
→ creates openspec/ directory
→ creates openspec/config.yaml
+ standalone commands:
openspec config init
openspec config validate
openspec config set <key> <value>
```
**Why optional?**
Config is **additive**, not required:
- OpenSpec works without config (uses defaults)
- Users can skip during setup and add manually later
- Teams can start simple and add config when they feel friction
- No config file in git = no problem, everyone gets defaults
**Design principle:** The system never *requires* config, but makes it easy to create when users want customization.
## Scope
### In Scope
**Core Config System:**
- Define `ProjectConfig` type with Zod schema
- Add `readProjectConfig()` function with graceful error handling
- Update instruction generation to inject context (all artifacts)
- Update instruction generation to inject rules (per-artifact)
- Update schema resolution to use config's `schema` field as default
- Update `openspec new change` to use config's schema as default
**Config Creation (Experimental Setup):**
- Extend `artifact-experimental-setup` command to optionally create config
- Interactive prompts for schema selection (with description of each schema)
- Interactive prompts for project context (optional multi-line input)
- Interactive prompts for per-artifact rules (optional)
- Validate config immediately after creation
- Show clear "skip" option for users who want to create config manually later
- Display created config location and usage examples
### Out of Scope
- `skip` / `add` for structural changes (use fork path for structural changes)
- File reference for context (`context: ./CONTEXT.md`) - start with string, add later if needed
- Global user-level config (XDG directories, etc.)
- Integration with standard `openspec init` (will add when experimental graduates)
- Standalone `openspec config init` command (may add in future change)
- `openspec config validate` command (may add in future change)
- Config editing/updating commands (users edit YAML directly)
## User Experience
### Setting Up Config (Experimental Workflow)
When users set up the experimental workflow, they're prompted to optionally create config:
```bash
$ openspec artifact-experimental-setup
Setting up experimental artifact workflow...
✓ Created .claude/skills/openspec-explore/SKILL.md
✓ Created .claude/skills/openspec-new-change/SKILL.md
✓ Created .claude/skills/openspec-continue-change/SKILL.md
✓ Created .claude/skills/openspec-apply-change/SKILL.md
✓ Created .claude/skills/openspec-ff-change/SKILL.md
✓ Created .claude/skills/openspec-sync-specs/SKILL.md
✓ Created .claude/skills/openspec-archive-change/SKILL.md
✓ Created .claude/commands/opsx/explore.md
✓ Created .claude/commands/opsx/new.md
✓ Created .claude/commands/opsx/continue.md
✓ Created .claude/commands/opsx/apply.md
✓ Created .claude/commands/opsx/ff.md
✓ Created .claude/commands/opsx/sync.md
✓ Created .claude/commands/opsx/archive.md
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 Project Configuration (Optional)
Configure project defaults for OpenSpec workflows.
? Create openspec/config.yaml? (Y/n) Y
? Default schema for new changes?
❯ spec-driven (proposal → specs → design → tasks)
tdd (spec → tests → implementation → docs)
? Add project context? (optional)
Context is shown to AI when creating artifacts.
Examples: tech stack, conventions, style guides, domain knowledge
Press Enter to skip, or type/paste context:
│ Tech stack: TypeScript, React, Node.js, PostgreSQL
│ API style: RESTful, documented in docs/api-conventions.md
│ Testing: Jest + React Testing Library
│ We value backwards compatibility for all public APIs
│
[Press Enter when done]
? Add per-artifact rules? (optional) (Y/n) Y
Which artifacts should have custom rules?
[Space to select, Enter when done]
◯ proposal
◉ specs
◯ design
◯ tasks
? Rules for specs artifact:
Enter rules one per line, press Enter on empty line to finish:
│ Use Given/When/Then format for scenarios
│ Reference existing patterns before inventing new ones
│
[Empty line to finish]
✓ Created openspec/config.yaml
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎉 Setup Complete!
📖 Config created at: openspec/config.yaml
• Default schema: spec-driven
• Project context: Added (4 lines)
• Rules: 1 artifact configured
Usage:
• New changes automatically use 'spec-driven' schema
• Context injected into all artifact instructions
• Rules applied to matching artifacts
To share with team:
git add openspec/config.yaml .claude/
git commit -m "Setup OpenSpec experimental workflow with project config"
[Rest of experimental setup output...]
```
**Key UX decisions:**
1. **Prompted during setup** - Natural place since users are already configuring experimental features
2. **Optional at every step** - Clear skip options, no forced configuration
3. **Guided prompts** - Schema descriptions, example context, artifact selection
4. **Immediate validation** - Config is validated after creation, errors shown immediately
5. **Clear output** - Shows exactly what was created and how it affects workflow
### Setting Up Config (Manual Creation)
Users can also create config manually (or skip during setup and add later):
```bash
# Create config file manually
cat > openspec/config.yaml << 'EOF'
schema: spec-driven
context: |
Tech stack: TypeScript, React, Node.js
We follow REST conventions documented in docs/api.md
All changes require backwards compatibility consideration
rules:
proposal:
- Must include rollback plan
- Must identify affected teams
specs:
- Use Given/When/Then format
EOF
```
### Effect on Workflow
Once config is created, it affects the experimental workflow in three ways:
**1. Default Schema Selection**
```bash
# Before config: must specify schema
/opsx:new my-feature --schema spec-driven
# After config (with schema: spec-driven): schema is automatic
/opsx:new my-feature
# Automatically uses spec-driven from config
# Override still works
/opsx:new my-feature --schema tdd
# Uses tdd, ignoring config
```
**2. Context Injection (All Artifacts)**
```bash
# Get instructions for any artifact
openspec instructions proposal --change my-feature
# Output now includes project context:
<context>
Tech stack: TypeScript, React, Node.js, PostgreSQL
API style: RESTful, documented in docs/api-conventions.md
Testing: Jest + React Testing Library
We value backwards compatibility for all public APIs
</context>
<template>
[Schema's proposal template]
</template>
```
Context appears in instructions for **all artifacts** (proposal, specs, design, tasks).
**3. Rules Injection (Per-Artifact)**
```bash
# Get instructions for artifact with rules configured
openspec instructions specs --change my-feature
# Output includes artifact-specific rules:
<context>
[Project context]
</context>
<rules>
- Use Given/When/Then format for scenarios
- Reference existing patterns before inventing new ones
</rules>
<template>
[Schema's specs template]
</template>
```
Rules only appear for the **specific artifact** they're configured for.
**Artifacts without rules** (e.g., design, tasks) don't get a `<rules>` section:
```bash
openspec instructions design --change my-feature
# Output: <context> then <template> only (no rules)
```
### Team Sharing
```bash
# Commit config
git add openspec/config.yaml
git commit -m "Add project config with context and rules"
# Everyone gets the same context and rules automatically
```
## Implementation Notes
### Files to Modify/Create
| File | Changes |
|------|---------|
| `src/core/project-config.ts` | **NEW FILE:** Types, parsing, reading, validation helpers |
| `src/core/artifact-graph/instruction-loader.ts` | Inject context (all artifacts) and rules (per-artifact) |
| `src/utils/change-utils.ts` | Use config schema as default in `createChange()` |
| `src/utils/change-metadata.ts` | Update `resolveSchemaForChange()` to check config |
| `src/commands/artifact-workflow.ts` | Extend `artifactExperimentalSetupCommand()` to prompt for config creation |
| `src/core/config-prompts.ts` | **NEW FILE:** Interactive prompts for config creation (reusable) |
### Config Location
Always at `./openspec/config.yaml` relative to project root. No XDG/global config for simplicity.
### Resolution Order Update
Schema selection order becomes:
```
1. --schema CLI flag # Explicit override
2. .openspec.yaml in change directory # Change-specific binding
3. openspec/config.yaml schema field # Project default (NEW)
4. "spec-driven" # Hardcoded fallback
```
### Validation
- `schema` must be a valid schema name (exists in resolution)
- `context` must be string
- `rules` must be object with string keys (artifact IDs) and array values
- Unknown artifact IDs in `rules` should warn, not error (allows forward compat)
### Experimental Setup Integration
**Changes to `artifactExperimentalSetupCommand()` in `src/commands/artifact-workflow.ts`:**
After creating skills and commands, the setup command will:
1. **Display section header:**
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 Project Configuration (Optional)
Configure project defaults for OpenSpec workflows.
```
2. **Prompt: Create config?**
- Yes/No prompt with default "Yes"
- If No → skip entire config section, show usage instructions
- If Yes → continue to detailed prompts
3. **Prompt: Schema selection**
- Use `listSchemasWithInfo()` to get available schemas
- Display each with description and artifact flow
- Default to first schema (likely "spec-driven")
4. **Prompt: Project context**
- Multi-line input (or editor if available)
- Show examples: "tech stack, conventions, style guides"
- Allow empty (skip)
5. **Prompt: Per-artifact rules**
- Yes/No prompt, default "No" (rules are less common)
- If Yes:
- Show checklist of artifacts from selected schema
- For each selected artifact, prompt for rules (line-by-line input)
- Allow empty line to finish each artifact's rules
6. **Create and validate config:**
- Build `ProjectConfig` object from inputs
- Validate with Zod schema
- Write to `openspec/config.yaml` using YAML serializer
- If validation fails, show error and ask to retry or skip
7. **Display success summary:**
- Path to created config
- Summary: schema used, context added (line count), rules count
- Usage examples showing how config affects workflow
- Suggestion to commit config to git
**Error handling:**
- Invalid schema selection → show available schemas with fuzzy match suggestions, retry
- Context too large (>50KB) → reject with error, ask to reduce size
- Rules reference invalid artifact → warn but continue (forward compat)
- File write fails → show error, suggest manual creation
- Config already exists → show message, skip config section, continue with setup
- User cancellation (Ctrl+C) → log "Config creation cancelled", continue with rest of setup (skills/commands already created)
**If config already exists:**
When `openspec/config.yaml` already exists:
```bash
$ openspec artifact-experimental-setup
[Skills and commands created...]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 Project Configuration
ℹ️ openspec/config.yaml already exists. Skipping config creation.
To update config, edit openspec/config.yaml manually or:
1. Delete openspec/config.yaml
2. Run openspec artifact-experimental-setup again
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Rest of setup output...]
```
This prevents accidentally overwriting user's config.
**Implementation approach:**
Create separate `src/core/config-prompts.ts` module:
```typescript
export interface ConfigPromptResult {
createConfig: boolean;
schema?: string;
context?: string;
rules?: Record<string, string[]>;
}
export async function promptForConfig(): Promise<ConfigPromptResult> {
// Prompt logic using inquirer or similar
// Returns structured result for config creation
// Throws ExitPromptError on Ctrl+C (handled by caller)
}
```
**Ctrl+C handling in setup command:**
```typescript
try {
const configResult = await promptForConfig();
if (configResult.createConfig) {
writeConfigFile(configResult);
console.log('✓ Created openspec/config.yaml');
}
} catch (error) {
if (error.name === 'ExitPromptError') {
console.log('\nℹ️ Config creation cancelled');
console.log(' Skills and commands already created');
console.log(' Run setup again to create config later');
// Continue with rest of setup (not a fatal error)
} else {
throw error; // Re-throw unexpected errors
}
}
```
This keeps prompts reusable and testable separately from the setup command.
### Dependencies
**Interactive Prompting Library:**
The experimental setup command will need an interactive prompting library for the config creation flow. Options:
1. **@inquirer/prompts** (recommended)
- Modern, tree-shakeable, TypeScript-first
- Individual imports: `@inquirer/input`, `@inquirer/confirm`, `@inquirer/checkbox`, `@inquirer/editor`
- Already used in OpenSpec (if not, lightweight addition)
2. **inquirer** (classic)
- More established, larger ecosystem
- Heavier bundle size
- Single package with all prompt types
**Prompts needed:**
- `confirm` - "Create config?" "Add rules?"
- `select` - Schema selection with descriptions
- `editor` or multi-line `input` - Project context
- `checkbox` - Artifact selection for rules
- `input` (repeated) - Rule entry (line-by-line)
**Alternative (no dependency):**
Use Node's built-in `readline` for basic prompts:
- More code to write
- Less polished UX (no arrow key navigation, checkbox selection)
- Zero dependency cost
**Recommendation:** Use `@inquirer/prompts` for best UX. Config setup is a one-time operation where UX matters.
### YAML Serialization
Config creation needs YAML serialization:
- **yaml** package (already a dependency)
- Use `yaml.stringify()` to write config
- Preserve multi-line strings with `|` literal style
- Format: 2-space indent, no quotes unless needed
Example:
```typescript
import { stringify } from 'yaml';
const config = {
schema: 'spec-driven',
context: 'Multi-line\ncontext\nhere',
rules: { proposal: ['Rule 1', 'Rule 2'] }
};
const yamlContent = stringify(config, {
indent: 2,
defaultStringType: 'QUOTE_DOUBLE',
defaultKeyType: 'PLAIN',
});
// context will use | literal style automatically for multi-line
```
## Testing Considerations
**Core Config Functionality:**
- Create config with all fields (schema, context, rules), verify parsing
- Create minimal config (schema only), verify parsing
- Verify context appears in instruction output for all artifacts
- Verify rules appear only for matching artifact (not all artifacts)
- Verify schema from config is used for new changes
- Verify CLI `--schema` flag overrides config
- Verify change's `.openspec.yaml` overrides config
- Verify graceful handling of missing config (fallback to defaults)
- Verify graceful handling of invalid YAML syntax (warning, fallback)
- Verify graceful handling of invalid schema (warning, show valid schemas)
- Verify unknown artifact IDs in rules emit warnings but don't halt
**Schema Resolution Precedence:**
- Test all four levels of schema resolution:
1. CLI flag `--schema` (highest priority)
2. Change metadata `.openspec.yaml`
3. Project config `openspec/config.yaml`
4. Hardcoded default "spec-driven" (lowest priority)
- Verify each level correctly overrides lower levels
**Context and Rules Injection:**
- Verify context injection uses `<context>` XML-style tags
- Verify rules injection uses `<rules>` XML-style tags with bullets
- Verify injection order: `<context>` → `<rules>` → `<template>`
- Verify multi-line context is preserved
- Verify special characters in context/rules are not escaped
- Verify empty context/rules don't create tags
**Experimental Setup Integration:**
- Test `artifact-experimental-setup` with user skipping config creation
- Test `artifact-experimental-setup` with minimal config (schema only)
- Test `artifact-experimental-setup` with full config (schema + context + rules)
- Test schema selection from available schemas
- Test multi-line context input
- Test per-artifact rules prompts
- Test artifact selection (checkboxes)
- Test validation errors during config creation
- Test file write errors (permissions, etc.)
- Verify created config can be parsed by `readProjectConfig()`
- Verify success summary shows correct information
**Edge Cases:**
- Config file exists but is empty → treat as invalid, warn
- Config has `.yml` extension instead of `.yaml` → accept both
- Both `.yaml` and `.yml` exist → prefer `.yaml`
- Context contains YAML-significant characters → properly escape in output
- Rules array contains empty strings → filter out or warn
- Schema references non-existent schema → error with suggestions
- Config in subdirectory (not project root) → not found, use defaults
**Backward Compatibility:**
- Existing projects without config continue to work
- Existing changes with `.openspec.yaml` metadata aren't affected by config
- Adding config to existing project doesn't break in-progress changes
**Integration Tests:**
- Create config → create change → verify schema used
- Create config → get instructions → verify context injected
- Create config → get instructions → verify rules injected
- Update config → verify changes reflected immediately (no caching)
- Run `artifact-experimental-setup` → create config → create change → verify flow
## Related Changes
- **project-local-schemas**: Enables `schema: my-workflow` to reference project-local schemas
## Appendix: Full Config Schema
```typescript
import { z } from 'zod';
// Zod schema serves as both runtime validation and documentation
// Type is inferred from schema for type safety
export const ProjectConfigSchema = z.object({
// Required: which schema to use (e.g., "spec-driven", "tdd", or project-local schema name)
schema: z.string().min(1).describe('The workflow schema to use (e.g., "spec-driven", "tdd")'),
// Optional: project context (injected into all artifact instructions)
// Max size: 50KB (enforced during parsing)
context: z.string().optional().describe('Project context injected into all artifact instructions'),
// Optional: per-artifact rules (additive to schema's built-in guidance)
rules: z.record(
z.string(), // artifact ID
z.array(z.string()) // list of rules
).optional().describe('Per-artifact rules, keyed by artifact ID'),
});
export type ProjectConfig = z.infer<typeof ProjectConfigSchema>;
// Note: Parsing uses safeParse() on individual fields for resilient error handling
// Invalid fields are warned about but don't prevent other fields from being loaded
```
## Appendix: Visual Summary
```
┌─────────────────────────────────────────────────────────────────┐
│ │
│ User provides: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ openspec/config.yaml │ │
│ │ │ │
│ │ schema: spec-driven │ │
│ │ context: "We use React, TypeScript..." │ │
│ │ rules: │ │
│ │ proposal: [...] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ OpenSpec merges: │ │
│ │ │ │
│ │ Schema (spec-driven) │ │
│ │ + User's context │ │
│ │ + User's rules │ │
│ │ ───────────────────────── │ │
│ │ = Enriched instructions │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Agent sees (for proposal artifact): │ │
│ │ │ │
│ │ <context> │ │
│ │ We use React, TypeScript... │ │
│ │ </context> │ │
│ │ │ │
│ │ <rules> │ │
│ │ - Include rollback plan │ │
│ │ - Identify affected teams │ │
│ │ </rules> │ │
│ │ │ │
│ │ <template> │ │
│ │ [Built-in proposal template] │ │
│ │ </template> │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
@@ -0,0 +1,119 @@
# Spec: Config Loading
## ADDED Requirements
### Requirement: Load project config from openspec/config.yaml
The system SHALL read and parse the project configuration file located at `openspec/config.yaml` relative to the project root.
#### Scenario: Valid config file exists
- **WHEN** `openspec/config.yaml` exists with valid YAML content
- **THEN** system parses the file and returns a ProjectConfig object
#### Scenario: Config file does not exist
- **WHEN** `openspec/config.yaml` does not exist
- **THEN** system returns null without error
#### Scenario: Config file has invalid YAML syntax
- **WHEN** `openspec/config.yaml` contains malformed YAML
- **THEN** system logs a warning message and returns null
#### Scenario: Config file has valid YAML but invalid schema
- **WHEN** `openspec/config.yaml` contains valid YAML that fails Zod schema validation
- **THEN** system logs a warning message with validation details and returns null
### Requirement: Support .yml file extension alias
The system SHALL accept both `.yaml` and `.yml` file extensions for the config file.
#### Scenario: Config file uses .yml extension
- **WHEN** `openspec/config.yml` exists and `openspec/config.yaml` does not exist
- **THEN** system reads from `openspec/config.yml`
#### Scenario: Both .yaml and .yml exist
- **WHEN** both `openspec/config.yaml` and `openspec/config.yml` exist
- **THEN** system prefers `openspec/config.yaml`
### Requirement: Use resilient field-by-field parsing
The system SHALL parse each config field independently, collecting valid fields and warning about invalid ones without rejecting the entire config.
#### Scenario: Schema field is valid
- **WHEN** config contains `schema: "spec-driven"`
- **THEN** schema field is included in returned config
#### Scenario: Schema field is missing
- **WHEN** config lacks the `schema` field
- **THEN** no warning is logged (field is optional at parse level)
#### Scenario: Schema field is empty string
- **WHEN** config contains `schema: ""`
- **THEN** warning is logged and schema field is not included in returned config
#### Scenario: Schema field is invalid type
- **WHEN** config contains `schema: 123` (number instead of string)
- **THEN** warning is logged and schema field is not included in returned config
#### Scenario: Context field is valid
- **WHEN** config contains `context: "Tech stack: TypeScript"`
- **THEN** context field is included in returned config
#### Scenario: Context field is invalid type
- **WHEN** config contains `context: 123` (number instead of string)
- **THEN** warning is logged and context field is not included in returned config
#### Scenario: Rules field has valid structure
- **WHEN** config contains `rules: { proposal: ["Rule 1"], specs: ["Rule 2"] }`
- **THEN** rules field is included in returned config with valid rules
#### Scenario: Rules field has non-array value for artifact
- **WHEN** config contains `rules: { proposal: "not an array", specs: ["Valid"] }`
- **THEN** warning is logged for proposal, but specs rules are still included in returned config
#### Scenario: Rules array contains non-string elements
- **WHEN** config contains `rules: { proposal: ["Valid rule", 123, ""] }`
- **THEN** only "Valid rule" is included, warning logged about invalid elements
#### Scenario: Mix of valid and invalid fields
- **WHEN** config contains valid schema, invalid context type, valid rules
- **THEN** config is returned with schema and rules fields, warning logged about context
### Requirement: Enforce context size limit
The system SHALL reject context fields exceeding 50KB and log a warning.
#### Scenario: Context within size limit
- **WHEN** config contains context of 1KB
- **THEN** context is included in returned config
#### Scenario: Context at size limit
- **WHEN** config contains context of exactly 50KB
- **THEN** context is included in returned config
#### Scenario: Context exceeds size limit
- **WHEN** config contains context of 51KB
- **THEN** warning is logged with size and limit, context field is not included in returned config
### Requirement: Defer artifact ID validation to instruction loading
The system SHALL NOT validate artifact IDs in rules during config load time. Validation happens during instruction loading when schema is known.
#### Scenario: Config with rules is loaded
- **WHEN** config contains `rules: { unknownartifact: [...] }`
- **THEN** config is loaded successfully without validation errors
#### Scenario: Validation happens at instruction load time
- **WHEN** instructions are loaded for any artifact and config has unknown artifact IDs in rules
- **THEN** warnings are emitted about unknown artifact IDs (see rules-injection spec for details)
### Requirement: Gracefully handle config errors without halting
The system SHALL continue operation with default values when config loading or parsing fails.
#### Scenario: Config parse failure during command execution
- **WHEN** config file has syntax errors and user runs `openspec new change`
- **THEN** command executes using default schema "spec-driven"
#### Scenario: Warning is visible to user
- **WHEN** config loading fails
- **THEN** system outputs warning message to stderr with details about the failure
@@ -0,0 +1,51 @@
# Spec: Context Injection
## ADDED Requirements
### Requirement: Inject context into all artifact instructions
The system SHALL inject the context field from project config into instructions for all artifacts, wrapped in XML-style `<context>` tags.
#### Scenario: Config has context field
- **WHEN** config contains `context: "Tech stack: TypeScript, React"`
- **THEN** instruction output includes `<context>\nTech stack: TypeScript, React\n</context>`
#### Scenario: Config has no context field
- **WHEN** config omits the context field or context is undefined
- **THEN** instruction output does not include `<context>` tags
#### Scenario: Context is multi-line string
- **WHEN** config contains context with multiple lines
- **THEN** instruction output preserves line breaks within `<context>` tags
#### Scenario: Context applied to all artifacts
- **WHEN** instructions are loaded for any artifact (proposal, specs, design, tasks)
- **THEN** context section appears in all instruction outputs
### Requirement: Format context with XML-style tags
The system SHALL wrap context content in `<context>` opening and `</context>` closing tags with content on separate lines.
#### Scenario: Context tag structure
- **WHEN** context is injected into instructions
- **THEN** format is exactly `<context>\n{content}\n</context>\n\n`
#### Scenario: Context appears before template
- **WHEN** instructions are generated with context
- **THEN** `<context>` section appears before the `<template>` section
### Requirement: Preserve context content exactly as provided
The system SHALL inject context content without modification, escaping, or interpretation.
#### Scenario: Context contains special characters
- **WHEN** context includes characters like `<`, `>`, `&`, quotes
- **THEN** characters are preserved exactly as written in the config
#### Scenario: Context contains URLs
- **WHEN** context includes URLs like "docs at https://example.com"
- **THEN** URLs are preserved exactly in the injected content
#### Scenario: Context contains markdown
- **WHEN** context includes markdown formatting like `**bold**` or `[links](url)`
- **THEN** markdown is preserved without rendering or escaping
@@ -0,0 +1,99 @@
# Spec: Rules Injection
## ADDED Requirements
### Requirement: Inject rules only for matching artifact
The system SHALL inject rules from config into instructions only when the artifact ID matches a key in the rules object.
#### Scenario: Rules exist for the artifact
- **WHEN** loading instructions for "proposal" and config has `rules: { proposal: ["Rule 1", "Rule 2"] }`
- **THEN** instruction output includes rules section with both rules
#### Scenario: No rules for the artifact
- **WHEN** loading instructions for "design" and config has `rules: { proposal: [...] }`
- **THEN** instruction output does not include `<rules>` tags
#### Scenario: Rules object is undefined
- **WHEN** config omits the rules field or rules is undefined
- **THEN** instruction output does not include `<rules>` tags for any artifact
#### Scenario: Rules array is empty for artifact
- **WHEN** config has `rules: { proposal: [] }`
- **THEN** instruction output does not include `<rules>` tags
### Requirement: Format rules with XML-style tags and bullet list
The system SHALL wrap rules in `<rules>` tags with each rule as a bulleted list item.
#### Scenario: Single rule for artifact
- **WHEN** config has `rules: { proposal: ["Include rollback plan"] }`
- **THEN** instruction output includes `<rules>\n- Include rollback plan\n</rules>\n\n`
#### Scenario: Multiple rules for artifact
- **WHEN** config has `rules: { proposal: ["Rule 1", "Rule 2", "Rule 3"] }`
- **THEN** instruction output includes each rule as separate bullet point
#### Scenario: Rules appear after context and before template
- **WHEN** instructions are generated with both context and rules
- **THEN** order is `<context>` then `<rules>` then `<template>`
### Requirement: Preserve rule text exactly as provided
The system SHALL inject rule text without modification, escaping, or interpretation.
#### Scenario: Rule contains markdown
- **WHEN** rule includes markdown like "Use **Given/When/Then** format"
- **THEN** markdown is preserved in the injected content
#### Scenario: Rule contains special characters
- **WHEN** rule includes characters like `<`, `>`, quotes
- **THEN** characters are preserved exactly as written
#### Scenario: Rule is multi-line string
- **WHEN** rule text contains line breaks
- **THEN** line breaks are preserved within the bullet point
### Requirement: Support multiple artifacts with different rules
The system SHALL allow different rule sets for different artifacts in the same config.
#### Scenario: Multiple artifacts have rules
- **WHEN** config has `rules: { proposal: ["P1"], specs: ["S1", "S2"], tasks: ["T1"] }`
- **THEN** proposal instructions show only ["P1"], specs show only ["S1", "S2"], tasks show only ["T1"]
#### Scenario: Some artifacts have rules, others do not
- **WHEN** config has rules for proposal and specs only
- **THEN** design and tasks instructions have no `<rules>` section
### Requirement: Rules are additive to schema guidance
The system SHALL add config rules to the schema's built-in artifact instruction, not replace it.
#### Scenario: Artifact has schema instruction and config rules
- **WHEN** artifact has built-in instruction from schema and config provides rules
- **THEN** final instruction contains both schema guidance and config rules
#### Scenario: Rules provide additional constraints
- **WHEN** schema says "create proposal" and config rules say "include rollback plan"
- **THEN** agent sees both the schema template and the additional rule
### Requirement: Validate artifact IDs during instruction loading
The system SHALL validate artifact IDs in rules against the schema when instructions are loaded and emit warnings for unknown IDs.
#### Scenario: All artifact IDs are valid
- **WHEN** instructions loaded and config has `rules: { proposal: [...], specs: [...] }` for schema with those artifacts
- **THEN** no validation warnings are emitted
#### Scenario: Unknown artifact ID in rules
- **WHEN** instructions loaded and config has `rules: { unknownartifact: [...] }`
- **THEN** warning emitted: "Unknown artifact ID in rules: 'unknownartifact'. Valid IDs for schema 'spec-driven': design, proposal, specs, tasks"
#### Scenario: Multiple unknown artifact IDs
- **WHEN** instructions loaded and config has multiple unknown artifact IDs
- **THEN** separate warning emitted for each unknown artifact ID
#### Scenario: Validation warnings shown once per session
- **WHEN** instructions loaded multiple times in same CLI session
- **THEN** each unique validation warning is shown only once (cached)
@@ -0,0 +1,83 @@
# Spec: Schema Resolution with Config
## ADDED Requirements
### Requirement: Use config schema as default for new changes
The system SHALL use the schema field from `openspec/config.yaml` as the default when creating new changes without explicit `--schema` flag.
#### Scenario: Create change without --schema flag and config exists
- **WHEN** user runs `openspec new change foo` and config contains `schema: "tdd"`
- **THEN** system creates change with schema "tdd"
#### Scenario: Create change without --schema flag and no config
- **WHEN** user runs `openspec new change foo` and no config file exists
- **THEN** system creates change with default schema "spec-driven"
#### Scenario: Create change with explicit --schema flag
- **WHEN** user runs `openspec new change foo --schema custom` and config contains `schema: "tdd"`
- **THEN** system creates change with schema "custom" (CLI flag overrides config)
### Requirement: Resolve schema with updated precedence order
The system SHALL resolve the schema for a change using the following precedence order: CLI flag, change metadata, project config, hardcoded default.
#### Scenario: CLI flag is provided
- **WHEN** user runs command with `--schema custom`
- **THEN** system uses "custom" regardless of change metadata or config
#### Scenario: Change metadata specifies schema
- **WHEN** change has `.openspec.yaml` with `schema: bound` and config has `schema: tdd`
- **THEN** system uses "bound" from change metadata
#### Scenario: Only project config specifies schema
- **WHEN** no CLI flag or change metadata, but config has `schema: tdd`
- **THEN** system uses "tdd" from project config
#### Scenario: No schema specified anywhere
- **WHEN** no CLI flag, change metadata, or project config
- **THEN** system uses hardcoded default "spec-driven"
### Requirement: Support project-local schema names in config
The system SHALL allow the config schema field to reference project-local schemas defined in `openspec/schemas/`.
#### Scenario: Config references project-local schema
- **WHEN** config contains `schema: "my-workflow"` and `openspec/schemas/my-workflow/` exists
- **THEN** system resolves to the project-local schema
#### Scenario: Config references non-existent schema
- **WHEN** config contains `schema: "nonexistent"` and that schema does not exist
- **THEN** system shows error when attempting to load the schema with fuzzy match suggestions and list of all valid schemas
### Requirement: Provide helpful error message for invalid schema
The system SHALL display schema error with fuzzy match suggestions, list of available schemas, and fix instructions.
#### Scenario: Schema name with typo (close match)
- **WHEN** config contains `schema: "spce-driven"` (typo)
- **THEN** error message includes "Did you mean: spec-driven (built-in)" as suggestion
#### Scenario: Schema name with no close matches
- **WHEN** config contains `schema: "completely-wrong"`
- **THEN** error message shows list of all available built-in and project-local schemas
#### Scenario: Error message includes fix instructions
- **WHEN** config references invalid schema
- **THEN** error message includes "Fix: Edit openspec/config.yaml and change 'schema: X' to a valid schema name"
#### Scenario: Error distinguishes built-in vs project-local schemas
- **WHEN** error lists available schemas
- **THEN** output clearly labels each as "built-in" or "project-local"
### Requirement: Maintain backwards compatibility for existing changes
The system SHALL continue to work with existing changes that do not have project config.
#### Scenario: Existing change without config
- **WHEN** change was created before config feature and no config file exists
- **THEN** system resolves schema using existing logic (change metadata or hardcoded default)
#### Scenario: Existing change with config added later
- **WHEN** config file is added to project with existing changes
- **THEN** existing changes continue to use their bound schema from `.openspec.yaml`
+72
View File
@@ -0,0 +1,72 @@
## 1. Core Config System
- [x] 1.1 Create `src/core/project-config.ts` with ProjectConfigSchema using Zod (for docs and type inference)
- [x] 1.2 Implement `readProjectConfig()` with resilient field-by-field parsing using Zod's `safeParse()`
- [x] 1.3 Add support for both .yaml and .yml extensions (prefer .yaml)
- [x] 1.4 Add 50KB hard limit for context field with size check and warning
- [x] 1.5 Implement `validateConfigRules()` to validate artifact IDs against schema (called during instruction loading)
- [x] 1.6 Implement `suggestSchemas()` with Levenshtein distance fuzzy matching for helpful error messages
- [x] 1.7 Add unit tests for resilient parsing (partial configs, field-level errors with Zod safeParse)
- [x] 1.8 Add unit tests for context size limit enforcement
- [x] 1.9 Add unit tests for .yml/.yaml precedence
- [x] 1.10 Add unit tests for fuzzy schema matching with typos
## 2. Schema Resolution Integration
- [x] 2.1 Update `resolveSchemaForChange()` in `src/utils/change-metadata.ts` to check project config (3rd in precedence)
- [x] 2.2 Update `createNewChange()` in `src/utils/change-utils.ts` to use config schema as default
- [x] 2.3 Add integration tests for schema resolution precedence (CLI → change metadata → config → default)
- [x] 2.4 Add test for project-local schema names in config
- [x] 2.5 Add test for non-existent schema error handling with suggestions
## 3. Context and Rules Injection
- [x] 3.1 Update `loadInstructions()` in `src/core/artifact-graph/instruction-loader.ts` to inject context for all artifacts
- [x] 3.2 Add rules injection logic for matching artifacts only with XML tags and bullet formatting
- [x] 3.3 Add validation call during instruction loading to check artifact IDs in rules
- [x] 3.4 Implement session-level warning cache to avoid repeating same validation warnings
- [x] 3.5 Implement proper ordering: `<context>` → `<rules>` → `<template>`
- [x] 3.6 Preserve multi-line strings and special characters without escaping
- [x] 3.7 Add unit tests for context injection (present, absent, multi-line, special chars)
- [x] 3.8 Add unit tests for rules injection (matching artifact, non-matching, empty array, multiple artifacts)
- [x] 3.9 Add unit tests for validation timing (warnings during instruction load, not config load)
- [x] 3.10 Add unit tests for warning deduplication (same warning shown once per session)
- [x] 3.11 Add integration test verifying full instruction output with context + rules + template
## 4. Interactive Config Creation
- [x] 4.1 Add @inquirer/prompts dependency to package.json
- [x] 4.2 Create `src/core/config-prompts.ts` with ConfigPromptResult interface
- [x] 4.3 Implement `promptForConfig()` function with schema selection prompt
- [x] 4.4 Add multi-line context input prompt with examples and skip option
- [x] 4.5 Add per-artifact rules prompts with checkbox selection and line-by-line input
- [x] 4.6 Implement YAML serialization with proper multi-line string formatting
- [x] 4.7 Add validation and retry logic for prompt errors
## 5. Experimental Setup Integration
- [x] 5.1 Update `artifactExperimentalSetupCommand()` in `src/commands/artifact-workflow.ts` to check for existing config
- [x] 5.2 Add config creation section after skills/commands creation with header and description
- [x] 5.3 Integrate `promptForConfig()` calls with proper flow control
- [x] 5.4 Add Ctrl+C (ExitPromptError) handling - log cancellation message, continue with setup (non-fatal)
- [x] 5.5 Write created config to `openspec/config.yaml` using YAML stringify
- [x] 5.6 Display success summary showing path, schema, context lines, rules count
- [x] 5.7 Show usage examples and git commit suggestion
- [x] 5.8 Handle existing config case with skip message and manual update instructions
- [x] 5.9 Add error handling for file write failures with fallback suggestions
- [x] 5.10 Add test for cancellation behavior (skills/commands preserved, config not created)
## 6. Testing and Documentation
- [x] 6.1 Add end-to-end test: run experimental setup → create config → create change → verify schema used
- [x] 6.2 Add end-to-end test: create config → get instructions → verify context and rules injected
- [x] 6.3 Test backwards compatibility: existing changes work without config
- [x] 6.4 Test config changes are reflected immediately (no stale cache)
- [x] 6.5 Add performance benchmark: measure config read time with typical config (1KB context)
- [x] 6.6 Add performance benchmark: measure config read time with large config (50KB context)
- [x] 6.7 Add performance benchmark: measure repeated reads within single command
- [x] 6.8 Document benchmark results and decide if caching is needed (target: <10ms typical, <50ms acceptable)
- [x] 6.9 If benchmarks fail: implement mtime-based caching with cache invalidation
- [x] 6.10 Update README or docs with config feature examples and schema
- [x] 6.11 Document common artifact IDs for different schemas
- [x] 6.12 Add troubleshooting section for config validation errors
@@ -0,0 +1,2 @@
schema: spec-driven
created: "2025-01-13"
@@ -0,0 +1,117 @@
## Context
OpenSpec currently resolves schemas from two locations:
1. User override: `~/.local/share/openspec/schemas/<name>/`
2. Package built-in: `<npm-package>/schemas/<name>/`
This change adds a third, highest-priority level: project-local schemas at `./openspec/schemas/<name>/`.
The resolver functions in `src/core/artifact-graph/resolver.ts` currently don't take a `projectRoot` parameter because user and package paths are absolute. To support project-local schemas, we need to pass project root context into the resolver.
## Goals / Non-Goals
**Goals:**
- Enable version-controlled custom workflow schemas
- Allow teams to share schemas via git without per-machine setup
- Maintain backward compatibility with existing resolver API
- Integrate with `config.yaml`'s `schema` field (from project-config change)
**Non-Goals:**
- Schema inheritance or `extends` keyword
- Template-level overrides (partial forks)
- Schema management CLI commands (`openspec schema copy/which/diff/reset`)
- Validation that project-local schema names don't conflict with built-ins (shadowing is intentional)
## Decisions
### Decision 1: Add optional `projectRoot` parameter to resolver functions
**Choice:** Add optional `projectRoot?: string` parameter to resolver functions rather than using `process.cwd()` internally.
**Alternatives considered:**
- Use `process.cwd()` internally: Simpler API but implicit, harder to test, doesn't match existing codebase patterns
- Create separate project-aware functions: No breaking changes but awkward API, callers must compose
**Rationale:** The codebase already follows a pattern where CLI commands get project root via `process.cwd()` and pass it down to functions that need it. Adding an optional parameter maintains backward compatibility while enabling explicit, testable behavior.
**Affected functions:**
```typescript
getSchemaDir(name: string, projectRoot?: string): string | null
listSchemas(projectRoot?: string): string[]
listSchemasWithInfo(projectRoot?: string): SchemaInfo[]
resolveSchema(name: string, projectRoot?: string): SchemaYaml
```
### Decision 2: Resolution order is project → user → package
**Choice:** Project-local schemas have highest priority, then user overrides, then package built-ins.
**Rationale:**
- Project-local should win because it represents team intent (version controlled, shared)
- User overrides still useful for personal experimentation without affecting team
- Package built-ins are the fallback defaults
```
1. ./openspec/schemas/<name>/ # Project-local (highest)
2. ~/.local/share/openspec/schemas/<name>/ # User override
3. <npm-package>/schemas/<name>/ # Package built-in (lowest)
```
### Decision 3: Add `getProjectSchemasDir()` helper function
**Choice:** Create a dedicated function to get the project schemas directory path.
```typescript
function getProjectSchemasDir(projectRoot: string): string {
return path.join(projectRoot, 'openspec', 'schemas');
}
```
**Rationale:** Matches existing pattern with `getPackageSchemasDir()` and `getUserSchemasDir()`. Keeps path logic centralized.
### Decision 4: Extend `SchemaInfo.source` to include `'project'`
**Choice:** Update the source type from `'package' | 'user'` to `'project' | 'user' | 'package'`.
**Rationale:** Consumers need to distinguish project-local schemas for display purposes (e.g., `schemasCommand` output).
### Decision 5: No special handling for schema name conflicts
**Choice:** If a project-local schema has the same name as a built-in (e.g., `spec-driven`), the project-local version wins. No warning, no error.
**Rationale:** This is intentional shadowing. Teams may want to customize a built-in schema while keeping the same name for familiarity.
## Risks / Trade-offs
### Risk: Confusion when project schema shadows built-in
A team could create `openspec/schemas/spec-driven/` that shadows the built-in, causing confusion when someone expects default behavior.
**Mitigation:** The `openspec schemas` command shows the source of each schema. Users can see `spec-driven (project)` vs `spec-driven (package)`.
### Risk: Missing projectRoot parameter
If callers forget to pass `projectRoot`, project-local schemas won't be found.
**Mitigation:**
- Make the change incrementally, updating call sites that need project-local support
- Existing behavior (user + package only) is preserved when `projectRoot` is undefined
### Trade-off: Optional parameter vs required
Making `projectRoot` optional maintains backward compatibility but means some code paths may silently skip project-local resolution.
**Accepted:** Backward compatibility is more important. The main entry points (CLI commands) will always pass `projectRoot`.
## Implementation Approach
1. **Update `resolver.ts`:**
- Add `getProjectSchemasDir(projectRoot: string)` function
- Update `getSchemaDir()` to check project-local first when `projectRoot` provided
- Update `listSchemas()` to include project schemas when `projectRoot` provided
- Update `listSchemasWithInfo()` to return `source: 'project'` for project schemas
- Update `SchemaInfo` type to include `'project'` in source union
2. **Update `artifact-workflow.ts`:**
- Update `schemasCommand` to pass `projectRoot` and display source labels
3. **Update call sites:**
- Any existing code that needs project-local resolution should pass `projectRoot`
- `config.yaml` schema resolution already has access to `projectRoot`
@@ -0,0 +1,167 @@
# Project-Local Schemas
## Summary
Add project-local schema resolution (`./openspec/schemas/`) as the highest priority in the schema lookup chain. This enables teams to version control custom workflow schemas with their repository.
## Motivation
Currently, schema resolution is 2-level:
1. User override: `~/.local/share/openspec/schemas/<name>/`
2. Package built-in: `<npm-package>/schemas/<name>/`
This creates friction for teams:
- Custom schemas must be set up per-machine via XDG paths
- Cannot share schemas via version control
- No single source of truth for team workflows
## Design Decisions
### 3-Level Resolution Order
```
1. ./openspec/schemas/<name>/ # Project-local (NEW)
2. ~/.local/share/openspec/schemas/<name>/ # User global (XDG)
3. <npm-package>/schemas/<name>/ # Package built-in
```
Project-local takes highest priority, enabling:
- Version-controlled custom workflows
- Automatic team sharing via git
- No per-machine setup required
### Fork Model (Not Inheritance)
Custom schemas are complete definitions, not extensions. There is no `extends` keyword.
**Rationale:** Simplicity. Inheritance adds complexity (conflict resolution, partial overrides, debugging "where did this come from?"). Users who need custom workflows can define them fully. This keeps the mental model simple:
- Use a preset → Configure path (see project-config change)
- Need different structure → Fork path (define your own)
### Directory Structure
```
openspec/
├── schemas/ # Project-local schemas
│ └── my-workflow/
│ ├── schema.yaml # Full schema definition
│ └── templates/
│ ├── artifact1.md
│ ├── artifact2.md
│ └── ...
└── changes/
```
### Schema Naming
Project-local schemas are referenced by their directory name:
- `openspec/schemas/my-workflow/` → referenced as `my-workflow`
- Works with `--schema my-workflow` flag
- Works with `schema: my-workflow` in config.yaml (see project-config change)
## Scope
### In Scope
- Add `getProjectSchemasDir()` function to resolver
- Update `getSchemaDir()` to check project-local first
- Update `listSchemas()` to include project schemas
- Update `listSchemasWithInfo()` to include `source: 'project'`
- Update `schemasCommand` output to show project schemas
### Out of Scope
- Schema management CLI (`openspec schema copy/which/diff/reset`) - future enhancement
- Schema inheritance/extends - explicitly not supported
- Template-level overrides (partial fork) - explicitly not supported
## User Experience
### Creating a Custom Schema
```bash
# Create schema directory
mkdir -p openspec/schemas/my-workflow/templates
# Define schema
cat > openspec/schemas/my-workflow/schema.yaml << 'EOF'
name: my-workflow
version: 1
description: Our team's planning workflow
artifacts:
- id: research
generates: research.md
template: research.md
description: Background research
requires: []
- id: proposal
generates: proposal.md
template: proposal.md
description: Change proposal
requires: [research]
- id: tasks
generates: tasks.md
template: tasks.md
description: Implementation tasks
requires: [proposal]
EOF
# Create templates
echo "# Research\n\n..." > openspec/schemas/my-workflow/templates/research.md
# ... etc
```
### Using the Custom Schema
```bash
# Via CLI flag
openspec new change add-feature --schema my-workflow
openspec status --change add-feature --schema my-workflow
# Via config.yaml (requires project-config change)
# schema: my-workflow
```
### Team Sharing
```bash
# Commit to repo
git add openspec/schemas/
git commit -m "Add custom workflow schema"
git push
# Team members get it automatically
git pull
openspec status --change add-feature --schema my-workflow # Just works
```
## Implementation Notes
### Files to Modify
| File | Changes |
|------|---------|
| `src/core/artifact-graph/resolver.ts` | Add `getProjectSchemasDir()`, update resolution order |
| `src/commands/artifact-workflow.ts` | Update `schemasCommand` to show source |
### Project Root Detection
Use existing `findProjectRoot()` pattern or current working directory. The project-local schemas directory is always `./openspec/schemas/` relative to project root.
### Source Indication
`listSchemasWithInfo()` returns `source: 'project' | 'user' | 'package'`. Update type definition and implementation.
## Testing Considerations
- Create temp project with local schema, verify resolution priority
- Verify local schema overrides user override with same name
- Verify `listSchemas()` includes project schemas
- Verify `schemasCommand` shows correct source labels
## Related Changes
- **project-config**: Adds `config.yaml` with `schema` field that can reference project-local schemas
@@ -0,0 +1,88 @@
## ADDED Requirements
### Requirement: Project-local schema resolution
The system SHALL resolve schemas from the project-local directory (`./openspec/schemas/<name>/`) with highest priority when a `projectRoot` is provided.
#### Scenario: Project-local schema takes precedence over user override
- **WHEN** a schema named "my-workflow" exists at `./openspec/schemas/my-workflow/schema.yaml`
- **AND** a schema named "my-workflow" exists at `~/.local/share/openspec/schemas/my-workflow/schema.yaml`
- **AND** `getSchemaDir("my-workflow", projectRoot)` is called
- **THEN** the system SHALL return the project-local path
#### Scenario: Project-local schema takes precedence over package built-in
- **WHEN** a schema named "spec-driven" exists at `./openspec/schemas/spec-driven/schema.yaml`
- **AND** "spec-driven" is a package built-in schema
- **AND** `getSchemaDir("spec-driven", projectRoot)` is called
- **THEN** the system SHALL return the project-local path
#### Scenario: Falls back to user override when no project-local schema
- **WHEN** no schema named "my-workflow" exists at `./openspec/schemas/my-workflow/`
- **AND** a schema named "my-workflow" exists at `~/.local/share/openspec/schemas/my-workflow/schema.yaml`
- **AND** `getSchemaDir("my-workflow", projectRoot)` is called
- **THEN** the system SHALL return the user override path
#### Scenario: Falls back to package built-in when no project-local or user schema
- **WHEN** no schema named "spec-driven" exists at `./openspec/schemas/spec-driven/`
- **AND** no schema named "spec-driven" exists at `~/.local/share/openspec/schemas/spec-driven/`
- **AND** "spec-driven" is a package built-in schema
- **AND** `getSchemaDir("spec-driven", projectRoot)` is called
- **THEN** the system SHALL return the package built-in path
#### Scenario: Backward compatibility when projectRoot not provided
- **WHEN** `getSchemaDir("my-workflow")` is called without a `projectRoot` parameter
- **THEN** the system SHALL only check user override and package built-in locations
- **AND** the system SHALL NOT check project-local location
### Requirement: Project schemas directory helper
The system SHALL provide a `getProjectSchemasDir(projectRoot)` function that returns the project-local schemas directory path.
#### Scenario: Returns correct path
- **WHEN** `getProjectSchemasDir("/path/to/project")` is called
- **THEN** the system SHALL return `/path/to/project/openspec/schemas`
### Requirement: List schemas includes project-local
The system SHALL include project-local schemas when listing available schemas if `projectRoot` is provided.
#### Scenario: Project-local schemas appear in list
- **WHEN** a schema named "team-flow" exists at `./openspec/schemas/team-flow/schema.yaml`
- **AND** `listSchemas(projectRoot)` is called
- **THEN** the returned list SHALL include "team-flow"
#### Scenario: Project-local schema shadows same-named user schema in list
- **WHEN** a schema named "custom" exists at both project-local and user override locations
- **AND** `listSchemas(projectRoot)` is called
- **THEN** the returned list SHALL include "custom" exactly once
#### Scenario: Backward compatibility for listSchemas
- **WHEN** `listSchemas()` is called without a `projectRoot` parameter
- **THEN** the system SHALL only include user override and package built-in schemas
### Requirement: Schema info includes project source
The system SHALL indicate `source: 'project'` for project-local schemas in `listSchemasWithInfo()` results.
#### Scenario: Project-local schema shows project source
- **WHEN** a schema named "team-flow" exists at `./openspec/schemas/team-flow/schema.yaml`
- **AND** `listSchemasWithInfo(projectRoot)` is called
- **THEN** the schema info for "team-flow" SHALL have `source: 'project'`
#### Scenario: User override schema shows user source
- **WHEN** a schema named "my-custom" exists only at `~/.local/share/openspec/schemas/my-custom/`
- **AND** `listSchemasWithInfo(projectRoot)` is called
- **THEN** the schema info for "my-custom" SHALL have `source: 'user'`
#### Scenario: Package built-in schema shows package source
- **WHEN** "spec-driven" exists only as a package built-in
- **AND** `listSchemasWithInfo(projectRoot)` is called
- **THEN** the schema info for "spec-driven" SHALL have `source: 'package'`
### Requirement: Schemas command shows source
The `openspec schemas` command SHALL display the source of each schema.
#### Scenario: Display format includes source
- **WHEN** user runs `openspec schemas`
- **THEN** the output SHALL show each schema with its source label (project, user, or package)
@@ -0,0 +1,28 @@
## 1. Update Resolver Types and Helpers
- [x] 1.1 Update `SchemaInfo.source` type to include `'project'` in `src/core/artifact-graph/resolver.ts`
- [x] 1.2 Add `getProjectSchemasDir(projectRoot: string): string` function
## 2. Update Schema Resolution Functions
- [x] 2.1 Update `getSchemaDir(name, projectRoot?)` to check project-local first when projectRoot provided
- [x] 2.2 Update `resolveSchema(name, projectRoot?)` to pass projectRoot to getSchemaDir
- [x] 2.3 Update `listSchemas(projectRoot?)` to include project-local schemas
- [x] 2.4 Update `listSchemasWithInfo(projectRoot?)` to include project schemas with `source: 'project'`
## 3. Update CLI Commands
- [x] 3.1 Update `schemasCommand` to pass projectRoot and display source labels in output
## 4. Update Call Sites
- [x] 4.1 Review and update call sites that need project-local schema support to pass projectRoot
## 5. Testing
- [x] 5.1 Add unit tests for `getProjectSchemasDir()`
- [x] 5.2 Add unit tests for project-local schema resolution priority
- [x] 5.3 Add unit tests for backward compatibility (no projectRoot = user + package only)
- [x] 5.4 Add unit tests for `listSchemas()` including project schemas
- [x] 5.5 Add unit tests for `listSchemasWithInfo()` with `source: 'project'`
- [x] 5.6 Add integration test with temp project containing local schema
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-20
@@ -0,0 +1,28 @@
## Why
We want to rename `spec-driven` to `openspec-default` to better reflect that it's the standard/default workflow. However, renaming directly would break existing projects that have `schema: spec-driven` in their `openspec/config.yaml`. Adding alias support allows both names to work interchangeably, enabling a smooth transition with no breaking changes.
## What Changes
- Add schema alias resolution in the schema resolver
- `openspec-default` and `spec-driven` will both resolve to the same schema
- The physical directory remains `schemas/spec-driven/` (or could be renamed to `schemas/openspec-default/` with `spec-driven` as the alias)
- All CLI commands and config files accept either name
- No changes required to existing user configs
## Capabilities
### New Capabilities
- `schema-aliases`: Support for schema name aliases so multiple names can resolve to the same schema directory
### Modified Capabilities
<!-- No existing spec-level behavior is changing - this is purely additive -->
## Impact
- `src/core/artifact-graph/resolver.ts` - Add alias resolution logic
- `schemas/` directory - Potentially rename `spec-driven` to `openspec-default`
- Documentation - Update to prefer `openspec-default` while noting `spec-driven` still works
- Default schema constants - Update `DEFAULT_SCHEMA` to `openspec-default`
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-20

Some files were not shown because too many files have changed in this diff Show More