diff --git a/README.md b/README.md index 7b6c7354..ef0f4187 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ See the full comparison in [How OpenSpec Compares](#how-openspec-compares). ▼ ┌────────────────────┐ │ Review & Align │ -│ (edit specs/tasks) │◀──── feedback loop ──────┐ +│ (edit specs/tasks) │◀───── refine loop ──────┐ └────────┬───────────┘ │ │ approved plan │ ▼ │ @@ -78,7 +78,8 @@ See the full comparison in [How OpenSpec Compares](#how-openspec-compares). 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. +4. If apply feedback requires changes, refine proposal artifacts and re-approve before continuing. +5. Archive the change to merge the approved updates back into the source-of-truth specs. ``` ## Getting Started @@ -90,30 +91,30 @@ See the full comparison in [How OpenSpec Compares](#how-openspec-compares). 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`. +| Tool | Commands | +| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Amazon Q Developer** | `@openspec-proposal`, `@openspec-apply`, `@openspec-refine`, `@openspec-archive` (`.amazonq/prompts/`) | +| **Antigravity** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (`.agent/workflows/`) | +| **Auggie (Augment CLI)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (`.augment/commands/`) | +| **Claude Code** | `/openspec:proposal`, `/openspec:apply`, `/openspec:refine`, `/openspec:archive` | +| **Cline** | Workflows in `.clinerules/workflows/` directory (`.clinerules/workflows/openspec-*.md`, includes refine) | +| **CodeBuddy Code (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:refine`, `/openspec:archive` (`.codebuddy/commands/`) — see [docs](https://www.codebuddy.ai/cli) | +| **Codex** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (global: `~/.codex/prompts`, auto-installed) | +| **CoStrict** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (`.cospec/openspec/commands/`) — see [docs](https://costrict.ai) | +| **Crush** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (`.crush/commands/openspec/`) | +| **Cursor** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` | +| **Factory Droid** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (`.factory/commands/`) | +| **Gemini CLI** | `/openspec:proposal`, `/openspec:apply`, `/openspec:refine`, `/openspec:archive` (`.gemini/commands/openspec/`) | +| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (`.github/prompts/`) | +| **iFlow (iflow-cli)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (`.iflow/commands/`) | +| **Kilo Code** | `/openspec-proposal.md`, `/openspec-apply.md`, `/openspec-refine.md`, `/openspec-archive.md` (`.kilocode/workflows/`) | +| **OpenCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` | +| **Qoder (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:refine`, `/openspec:archive` (`.qoder/commands/openspec/`) — see [docs](https://qoder.com/cli) | +| **Qwen Code** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (`.qwen/commands/`) | +| **RooCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/openspec-archive` (`.roo/commands/`) | +| **Windsurf** | `/openspec-proposal`, `/openspec-apply`, `/openspec-refine`, `/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`, `/openspec-refine.md`, or `/openspec-archive.md`. @@ -122,8 +123,8 @@ Kilo Code discovers team workflows automatically. Save the generated files under 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 | -|-------| +| Tools | +| -------------------- | | Amp • Jules • Others | @@ -202,13 +203,14 @@ $ 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: +#### 3. Refine the Proposal (Optional) +Iterate on the proposal artifacts if apply feedback requires changes: ```text You: Can you add acceptance criteria for the role and team filters? + (Shortcut for tools with slash commands: /openspec:refine add-profile-filters Add acceptance criteria for role and team filters) -AI: I'll update the spec delta with scenarios for role and team filters. +AI: I'll update the proposal artifacts with scenarios for role and team filters. *Edits openspec/changes/add-profile-filters/specs/profile/spec.md and tasks.md.* ``` @@ -243,7 +245,7 @@ Or run the command yourself in terminal: $ 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". +**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", "refine the OpenSpec change", "apply the OpenSpec change", or "archive the change". ## Command Reference diff --git a/openspec/changes/archive/2025-12-19-add-refine-command/proposal.md b/openspec/changes/archive/2025-12-19-add-refine-command/proposal.md new file mode 100644 index 00000000..4af06d4c --- /dev/null +++ b/openspec/changes/archive/2025-12-19-add-refine-command/proposal.md @@ -0,0 +1,31 @@ +## Why +Teams often need to adjust an approved change after an apply pass produces unexpected results. Today that forces either a brand-new proposal or ad-hoc instructions outside the OpenSpec workflow. A dedicated refine command keeps adjustments inside the workflow while enforcing re-approval and leaving code changes to apply. + +## What Changes +**Slash command templates** +- From: Only `proposal`, `apply`, and `archive` shared templates exist. +- To: Add a `refine` shared template that updates proposal/design/tasks/spec deltas only, runs strict validation, and stops for re-approval. +- Reason: Provide a first-class refinement loop without mixing in code changes. +- Impact: New `openspec-refine` command body and template entry. + +**Init scaffolding** +- From: `openspec init` scaffolds proposal/apply/archive command files. +- To: Scaffold refine command files alongside the existing three for every supported tool. +- Reason: Make refine available wherever slash commands are generated. +- Impact: One additional command file per tool. + +**Update behavior** +- From: `openspec update` refreshes only proposal/apply/archive command files. +- To: Refresh refine files when they already exist, without creating missing ones. +- Reason: Keep refine guidance current while preserving update semantics. +- Impact: Update covers refine templates too. + +**Agent instructions** +- From: The workflow guidance focuses on proposal → apply → archive only. +- To: Add refine guidance for post-apply adjustments, including no code changes, re-approval gating, and out-of-scope handling. +- Reason: Keep the documented workflow aligned with the new command. +- Impact: Updated `openspec/AGENTS.md` content. + +## Impact +- Affected specs: cli-init, cli-update, docs-agent-instructions +- Affected code: src/core/templates/slash-command-templates.ts, src/core/configurators/slash/*, src/core/templates/agents-template.ts diff --git a/openspec/changes/archive/2025-12-19-add-refine-command/specs/cli-init/spec.md b/openspec/changes/archive/2025-12-19-add-refine-command/specs/cli-init/spec.md new file mode 100644 index 00000000..61012dfd --- /dev/null +++ b/openspec/changes/archive/2025-12-19-add-refine-command/specs/cli-init/spec.md @@ -0,0 +1,97 @@ +## 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 Claude Code +- **WHEN** the user selects Claude Code during initialization +- **THEN** create `.claude/commands/openspec/proposal.md`, `.claude/commands/openspec/apply.md`, `.claude/commands/openspec/refine.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`, `.codebuddy/commands/openspec/refine.md`, and `.codebuddy/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 Cline +- **WHEN** the user selects Cline during initialization +- **THEN** create `.clinerules/openspec-proposal.md`, `.clinerules/openspec-apply.md`, `.clinerules/openspec-refine.md`, and `.clinerules/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`, `.crush/commands/openspec/refine.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`, `.cursor/commands/openspec-refine.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`, `.factory/commands/openspec-refine.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`, `.opencode/commands/openspec-refine.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`, `.windsurf/workflows/openspec-refine.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`, `.kilocode/workflows/openspec-refine.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`, `~/.codex/prompts/openspec-refine.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 + +#### Scenario: Generating slash commands for GitHub Copilot +- **WHEN** the user selects GitHub Copilot during initialization +- **THEN** create `.github/prompts/openspec-proposal.prompt.md`, `.github/prompts/openspec-apply.prompt.md`, `.github/prompts/openspec-refine.prompt.md`, and `.github/prompts/openspec-archive.prompt.md` +- **AND** populate each file with YAML frontmatter containing a `description` field that summarizes the workflow stage +- **AND** include `$ARGUMENTS` placeholder to capture user input +- **AND** wrap the shared template body with OpenSpec markers so `openspec update` can refresh the content +- **AND** each template includes instructions for the relevant OpenSpec workflow stage + +#### Scenario: Generating slash commands for Gemini CLI +- **WHEN** the user selects Gemini CLI during initialization +- **THEN** create `.gemini/commands/openspec/proposal.toml`, `.gemini/commands/openspec/apply.toml`, `.gemini/commands/openspec/refine.toml`, and `.gemini/commands/openspec/archive.toml` +- **AND** populate each file as TOML that sets a stage-specific `description = ""` and a multi-line `prompt = """` block with the shared OpenSpec template +- **AND** wrap the OpenSpec managed markers (`` / ``) inside the `prompt` value so `openspec update` can safely refresh the body between markers without touching the TOML framing +- **AND** ensure the slash-command copy matches the existing proposal/apply/refine/archive templates used by other tools + +#### Scenario: Generating slash commands for iFlow CLI +- **WHEN** the user selects iFlow CLI during initialization +- **THEN** create `.iflow/commands/openspec-proposal.md`, `.iflow/commands/openspec-apply.md`, `.iflow/commands/openspec-refine.md`, and `.iflow/commands/openspec-archive.md` +- **AND** populate each file from shared templates so command text matches other tools +- **AND** include YAML frontmatter with `name`, `id`, `category`, and `description` fields for each command +- **AND** wrap the generated content in OpenSpec managed markers so `openspec update` can safely refresh the commands +- **AND** each template includes instructions for the relevant OpenSpec workflow stage + +#### Scenario: Generating slash commands for RooCode +- **WHEN** the user selects RooCode during initialization +- **THEN** create `.roo/commands/openspec-proposal.md`, `.roo/commands/openspec-apply.md`, `.roo/commands/openspec-refine.md`, and `.roo/commands/openspec-archive.md` +- **AND** populate each file from shared templates so command text matches other tools +- **AND** include simple Markdown headings (e.g., `# OpenSpec: Proposal`) without YAML frontmatter +- **AND** wrap the generated content in OpenSpec managed markers where applicable so `openspec update` can safely refresh the commands +- **AND** each template includes instructions for the relevant OpenSpec workflow stage diff --git a/openspec/changes/archive/2025-12-19-add-refine-command/specs/cli-update/spec.md b/openspec/changes/archive/2025-12-19-add-refine-command/specs/cli-update/spec.md new file mode 100644 index 00000000..e90481cf --- /dev/null +++ b/openspec/changes/archive/2025-12-19-add-refine-command/specs/cli-update/spec.md @@ -0,0 +1,85 @@ +## 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 Claude Code +- **WHEN** `.claude/commands/openspec/` contains `proposal.md`, `apply.md`, `refine.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`, `refine.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 Cline +- **WHEN** `.clinerules/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.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`, `openspec/refine.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`, `openspec-refine.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`, `openspec-refine.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`, `openspec-refine.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`, `openspec-refine.md`, and `openspec-archive.md` +- **THEN** refresh each file using shared templates wrapped in OpenSpec markers +- **AND** ensure templates include instructions for the relevant workflow stage +- **AND** skip creating missing files (the update command only refreshes what already exists) + +#### Scenario: Updating slash commands for Kilo Code +- **WHEN** `.kilocode/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.md`, and `openspec-archive.md` +- **THEN** refresh each file using shared templates wrapped in OpenSpec markers +- **AND** ensure templates include instructions for the relevant workflow stage +- **AND** skip creating missing files (the update command only refreshes what already exists) + +#### Scenario: Updating slash commands for Codex +- **GIVEN** the global Codex prompt directory contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.md`, and `openspec-archive.md` +- **WHEN** a user runs `openspec update` +- **THEN** refresh each file using the shared slash-command templates (including placeholder guidance) +- **AND** preserve any unmanaged content outside the OpenSpec marker block +- **AND** skip creation when a Codex prompt file is missing + +#### Scenario: Updating slash commands for GitHub Copilot +- **WHEN** `.github/prompts/` contains `openspec-proposal.prompt.md`, `openspec-apply.prompt.md`, `openspec-refine.prompt.md`, and `openspec-archive.prompt.md` +- **THEN** refresh each file using shared templates while preserving the YAML frontmatter +- **AND** update only the OpenSpec-managed block between markers +- **AND** ensure templates include instructions for the relevant workflow stage + +#### Scenario: Updating slash commands for Gemini CLI +- **WHEN** `.gemini/commands/openspec/` contains `proposal.toml`, `apply.toml`, `refine.toml`, and `archive.toml` +- **THEN** refresh the body of each file using the shared proposal/apply/refine/archive templates +- **AND** replace only the content between `` and `` markers inside the `prompt = """` block so the TOML framing (`description`, `prompt`) stays intact +- **AND** skip creating any missing `.toml` files during update; only pre-existing Gemini commands are refreshed + +#### Scenario: Updating slash commands for iFlow CLI +- **WHEN** `.iflow/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.md`, and `openspec-archive.md` +- **THEN** refresh each file using shared templates +- **AND** preserve the YAML frontmatter with `name`, `id`, `category`, and `description` fields +- **AND** update only the OpenSpec-managed block between markers +- **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/archive/2025-12-19-add-refine-command/specs/docs-agent-instructions/spec.md b/openspec/changes/archive/2025-12-19-add-refine-command/specs/docs-agent-instructions/spec.md new file mode 100644 index 00000000..ea88b3b2 --- /dev/null +++ b/openspec/changes/archive/2025-12-19-add-refine-command/specs/docs-agent-instructions/spec.md @@ -0,0 +1,12 @@ +## ADDED Requirements +### Requirement: Refine Workflow Guidance +The AI instructions SHALL describe how to refine an existing change proposal after apply feedback without making code edits. + +#### Scenario: Documenting refine usage +- **WHEN** `openspec/AGENTS.md` is generated or updated +- **THEN** include guidance to refine proposal artifacts only when the user requests adjustments after apply feedback +- **AND** specify that refinement updates proposal/design/tasks/spec deltas only +- **AND** state that refine makes no code changes +- **AND** require an explicit re-approval before running apply again +- **AND** instruct that scope expansions should stop and recommend creating a new change +- **AND** include the step to run `openspec validate --strict` after refining diff --git a/openspec/changes/archive/2025-12-19-add-refine-command/tasks.md b/openspec/changes/archive/2025-12-19-add-refine-command/tasks.md new file mode 100644 index 00000000..fe58dd24 --- /dev/null +++ b/openspec/changes/archive/2025-12-19-add-refine-command/tasks.md @@ -0,0 +1,15 @@ +## 1. Templates and command registry +- [x] 1.1 Add `refine` to the slash command IDs and shared bodies in `src/core/templates/slash-command-templates.ts`. +- [x] 1.2 Ensure the refine template enforces no code changes, runs strict validation, and blocks for explicit re-approval. + +## 2. Init/update integration +- [x] 2.1 Update slash command configurators in `src/core/configurators/slash/` to include refine paths and any required frontmatter. +- [x] 2.2 Update init scaffolding so refine files are created alongside proposal/apply/archive for supported tools. +- [x] 2.3 Update update logic to refresh refine files only when they already exist. + +## 3. Documentation +- [x] 3.1 Update `src/core/templates/agents-template.ts` to add refine workflow guidance and out-of-scope handling. +- [x] 3.2 Confirm generated agent instructions include refine guidance without changing the overall format. + +## 4. Validation +- [x] 4.1 Run `openspec validate add-refine-command --strict`. diff --git a/openspec/specs/cli-init/spec.md b/openspec/specs/cli-init/spec.md index 10866f39..39b271fd 100644 --- a/openspec/specs/cli-init/spec.md +++ b/openspec/specs/cli-init/spec.md @@ -174,70 +174,70 @@ The init command SHALL generate slash command files for supported editors using #### 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` +- **THEN** create `.claude/commands/openspec/proposal.md`, `.claude/commands/openspec/apply.md`, `.claude/commands/openspec/refine.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` +- **THEN** create `.codebuddy/commands/openspec/proposal.md`, `.codebuddy/commands/openspec/apply.md`, `.codebuddy/commands/openspec/refine.md`, and `.codebuddy/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 Cline - **WHEN** the user selects Cline during initialization -- **THEN** create `.clinerules/openspec-proposal.md`, `.clinerules/openspec-apply.md`, and `.clinerules/openspec-archive.md` +- **THEN** create `.clinerules/openspec-proposal.md`, `.clinerules/openspec-apply.md`, `.clinerules/openspec-refine.md`, and `.clinerules/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` +- **THEN** create `.crush/commands/openspec/proposal.md`, `.crush/commands/openspec/apply.md`, `.crush/commands/openspec/refine.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` +- **THEN** create `.cursor/commands/openspec-proposal.md`, `.cursor/commands/openspec-apply.md`, `.cursor/commands/openspec-refine.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` +- **THEN** create `.factory/commands/openspec-proposal.md`, `.factory/commands/openspec-apply.md`, `.factory/commands/openspec-refine.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` +- **THEN** create `.opencode/commands/openspec-proposal.md`, `.opencode/commands/openspec-apply.md`, `.opencode/commands/openspec-refine.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` +- **THEN** create `.windsurf/workflows/openspec-proposal.md`, `.windsurf/workflows/openspec-apply.md`, `.windsurf/workflows/openspec-refine.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` +- **THEN** create `.kilocode/workflows/openspec-proposal.md`, `.kilocode/workflows/openspec-apply.md`, `.kilocode/workflows/openspec-refine.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) +- **THEN** create global prompt files at `~/.codex/prompts/openspec-proposal.md`, `~/.codex/prompts/openspec-apply.md`, `~/.codex/prompts/openspec-refine.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 #### Scenario: Generating slash commands for GitHub Copilot - **WHEN** the user selects GitHub Copilot during initialization -- **THEN** create `.github/prompts/openspec-proposal.prompt.md`, `.github/prompts/openspec-apply.prompt.md`, and `.github/prompts/openspec-archive.prompt.md` +- **THEN** create `.github/prompts/openspec-proposal.prompt.md`, `.github/prompts/openspec-apply.prompt.md`, `.github/prompts/openspec-refine.prompt.md`, and `.github/prompts/openspec-archive.prompt.md` - **AND** populate each file with YAML frontmatter containing a `description` field that summarizes the workflow stage - **AND** include `$ARGUMENTS` placeholder to capture user input - **AND** wrap the shared template body with OpenSpec markers so `openspec update` can refresh the content @@ -245,14 +245,14 @@ The init command SHALL generate slash command files for supported editors using #### Scenario: Generating slash commands for Gemini CLI - **WHEN** the user selects Gemini CLI during initialization -- **THEN** create `.gemini/commands/openspec/proposal.toml`, `.gemini/commands/openspec/apply.toml`, and `.gemini/commands/openspec/archive.toml` +- **THEN** create `.gemini/commands/openspec/proposal.toml`, `.gemini/commands/openspec/apply.toml`, `.gemini/commands/openspec/refine.toml`, and `.gemini/commands/openspec/archive.toml` - **AND** populate each file as TOML that sets a stage-specific `description = ""` and a multi-line `prompt = """` block with the shared OpenSpec template - **AND** wrap the OpenSpec managed markers (`` / ``) inside the `prompt` value so `openspec update` can safely refresh the body between markers without touching the TOML framing -- **AND** ensure the slash-command copy matches the existing proposal/apply/archive templates used by other tools +- **AND** ensure the slash-command copy matches the existing proposal/apply/refine/archive templates used by other tools #### Scenario: Generating slash commands for iFlow CLI - **WHEN** the user selects iFlow CLI during initialization -- **THEN** create `.iflow/commands/openspec-proposal.md`, `.iflow/commands/openspec-apply.md`, and `.iflow/commands/openspec-archive.md` +- **THEN** create `.iflow/commands/openspec-proposal.md`, `.iflow/commands/openspec-apply.md`, `.iflow/commands/openspec-refine.md`, and `.iflow/commands/openspec-archive.md` - **AND** populate each file from shared templates so command text matches other tools - **AND** include YAML frontmatter with `name`, `id`, `category`, and `description` fields for each command - **AND** wrap the generated content in OpenSpec managed markers so `openspec update` can safely refresh the commands @@ -260,7 +260,7 @@ The init command SHALL generate slash command files for supported editors using #### Scenario: Generating slash commands for RooCode - **WHEN** the user selects RooCode during initialization -- **THEN** create `.roo/commands/openspec-proposal.md`, `.roo/commands/openspec-apply.md`, and `.roo/commands/openspec-archive.md` +- **THEN** create `.roo/commands/openspec-proposal.md`, `.roo/commands/openspec-apply.md`, `.roo/commands/openspec-refine.md`, and `.roo/commands/openspec-archive.md` - **AND** populate each file from shared templates so command text matches other tools - **AND** include simple Markdown headings (e.g., `# OpenSpec: Proposal`) without YAML frontmatter - **AND** wrap the generated content in OpenSpec managed markers where applicable so `openspec update` can safely refresh the commands diff --git a/openspec/specs/cli-update/spec.md b/openspec/specs/cli-update/spec.md index d181d7d0..5b81a87c 100644 --- a/openspec/specs/cli-update/spec.md +++ b/openspec/specs/cli-update/spec.md @@ -53,78 +53,78 @@ The update command SHALL always update the core OpenSpec files and display an AS 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 Claude Code -- **WHEN** `.claude/commands/openspec/` contains `proposal.md`, `apply.md`, and `archive.md` +- **WHEN** `.claude/commands/openspec/` contains `proposal.md`, `apply.md`, `refine.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` +- **WHEN** `.codebuddy/commands/openspec/` contains `proposal.md`, `apply.md`, `refine.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 Cline -- **WHEN** `.clinerules/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md` +- **WHEN** `.clinerules/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.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` +- **WHEN** `.crush/commands/` contains `openspec/proposal.md`, `openspec/apply.md`, `openspec/refine.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` +- **WHEN** `.cursor/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.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` +- **WHEN** `.factory/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.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` +- **WHEN** `.opencode/command/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.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` +- **WHEN** `.windsurf/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.md`, and `openspec-archive.md` - **THEN** refresh each file using shared templates wrapped in OpenSpec markers - **AND** ensure templates include instructions for the relevant workflow stage - **AND** skip creating missing files (the update command only refreshes what already exists) #### Scenario: Updating slash commands for Kilo Code -- **WHEN** `.kilocode/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md` +- **WHEN** `.kilocode/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.md`, and `openspec-archive.md` - **THEN** refresh each file using shared templates wrapped in OpenSpec markers - **AND** ensure templates include instructions for the relevant workflow stage - **AND** skip creating missing files (the update command only refreshes what already exists) #### Scenario: Updating slash commands for Codex -- **GIVEN** the global Codex prompt directory contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md` +- **GIVEN** the global Codex prompt directory contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.md`, and `openspec-archive.md` - **WHEN** a user runs `openspec update` - **THEN** refresh each file using the shared slash-command templates (including placeholder guidance) - **AND** preserve any unmanaged content outside the OpenSpec marker block - **AND** skip creation when a Codex prompt file is missing #### Scenario: Updating slash commands for GitHub Copilot -- **WHEN** `.github/prompts/` contains `openspec-proposal.prompt.md`, `openspec-apply.prompt.md`, and `openspec-archive.prompt.md` +- **WHEN** `.github/prompts/` contains `openspec-proposal.prompt.md`, `openspec-apply.prompt.md`, `openspec-refine.prompt.md`, and `openspec-archive.prompt.md` - **THEN** refresh each file using shared templates while preserving the YAML frontmatter - **AND** update only the OpenSpec-managed block between markers - **AND** ensure templates include instructions for the relevant workflow stage #### Scenario: Updating slash commands for Gemini CLI -- **WHEN** `.gemini/commands/openspec/` contains `proposal.toml`, `apply.toml`, and `archive.toml` -- **THEN** refresh the body of each file using the shared proposal/apply/archive templates +- **WHEN** `.gemini/commands/openspec/` contains `proposal.toml`, `apply.toml`, `refine.toml`, and `archive.toml` +- **THEN** refresh the body of each file using the shared proposal/apply/refine/archive templates - **AND** replace only the content between `` and `` markers inside the `prompt = """` block so the TOML framing (`description`, `prompt`) stays intact - **AND** skip creating any missing `.toml` files during update; only pre-existing Gemini commands are refreshed #### Scenario: Updating slash commands for iFlow CLI -- **WHEN** `.iflow/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md` +- **WHEN** `.iflow/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, `openspec-refine.md`, and `openspec-archive.md` - **THEN** refresh each file using shared templates - **AND** preserve the YAML frontmatter with `name`, `id`, `category`, and `description` fields - **AND** update only the OpenSpec-managed block between markers diff --git a/openspec/specs/docs-agent-instructions/spec.md b/openspec/specs/docs-agent-instructions/spec.md index 9d0f88be..7e213c23 100644 --- a/openspec/specs/docs-agent-instructions/spec.md +++ b/openspec/specs/docs-agent-instructions/spec.md @@ -36,3 +36,15 @@ The documentation SHALL separate beginner essentials from advanced topics so new - **AND** move advanced topics (multi-capability changes, archiving details, tooling deep dives) into clearly labeled later sections - **AND** provide anchor links from the quick-reference to those advanced sections +### Requirement: Refine Workflow Guidance +The AI instructions SHALL describe how to refine an existing change proposal after apply feedback without making code edits. + +#### Scenario: Documenting refine usage +- **WHEN** `openspec/AGENTS.md` is generated or updated +- **THEN** include guidance to refine proposal artifacts only when the user requests adjustments after apply feedback +- **AND** specify that refinement updates proposal/design/tasks/spec deltas only +- **AND** state that refine makes no code changes +- **AND** require an explicit re-approval before running apply again +- **AND** instruct that scope expansions should stop and recommend creating a new change +- **AND** include the step to run `openspec validate --strict` after refining + diff --git a/src/core/configurators/slash/amazon-q.ts b/src/core/configurators/slash/amazon-q.ts index e33e399f..0ab515d0 100644 --- a/src/core/configurators/slash/amazon-q.ts +++ b/src/core/configurators/slash/amazon-q.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.amazonq/prompts/openspec-proposal.md', apply: '.amazonq/prompts/openspec-apply.md', + refine: '.amazonq/prompts/openspec-refine.md', archive: '.amazonq/prompts/openspec-archive.md' }; @@ -26,6 +27,15 @@ The user wants to apply the following change. Use the openspec instructions to i $ARGUMENTS `, + refine: `--- +description: Refine an approved OpenSpec change without editing code. +--- + +The user wants to refine an OpenSpec change. Use the change ID and adjustment request below to update proposal details and spec deltas without editing code. + + + $ARGUMENTS +`, archive: `--- description: Archive a deployed OpenSpec change and update specs. --- @@ -48,4 +58,4 @@ export class AmazonQSlashCommandConfigurator extends SlashCommandConfigurator { protected getFrontmatter(id: SlashCommandId): string { return FRONTMATTER[id]; } -} \ No newline at end of file +} diff --git a/src/core/configurators/slash/antigravity.ts b/src/core/configurators/slash/antigravity.ts index f291f2a0..259e97d0 100644 --- a/src/core/configurators/slash/antigravity.ts +++ b/src/core/configurators/slash/antigravity.ts @@ -4,12 +4,14 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.agent/workflows/openspec-proposal.md', apply: '.agent/workflows/openspec-apply.md', + refine: '.agent/workflows/openspec-refine.md', archive: '.agent/workflows/openspec-archive.md' }; const DESCRIPTIONS: Record = { proposal: 'Scaffold a new OpenSpec change and validate strictly.', apply: 'Implement an approved OpenSpec change and keep tasks in sync.', + refine: 'Refine an approved OpenSpec change without editing code.', archive: 'Archive a deployed OpenSpec change and update specs.' }; diff --git a/src/core/configurators/slash/auggie.ts b/src/core/configurators/slash/auggie.ts index 677bc420..2c0195d3 100644 --- a/src/core/configurators/slash/auggie.ts +++ b/src/core/configurators/slash/auggie.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.augment/commands/openspec-proposal.md', apply: '.augment/commands/openspec-apply.md', + refine: '.augment/commands/openspec-refine.md', archive: '.augment/commands/openspec-archive.md' }; @@ -15,6 +16,10 @@ argument-hint: feature description or request apply: `--- description: Implement an approved OpenSpec change and keep tasks in sync. argument-hint: change-id +---`, + refine: `--- +description: Refine an approved OpenSpec change without editing code. +argument-hint: change-id and refinement notes ---`, archive: `--- description: Archive a deployed OpenSpec change and update specs. diff --git a/src/core/configurators/slash/base.ts b/src/core/configurators/slash/base.ts index beffd845..c609fa61 100644 --- a/src/core/configurators/slash/base.ts +++ b/src/core/configurators/slash/base.ts @@ -8,7 +8,7 @@ export interface SlashCommandTarget { kind: 'slash'; } -const ALL_COMMANDS: SlashCommandId[] = ['proposal', 'apply', 'archive']; +const ALL_COMMANDS: SlashCommandId[] = ['proposal', 'apply', 'refine', 'archive']; export abstract class SlashCommandConfigurator { abstract readonly toolId: string; diff --git a/src/core/configurators/slash/claude.ts b/src/core/configurators/slash/claude.ts index 012f661e..dad9fd33 100644 --- a/src/core/configurators/slash/claude.ts +++ b/src/core/configurators/slash/claude.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.claude/commands/openspec/proposal.md', apply: '.claude/commands/openspec/apply.md', + refine: '.claude/commands/openspec/refine.md', archive: '.claude/commands/openspec/archive.md' }; @@ -19,6 +20,12 @@ name: OpenSpec: Apply description: Implement an approved OpenSpec change and keep tasks in sync. category: OpenSpec tags: [openspec, apply] +---`, + refine: `--- +name: OpenSpec: Refine +description: Refine an approved OpenSpec change without editing code. +category: OpenSpec +tags: [openspec, refine] ---`, archive: `--- name: OpenSpec: Archive diff --git a/src/core/configurators/slash/cline.ts b/src/core/configurators/slash/cline.ts index 1c7d8c5c..4191aeb1 100644 --- a/src/core/configurators/slash/cline.ts +++ b/src/core/configurators/slash/cline.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.clinerules/workflows/openspec-proposal.md', apply: '.clinerules/workflows/openspec-apply.md', + refine: '.clinerules/workflows/openspec-refine.md', archive: '.clinerules/workflows/openspec-archive.md' }; @@ -19,6 +20,7 @@ export class ClineSlashCommandConfigurator extends SlashCommandConfigurator { const descriptions: Record = { proposal: 'Scaffold a new OpenSpec change and validate strictly.', apply: 'Implement an approved OpenSpec change and keep tasks in sync.', + refine: 'Refine an approved OpenSpec change without editing code.', archive: 'Archive a deployed OpenSpec change and update specs.' }; const description = descriptions[id]; diff --git a/src/core/configurators/slash/codebuddy.ts b/src/core/configurators/slash/codebuddy.ts index 94a098d7..16307951 100644 --- a/src/core/configurators/slash/codebuddy.ts +++ b/src/core/configurators/slash/codebuddy.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.codebuddy/commands/openspec/proposal.md', apply: '.codebuddy/commands/openspec/apply.md', + refine: '.codebuddy/commands/openspec/refine.md', archive: '.codebuddy/commands/openspec/archive.md' }; @@ -19,6 +20,12 @@ name: OpenSpec: Apply description: Implement an approved OpenSpec change and keep tasks in sync. category: OpenSpec tags: [openspec, apply] +---`, + refine: `--- +name: OpenSpec: Refine +description: Refine an approved OpenSpec change without editing code. +category: OpenSpec +tags: [openspec, refine] ---`, archive: `--- name: OpenSpec: Archive diff --git a/src/core/configurators/slash/codex.ts b/src/core/configurators/slash/codex.ts index d9a2d2c0..1cc1347a 100644 --- a/src/core/configurators/slash/codex.ts +++ b/src/core/configurators/slash/codex.ts @@ -9,6 +9,7 @@ import { OPENSPEC_MARKERS } from "../../config.js"; const FILE_PATHS: Record = { proposal: ".codex/prompts/openspec-proposal.md", apply: ".codex/prompts/openspec-apply.md", + refine: ".codex/prompts/openspec-refine.md", archive: ".codex/prompts/openspec-archive.md", }; @@ -35,6 +36,12 @@ description: Implement an approved OpenSpec change and keep tasks in sync. argument-hint: change-id --- +$ARGUMENTS`, + refine: `--- +description: Refine an approved OpenSpec change without editing code. +argument-hint: change-id and refinement notes +--- + $ARGUMENTS`, archive: `--- description: Archive a deployed OpenSpec change and update specs. diff --git a/src/core/configurators/slash/costrict.ts b/src/core/configurators/slash/costrict.ts index 0ba92c64..9547419c 100644 --- a/src/core/configurators/slash/costrict.ts +++ b/src/core/configurators/slash/costrict.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS = { proposal: '.cospec/openspec/commands/openspec-proposal.md', apply: '.cospec/openspec/commands/openspec-apply.md', + refine: '.cospec/openspec/commands/openspec-refine.md', archive: '.cospec/openspec/commands/openspec-archive.md', } as const satisfies Record; @@ -15,6 +16,10 @@ argument-hint: feature description or request apply: `--- description: "Implement an approved OpenSpec change and keep tasks in sync." argument-hint: change-id +---`, + refine: `--- +description: "Refine an approved OpenSpec change without editing code." +argument-hint: change-id and refinement notes ---`, archive: `--- description: "Archive a deployed OpenSpec change and update specs." @@ -33,4 +38,4 @@ export class CostrictSlashCommandConfigurator extends SlashCommandConfigurator { protected getFrontmatter(id: SlashCommandId): string | undefined { return FRONTMATTER[id]; } -} \ No newline at end of file +} diff --git a/src/core/configurators/slash/crush.ts b/src/core/configurators/slash/crush.ts index c4ae1f1f..51a46e87 100644 --- a/src/core/configurators/slash/crush.ts +++ b/src/core/configurators/slash/crush.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.crush/commands/openspec/proposal.md', apply: '.crush/commands/openspec/apply.md', + refine: '.crush/commands/openspec/refine.md', archive: '.crush/commands/openspec/archive.md' }; @@ -19,6 +20,12 @@ name: OpenSpec: Apply description: Implement an approved OpenSpec change and keep tasks in sync. category: OpenSpec tags: [openspec, apply] +---`, + refine: `--- +name: OpenSpec: Refine +description: Refine an approved OpenSpec change without editing code. +category: OpenSpec +tags: [openspec, refine] ---`, archive: `--- name: OpenSpec: Archive @@ -39,4 +46,4 @@ export class CrushSlashCommandConfigurator extends SlashCommandConfigurator { protected getFrontmatter(id: SlashCommandId): string { return FRONTMATTER[id]; } -} \ No newline at end of file +} diff --git a/src/core/configurators/slash/cursor.ts b/src/core/configurators/slash/cursor.ts index 58b07cef..48cf04a5 100644 --- a/src/core/configurators/slash/cursor.ts +++ b/src/core/configurators/slash/cursor.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.cursor/commands/openspec-proposal.md', apply: '.cursor/commands/openspec-apply.md', + refine: '.cursor/commands/openspec-refine.md', archive: '.cursor/commands/openspec-archive.md' }; @@ -19,6 +20,12 @@ name: /openspec-apply id: openspec-apply category: OpenSpec description: Implement an approved OpenSpec change and keep tasks in sync. +---`, + refine: `--- +name: /openspec-refine +id: openspec-refine +category: OpenSpec +description: Refine an approved OpenSpec change without editing code. ---`, archive: `--- name: /openspec-archive diff --git a/src/core/configurators/slash/factory.ts b/src/core/configurators/slash/factory.ts index 490a0ccd..b4bd8b87 100644 --- a/src/core/configurators/slash/factory.ts +++ b/src/core/configurators/slash/factory.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.factory/commands/openspec-proposal.md', apply: '.factory/commands/openspec-apply.md', + refine: '.factory/commands/openspec-refine.md', archive: '.factory/commands/openspec-archive.md' }; @@ -15,6 +16,10 @@ argument-hint: request or feature description apply: `--- description: Implement an approved OpenSpec change and keep tasks in sync. argument-hint: change-id +---`, + refine: `--- +description: Refine an approved OpenSpec change without editing code. +argument-hint: change-id and refinement notes ---`, archive: `--- description: Archive a deployed OpenSpec change and update specs. diff --git a/src/core/configurators/slash/gemini.ts b/src/core/configurators/slash/gemini.ts index 91bacc3e..40886d6e 100644 --- a/src/core/configurators/slash/gemini.ts +++ b/src/core/configurators/slash/gemini.ts @@ -4,12 +4,14 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.gemini/commands/openspec/proposal.toml', apply: '.gemini/commands/openspec/apply.toml', + refine: '.gemini/commands/openspec/refine.toml', archive: '.gemini/commands/openspec/archive.toml' }; const DESCRIPTIONS: Record = { proposal: 'Scaffold a new OpenSpec change and validate strictly.', apply: 'Implement an approved OpenSpec change and keep tasks in sync.', + refine: 'Refine an approved OpenSpec change without editing code.', archive: 'Archive a deployed OpenSpec change and update specs.' }; diff --git a/src/core/configurators/slash/github-copilot.ts b/src/core/configurators/slash/github-copilot.ts index d7792643..2d87eab5 100644 --- a/src/core/configurators/slash/github-copilot.ts +++ b/src/core/configurators/slash/github-copilot.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.github/prompts/openspec-proposal.prompt.md', apply: '.github/prompts/openspec-apply.prompt.md', + refine: '.github/prompts/openspec-refine.prompt.md', archive: '.github/prompts/openspec-archive.prompt.md' }; @@ -17,6 +18,11 @@ $ARGUMENTS`, description: Implement an approved OpenSpec change and keep tasks in sync. --- +$ARGUMENTS`, + refine: `--- +description: Refine an approved OpenSpec change without editing code. +--- + $ARGUMENTS`, archive: `--- description: Archive a deployed OpenSpec change and update specs. diff --git a/src/core/configurators/slash/iflow.ts b/src/core/configurators/slash/iflow.ts index c7c79618..66e10bc2 100644 --- a/src/core/configurators/slash/iflow.ts +++ b/src/core/configurators/slash/iflow.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.iflow/commands/openspec-proposal.md', apply: '.iflow/commands/openspec-apply.md', + refine: '.iflow/commands/openspec-refine.md', archive: '.iflow/commands/openspec-archive.md' }; @@ -19,6 +20,12 @@ name: /openspec-apply id: openspec-apply category: OpenSpec description: Implement an approved OpenSpec change and keep tasks in sync. +---`, + refine: `--- +name: /openspec-refine +id: openspec-refine +category: OpenSpec +description: Refine an approved OpenSpec change without editing code. ---`, archive: `--- name: /openspec-archive diff --git a/src/core/configurators/slash/kilocode.ts b/src/core/configurators/slash/kilocode.ts index 9717bef7..29003b85 100644 --- a/src/core/configurators/slash/kilocode.ts +++ b/src/core/configurators/slash/kilocode.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from "../../templates/index.js"; const FILE_PATHS: Record = { proposal: ".kilocode/workflows/openspec-proposal.md", apply: ".kilocode/workflows/openspec-apply.md", + refine: ".kilocode/workflows/openspec-refine.md", archive: ".kilocode/workflows/openspec-archive.md" }; diff --git a/src/core/configurators/slash/opencode.ts b/src/core/configurators/slash/opencode.ts index 48d37807..1ec913f0 100644 --- a/src/core/configurators/slash/opencode.ts +++ b/src/core/configurators/slash/opencode.ts @@ -6,6 +6,7 @@ import { OPENSPEC_MARKERS } from "../../config.js"; const FILE_PATHS: Record = { proposal: ".opencode/command/openspec-proposal.md", apply: ".opencode/command/openspec-apply.md", + refine: ".opencode/command/openspec-refine.md", archive: ".opencode/command/openspec-archive.md", }; @@ -25,6 +26,14 @@ The user has requested to implement the following change proposal. Find the chan $ARGUMENTS +`, + refine: `--- +description: Refine an approved OpenSpec change without editing code. +--- +The user has requested to refine the following change proposal. The request may include a change ID and adjustment notes. Update the proposal details and spec deltas without editing code. If you're not sure or if ambiguous, ask for clarification from the user. + + $ARGUMENTS + `, archive: `--- description: Archive a deployed OpenSpec change and update specs. diff --git a/src/core/configurators/slash/qoder.ts b/src/core/configurators/slash/qoder.ts index f147e08c..d765d30b 100644 --- a/src/core/configurators/slash/qoder.ts +++ b/src/core/configurators/slash/qoder.ts @@ -12,6 +12,9 @@ const FILE_PATHS: Record = { // Implement approved changes with task tracking apply: '.qoder/commands/openspec/apply.md', + + // Refine approved changes without editing code + refine: '.qoder/commands/openspec/refine.md', // Archive completed changes and update specs archive: '.qoder/commands/openspec/archive.md' @@ -34,6 +37,12 @@ name: OpenSpec: Apply description: Implement an approved OpenSpec change and keep tasks in sync. category: OpenSpec tags: [openspec, apply] +---`, + refine: `--- +name: OpenSpec: Refine +description: Refine an approved OpenSpec change without editing code. +category: OpenSpec +tags: [openspec, refine] ---`, archive: `--- name: OpenSpec: Archive @@ -47,7 +56,7 @@ tags: [openspec, archive] * Qoder Slash Command Configurator * * Manages OpenSpec slash commands for Qoder AI assistant. - * Creates three workflow commands: proposal, apply, and archive. + * Creates four workflow commands: proposal, apply, refine, and archive. * Uses colon-separated command format (/openspec:proposal). * * @extends {SlashCommandConfigurator} @@ -62,7 +71,7 @@ export class QoderSlashCommandConfigurator extends SlashCommandConfigurator { /** * Get relative file path for a slash command * - * @param {SlashCommandId} id - Command identifier (proposal, apply, or archive) + * @param {SlashCommandId} id - Command identifier (proposal, apply, refine, or archive) * @returns {string} Relative path from project root to command file */ protected getRelativePath(id: SlashCommandId): string { @@ -75,10 +84,10 @@ export class QoderSlashCommandConfigurator extends SlashCommandConfigurator { * Frontmatter defines how the command appears in Qoder's UI, * including display name, description, and categorization. * - * @param {SlashCommandId} id - Command identifier (proposal, apply, or archive) + * @param {SlashCommandId} id - Command identifier (proposal, apply, refine, or archive) * @returns {string} YAML frontmatter block with command metadata */ protected getFrontmatter(id: SlashCommandId): string { return FRONTMATTER[id]; } -} \ No newline at end of file +} diff --git a/src/core/configurators/slash/qwen.ts b/src/core/configurators/slash/qwen.ts index b1f9ebfb..e6f3dffe 100644 --- a/src/core/configurators/slash/qwen.ts +++ b/src/core/configurators/slash/qwen.ts @@ -15,12 +15,14 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.qwen/commands/openspec-proposal.toml', apply: '.qwen/commands/openspec-apply.toml', + refine: '.qwen/commands/openspec-refine.toml', archive: '.qwen/commands/openspec-archive.toml' }; const DESCRIPTIONS: Record = { proposal: 'Scaffold a new OpenSpec change and validate strictly.', apply: 'Implement an approved OpenSpec change and keep tasks in sync.', + refine: 'Refine an approved OpenSpec change without editing code.', archive: 'Archive a deployed OpenSpec change and update specs.' }; @@ -31,6 +33,7 @@ const DESCRIPTIONS: Record = { * The slash commands include: * - /openspec-proposal: Create an OpenSpec change proposal * - /openspec-apply: Apply an approved OpenSpec change + * - /openspec-refine: Refine an approved OpenSpec change without editing code * - /openspec-archive: Archive a deployed OpenSpec change */ export class QwenSlashCommandConfigurator extends TomlSlashCommandConfigurator { @@ -52,4 +55,4 @@ export class QwenSlashCommandConfigurator extends TomlSlashCommandConfigurator { protected getDescription(id: SlashCommandId): string { return DESCRIPTIONS[id]; } -} \ No newline at end of file +} diff --git a/src/core/configurators/slash/roocode.ts b/src/core/configurators/slash/roocode.ts index faf89b41..949bddd6 100644 --- a/src/core/configurators/slash/roocode.ts +++ b/src/core/configurators/slash/roocode.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const NEW_FILE_PATHS: Record = { proposal: '.roo/commands/openspec-proposal.md', apply: '.roo/commands/openspec-apply.md', + refine: '.roo/commands/openspec-refine.md', archive: '.roo/commands/openspec-archive.md' }; @@ -19,6 +20,7 @@ export class RooCodeSlashCommandConfigurator extends SlashCommandConfigurator { const descriptions: Record = { proposal: 'Scaffold a new OpenSpec change and validate strictly.', apply: 'Implement an approved OpenSpec change and keep tasks in sync.', + refine: 'Refine an approved OpenSpec change without editing code.', archive: 'Archive a deployed OpenSpec change and update specs.' }; const description = descriptions[id]; diff --git a/src/core/configurators/slash/windsurf.ts b/src/core/configurators/slash/windsurf.ts index c0542eca..3e540381 100644 --- a/src/core/configurators/slash/windsurf.ts +++ b/src/core/configurators/slash/windsurf.ts @@ -4,6 +4,7 @@ import { SlashCommandId } from '../../templates/index.js'; const FILE_PATHS: Record = { proposal: '.windsurf/workflows/openspec-proposal.md', apply: '.windsurf/workflows/openspec-apply.md', + refine: '.windsurf/workflows/openspec-refine.md', archive: '.windsurf/workflows/openspec-archive.md' }; @@ -19,6 +20,7 @@ export class WindsurfSlashCommandConfigurator extends SlashCommandConfigurator { const descriptions: Record = { proposal: 'Scaffold a new OpenSpec change and validate strictly.', apply: 'Implement an approved OpenSpec change and keep tasks in sync.', + refine: 'Refine an approved OpenSpec change without editing code.', archive: 'Archive a deployed OpenSpec change and update specs.' }; const description = descriptions[id]; diff --git a/src/core/templates/agents-template.ts b/src/core/templates/agents-template.ts index ad6dbdae..19fa1778 100644 --- a/src/core/templates/agents-template.ts +++ b/src/core/templates/agents-template.ts @@ -56,6 +56,14 @@ Track these steps as TODOs and complete them one by one. 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 2.5: Refining Changes +Refine proposal artifacts only when the user requests adjustments after apply feedback; otherwise skip this stage. +1. **Adjust proposal artifacts** - Update only \`proposal.md\`, \`tasks.md\`, \`design.md\` (if needed), and spec deltas under \`changes//specs/\` +2. **No code edits** - Do not change production code during refine +3. **Scope check** - If the request expands beyond the approved change, stop and create a new change proposal +4. **Validate** - Run \`openspec validate --strict\` after updates +5. **Re-approval gate** - Require explicit approval before running apply again + ### Stage 3: Archiving Changes After deployment, create separate PR to: - Move \`changes/[name]/\` → \`changes/archive/YYYY-MM-DD-[name]/\` diff --git a/src/core/templates/slash-command-templates.ts b/src/core/templates/slash-command-templates.ts index be21328a..a1178727 100644 --- a/src/core/templates/slash-command-templates.ts +++ b/src/core/templates/slash-command-templates.ts @@ -1,4 +1,4 @@ -export type SlashCommandId = 'proposal' | 'apply' | 'archive'; +export type SlashCommandId = 'proposal' | 'apply' | 'refine' | 'archive'; const baseGuardrails = `**Guardrails** - Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required. @@ -34,6 +34,25 @@ Track these steps as TODOs and complete them one by one. const applyReferences = `**Reference** - Use \`openspec show --json --deltas-only\` if you need additional context from the proposal while implementing.`; +const refineGuardrails = `${baseGuardrails}\n- Do not edit production code during refine. Only update \`openspec/changes//\` files (proposal, tasks, design, and spec deltas).\n- Use the user's refinement request to drive the updates; ask clarifying questions if the requested adjustments are unclear.\n- If the request expands beyond the approved change scope, stop and recommend creating a new change proposal instead of refining.`; + +const refineSteps = `**Steps** +1. Determine the change ID to refine: + - If this prompt includes a change ID (for example inside a \`\` block), use that value after trimming whitespace. + - If the user provided a \`/openspec-refine \` style input, parse the change ID and keep the remaining text as refinement instructions. + - If the conversation references a change loosely, run \`openspec list\` to surface likely IDs, share the candidates, and confirm the intended change. + - Otherwise, run \`openspec list\` and ask the user to confirm the change ID before proceeding. +2. Capture the refinement request from the prompt or user message; if the desired adjustments are missing or ambiguous, ask clarifying questions before editing. +3. Read \`changes//proposal.md\`, \`design.md\` (if present), \`tasks.md\`, and the spec deltas under \`changes//specs/\` to confirm scope and acceptance criteria. +4. Update only the proposal, design, and spec delta files to reflect the requested adjustments (no code edits). Adjust technical decisions in \`design.md\` when needed. +5. If a spec delta changes, ensure requirements include \`#### Scenario:\` entries. If the requested behavior does not fit existing spec deltas, create additional spec delta files as needed. +6. Add or roll back tasks so the checklist matches the updated implementation plan. +7. Run \`openspec validate --strict\` and fix any issues before reporting back. +8. State clearly that no code changes were made, summarize the refined proposal, and stop to request explicit re-approval before any apply work continues.`; + +const refineReferences = `**Reference** +- Use \`openspec show --json --deltas-only\` to inspect the current change deltas before editing.`; + const archiveSteps = `**Steps** 1. Determine the change ID to archive: - If this prompt already includes a specific change ID (for example inside a \`\` block populated by slash-command arguments), use that value after trimming whitespace. @@ -52,6 +71,7 @@ const archiveReferences = `**Reference** export const slashCommandBodies: Record = { proposal: [proposalGuardrails, proposalSteps, proposalReferences].join('\n\n'), apply: [baseGuardrails, applySteps, applyReferences].join('\n\n'), + refine: [refineGuardrails, refineSteps, refineReferences].join('\n\n'), archive: [baseGuardrails, archiveSteps, archiveReferences].join('\n\n') };