diff --git a/openspec/README.md b/openspec/README.md index 81a58328..a9d0cafe 100644 --- a/openspec/README.md +++ b/openspec/README.md @@ -87,6 +87,7 @@ After deployment, create separate PR to: openspec list # List active changes openspec list --specs # List specifications openspec show [item] # Display change or spec +openspec diff [change] # Show spec differences openspec validate [item] # Validate changes or specs openspec archive [change] # Archive after deployment @@ -255,6 +256,19 @@ Every requirement MUST have at least one scenario. 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//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 @@ -425,6 +439,7 @@ Only add complexity with: ```bash openspec list # What's in progress? openspec show [item] # View details +openspec diff [change] # What's changing? openspec validate --strict # Is it correct? openspec archive [change] # Mark complete ``` diff --git a/openspec/changes/add-slash-command-support/proposal.md b/openspec/changes/add-slash-command-support/proposal.md new file mode 100644 index 00000000..f11413a1 --- /dev/null +++ b/openspec/changes/add-slash-command-support/proposal.md @@ -0,0 +1,119 @@ +# Add Slash Command Support for Coding Agents + +## Summary +- Enable OpenSpec to generate and update custom slash commands for supported coding agents (Claude Code and Cursor). +- Provide three slash commands aligned with OpenSpec's workflow: proposal (start a change proposal), apply (implement), and archive. +- Share slash command templating between agents to make future extensions simple. + +## Motivation +Developers use different coding agents and editors. Having consistent slash commands across tools for the OpenSpec workflow reduces friction and ensures a standard way to trigger the workflow. Supporting both Claude Code and Cursor now lays a foundation for future agents that introduce slash command features. + +## Proposal +1. During `openspec init`, when a user selects a supported tool, generate slash command configuration for three OpenSpec workflow stages: + - Claude (namespaced): `/openspec/proposal`, `/openspec/apply`, `/openspec/archive`. + - Cursor (flat, prefixed): `/openspec-proposal`, `/openspec-apply`, `/openspec-archive`. + - Semantics: + - Create – scaffold a change (ID, `proposal.md`, `tasks.md`, delta specs); validate strictly. + - Apply – implement an approved change; complete tasks; validate strictly. + - Archive – archive after deployment; update specs if needed. + - Each command file MUST embed concise, step-by-step instructions sourced from `openspec/README.md` (see Template Content section). +2. Store slash command files per tool: + - Claude Code: `.claude/commands/openspec/{proposal,apply,archive}.md` + - Cursor: `.cursor/commands/{openspec-proposal,openspec-apply,openspec-archive}.md` + - Ensure nested directories are created. +3. Command file format and metadata: + - Use Markdown with optional YAML frontmatter for tool metadata (name/title, description, category/tags) when supported by the tool. + - Place OpenSpec markers around the body only, never inside frontmatter. + - Keep the visible slash name, file name, and any frontmatter `name`/`id` consistently aligned (e.g., `proposal`, `openspec-proposal`). + - Namespacing: categorize these under “OpenSpec” and prefer unique IDs (e.g., `openspec-proposal`) to avoid collisions. +4. Centralize templates: define command bodies once and reuse across tools; apply minimal per-tool wrappers (frontmatter, categories, filenames). +5. During `openspec update`, refresh only existing slash command files (per-file basis) within markers; do not create missing files or new tools. + +## Design Ideas +- Introduce `SlashCommandConfigurator` to manage multiple files per tool. + - Expose targets rather than a single `configFileName` (e.g., `getTargets(): Array<{ path: string; kind: 'slash'; id: string }>`). + - Provide `generateAll(projectPath, openspecDir)` for init and `updateExisting(projectPath, openspecDir)` for update. +- Per-tool adapters add only frontmatter and pathing; bodies come from shared templates. +- Templates live in `TemplateManager` with helpers that extract concise, authoritative snippets from `openspec/README.md`. +- Update flow logs per-file results so users see exactly which slash files were refreshed. + +### Marker Placement +- Markers MUST wrap only the Markdown body contents: + - Frontmatter (if present) goes first. + - Then `` … body … ``. + - Avoid inserting markers into the YAML block to prevent parse errors. + +### Idempotency and Creation Rules +- `init`: create all three files for the chosen tool(s) once; subsequent `init` runs are no-ops for existing files. +- `update`: refresh only files that exist; skip missing ones without creating new files. +- Directory creation for `.claude/commands/openspec/` and `.cursor/commands/` is the configurator’s responsibility. + +### Command Naming & UX +- Claude Code: use namespacing in the slash itself for readability and grouping: `/openspec/proposal`, `/openspec/apply`, `/openspec/archive`. +- Cursor: use flat names with an `openspec-` prefix: `/openspec-proposal`, `/openspec-apply`, `/openspec-archive`. Group via `category: OpenSpec` when supported. +- Consistency: align file names, visible slash names, and any frontmatter `id` (e.g., `id: openspec-apply`). +- Migration: do not rename existing commands during `update`; apply new naming only on `init` (or via an explicit migrate step). + +## Open Questions +- Validate exact metadata/frontmatter supported by each tool version; if unsupported, omit frontmatter and ship Markdown body only. +- Confirm the final Cursor command file location for the targeted versions; fall back to Markdown-only if Cursor does not parse frontmatter. +- Evaluate additional commands beyond the initial three (e.g., `/show-change`, `/validate-all`) based on user demand. + +## Alternatives +- Hard-code slash command text per tool (rejected: duplicates content; increases maintenance). +- Delay Cursor support until its config stabilizes (partial accept): gate Cursor behind a feature flag until verified in real environments. + +## Risks +- Tool configuration formats may change, requiring updates to wrappers/frontmatter. +- Incorrect paths or categories can hide commands; add path existence checks and clear logging. +- Marker misuse (inside frontmatter) can break parsing; enforce placement rules in tests. + +## Future Work +- Support additional editors/agents that expose slash command APIs. +- Allow users to customize command names and categories during `openspec init`. +- Provide a dedicated command to regenerate slash commands without running full `update`. + +## File Format Examples +The following examples illustrate expected structure. If a tool does not support frontmatter, omit the YAML block and keep only the markers + body. + +### Claude Code: `.claude/commands/openspec/proposal.md` +```markdown +--- +name: OpenSpec: Proposal +description: Scaffold a new OpenSpec change and validate strictly. +category: OpenSpec +tags: [openspec, change] +--- + +...command body from shared template... + +``` + +Slash invocation: `/openspec/proposal` (namespaced) + +### Cursor: `.cursor/commands/openspec-proposal.md` +```markdown +--- +name: /openspec-proposal +id: openspec-proposal +category: OpenSpec +description: Scaffold a new OpenSpec change and validate strictly. +--- + +...command body from shared template... + +``` + +Slash invocation: `/openspec-proposal` (flat, prefixed) + +## Template Content +Templates should be brief, actionable, and sourced from `openspec/README.md` to avoid duplication. Each command body includes: +- Guardrails: ask 1–2 clarifying questions if needed; follow minimal-complexity rules; use `pnpm` for Node projects. +- Step list tailored to the workflow stage (proposal, apply, archive), including strict validation commands. +- Pointers to `openspec show`, `openspec list`, and troubleshooting tips when validation fails. + +## Testing Strategy +- Golden snapshots for generated files per tool (frontmatter + markers + body). +- Partial presence tests: if 1–2 files exist, `update` only refreshes those and does not create missing ones. +- Marker placement tests: ensure markers never appear inside frontmatter; cover missing/duplicated marker recovery behavior. +- Logging tests: `update` reports per-file updates for slash commands. diff --git a/openspec/changes/add-slash-command-support/specs/cli-init/spec.md b/openspec/changes/add-slash-command-support/specs/cli-init/spec.md new file mode 100644 index 00000000..afa057f2 --- /dev/null +++ b/openspec/changes/add-slash-command-support/specs/cli-init/spec.md @@ -0,0 +1,15 @@ +## ADDED Requirements +### Requirement: Slash Command Configuration +The init command SHALL generate slash command files for supported editors using shared templates. + +#### 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 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 diff --git a/openspec/changes/add-slash-command-support/specs/cli-update/spec.md b/openspec/changes/add-slash-command-support/specs/cli-update/spec.md new file mode 100644 index 00000000..03f054bc --- /dev/null +++ b/openspec/changes/add-slash-command-support/specs/cli-update/spec.md @@ -0,0 +1,17 @@ +## ADDED Requirements +### Requirement: Slash Command Updates +The update command SHALL refresh existing slash command files for configured tools without creating new ones. + +#### 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 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: Missing slash command file +- **WHEN** a tool lacks a slash command file +- **THEN** do not create a new file during update diff --git a/openspec/changes/add-slash-command-support/tasks.md b/openspec/changes/add-slash-command-support/tasks.md new file mode 100644 index 00000000..cb194c04 --- /dev/null +++ b/openspec/changes/add-slash-command-support/tasks.md @@ -0,0 +1,16 @@ +# Implementation Tasks + +## 1. Templates and Configurators +- [ ] 1.1 Create shared templates for the Proposal, Apply, and Archive commands with instructions for each workflow stage from `openspec/README.md`. +- [ ] 1.2 Implement a `SlashCommandConfigurator` base and tool-specific configurators for Claude Code and Cursor. + +## 2. Claude Code Integration +- [ ] 2.1 Generate `.claude/commands/openspec/{proposal,apply,archive}.md` during `openspec init` using shared templates. +- [ ] 2.2 Update existing `.claude/commands/openspec/*` files during `openspec update`. + +## 3. Cursor Integration +- [ ] 3.1 Generate `.cursor/commands/{openspec-proposal,openspec-apply,openspec-archive}.md` during `openspec init` using shared templates. +- [ ] 3.2 Update existing `.cursor/commands/*` files during `openspec update`. + +## 4. Verification +- [ ] 4.1 Add tests verifying slash command files are created and updated correctly. diff --git a/openspec/changes/make-validation-scope-aware/proposal.md b/openspec/changes/make-validation-scope-aware/proposal.md new file mode 100644 index 00000000..391b703c --- /dev/null +++ b/openspec/changes/make-validation-scope-aware/proposal.md @@ -0,0 +1,12 @@ +## Why +Validation currently errors on changes without spec deltas, even when the change is intentionally proposal-only or tooling-only. This creates false negatives and noisy CI. + +## What Changes +- Make change validation scope-aware: validate only artifacts that exist. +- Only error on "No deltas found" if spec delta files exist but parse to zero deltas. +- Keep archive stricter: if specs exist but parse to zero deltas, fail; allow `--skip-specs` for tooling-only changes. + +## Impact +- Affected specs: cli-validate +- Affected code: `src/commands/validate.ts`, `src/core/validation/validator.ts` + diff --git a/openspec/changes/make-validation-scope-aware/specs/cli-validate/spec.md b/openspec/changes/make-validation-scope-aware/specs/cli-validate/spec.md new file mode 100644 index 00000000..cae28c91 --- /dev/null +++ b/openspec/changes/make-validation-scope-aware/specs/cli-validate/spec.md @@ -0,0 +1,25 @@ +## ADDED Requirements +### Requirement: Scope-Aware Change Validation +The validator SHALL validate only artifacts that exist for a change, avoiding errors for proposal-only or tooling-only changes. + +#### Scenario: Proposal-only change +- **WHEN** a change contains `proposal.md` but has no `specs/` directory or contains no `*/spec.md` files +- **THEN** validate the proposal (Why/What sections) +- **AND** do not require or validate spec deltas + +#### Scenario: Delta validation when specs exist +- **WHEN** a change contains one or more `specs//spec.md` files +- **THEN** validate delta-formatted specs with existing rules (SHALL/MUST, scenarios, duplicates, conflicts) + +## MODIFIED Requirements +### Requirement: Validation SHALL provide actionable remediation steps +Validation output SHALL include specific guidance to fix each error, including expected structure, example headers, and suggested commands to verify fixes. + +#### Scenario: No deltas found in change +- **WHEN** validating a change that contains `specs/` with one or more `*/spec.md` files but the parser finds zero deltas +- **THEN** show error "No deltas found" with guidance: + - Ensure `openspec/changes/{id}/specs/` has `.md` files that include delta headers + - Use delta headers: `## ADDED Requirements`, `## MODIFIED Requirements`, `## REMOVED Requirements`, `## RENAMED Requirements` + - Each requirement must include at least one `#### Scenario:` block + - Try: `openspec change show {id} --json --deltas-only` to inspect parsed deltas + diff --git a/openspec/changes/make-validation-scope-aware/tasks.md b/openspec/changes/make-validation-scope-aware/tasks.md new file mode 100644 index 00000000..9ccf3906 --- /dev/null +++ b/openspec/changes/make-validation-scope-aware/tasks.md @@ -0,0 +1,16 @@ +## 1. Validator changes +- [ ] 1.1 Change `validateChangeDeltaSpecs` to only emit "Change must have at least one delta" when `specs/` exists and contains at least one `*/spec.md` but parsed total deltas is 0 +- [ ] 1.2 Return valid (no error) when `specs/` directory is missing or has no `spec.md` files + +## 2. CLI changes +- [ ] 2.1 In bulk validation, keep current behavior (call delta validator). Behavior remains correct after 1.1 +- [ ] 2.2 Add a short INFO log in human-readable mode when a change has no `specs/` (optional) + +## 3. Documentation +- [ ] 3.1 Update README and template: "Validation checks only existing artifacts. Proposal-only changes are valid without spec deltas." + +## 4. Tests +- [ ] 4.1 Add test: proposal-only change passes validation without deltas +- [ ] 4.2 Add test: specs present but zero parsed deltas → ERROR +- [ ] 4.3 Add test: specs present with proper deltas → valid + diff --git a/src/core/templates/readme-template.ts b/src/core/templates/readme-template.ts index 4e5cbd23..aa78424d 100644 --- a/src/core/templates/readme-template.ts +++ b/src/core/templates/readme-template.ts @@ -256,6 +256,19 @@ Every requirement MUST have at least one scenario. 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//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