Compare commits

...
Author SHA1 Message Date
Tabish Bidiwale 6fdf4253ae feat: fix design.md as optional, update docs, add schema-aware apply proposal
Changes:
- Fix generateApplyInstructions to treat design.md as optional (not required)
- Update experimental-release-plan.md CLI output to match implementation
- Add openspec-apply-change skill to docs (was missing)
- Fix test flow numbering after adding apply skill verification step
- Add change proposal for making apply instructions schema-aware

The schema-aware proposal introduces an `implementation` block in schema.yaml
to define when a change becomes implementable and how to track progress.
2026-01-05 16:37:32 -08:00
Tabish Bidiwale f608192bff remove test change 2026-01-05 16:00:45 -08:00
Tabish Bidiwale 91aa68abc3 feat: add change proposals for per-change schema metadata
Add two related change proposals for enabling schema selection in the
experimental artifact workflow:

1. add-per-change-schema-metadata: Store schema choice in .openspec.yaml
   per change, enabling auto-detection in workflow commands. Includes
   Zod schema design and delta specs for cli-artifact-workflow.

2. add-agent-schema-selection: Follow-up to update agent skills to
   support dynamic schema selection (depends on metadata change).

Also includes:
- Example .openspec.yaml in add-frontmatter-to-openspec-artifact-files
- Clarify "Modified Capabilities" guidance in proposal template
2026-01-05 15:59:39 -08:00
Tabish Bidiwale f3d5fb150f docs: clarify kebab-case naming in proposal template
Update HTML comments in the Capabilities section to explicitly instruct
using kebab-case identifiers with examples (user-auth, data-export,
api-rate-limiting).
2026-01-05 13:02:11 -08:00
Tabish Bidiwale 141dcb4be6 feat: remove redundant openspec next command
The `next` command was redundant with `status` - both show which artifacts
are ready to create. The status command provides more context (done/ready/blocked)
and is the single source of truth for artifact state.

Changes:
- Remove nextCommand function and CLI registration from artifact-workflow.ts
- Update skill templates to use status instead of next
- Update docs and specs to reflect removal
- Add REMOVED Requirements section to cli-artifact-workflow spec
- Remove 7 tests for the next command

Migration: Use `openspec status --change <id> --json` and filter artifacts
with `status: "ready"` to find artifacts that can be created next.
2026-01-05 12:51:33 -08:00
Tabish Bidiwale 9f58815e06 feat: add Capabilities section to proposal template
Enrich the proposal template to explicitly capture capability discovery:

- Add "Capabilities" section with "New Capabilities" and "Modified
  Capabilities" subsections to proposal template
- Update proposal instruction in schema.yaml to guide agents on
  researching existing specs and listing capabilities
- Update skill instructions with detailed guidance for the Capabilities
  section

This creates a clear contract between proposal and specs phases - each
capability listed in the proposal will need a corresponding spec file.
2026-01-05 12:47:35 -08:00
Tabish Bidiwale 5b22ee5811 docs: mark apply skill implementation steps as complete 2026-01-05 12:42:23 -08:00
Tabish Bidiwale 7832c4dce0 feat: add openspec-apply-change skill for task implementation
Add the apply skill to guide agents through implementing tasks from an
OpenSpec change:

- Add `openspec instructions apply` CLI command that parses tasks.md and
  returns context files, progress tracking, and dynamic instructions
- Add `getApplyChangeSkillTemplate()` to skill-templates.ts with full
  workflow guidance for implementing tasks
- Update artifact-experimental-setup to generate all three skills:
  openspec-new-change, openspec-continue-change, openspec-apply-change

The apply skill supports the fluid "actions on a change" model:
- Can be invoked anytime (if tasks.md exists)
- Handles blocked/ready/all_done states
- Guides agents to pause on issues and suggest artifact updates
- Tracks progress via task checkboxes
2026-01-05 12:39:49 -08:00
Tabish Bidiwale c28020d6d5 fix: update GitHub issues URL to correct repository
Replace placeholder `https://github.com/your-org/openspec/issues`
with correct URL `https://github.com/Fission-AI/OpenSpec/issues` in:
- docs/experimental-release-plan.md
- src/commands/artifact-workflow.ts

Verified against package.json repository.url field.
2026-01-01 00:39:46 +11:00
Tabish Bidiwale 84215c7e6f Merge branch 'main' into add-agent-skills-experimental-workflow 2025-12-31 20:48:53 +11:00
Tabish Bidiwale 5bcdfb8901 feat: add Agent Skills for experimental artifact workflow
Implements Task #3 from the experimental release plan:
- Create skill templates for openspec-new-change and openspec-continue-change
- Add artifact-experimental-setup CLI command
- Generate Agent Skills in .claude/skills/ directory
- Support cross-editor compatibility (Claude Code, Cursor, Windsurf)

