mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-16 05:40:40 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fdf4253ae | ||
|
|
f608192bff | ||
|
|
91aa68abc3 | ||
|
|
f3d5fb150f | ||
|
|
141dcb4be6 | ||
|
|
9f58815e06 | ||
|
|
5b22ee5811 | ||
|
|
7832c4dce0 | ||
|
|
c28020d6d5 | ||
|
|
84215c7e6f | ||
|
|
5bcdfb8901 |
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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`
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user