Skills follow the Agent Skills specification and provide:
- Natural language invocation by AI assistants
- Step-by-step artifact creation workflow
- Dependency-driven change management
2025-12-31 02:21:25 +11:00
16 changed files with 2137 additions and 176 deletions
+926
View File
@@ -0,0 +1,926 @@
# OpenSpec Experimental Release Plan
This document outlines the plan to release the experimental artifact workflow system for user testing.
## Overview
The goal is to allow users to test the new artifact-driven workflow system alongside the existing OpenSpec commands. This experimental system (`opsx`) provides a more granular, step-by-step approach to creating change artifacts.
## Three Workflow Modes
### 1. Old Workflow (Current Production)
- **Commands**: `/openspec:proposal`, `/openspec:apply`, `/openspec:archive`
- **Behavior**: Hardcoded slash commands that generate all artifacts in one command
- **Status**: Production, unchanged
### 2. New Artifact System - Batch Mode (Future)
- **Commands**: Refactored `/openspec:proposal` using schemas
- **Behavior**: Schema-driven but generates all artifacts at once (like legacy)
- **Status**: Not in scope for this experimental release
- **Note**: This is a future refactor to unify the old system with schemas
### 3. New Artifact System - Granular Mode (Experimental)
- **Commands**: `/opsx:new`, `/opsx:continue`
- **Behavior**: One artifact at a time, dependency-driven, iterative
- **Status**: Target for this experimental release
---
## Work Items
### 1. Rename AWF to OPSX
**Current State:**
- Commands: `/awf:start`, `/awf:continue`
- Files: `.claude/commands/awf/start.md`, `.claude/commands/awf/continue.md`
**Target State:**
- Commands: `/opsx:new`, `/opsx:continue`
- Files: `.claude/commands/opsx/new.md`, `.claude/commands/opsx/continue.md`
**Tasks:**
- [x] Create `.claude/commands/opsx/` directory
- [x] Rename `start.md` → `new.md` and update content
- [x] Copy `continue.md` with updated references
- [x] Update all references from "awf" to "opsx" in command content
- [x] Update frontmatter (name, description) to use "opsx" naming
- [x] Remove `.claude/commands/awf/` directory
**CLI Commands:**
The underlying CLI commands (`openspec status`, `openspec instructions`, etc.) remain unchanged. Only the slash command names change.
---
### 2. Remove WF Skill Files
**Current State:**
- `.claude/commands/wf/start.md` - References non-existent `openspec wf` commands
- `.claude/commands/wf/continue.md` - References non-existent `openspec wf` commands
**Target State:**
- Directory and files removed
**Tasks:**
- [x] Delete `.claude/commands/wf/start.md`
- [x] Delete `.claude/commands/wf/continue.md`
- [x] Delete `.claude/commands/wf/` directory
---
### 3. Add Agent Skills for Experimental Workflow
**Purpose:**
Generate experimental workflow skills using the [Agent Skills](https://agentskills.io/specification) open standard.
**Why Skills Instead of Slash Commands:**
- **Cross-editor compatibility**: Skills work in Claude Code, Cursor, Windsurf, and other compatible editors automatically
- **Simpler implementation**: Single directory (`.claude/skills/`) instead of 18+ editor-specific configurators
- **Standard format**: Open standard with simple YAML frontmatter + markdown
- **User invocation**: Users explicitly invoke skills when they want to use them
**Behavior:**
1. Create `.claude/skills/` directory if it doesn't exist
2. Generate two skills using the Agent Skills specification:
- `openspec-new-change/SKILL.md` - Start a new change with artifact workflow
- `openspec-continue-change/SKILL.md` - Continue working on a change (create next artifact)
3. Skills are added **alongside** existing `/openspec:*` commands (not replacing)
**Supported Editors:**
- Claude Code (native support)
- Cursor (native support via Settings → Rules → Import Settings)
- Windsurf (imports `.claude` configs)
- Cline, Codex, and other Agent Skills-compatible editors
**Tasks:**
- [x] Create skill template content for `openspec-new-change` (based on current opsx:new)
- [x] Create skill template content for `openspec-continue-change` (based on current opsx:continue)
- [x] Add temporary `artifact-experimental-setup` command to CLI
- [x] Implement skill file generation (YAML frontmatter + markdown body)
- [x] Add success message with usage instructions
**Note:** The `artifact-experimental-setup` command is temporary and will be merged into `openspec init` once the experimental workflow is promoted to stable.
**Skill Format:**
Each skill is a directory with a `SKILL.md` file:
```
.claude/skills/
├── openspec-new-change/
│ └── SKILL.md # name, description, instructions
├── openspec-continue-change/
│ └── SKILL.md # name, description, instructions
└── openspec-apply-change/
└── SKILL.md # name, description, instructions
```
**CLI Interface:**
```bash
openspec artifact-experimental-setup
# Output:
# 🧪 Experimental Artifact Workflow Skills Created
#
# ✓ .claude/skills/openspec-new-change/SKILL.md
# ✓ .claude/skills/openspec-continue-change/SKILL.md
# ✓ .claude/skills/openspec-apply-change/SKILL.md
#
# 📖 Usage:
#
# Skills work automatically in compatible editors:
# • Claude Code - Auto-detected, ready to use
# • Cursor - Enable in Settings → Rules → Import Settings
# • Windsurf - Auto-imports from .claude directory
#
# Ask Claude naturally:
# • "I want to start a new OpenSpec change to add <feature>"
# • "Continue working on this change"
#
# Claude will automatically use the appropriate skill.
#
# 💡 This is an experimental feature.
# Feedback welcome at: https://github.com/Fission-AI/OpenSpec/issues
```
**Implementation Notes:**
- Simple file writing: Create directories and write templated `SKILL.md` files (no complex logic)
- Use existing `FileSystemUtils.writeFile()` pattern like slash command configurators
- Template structure: YAML frontmatter + markdown body
- Keep existing `/opsx:*` slash commands for now (manual cleanup later)
- Skills use invocation model (user explicitly asks Claude to use them)
- Skill `description` field guides when Claude suggests using the skill
- Each `SKILL.md` has required fields: `name` (matches directory) and `description`
---
### 4. Update `/opsx:new` Command Content
**Current Behavior (awf:start):**
1. Ask user what they want to build (if no input)
2. Create change directory
3. Show artifact status
4. Show what's ready
5. Get instructions for proposal
6. STOP and wait
**New Behavior (opsx:new):**
Same flow but with updated naming:
- References to "awf" → "opsx"
- References to `/awf:continue` → `/opsx:continue`
- Update frontmatter name/description
**Tasks:**
- [x] Update all "awf" references to "opsx"
- [x] Update command references in prompt text
- [x] Verify CLI commands still work (they use `openspec`, not `awf`)
---
### 5. Update `/opsx:continue` Command Content
**Current Behavior (awf:continue):**
1. Prompt for change selection (if not provided)
2. Check current status
3. Create ONE artifact based on what's ready
4. Show progress and what's unlocked
5. STOP
**New Behavior (opsx:continue):**
Same flow with updated naming.
**Tasks:**
- [x] Update all "awf" references to "opsx"
- [x] Update command references in prompt text
---
### 6. End-to-End Testing
**Objective:**
Run through a complete workflow with Claude using the new skills to create a real feature, validating the entire flow works.
**Test Scenario:**
Use a real OpenSpec feature as the test case (dog-fooding).
**Test Flow:**
1. Run `openspec artifact-experimental-setup` to create skills
2. Verify `.claude/skills/openspec-new-change/SKILL.md` created
3. Verify `.claude/skills/openspec-continue-change/SKILL.md` created
4. Verify `.claude/skills/openspec-apply-change/SKILL.md` created
5. Ask Claude: "I want to start a new OpenSpec change to add feature X"
6. Verify Claude invokes the `openspec-new-change` skill
7. Verify change directory created at `openspec/changes/add-feature-x/`
8. Verify proposal template shown
9. Ask Claude: "Continue working on this change"
10. Verify Claude invokes the `openspec-continue-change` skill
11. Verify `proposal.md` created with content
12. Ask Claude: "Continue" (create specs)
13. Verify `specs/*.md` created
14. Ask Claude: "Continue" (create design)
15. Verify `design.md` created
16. Ask Claude: "Continue" (create tasks)
17. Verify `tasks.md` created
18. Verify status shows 4/4 complete
19. Implement the feature based on tasks
20. Run `/openspec:archive` to archive the change
**Validation Checklist:**
- [ ] `openspec artifact-experimental-setup` creates correct directory structure
- [ ] Skills are auto-detected in Claude Code
- [ ] Skill descriptions trigger appropriate invocations
- [ ] Skills create change directory and show proposal template
- [ ] Skills correctly identify ready artifacts
- [ ] Skills create artifacts with meaningful content
- [ ] Dependency detection works (specs requires proposal, etc.)
- [ ] Progress tracking is accurate
- [ ] Template content is useful and well-structured
- [ ] Error handling works (invalid names, missing changes, etc.)
- [ ] Works with different schemas (spec-driven, tdd)
- [ ] Test in Cursor (Settings → Rules → Import Settings)
**Document Results:**
- Create test log documenting what worked and what didn't
- Note any friction points or confusing UX
- Identify bugs or improvements needed before user release
---
### 7. Documentation for Users
**Create user-facing documentation explaining:**
1. **What is the experimental workflow?**
- A new way to create OpenSpec changes step-by-step using Agent Skills
- One artifact at a time with dependency tracking
- More interactive and iterative than the batch approach
- Works across Claude Code, Cursor, Windsurf, and other compatible editors
2. **How to set up experimental workflow**
```bash
openspec artifact-experimental-setup
```
Note: This is a temporary command that will be integrated into `openspec init` once promoted to stable.
3. **Available skills**
- `openspec-new-change` - Start a new change with artifact workflow
- `openspec-continue-change` - Continue working (create next artifact)
4. **How to use**
- **Claude Code**: Skills are auto-detected, just ask Claude naturally
- "I want to start a new OpenSpec change to add X"
- "Continue working on this change"
- **Cursor**: Enable in Settings → Rules → Import Settings
- **Windsurf**: Auto-imports `.claude` directory
5. **Example workflow**
- Step-by-step walkthrough with natural language interactions
- Show how Claude invokes skills based on user requests
6. **Feedback mechanism**
- GitHub issue template for feedback
- What to report (bugs, UX issues, suggestions)
**Tasks:**
- [ ] Create `docs/experimental-workflow.md` user guide
- [ ] Add GitHub issue template for experimental feedback
- [ ] Update README with mention of experimental features
---
## Dependency Graph
```
1. Remove WF skill files
└── (no dependencies)
2. Rename AWF to OPSX
└── (no dependencies)
3. Add Agent Skills
└── Depends on: Rename AWF to OPSX (uses opsx content as templates)
4. Update opsx:new content
└── Depends on: Rename AWF to OPSX
5. Update opsx:continue content
└── Depends on: Rename AWF to OPSX
6. E2E Testing
└── Depends on: Add Agent Skills (tests the skills workflow)
7. User Documentation
└── Depends on: E2E Testing (need to know final behavior)
```
---
## Out of Scope
The following are explicitly NOT part of this experimental release:
1. **Batch mode refactor** - Making legacy `/openspec:proposal` use schemas
2. **New schemas** - Only shipping with existing `spec-driven` and `tdd`
3. **Schema customization UI** - No `openspec schema list` or similar
4. **Multiple editor support in CLI** - Skills work cross-editor automatically via `.claude/skills/`
5. **Replacing existing commands** - Skills are additive, not replacing `/openspec:*` or `/opsx:*`
---
## Success Criteria
The experimental release is ready when:
1. `openspec-new-change`, `openspec-continue-change`, and `openspec-apply-change` skills work end-to-end
2. `openspec artifact-experimental-setup` creates skills in `.claude/skills/`
3. Skills work in Claude Code and are compatible with Cursor/Windsurf
4. At least one complete workflow has been tested manually
5. User documentation exists explaining how to generate and use skills
6. Feedback mechanism is in place
7. WF skill files are removed
8. No references to "awf" remain in user-facing content
---
## Open Questions
1. **Schema selection** - Should `opsx:new` allow selecting a schema, or always use `spec-driven`?
- Current: Always uses `spec-driven` as default
- Consider: Add `--schema tdd` option or prompt
2. **Namespace in CLI** - Should experimental CLI commands be namespaced?
- Current: `openspec status`, `openspec instructions` (no namespace)
- Alternative: `openspec opsx status` (explicit experimental namespace)
- Recommendation: Keep current, less typing for users
3. **Deprecation path** - If opsx becomes the default, how do we migrate?
- Not needed for experimental release
- Document that command names may change
---
## Estimated Work Breakdown
| Item | Complexity | Notes |
|------|------------|-------|
| Remove WF files | Trivial | Just delete 2 files + directory |
| Rename AWF → OPSX | Low | File renames + content updates |
| Add Agent Skills | **Low** | **Simple: 3-4 files, single output directory, standard format** |
| Update opsx:new content | Low | Text replacements |
| Update opsx:continue content | Low | Text replacements |
| E2E Testing | Medium | Manual testing, documenting results |
| User Documentation | Medium | New docs, issue template |
**Key Improvement:** Switching to Agent Skills reduces complexity significantly:
- **Before:** 20+ files (type definitions, 18+ editor configurators, editor selection UI)
- **After:** 3-4 files (skill templates, simple CLI command)
- **Cross-editor:** Works automatically in Claude Code, Cursor, Windsurf without extra code
---
## User Feedback from E2E Testing
### What Worked Well
1. **Clear dependency graph** ⭐ HIGH PRIORITY - KEEP
- The status command showing blocked/unblocked artifacts was intuitive:
```
[x] proposal
[ ] design
[-] tasks (blocked by: design, specs)
```
- Users always knew what they could work on next
- **Relevance**: Core UX strength to preserve
2. **Structured instructions output** ⭐ HIGH PRIORITY - KEEP
- `openspec instructions <artifact>` gave templates, output paths, and context in one call
- Very helpful for understanding what to create
- **Relevance**: Essential for agent-driven workflow
3. **Simple scaffolding** ✅ WORKS WELL
- `openspec new change "name"` just worked - created directory structure without fuss
- **Relevance**: Good baseline, room for improvement (see pain points)
---
### Pain Points & Confusion
1. **Redundant CLI calls** ⚠️ MEDIUM PRIORITY
- Users called both `status` AND `next` every time, but they overlap significantly
- `status` already shows what's blocked
- **Recommendation**: Consider merging or making `next` give actionable guidance beyond just listing names
- **Relevance**: Reduces friction in iterative workflow
2. **Specs directory structure was ambiguous** 🔥 HIGH PRIORITY - FIX
- Instructions said: `Write to: .../specs/**/*.md`
- Users had to guess: `specs/spec.md`? `specs/game/spec.md`? `specs/tic-tac-toe/spec.md`?
- Users ended up doing manual `mkdir -p .../specs/tic-tac-toe` then writing `spec.md` inside
- **Recommendation**: CLI should scaffold this directory structure automatically
- **Relevance**: Critical agent UX - ambiguous paths cause workflow friction
3. **Repetitive --change flag** ⚠️ MEDIUM PRIORITY
- Every command needed `--change "tic-tac-toe-game"`
- After 10+ calls, this felt verbose
- **Recommendation**: `openspec use "tic-tac-toe-game"` to set context, then subsequent commands assume that change
- **Relevance**: Quality of life improvement for iterative sessions
4. **No validation feedback** 🔥 HIGH PRIORITY - ADD
- After writing each artifact, users just ran `status` hoping it would show `[x]`
- Questions raised:
- How did it know the artifact was "done"? File existence?
- What if spec format was wrong (e.g., wrong heading levels)?
- **Recommendation**: Add `openspec validate --change "name"` to check content quality
- **Relevance**: Critical for user confidence and catching errors early
5. **Query-heavy, action-light CLI** 🔥 HIGH PRIORITY - ENHANCE
- Most commands retrieve info. The only "action" is `new change`
- Artifact creation is manual Write to guessed paths
- **Recommendation**: `openspec create proposal --change "name"` could scaffold the file with template pre-filled, then user just edits
- **Relevance**: Directly impacts agent productivity - reduce manual file writing
6. **Instructions output was verbose** ⚠️ LOW PRIORITY
- XML-style output (`<artifact>`, `<template>`, `<instruction>`) was parseable but long
- Key info (output path, template) was buried in ~50 lines
- **Recommendation**: Add compact mode or structured JSON output for agents
- **Relevance**: Nice-to-have for agent parsing efficiency
---
### Workflow Friction
1. **Mandatory "STOP and wait" after showing proposal template** ⚠️ MEDIUM PRIORITY
- The skill said "STOP and wait" after showing the proposal template
- This felt overly cautious when user had already provided enough context (e.g., "tic tac toe, single player vs AI, minimal aesthetics")
- **Recommendation**: Make the pause optional or conditional based on context clarity
- **Relevance**: Reduces unnecessary round-trips in agent conversations
2. **No connection to implementation** 🔥 HIGH PRIORITY - ROADMAP ITEM
- After 4/4 artifacts complete, then what? The workflow ends at planning
- No `openspec apply` or guidance on how to execute the tasks
- User asked "would you like me to implement?" but that's outside OpenSpec's scope currently
- **Recommendation**: Add implementation bridge - either:
- `openspec apply` command to start execution phase
- Clear handoff to existing `/openspec:apply` workflow
- Documentation on next steps after planning completes
- **Relevance**: Critical missing piece - users expect end-to-end workflow
---
### Priority Summary
**MUST FIX (High Priority):**
1. Specs directory structure ambiguity (#2)
2. Add validation feedback (#4)
3. Make CLI more action-oriented (#5)
4. Bridge to implementation phase (#2 in Workflow Friction)
5. Keep clear dependency graph (#1 in What Worked)
6. Keep structured instructions (#2 in What Worked)
**SHOULD FIX (Medium Priority):**
1. Reduce redundant CLI calls (#1)
2. Repetitive `--change` flag (#3)
3. Mandatory STOP behavior (#1 in Workflow Friction)
**NICE TO HAVE (Low Priority):**
1. Compact instructions output mode (#6)
---
## Design Decisions (from E2E Testing Feedback)
Based on dev testing and analysis of agent workflow friction, we identified three blockers for experimental release and made the following decisions.
### Blockers Identified
From the pain points in E2E testing, three issues are blocking the experimental release:
1. **Specs directory ambiguity** - Agents don't know where to write spec files or how to name capabilities
2. **CLI is query-heavy** - Most commands retrieve info, artifact creation is manual
3. **Apply integration missing** - After 4/4 artifacts complete, no guidance on implementation phase
### Decision 1: Capability Discovery in Proposal (RESOLVED)
**Problem:** The specs artifact instruction says "Create one spec file per capability in `specs/<name>/spec.md`" but:
- Agent doesn't know what `<name>` should be
- Capability identification requires research (existing specs, codebase)
- Proposal template asks for "Affected specs" but doesn't structure it
- Research happens implicitly, output isn't captured
**Decision:** Enrich the proposal template to explicitly capture capability discovery.
**Current proposal template:**
```markdown
## Why
## What Changes
## Impact
- Affected specs: List capabilities... ← vague, easy to skip
- Affected code: ...
```
**New proposal template:**
```markdown
## Why
## What Changes
## Capabilities
### New Capabilities
<!-- Capabilities being introduced (will create new specs/<name>/spec.md) -->
- `<name>`: <brief description of what this capability covers>
### Modified Capabilities
<!-- Existing capabilities being changed (will update existing specs) -->
- `<existing-name>`: <what's changing>
## Impact
<!-- Affected code, APIs, dependencies, systems -->
```
**Rationale:**
- Proposal already asks for capabilities (just poorly) - this makes it explicit
- Captured output is reviewable (vs implicit research that can't be verified)
- Creates clear contract between proposal and specs phases
- Distinguishes NEW vs MODIFIED upfront (critical for specs phase)
- Agent can't skip research - it's part of the deliverable
**Implementation:**
- Update `schemas/spec-driven/templates/proposal.md`
- Update proposal instruction in `schemas/spec-driven/schema.yaml`
- Update skill instructions to guide capability discovery
### Decision 2: CLI Action Commands (IN PROGRESS)
**Problem:** CLI is mostly query-oriented. Agents run `openspec status`, `openspec next`, `openspec instructions` but then must manually write files.
#### Decision 2a: Remove `openspec next` command (RESOLVED)
**Problem:** The `next` command is redundant. It only shows which artifacts are ready, but `status` already shows this information (artifacts with status "ready" vs "blocked" vs "done").
**Current behavior:**
```bash
openspec status --change "X" # Shows: proposal (done), specs (ready), design (blocked), tasks (blocked)
openspec next --change "X" # Shows: ["specs"] ← redundant
```
**Decision:** Remove the `next` command. Agents should use `status` which provides the same info plus more context.
**Implementation:**
- Remove `next` command from CLI
- Update skill instructions to use `status` instead of `next`
- Update AGENTS.md references
#### Decision 2b: CLI Scaffolding (RESOLVED - NO)
**Problem:** After getting instructions, agents manually write files. Should CLI scaffold artifacts instead?
**Options considered:**
- Add `openspec create <artifact>` commands that scaffold files with templates
- Keep current approach where agent writes files directly from instructions
- Hybrid: CLI can scaffold, agent can also write directly
**Decision:** Keep current flow. No scaffolding commands.
**Rationale (from agent ergonomics perspective):**
- One Write is better than multiple Edits - agent composes full content atomically
- `instructions` already provides template in context - scaffolding just moves it to a file
- Fewer tool calls: `instructions` + Write (2) vs `create` + `instructions` + Read + Edit×N (4+)
- Scaffolding doesn't solve the real problem (not knowing WHAT to write)
- Real problem solved by proposal template change (capability discovery)
**For multi-file artifacts (specs):** Scaffolding can't help because CLI doesn't know capability names until proposal is complete. The capability discovery in proposal solves this.
### Decision 3: Apply Integration (RESOLVED)
**Original problem:** After planning completes (4/4 artifacts), the experimental workflow ends. No guidance on implementation.
**Key insight: No phases, just actions.**
Through discussion, we realized phases (planning → implementation → archive) are an artificial constraint. Work is fluid:
- You might start implementing, realize the design is wrong → update design.md
- You're halfway through tasks, discover a new requirement → update specs
- You bounce between "planning" and "implementing" constantly
**The better model: Actions on a Change**
A change is a thing (with artifacts). Actions are verbs you perform on a change. Actions aren't phases - they're fluid operations you can perform anytime.
| Action | What it does | Skill | CLI Command |
|--------|--------------|-------|-------------|
| `new` | Create a change (scaffold directory) | `opsx:new` | `openspec new change` |
| `continue` | Create next artifact (dependency-aware) | `opsx:continue` | `openspec instructions` |
| `apply` | Implement tasks (execute, check off) | `opsx:apply` (NEW) | TBD |
| `update` | Refresh/update artifacts based on learnings | `opsx:update` (NEW) | TBD |
| `explore` | Research, ask questions, understand | `opsx:explore` (NEW) | TBD |
| `validate` | Check artifacts are correct/complete | TBD | `openspec validate` |
| `archive` | Finalize and move to archive | existing | `openspec archive` |
**Key principles:**
- Actions are modeled as skills (primary interface for agents)
- Some skills have matching CLI commands for convenience
- Skills and CLI commands are decoupled - not everything needs both
- Actions can be performed in any order (with soft prerequisites)
- No linear phase gates
**What the schema defines:**
- Artifacts (what they are, where they go)
- Dependencies (what must exist first)
- Required vs optional
- Templates + instructions
**What the schema does NOT define:**
- Phases
- When you can modify things
- Linear workflow
**Progress tracking:**
- tasks.md checkboxes = implementation progress
- Artifact existence = planning progress
- Archive readiness = user decides (or all tasks done)
**For experimental release:**
- Create `opsx:apply` skill (guidance for implementing tasks)
- Document the "actions on a change" model
- Other actions (update, explore) can come later
---
### Design: `openspec-apply-change` Skill
#### Overview
The apply skill guides agents through implementing tasks from a completed (or in-progress) change. Unlike the old `/openspec:apply` command, this skill:
- Is **fluid** - can be invoked anytime, not just after all artifacts are done
- Allows **artifact updates** - if implementation reveals issues, update design/specs
- Works **until done** - keeps going through tasks until complete or blocked
- Tracks **progress via checkboxes** - tasks.md is the source of truth
#### Skill Metadata
```yaml
name: openspec-apply-change
description: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.
```
#### When to Invoke
The skill should be invoked when:
- User says "implement this change" or "start implementing"
- User says "work on the tasks" or "do the next task"
- User says "apply this change"
- All artifacts are complete and user wants to proceed
- User wants to continue implementation after a break
#### Input
- Optionally: change name
- Optionally: specific task number to work on
- If omitted: prompt for change selection (same pattern as continue-change)
#### Steps
```markdown
**Steps**
1. **If no change name provided, prompt for selection**
Run `openspec list --json` to get available changes. Use **AskUserQuestion** to let user select.
Show changes that have tasks.md (implementation-ready).
Mark changes with incomplete tasks as "(In Progress)".
2. **Get apply instructions**
```bash
openspec instructions apply --change "<name>" --json
```
This returns:
- Context file paths (proposal, specs, design, tasks)
- Progress (total, complete, remaining)
- Task list with status
- Dynamic instruction based on current state
**Handle states:**
- If blocked (missing artifacts): show message, suggest `openspec-continue-change`
- If all done: congratulate, suggest archive
- Otherwise: proceed to implementation
3. **Read context files**
Read the files listed in the instructions:
- `proposal.md` - why and what
- `specs/*.md` - requirements and scenarios
- `design.md` - technical approach (if exists)
- `tasks.md` - the implementation checklist
4. **Show current progress**
Display:
- Progress: "N/M tasks complete"
- Remaining tasks overview
- Dynamic instruction from CLI
5. **Implement tasks (loop until done or blocked)**
For each pending task:
- Show which task is being worked on
- Make the code changes required
- Keep changes minimal and focused
- Mark task complete in tasks.md: `- [ ]` → `- [x]`
- Continue to next task
**Pause if:**
- Task is unclear → ask for clarification
- Implementation reveals a design issue → suggest updating artifacts
- Error or blocker encountered → report and wait for guidance
- User interrupts
6. **On completion or pause, show status**
Display:
- Tasks completed this session
- Overall progress: "N/M tasks complete"
- If all done: suggest archive
- If paused: explain why and wait for guidance
```
#### Output Format
**During implementation:**
```
## Implementing: add-user-auth
Working on task 3/7: Create UserAuth service class
[...implementation happening...]
✓ Task complete
Working on task 4/7: Add login endpoint to AuthController
[...implementation happening...]
✓ Task complete
Working on task 5/7: Add JWT token generation
[...implementation happening...]
```
**On completion:**
```
## Implementation Complete
**Change:** add-user-auth
**Progress:** 7/7 tasks complete ✓
### Completed This Session
- [x] Create UserAuth service class
- [x] Add login endpoint to AuthController
- [x] Add JWT token generation
- [x] Add logout endpoint
- [x] Add auth middleware
- [x] Write unit tests
- [x] Update API documentation
All tasks complete! Ready to archive this change.
```
**On pause (issue encountered):**
```
## Implementation Paused
**Change:** add-user-auth
**Progress:** 4/7 tasks complete
### Issue Encountered
Task 5 "Add JWT token generation" - the design specifies using RS256 but
the existing auth library only supports HS256.
**Options:**
1. Update design.md to use HS256 instead
2. Add a new JWT library that supports RS256
3. Other approach
What would you like to do?
```
#### Guardrails
- Keep going through tasks until done or blocked
- Always read context before starting (specs, design)
- If task is ambiguous, pause and ask before implementing
- If implementation reveals issues, pause and suggest artifact updates
- Keep code changes minimal and scoped to each task
- Update task checkbox immediately after completing each task
- Pause on errors, blockers, or unclear requirements - don't guess
#### Fluid Workflow Integration
The apply skill supports the "actions on a change" model:
**Can be invoked anytime:**
- Before all artifacts are done (if tasks.md exists)
- After partial implementation
- Interleaved with other actions (update, continue)
**Allows artifact updates:**
- If implementation reveals design issues → suggest `opsx:update` or manual edit
- If requirements need clarification → suggest updating specs
- Not phase-locked - work fluidly
**Example fluid workflow:**
```
User: "Implement add-user-auth"
→ openspec-apply-change: implements tasks 1, 2, 3, 4...
→ Pauses at task 5: "Design says RS256 but library only supports HS256"
User: "Let's use HS256 instead, update the design"
→ User edits design.md (or uses opsx:update in future)
User: "Continue implementing"
→ openspec-apply-change: implements tasks 5, 6, 7
→ "All tasks complete! Ready to archive."
```
#### CLI Commands Used
```bash
openspec list --json # List changes for selection
openspec status --change "<name>" # Check artifact completion
openspec instructions apply --change "<name>" # Get apply instructions (NEW)
# File reads via Read tool for proposal, specs, design, tasks
# File edits via Edit tool for checking off tasks
```
#### New CLI Command: `openspec instructions apply`
For consistency with artifact instructions.
**Usage:**
```bash
openspec instructions apply --change "<name>" [--json]
```
**Output (Markdown format):**
```markdown
## Apply: add-user-auth
### Context Files
- proposal: openspec/changes/add-user-auth/proposal.md
- specs: openspec/changes/add-user-auth/specs/**/*.md
- design: openspec/changes/add-user-auth/design.md
- tasks: openspec/changes/add-user-auth/tasks.md
### Progress
2/7 complete
### Tasks
- [x] Create UserAuth service class
- [x] Add login endpoint
- [ ] Add JWT token generation
- [ ] Add logout endpoint
- [ ] Add auth middleware
- [ ] Write unit tests
- [ ] Update API documentation
### Instruction
Read context files, work through pending tasks, mark complete as you go.
Pause if you hit blockers or need clarification.
```
**Benefits of CLI command:**
- **Consistency** - same pattern as `openspec instructions <artifact>`
- **Structured output** - progress, tasks, context paths in one call
- **Clean format** - markdown is readable and compact (vs verbose XML)
- **Extensibility** - can add more sections later if needed
- **JSON option** - `--json` flag available for programmatic use
#### Differences from Old `/openspec:apply`
| Aspect | Old `/openspec:apply` | New `openspec-apply-change` |
|--------|----------------------|----------------------------|
| Invocation | After all artifacts done | Anytime (if tasks.md exists) |
| Granularity | All tasks at once | All tasks, but pauses on issues |
| Artifact updates | Not mentioned | Encouraged when needed |
| Progress tracking | Update all at end | Update after each task |
| Flow control | Push through everything | Pause on blockers, resume after |
| Context loading | Read once at start | Read context, reference as needed |
| Issue handling | Not specified | Pause, present options, wait for guidance |
#### Implementation Notes
1. **Add CLI command**: Add `openspec instructions apply` to artifact-workflow.ts
- Parse tasks.md for progress (count done/pending)
- Return context paths, progress, task list, simple instruction
2. **Add to skill-templates.ts**: Create `getApplyChangeSkillTemplate()` function
3. **Update artifact-experimental-setup**: Generate this skill alongside new/continue
4. **Update skills list**: Add to `.claude/skills/` directory
5. **Test the flow**: Verify it works with existing changes that have tasks.md
---
## Next Steps
1. ~~Review this plan and confirm scope~~ (Done - blockers identified)
2. ~~Design decisions~~ (Done - all 3 blockers resolved)
3. ~~Design apply skill~~ (Done - documented above)
4. ~~Implement proposal template change (Decision 1 - capability discovery)~~ (Done)
5. ~~Remove `openspec next` command (Decision 2a)~~ (Done)
6. ~~Add `openspec instructions apply` CLI command~~ (Done)
7. ~~Create `openspec-apply-change` skill~~ (Done)
8. Conduct E2E testing with updated workflow
9. Write user docs (document "actions on a change" model)
10. Release to test users
+1 -3
View File
@@ -210,9 +210,7 @@ openspec new change add-auth --schema tdd
# Auto-reads schema from change.yaml — no --schema needed
openspec status --change add-auth
# Output: "Change: add-auth (schema: tdd)"
openspec next --change add-auth
# Knows to use TDD artifacts
# Shows which artifacts are ready/blocked/done
# Explicit override still works (with informational message)
openspec status --change add-auth --schema spec-driven
@@ -0,0 +1,26 @@
## Why
With per-change schema metadata in place (see `add-per-change-schema-metadata`), agents can now create changes with different workflow schemas. However, the agent skills are still hardcoded to `spec-driven` artifacts and don't offer schema selection to users.
## What Changes
**Scope: Experimental artifact workflow agent skills**
**Depends on:** `add-per-change-schema-metadata` (must be implemented first)
- Update `openspec-new-change` skill to prompt user for schema selection
- Update `openspec-continue-change` skill to work with any schema's artifacts
- Update `openspec-apply-change` skill to handle schema-specific task structures
- Add schema descriptions to help users choose appropriate workflow
## Capabilities
### Modified Capabilities
- `cli-artifact-workflow`: Agent skills support dynamic schema selection
## Impact
- **Affected code**: `src/core/templates/skill-templates.ts`
- **User experience**: Users can choose TDD, spec-driven, or future workflows when starting a change
- **Agent behavior**: Skills read artifact list from schema rather than hardcoding
- **Backward compatible**: Default remains `spec-driven` if user doesn't choose
@@ -0,0 +1,32 @@
## Prerequisites
- [ ] 0.1 Implement `add-per-change-schema-metadata` change first
## 1. Schema Discovery
- [ ] 1.1 Add CLI command or helper to list schemas with descriptions (for agent use)
- [ ] 1.2 Ensure `openspec templates --schema <name>` returns artifact list for any schema
## 2. Update New Change Skill
- [ ] 2.1 Add schema selection prompt using AskUserQuestion tool
- [ ] 2.2 Present available schemas with descriptions (spec-driven, tdd, etc.)
- [ ] 2.3 Pass selected schema to `openspec new change --schema <name>`
- [ ] 2.4 Update output to show which schema/workflow was selected
## 3. Update Continue Change Skill
- [ ] 3.1 Remove hardcoded artifact references (proposal, specs, design, tasks)
- [ ] 3.2 Read artifact list dynamically from `openspec status --json`
- [ ] 3.3 Adjust artifact creation guidelines to be schema-agnostic
- [ ] 3.4 Handle schema-specific artifact types (e.g., TDD's `tests` artifact)
## 4. Update Apply Change Skill
- [ ] 4.1 Make task detection work with different schema structures
- [ ] 4.2 Adjust context file reading for schema-specific artifacts
## 5. Documentation
- [ ] 5.1 Add schema descriptions to help text or skill instructions
- [ ] 5.2 Document when to use each schema (TDD for bug fixes, spec-driven for features, etc.)
@@ -0,0 +1,147 @@
## Context
The experimental artifact workflow supports multiple schemas (`spec-driven`, `tdd`), but schema selection must be passed on every command. This creates friction for agents and users.
We need a lightweight metadata file to persist the schema choice per change.
## Goals / Non-Goals
**Goals:**
- Store schema choice once at change creation
- Auto-detect schema in experimental workflow commands
- Maintain backward compatibility (no metadata = default)
- Validate metadata with Zod schema
**Non-Goals:**
- Migrate existing changes (they use default)
- Extend to legacy commands
- Store additional metadata beyond schema (keep minimal for now)
## Decisions
### Decision: Zod Schema Design
The metadata file (`.openspec.yaml`) will be validated with this Zod schema:
```typescript
// src/core/artifact-graph/types.ts (or new metadata.ts)
import { z } from 'zod';
import { listSchemas } from './resolver.js';
/**
* Schema for per-change metadata stored in .openspec.yaml
*/
export const ChangeMetadataSchema = z.object({
// Required: which workflow schema this change uses
schema: z.string().min(1, { message: 'schema is required' }).refine(
(val) => listSchemas().includes(val),
(val) => ({ message: `Unknown schema '${val}'. Available: ${listSchemas().join(', ')}` })
),
// Optional: creation timestamp (ISO date string)
created: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, {
message: 'created must be YYYY-MM-DD format'
}).optional(),
});
export type ChangeMetadata = z.infer<typeof ChangeMetadataSchema>;
```
**Rationale:**
- `schema` is required and validated against available schemas at parse time
- `created` is optional, ISO date format for consistency
- Minimal fields - can extend later without breaking existing files
- Follows existing codebase pattern (see `ArtifactSchema`, `SchemaYamlSchema`)
### Decision: File Location and Format
**Location:** `openspec/changes/<name>/.openspec.yaml`
**Format:**
```yaml
schema: tdd
created: 2025-01-05
```
**Alternatives considered:**
- `change.yaml` - less hidden, but clutters directory
- Frontmatter in `proposal.md` - couples to proposal existence
- `openspec.json` - YAML matches existing schema files
### Decision: Read/Write Functions
```typescript
// src/utils/change-metadata.ts
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as yaml from 'yaml';
import { ChangeMetadataSchema, type ChangeMetadata } from '../core/artifact-graph/types.js';
const METADATA_FILENAME = '.openspec.yaml';
export function writeChangeMetadata(
changeDir: string,
metadata: ChangeMetadata
): void {
// Validate before writing
const validated = ChangeMetadataSchema.parse(metadata);
const content = yaml.stringify(validated);
fs.writeFileSync(path.join(changeDir, METADATA_FILENAME), content);
}
export function readChangeMetadata(
changeDir: string
): ChangeMetadata | null {
const metaPath = path.join(changeDir, METADATA_FILENAME);
if (!fs.existsSync(metaPath)) {
return null;
}
const content = fs.readFileSync(metaPath, 'utf-8');
const parsed = yaml.parse(content);
// Validate and return (throws ZodError if invalid)
return ChangeMetadataSchema.parse(parsed);
}
```
### Decision: Schema Resolution Order
When determining which schema to use:
1. **Explicit `--schema` flag** (highest priority - user override)
2. **`.openspec.yaml` metadata** (persisted choice)
3. **Default `spec-driven`** (fallback)
```typescript
function resolveSchemaForChange(
changeDir: string,
explicitSchema?: string
): string {
if (explicitSchema) return explicitSchema;
const metadata = readChangeMetadata(changeDir);
if (metadata?.schema) return metadata.schema;
return 'spec-driven';
}
```
## Risks / Trade-offs
- **Extra file per change** → Minimal overhead, hidden file
- **YAML parsing dependency** → Already using `yaml` package for schema files
- **Schema validation at read time** → Fail fast with clear error if corrupted
## Migration Plan
No migration needed:
- Existing changes without `.openspec.yaml` continue to work (use default)
- New changes created with `openspec new change --schema X` get metadata file
## Open Questions
- Should `openspec new change` prompt for schema interactively if not specified? (Leaning no - default is fine)
@@ -0,0 +1,29 @@
## Why
Currently, the schema (workflow type) must be passed via `--schema` flag on every experimental workflow command. This is repetitive and error-prone. Agents have no way to know which schema a change uses, so they default to `spec-driven` and cannot leverage alternative workflows like `tdd`.
## What Changes
**Scope: Experimental artifact workflow only** (`openspec new change`, `openspec status`, `openspec instructions`, `openspec templates`)
- Store schema choice in `.openspec.yaml` metadata file when creating a change via `openspec new change`
- Auto-detect schema from metadata in experimental workflow commands
- Make `--schema` flag optional (override only, metadata takes precedence)
- Add `--schema` option to `openspec new change` command
**Not affected**: Legacy commands (`openspec validate`, `openspec archive`, `openspec list`, `openspec show`)
## Capabilities
### New Capabilities
- `change-metadata`: Reading/writing per-change metadata files
### Modified Capabilities
- `cli-artifact-workflow`: Commands auto-detect schema from change metadata
## Impact
- **Affected code**: `src/utils/change-utils.ts`, `src/core/artifact-graph/instruction-loader.ts`, `src/commands/artifact-workflow.ts`
- **Agent skills**: Can be simplified - no longer need to pass schema explicitly
- **Backward compatible**: Changes without `.openspec.yaml` fall back to `spec-driven` default
- **Isolation**: All changes contained within experimental workflow code; legacy commands untouched
@@ -0,0 +1,98 @@
## ADDED Requirements
### Requirement: Change Metadata
The system SHALL store and validate per-change metadata in `.openspec.yaml` files using a Zod schema.
#### Scenario: Metadata file created with new change
- **WHEN** user runs `openspec new change add-feature --schema tdd`
- **THEN** the system creates `.openspec.yaml` in the change directory
- **AND** the file contains `schema: tdd` and `created: <YYYY-MM-DD>`
#### Scenario: Metadata validated on read
- **WHEN** the system reads `.openspec.yaml`
- **AND** the `schema` field references an unknown schema
- **THEN** the system displays a validation error listing available schemas
#### Scenario: Metadata schema validation
- **WHEN** `.openspec.yaml` contains invalid YAML or missing required fields
- **THEN** the system displays a Zod validation error with details
#### Scenario: Missing metadata file
- **WHEN** a change directory has no `.openspec.yaml` file
- **THEN** the system falls back to the default schema (`spec-driven`)
## MODIFIED Requirements
### Requirement: New Change Command
The system SHALL create new change directories with validation and optional schema metadata.
#### Scenario: Create valid change
- **WHEN** user runs `openspec new change add-feature`
- **THEN** the system creates `openspec/changes/add-feature/` directory
- **AND** creates `.openspec.yaml` with `schema: spec-driven` (default)
#### Scenario: Create change with schema
- **WHEN** user runs `openspec new change add-feature --schema tdd`
- **THEN** the system creates `openspec/changes/add-feature/` directory
- **AND** creates `.openspec.yaml` with `schema: tdd`
#### Scenario: Invalid schema on create
- **WHEN** user runs `openspec new change add-feature --schema unknown`
- **THEN** the system displays an error listing available schemas
- **AND** does not create the change directory
#### Scenario: Invalid change name
- **WHEN** user runs `openspec new change "Add Feature"` with invalid name
- **THEN** the system displays validation error with guidance
#### Scenario: Duplicate change name
- **WHEN** user runs `openspec new change existing-change` for an existing change
- **THEN** the system displays an error indicating the change already exists
#### Scenario: Create with description
- **WHEN** user runs `openspec new change add-feature --description "Add new feature"`
- **THEN** the system creates the change directory with description in README.md
### Requirement: Schema Selection
The system SHALL support custom schema selection for workflow commands, with automatic detection from change metadata.
#### Scenario: Schema auto-detected from metadata
- **WHEN** user runs `openspec status --change <id>` without `--schema`
- **AND** the change has `.openspec.yaml` with `schema: tdd`
- **THEN** the system uses the `tdd` schema
#### Scenario: Explicit schema overrides metadata
- **WHEN** user runs `openspec status --change <id> --schema spec-driven`
- **AND** the change has `.openspec.yaml` with `schema: tdd`
- **THEN** the system uses `spec-driven` (explicit flag wins)
#### Scenario: Default schema fallback
- **WHEN** user runs workflow commands without `--schema`
- **AND** the change has no `.openspec.yaml` file
- **THEN** the system uses the "spec-driven" schema
#### Scenario: Custom schema via flag
- **WHEN** user runs `openspec status --change <id> --schema tdd`
- **THEN** the system uses the specified schema for artifact graph
#### Scenario: Unknown schema
- **WHEN** user specifies an unknown schema
- **THEN** the system displays an error listing available schemas
@@ -0,0 +1,29 @@
## 1. Zod Schema and Types
- [ ] 1.1 Add `ChangeMetadataSchema` Zod schema to `src/core/artifact-graph/types.ts`
- [ ] 1.2 Export `ChangeMetadata` type inferred from schema
## 2. Core Metadata Functions
- [ ] 2.1 Create `src/utils/change-metadata.ts` with `writeChangeMetadata()` function
- [ ] 2.2 Add `readChangeMetadata()` function with Zod validation
- [ ] 2.3 Update `createChange()` to accept optional `schema` param and write metadata
## 3. Auto-Detection in Instruction Loader
- [ ] 3.1 Modify `loadChangeContext()` to read schema from `.openspec.yaml`
- [ ] 3.2 Make `schemaName` parameter optional (fall back to metadata, then default)
## 4. CLI Updates
- [ ] 4.1 Add `--schema <name>` option to `openspec new change` command
- [ ] 4.2 Verify existing commands (`status`, `instructions`) work with auto-detection
## 5. Tests
- [ ] 5.1 Test `ChangeMetadataSchema` validates correctly (valid/invalid cases)
- [ ] 5.2 Test `writeChangeMetadata()` creates valid YAML
- [ ] 5.3 Test `readChangeMetadata()` parses and validates schema
- [ ] 5.4 Test `loadChangeContext()` auto-detects schema from metadata
- [ ] 5.5 Test fallback to default when no metadata exists
- [ ] 5.6 Test `--schema` flag overrides metadata
@@ -0,0 +1,138 @@
## Why
The `generateApplyInstructions` function is hardcoded to check for `spec-driven` artifacts (`proposal.md`, `specs/`, `design.md`, `tasks.md`). If a user selects a different schema like `tdd`, the apply instructions are meaningless - they check for files that don't exist in that schema.
This blocks the experimental workflow from supporting multiple schemas properly.
## What Changes
**Scope: Experimental artifact workflow** (`openspec instructions apply`)
**Depends on:** `add-per-change-schema-metadata` (to know which schema a change uses)
- Make `generateApplyInstructions` read artifact definitions from the schema
- Dynamically determine which artifacts exist based on schema
- Define when a change becomes "implementable" (see Design Decision below)
- Generate schema-appropriate context files and instructions
## Design Decision: When is a change implementable?
This is the key question. Different approaches:
### Option A: Explicit `apply` artifact in schema
Add a field to mark which artifact is the "implementation gate":
```yaml
artifacts:
- id: tasks
generates: tasks.md
apply: true # ← This artifact triggers apply mode
```
**Pros:** Explicit, flexible
**Cons:** Another field to maintain, what if multiple artifacts are `apply: true`?
### Option B: Leaf artifacts are implementable
The artifact(s) with no dependents (nothing depends on them) are the apply target.
- `spec-driven`: `tasks` is a leaf → apply = execute tasks
- `tdd`: `docs` is a leaf → but that doesn't make sense for TDD...
**Pros:** No extra schema field, derived from graph
**Cons:** Doesn't match TDD semantics (implementation is the action, not docs)
### Option C: Schema-level `apply_phase` definition
Add a top-level field to the schema:
```yaml
name: spec-driven
apply_phase:
requires: [tasks] # Must exist before apply
tracks: tasks.md # File with checkboxes to track
instruction: "Work through tasks, mark complete as you go"
```
```yaml
name: tdd
apply_phase:
requires: [tests] # Must have tests before implementing
tracks: null # No checkbox tracking - just make tests pass
instruction: "Run tests, implement until green, refactor"
```
**Pros:** Full flexibility, schema controls its own apply semantics
**Cons:** More complex schema format
### Option D: Convention-based (artifact ID matching)
If artifact ID is `tasks` or `implementation`, it's the apply target.
**Pros:** Simple, no schema changes
**Cons:** Brittle, doesn't work for custom schemas
### Option E: All artifacts complete → apply available
Apply becomes available when ALL schema artifacts exist. Implementation is whatever the user does after planning.
**Pros:** Simple, no schema changes
**Cons:** Doesn't guide what "apply" means for different workflows
---
## Decision: Add `apply` block to schema.yaml
Add a top-level `apply` field to schema definitions:
```yaml
name: spec-driven
version: 1
description: Default OpenSpec workflow
artifacts:
# ... existing artifacts ...
apply:
requires: [tasks] # Artifacts that must exist before apply
tracks: tasks.md # File with checkboxes for progress (optional)
instruction: | # Guidance shown to agent
Read context files, work through pending tasks, mark complete as you go.
Pause if you hit blockers or need clarification.
```
```yaml
name: tdd
version: 1
description: Test-driven development workflow
artifacts:
# ... existing artifacts ...
apply:
requires: [tests] # Must have tests before implementing
tracks: null # No checkbox tracking
instruction: |
Run tests to see failures. Implement minimal code to pass each test.
Refactor while keeping tests green.
```
**Key properties:**
- `requires`: Array of artifact IDs that must exist before apply is available
- `tracks`: Path to file with checkboxes (relative to change dir), or `null` if no tracking
- `instruction`: Custom guidance for the apply phase
**Fallback behavior:** Schemas without `apply` block default to "all artifacts must exist"
## Capabilities
### Modified Capabilities
- `cli-artifact-workflow`: Apply instructions become schema-aware
## Impact
- **Affected code**: `src/commands/artifact-workflow.ts` (generateApplyInstructions)
- **Schema format**: May need new `apply_phase` field
- **Existing schemas**: Need to add apply_phase to `spec-driven` and `tdd`
- **Backward compatible**: Schemas without apply_phase can use default behavior
@@ -0,0 +1,35 @@
## Prerequisites
- [ ] 0.1 Implement `add-per-change-schema-metadata` first (to auto-detect schema)
## 1. Schema Format
- [ ] 1.1 Add `ApplyPhaseSchema` Zod schema to `src/core/artifact-graph/types.ts`
- [ ] 1.2 Update `SchemaYamlSchema` to include optional `apply` field
- [ ] 1.3 Export `ApplyPhase` type
## 2. Update Existing Schemas
- [ ] 2.1 Add `apply` block to `schemas/spec-driven/schema.yaml`
- [ ] 2.2 Add `apply` block to `schemas/tdd/schema.yaml`
## 3. Refactor generateApplyInstructions
- [ ] 3.1 Load schema via `resolveSchema(schemaName)`
- [ ] 3.2 Read `apply.requires` to determine required artifacts
- [ ] 3.3 Check artifact existence dynamically (not hardcoded paths)
- [ ] 3.4 Use `apply.tracks` for progress tracking (or skip if null)
- [ ] 3.5 Use `apply.instruction` for the instruction text
- [ ] 3.6 Build `contextFiles` from all existing artifacts in schema
## 4. Handle Fallback
- [ ] 4.1 If schema has no `apply` block, require all artifacts to exist
- [ ] 4.2 Default instruction: "All artifacts complete. Proceed with implementation."
## 5. Tests
- [ ] 5.1 Test apply instructions with spec-driven schema
- [ ] 5.2 Test apply instructions with tdd schema
- [ ] 5.3 Test fallback when schema has no apply block
- [ ] 5.4 Test blocked state when required artifacts missing
+8 -29
View File
@@ -46,35 +46,6 @@ The system SHALL display artifact completion status for a change, including scaf
- **AND** directory `openspec/changes/unknown-id/` does not exist
- **THEN** the system displays an error listing all available change directories
### Requirement: Next Command
The system SHALL show which artifacts are ready to be created, including for scaffolded changes.
#### Scenario: Show ready artifacts
- **WHEN** user runs `openspec next --change <id>`
- **THEN** the system lists artifacts whose dependencies are all satisfied
#### Scenario: No artifacts ready
- **WHEN** all artifacts are either completed or blocked
- **THEN** the system indicates no artifacts are ready (with explanation)
#### Scenario: All artifacts complete
- **WHEN** all artifacts in the change are completed
- **THEN** the system indicates the change is complete
#### Scenario: Next JSON output
- **WHEN** user runs `openspec next --change <id> --json`
- **THEN** the system outputs JSON array of ready artifact IDs
#### Scenario: Next on scaffolded change
- **WHEN** user runs `openspec next --change <id>` on a change with no artifacts
- **THEN** system shows root artifacts (e.g., "proposal") as ready to create
### Requirement: Instructions Command
The system SHALL output enriched instructions for creating an artifact, including for scaffolded changes.
@@ -188,3 +159,11 @@ The system SHALL implement artifact workflow commands in isolation for easy remo
- **WHEN** user runs `--help` on any artifact workflow command
- **THEN** help text indicates the command is experimental
## REMOVED Requirements
### Requirement: Next Command
**Reason**: Redundant with Status Command - `openspec status` already shows which artifacts are ready (status: "ready") vs blocked vs done.
**Migration**: Use `openspec status --change <id> --json` and filter artifacts with `status: "ready"` to find artifacts that can be created next.
+8 -3
View File
@@ -12,9 +12,14 @@ artifacts:
Sections:
- **Why**: 1-2 sentences on the problem or opportunity. What problem does this solve? Why now?
- **What Changes**: Bullet list of changes. Be specific about new capabilities, modifications, or removals. Mark breaking changes with **BREAKING**.
- **Impact**: Two parts:
- Affected specs: List capabilities that will have spec deltas
- Affected code: Key files, APIs, dependencies, or systems
- **Capabilities**: Identify which specs will be created or modified:
- **New Capabilities**: List capabilities being introduced. Each becomes a new `specs/<name>/spec.md`. Use kebab-case names (e.g., `user-auth`, `data-export`).
- **Modified Capabilities**: List existing capabilities whose REQUIREMENTS are changing. Only include if spec-level behavior changes (not just implementation details). Each needs a delta spec file. Check `openspec/specs/` for existing spec names. Leave empty if no requirement changes.
- **Impact**: Affected code, APIs, dependencies, or systems.
IMPORTANT: The Capabilities section is critical. It creates the contract between
proposal and specs phases. Research existing specs before filling this in.
Each capability listed here will need a corresponding spec file.
Keep it concise (1-2 pages). Focus on the "why" not the "how" -
implementation details belong in design.md.
+15 -3
View File
@@ -1,11 +1,23 @@
## Why
<!-- Explain the motivation for this change -->
<!-- Explain the motivation for this change. What problem does this solve? Why now? -->
## What Changes
<!-- Describe what will change -->
<!-- Describe what will change. Be specific about new capabilities, modifications, or removals. -->
## Capabilities
### New Capabilities
<!-- Capabilities being introduced. Replace <name> with kebab-case identifier (e.g., user-auth, data-export, api-rate-limiting). Each creates specs/<name>/spec.md -->
- `<name>`: <brief description of what this capability covers>
### Modified Capabilities
<!-- Existing capabilities whose REQUIREMENTS are changing (not just implementation).
Only list here if spec-level behavior changes. Each needs a delta spec file.
Use existing spec names from openspec/specs/. Leave empty if no requirement changes. -->
- `<existing-name>`: <what requirement is changing>
## Impact
<!-- List affected areas -->
<!-- Affected code, APIs, dependencies, systems -->
+324 -70
View File
@@ -26,6 +26,38 @@ import {
type ArtifactInstructions,
} from '../core/artifact-graph/index.js';
import { createChange, validateChangeName } from '../utils/change-utils.js';
import { getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate } from '../core/templates/skill-templates.js';
import { FileSystemUtils } from '../utils/file-system.js';
// -----------------------------------------------------------------------------
// Types for Apply Instructions
// -----------------------------------------------------------------------------
interface TaskItem {
id: string;
description: string;
done: boolean;
}
interface ApplyInstructions {
changeName: string;
changeDir: string;
contextFiles: {
proposal?: string;
specs: string;
design?: string;
tasks: string;
};
progress: {
total: number;
complete: number;
remaining: number;
};
tasks: TaskItem[];
state: 'blocked' | 'all_done' | 'ready';
missingArtifacts?: string[];
instruction: string;
}
const DEFAULT_SCHEMA = 'spec-driven';
@@ -201,57 +233,6 @@ function printStatusText(status: ChangeStatus): void {
}
}
// -----------------------------------------------------------------------------
// Next Command
// -----------------------------------------------------------------------------
interface NextOptions {
change?: string;
schema?: string;
json?: boolean;
}
async function nextCommand(options: NextOptions): Promise<void> {
const spinner = ora('Finding next artifacts...').start();
try {
const projectRoot = process.cwd();
const changeName = await validateChangeExists(options.change, projectRoot);
const schemaName = validateSchemaExists(options.schema ?? DEFAULT_SCHEMA);
const context = loadChangeContext(projectRoot, changeName, schemaName);
const ready = context.graph.getNextArtifacts(context.completed);
const isComplete = context.graph.isComplete(context.completed);
spinner.stop();
if (options.json) {
console.log(JSON.stringify(ready, null, 2));
return;
}
if (isComplete) {
console.log(chalk.green('All artifacts are complete!'));
return;
}
if (ready.length === 0) {
console.log('No artifacts are ready. All remaining artifacts are blocked.');
console.log('Run `openspec status --change ' + changeName + '` to see blocked dependencies.');
return;
}
console.log('Artifacts ready to create:');
for (const artifactId of ready) {
const color = getStatusColor('ready');
console.log(color(` ${artifactId}`));
}
} catch (error) {
spinner.stop();
throw error;
}
}
// -----------------------------------------------------------------------------
// Instructions Command
// -----------------------------------------------------------------------------
@@ -401,6 +382,198 @@ function printInstructionsText(instructions: ArtifactInstructions, isBlocked: bo
console.log('</artifact>');
}
// -----------------------------------------------------------------------------
// Apply Instructions Command
// -----------------------------------------------------------------------------
interface ApplyInstructionsOptions {
change?: string;
schema?: string;
json?: boolean;
}
/**
* Parses tasks.md content and extracts task items with their completion status.
*/
function parseTasksFile(content: string): TaskItem[] {
const tasks: TaskItem[] = [];
const lines = content.split('\n');
let taskIndex = 0;
for (const line of lines) {
// Match checkbox patterns: - [ ] or - [x] or - [X]
const checkboxMatch = line.match(/^[-*]\s*\[([ xX])\]\s*(.+)$/);
if (checkboxMatch) {
taskIndex++;
const done = checkboxMatch[1].toLowerCase() === 'x';
const description = checkboxMatch[2].trim();
tasks.push({
id: `${taskIndex}`,
description,
done,
});
}
}
return tasks;
}
/**
* Generates apply instructions for implementing tasks from a change.
*/
async function generateApplyInstructions(
projectRoot: string,
changeName: string,
schemaName: string
): Promise<ApplyInstructions> {
const context = loadChangeContext(projectRoot, changeName, schemaName);
const changeDir = path.join(projectRoot, 'openspec', 'changes', changeName);
// Check if required artifacts exist (tasks.md is the minimum requirement)
const tasksPath = path.join(changeDir, 'tasks.md');
const proposalPath = path.join(changeDir, 'proposal.md');
const designPath = path.join(changeDir, 'design.md');
const specsPath = path.join(changeDir, 'specs');
const hasProposal = fs.existsSync(proposalPath);
const hasDesign = fs.existsSync(designPath);
const hasTasks = fs.existsSync(tasksPath);
const hasSpecs = fs.existsSync(specsPath);
// Determine state and missing artifacts
const missingArtifacts: string[] = [];
if (!hasTasks) {
// Check what's missing to create tasks (design is optional)
if (!hasProposal) missingArtifacts.push('proposal');
if (!hasSpecs) missingArtifacts.push('specs');
if (missingArtifacts.length === 0) missingArtifacts.push('tasks');
}
// Build context files object
const contextFiles: ApplyInstructions['contextFiles'] = {
specs: path.join(changeDir, 'specs/**/*.md'),
tasks: tasksPath,
};
if (hasProposal) contextFiles.proposal = proposalPath;
if (hasDesign) contextFiles.design = designPath;
// Parse tasks if file exists
let tasks: TaskItem[] = [];
if (hasTasks) {
const tasksContent = await fs.promises.readFile(tasksPath, 'utf-8');
tasks = parseTasksFile(tasksContent);
}
// Calculate progress
const total = tasks.length;
const complete = tasks.filter((t) => t.done).length;
const remaining = total - complete;
// Determine state
let state: ApplyInstructions['state'];
let instruction: string;
if (!hasTasks || missingArtifacts.length > 0) {
state = 'blocked';
instruction = `Cannot apply this change yet. Missing artifacts: ${missingArtifacts.join(', ')}.\nUse the openspec-continue-change skill to create the missing artifacts first.`;
} else if (remaining === 0 && total > 0) {
state = 'all_done';
instruction = 'All tasks are complete! This change is ready to be archived.\nConsider running tests and reviewing the changes before archiving.';
} else if (total === 0) {
state = 'blocked';
instruction = 'The tasks.md file exists but contains no tasks.\nAdd tasks to tasks.md or regenerate it with openspec-continue-change.';
} else {
state = 'ready';
instruction = 'Read context files, work through pending tasks, mark complete as you go.\nPause if you hit blockers or need clarification.';
}
return {
changeName,
changeDir,
contextFiles,
progress: { total, complete, remaining },
tasks,
state,
missingArtifacts: missingArtifacts.length > 0 ? missingArtifacts : undefined,
instruction,
};
}
async function applyInstructionsCommand(options: ApplyInstructionsOptions): Promise<void> {
const spinner = ora('Generating apply instructions...').start();
try {
const projectRoot = process.cwd();
const changeName = await validateChangeExists(options.change, projectRoot);
const schemaName = validateSchemaExists(options.schema ?? DEFAULT_SCHEMA);
const instructions = await generateApplyInstructions(projectRoot, changeName, schemaName);
spinner.stop();
if (options.json) {
console.log(JSON.stringify(instructions, null, 2));
return;
}
printApplyInstructionsText(instructions);
} catch (error) {
spinner.stop();
throw error;
}
}
function printApplyInstructionsText(instructions: ApplyInstructions): void {
const { changeName, contextFiles, progress, tasks, state, missingArtifacts, instruction } = instructions;
console.log(`## Apply: ${changeName}`);
console.log();
// Warning for blocked state
if (state === 'blocked' && missingArtifacts) {
console.log('### ⚠️ Blocked');
console.log();
console.log(`Missing artifacts: ${missingArtifacts.join(', ')}`);
console.log('Use the openspec-continue-change skill to create these first.');
console.log();
}
// Context files
console.log('### Context Files');
if (contextFiles.proposal) {
console.log(`- proposal: ${contextFiles.proposal}`);
}
console.log(`- specs: ${contextFiles.specs}`);
if (contextFiles.design) {
console.log(`- design: ${contextFiles.design}`);
}
console.log(`- tasks: ${contextFiles.tasks}`);
console.log();
// Progress
console.log('### Progress');
if (state === 'all_done') {
console.log(`${progress.complete}/${progress.total} complete ✓`);
} else {
console.log(`${progress.complete}/${progress.total} complete`);
}
console.log();
// Tasks
if (tasks.length > 0) {
console.log('### Tasks');
for (const task of tasks) {
const checkbox = task.done ? '[x]' : '[ ]';
console.log(`- ${checkbox} ${task.description}`);
}
console.log();
}
// Instruction
console.log('### Instruction');
console.log(instruction);
}
// -----------------------------------------------------------------------------
// Templates Command
// -----------------------------------------------------------------------------
@@ -500,6 +673,85 @@ async function newChangeCommand(name: string | undefined, options: NewChangeOpti
}
}
// -----------------------------------------------------------------------------
// Artifact Experimental Setup Command
// -----------------------------------------------------------------------------
/**
* Generates Agent Skills for the experimental artifact workflow.
* Creates .claude/skills/ directory with SKILL.md files following Agent Skills spec.
*/
async function artifactExperimentalSetupCommand(): Promise<void> {
const spinner = ora('Setting up experimental artifact workflow skills...').start();
try {
const projectRoot = process.cwd();
const skillsDir = path.join(projectRoot, '.claude', 'skills');
// Get skill templates
const newChangeSkill = getNewChangeSkillTemplate();
const continueChangeSkill = getContinueChangeSkillTemplate();
const applyChangeSkill = getApplyChangeSkillTemplate();
// Create skill directories and SKILL.md files
const skills = [
{ template: newChangeSkill, dirName: 'openspec-new-change' },
{ template: continueChangeSkill, dirName: 'openspec-continue-change' },
{ template: applyChangeSkill, dirName: 'openspec-apply-change' },
];
const createdFiles: string[] = [];
for (const { template, dirName } of skills) {
const skillDir = path.join(skillsDir, dirName);
const skillFile = path.join(skillDir, 'SKILL.md');
// Generate SKILL.md content with YAML frontmatter
const skillContent = `---
name: ${template.name}
description: ${template.description}
---
${template.instructions}
`;
// Write the skill file
await FileSystemUtils.writeFile(skillFile, skillContent);
createdFiles.push(path.relative(projectRoot, skillFile));
}
spinner.succeed('Experimental artifact workflow setup complete!');
// Print success message
console.log();
console.log(chalk.bold('🧪 Experimental Artifact Workflow Skills Created'));
console.log();
for (const file of createdFiles) {
console.log(chalk.green(' ✓ ' + file));
}
console.log();
console.log(chalk.bold('📖 Usage:'));
console.log();
console.log(' Skills work automatically in compatible editors:');
console.log(' • ' + chalk.cyan('Claude Code') + ' - Auto-detected, ready to use');
console.log(' • ' + chalk.cyan('Cursor') + ' - Enable in Settings → Rules → Import Settings');
console.log(' • ' + chalk.cyan('Windsurf') + ' - Auto-imports from .claude directory');
console.log();
console.log(' Ask Claude naturally:');
console.log(' • "I want to start a new OpenSpec change to add <feature>"');
console.log(' • "Continue working on this change"');
console.log();
console.log(' Claude will automatically use the appropriate skill.');
console.log();
console.log(chalk.yellow('💡 This is an experimental feature.'));
console.log(' Feedback welcome at: https://github.com/Fission-AI/OpenSpec/issues');
console.log();
} catch (error) {
spinner.fail('Failed to setup experimental artifact workflow');
throw error;
}
}
// -----------------------------------------------------------------------------
// Command Registration
// -----------------------------------------------------------------------------
@@ -526,33 +778,21 @@ export function registerArtifactWorkflowCommands(program: Command): void {
}
});
// Next command
program
.command('next')
.description('[Experimental] Show artifacts ready to be created')
.option('--change <id>', 'Change name to check')
.option('--schema <name>', `Schema to use (default: ${DEFAULT_SCHEMA})`)
.option('--json', 'Output as JSON array of ready artifact IDs')
.action(async (options: NextOptions) => {
try {
await nextCommand(options);
} catch (error) {
console.log();
ora().fail(`Error: ${(error as Error).message}`);
process.exit(1);
}
});
// Instructions command
program
.command('instructions [artifact]')
.description('[Experimental] Output enriched instructions for creating an artifact')
.description('[Experimental] Output enriched instructions for creating an artifact or applying tasks')
.option('--change <id>', 'Change name')
.option('--schema <name>', `Schema to use (default: ${DEFAULT_SCHEMA})`)
.option('--json', 'Output as JSON')
.action(async (artifactId: string | undefined, options: InstructionsOptions) => {
try {
await instructionsCommand(artifactId, options);
// Special case: "apply" is not an artifact, but a command to get apply instructions
if (artifactId === 'apply') {
await applyInstructionsCommand(options);
} else {
await instructionsCommand(artifactId, options);
}
} catch (error) {
console.log();
ora().fail(`Error: ${(error as Error).message}`);
@@ -592,4 +832,18 @@ export function registerArtifactWorkflowCommands(program: Command): void {
process.exit(1);
}
});
// Artifact experimental setup command
program
.command('artifact-experimental-setup')
.description('[Experimental] Setup Agent Skills for the experimental artifact workflow')
.action(async () => {
try {
await artifactExperimentalSetupCommand();
} catch (error) {
console.log();
ora().fail(`Error: ${(error as Error).message}`);
process.exit(1);
}
});
}
+321
View File
@@ -0,0 +1,321 @@
/**
* Agent Skill Templates
*
* Templates for generating Agent Skills compatible with:
* - Claude Code
* - Cursor (Settings → Rules → Import Settings)
* - Windsurf
* - Other Agent Skills-compatible editors
*/
export interface SkillTemplate {
name: string;
description: string;
instructions: string;
}
/**
* Template for openspec-new-change skill
* Based on /opsx:new command
*/
export function getNewChangeSkillTemplate(): SkillTemplate {
return {
name: 'openspec-new-change',
description: 'Start a new OpenSpec change using the experimental artifact workflow. Use when the user wants to create a new feature, fix, or modification with a structured step-by-step approach.',
instructions: `Start a new change using the experimental artifact-driven approach.
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
**Steps**
1. **If no clear input provided, ask what they want to build**
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
> "What change do you want to work on? Describe what you want to build or fix."
From their description, derive a kebab-case name (e.g., "add user authentication" → \`add-user-auth\`).
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
2. **Create the change directory**
\`\`\`bash
openspec new change "<name>"
\`\`\`
This creates a scaffolded change at \`openspec/changes/<name>/\`.
3. **Show the artifact status**
\`\`\`bash
openspec status --change "<name>"
\`\`\`
This shows which artifacts need to be created and which are ready (dependencies satisfied).
4. **Get instructions for the first artifact**
The first artifact is always \`proposal\` (no dependencies).
\`\`\`bash
openspec instructions proposal --change "<name>"
\`\`\`
This outputs the template and context for creating the proposal.
5. **STOP and wait for user direction**
**Output**
After completing the steps, summarize:
- Change name and location
- Current status (0/4 artifacts complete)
- The template for the proposal artifact
- Prompt: "Ready to create the proposal? Just describe what this change is about and I'll draft the proposal, or ask me to continue."
**Guardrails**
- Do NOT create any artifacts yet - just show the instructions
- Do NOT advance beyond showing the proposal template
- If the name is invalid (not kebab-case), ask for a valid name
- If a change with that name already exists, suggest continuing that change instead`
};
}
/**
* Template for openspec-continue-change skill
* Based on /opsx:continue command
*/
export function getContinueChangeSkillTemplate(): SkillTemplate {
return {
name: 'openspec-continue-change',
description: 'Continue working on an OpenSpec change by creating the next artifact. Use when the user wants to progress their change, create the next artifact, or continue their workflow.',
instructions: `Continue working on a change by creating the next artifact.
**Input**: Optionally specify a change name. If omitted, MUST prompt for available changes.
**Steps**
1. **If no change name provided, prompt for selection**
Run \`openspec list --json\` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to work on.
Present the top 3-4 most recently modified changes as options, showing:
- Change name
- Status (e.g., "0/5 tasks", "complete", "no tasks")
- How recently it was modified (from \`lastModified\` field)
Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to continue.
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
2. **Check current status**
\`\`\`bash
openspec status --change "<name>" --json
\`\`\`
Parse the JSON to understand current state.
3. **Act based on status**:
---
**If all artifacts are complete (\`isComplete: true\`)**:
- Congratulate the user
- Show final status
- Suggest: "All artifacts created! You can now implement this change or archive it."
- STOP
---
**If artifacts are ready to create** (status shows artifacts with \`status: "ready"\`):
- Pick the FIRST artifact with \`status: "ready"\` from the status output
- Get its instructions:
\`\`\`bash
openspec instructions <artifact-id> --change "<name>" --json
\`\`\`
- Parse the JSON to get template, dependencies, and what it unlocks
- **Create the artifact file** using the template as a starting point:
- Read any completed dependency files for context
- Fill in the template based on context and user's goals
- Write to the output path specified in instructions
- Show what was created and what's now unlocked
- STOP after creating ONE artifact
---
**If no artifacts are ready (all blocked)**:
- This shouldn't happen with a valid schema
- Show status and suggest checking for issues
4. **After creating an artifact, show progress**
\`\`\`bash
openspec status --change "<name>"
\`\`\`
**Output**
After each invocation, show:
- Which artifact was created
- Current progress (N/M complete)
- What artifacts are now unlocked
- Prompt: "Want to continue? Just ask me to continue or tell me what to do next."
**Artifact Creation Guidelines**
When filling in templates:
- **proposal.md**: Ask user about the change if not clear. Fill in Why, What Changes, Capabilities, Impact.
- **IMPORTANT**: The Capabilities section is critical. Before filling it in:
- Check \`openspec/specs/\` for existing capabilities
- List new capabilities with kebab-case names (e.g., \`user-auth\`, \`data-export\`)
- List modified capabilities that need spec updates
- Each capability listed will need a corresponding spec file in the next phase.
- **specs/*.md**: Create one spec per capability listed in the proposal. Use \`specs/<capability-name>/spec.md\` path.
- **design.md**: Document technical decisions, architecture, and implementation approach.
- **tasks.md**: Break down implementation into checkboxed tasks based on specs and design.
**Guardrails**
- Create ONE artifact per invocation
- Always read dependency artifacts before creating a new one
- Never skip artifacts or create out of order
- If context is unclear, ask the user before creating
- Verify the artifact file exists after writing before marking progress`
};
}
/**
* Template for openspec-apply-change skill
* For implementing tasks from a completed (or in-progress) change
*/
export function getApplyChangeSkillTemplate(): SkillTemplate {
return {
name: 'openspec-apply-change',
description: 'Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.',
instructions: `Implement tasks from an OpenSpec change.
**Input**: Optionally specify a change name. If omitted, MUST prompt for available changes.
**Steps**
1. **If no change name provided, prompt for selection**
Run \`openspec list --json\` to get available changes. Use the **AskUserQuestion tool** to let the user select.
Show changes that have tasks.md (implementation-ready).
Mark changes with incomplete tasks as "(In Progress)".
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
2. **Get apply instructions**
\`\`\`bash
openspec instructions apply --change "<name>" --json
\`\`\`
This returns:
- Context file paths (proposal, specs, design, tasks)
- Progress (total, complete, remaining)
- Task list with status
- Dynamic instruction based on current state
**Handle states:**
- If \`state: "blocked"\` (missing artifacts): show message, suggest using openspec-continue-change
- If \`state: "all_done"\`: congratulate, suggest archive
- Otherwise: proceed to implementation
3. **Read context files**
Read the files listed in \`contextFiles\`:
- \`proposal\` - why and what
- \`specs\` - requirements and scenarios (use glob pattern to find all)
- \`design\` - technical approach (if exists)
- \`tasks\` - the implementation checklist
4. **Show current progress**
Display:
- Progress: "N/M tasks complete"
- Remaining tasks overview
- Dynamic instruction from CLI
5. **Implement tasks (loop until done or blocked)**
For each pending task:
- Show which task is being worked on
- Make the code changes required
- Keep changes minimal and focused
- Mark task complete in tasks.md: \`- [ ]\` → \`- [x]\`
- Continue to next task
**Pause if:**
- Task is unclear → ask for clarification
- Implementation reveals a design issue → suggest updating artifacts
- Error or blocker encountered → report and wait for guidance
- User interrupts
6. **On completion or pause, show status**
Display:
- Tasks completed this session
- Overall progress: "N/M tasks complete"
- If all done: suggest archive
- If paused: explain why and wait for guidance
**Output During Implementation**
\`\`\`
## Implementing: <change-name>
Working on task 3/7: <task description>
[...implementation happening...]
✓ Task complete
Working on task 4/7: <task description>
[...implementation happening...]
✓ Task complete
\`\`\`
**Output On Completion**
\`\`\`
## Implementation Complete
**Change:** <change-name>
**Progress:** 7/7 tasks complete ✓
### Completed This Session
- [x] Task 1
- [x] Task 2
...
All tasks complete! Ready to archive this change.
\`\`\`
**Output On Pause (Issue Encountered)**
\`\`\`
## Implementation Paused
**Change:** <change-name>
**Progress:** 4/7 tasks complete
### Issue Encountered
<description of the issue>
**Options:**
1. <option 1>
2. <option 2>
3. Other approach
What would you like to do?
\`\`\`
**Guardrails**
- Keep going through tasks until done or blocked
- Always read context before starting (specs, design)
- If task is ambiguous, pause and ask before implementing
- If implementation reveals issues, pause and suggest artifact updates
- Keep code changes minimal and scoped to each task
- Update task checkbox immediately after completing each task
- Pause on errors, blockers, or unclear requirements - don't guess
**Fluid Workflow Integration**
This skill supports the "actions on a change" model:
- **Can be invoked anytime**: Before all artifacts are done (if tasks.md exists), after partial implementation, interleaved with other actions
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly`
};
}
-68
View File
@@ -187,68 +187,6 @@ describe('artifact-workflow CLI commands', () => {
});
});
describe('next command', () => {
it('shows proposal as next for scaffolded change', async () => {
// Create empty change directory (no proposal.md)
const changeDir = path.join(changesDir, 'scaffolded-change');
await fs.mkdir(changeDir, { recursive: true });
const result = await runCLI(['next', '--change', 'scaffolded-change'], { cwd: tempDir });
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain('Artifacts ready to create');
expect(result.stdout).toContain('proposal');
});
it('shows design and specs as next when proposal exists', async () => {
// createTestChange always creates proposal.md, so design and specs are ready
await createTestChange('minimal-change');
const result = await runCLI(['next', '--change', 'minimal-change'], { cwd: tempDir });
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain('Artifacts ready to create');
expect(result.stdout).toContain('design');
expect(result.stdout).toContain('specs');
});
it('shows tasks as next after proposal, design, and specs', async () => {
await createTestChange('after-specs', ['proposal', 'design', 'specs']);
const result = await runCLI(['next', '--change', 'after-specs'], { cwd: tempDir });
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain('tasks');
});
it('shows complete message when all done', async () => {
await createTestChange('complete-change', ['proposal', 'design', 'specs', 'tasks']);
const result = await runCLI(['next', '--change', 'complete-change'], { cwd: tempDir });
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain('All artifacts are complete!');
});
it('outputs JSON array of ready artifacts', async () => {
await createTestChange('json-next', ['proposal']);
const result = await runCLI(['next', '--change', 'json-next', '--json'], { cwd: tempDir });
expect(result.exitCode).toBe(0);
const json = JSON.parse(result.stdout);
expect(Array.isArray(json)).toBe(true);
expect(json).toContain('design');
expect(json).toContain('specs');
});
it('errors when --change is missing and lists available changes', async () => {
await createTestChange('some-change');
const result = await runCLI(['next'], { cwd: tempDir });
expect(result.exitCode).toBe(1);
const output = getOutput(result);
expect(output).toContain('Missing required option --change');
expect(output).toContain('some-change');
});
});
describe('instructions command', () => {
it('shows instructions for proposal on scaffolded change', async () => {
// Create empty change directory (no proposal.md)
@@ -417,12 +355,6 @@ describe('artifact-workflow CLI commands', () => {
expect(result.stdout).toContain('[Experimental]');
});
it('marks next command as experimental in help', async () => {
const result = await runCLI(['next', '--help']);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain('[Experimental]');
});
it('marks instructions command as experimental in help', async () => {
const result = await runCLI(['instructions', '--help']);
expect(result.exitCode).toBe(0);