diff --git a/src/commands/artifact-workflow.ts b/src/commands/artifact-workflow.ts index cedd5145..3c31aaec 100644 --- a/src/commands/artifact-workflow.ts +++ b/src/commands/artifact-workflow.ts @@ -26,7 +26,7 @@ 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 { getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getOpsxNewCommandTemplate, getOpsxContinueCommandTemplate, getOpsxApplyCommandTemplate } from '../core/templates/skill-templates.js'; import { FileSystemUtils } from '../utils/file-system.js'; // ----------------------------------------------------------------------------- @@ -678,21 +678,28 @@ async function newChangeCommand(name: string | undefined, options: NewChangeOpti // ----------------------------------------------------------------------------- /** - * Generates Agent Skills for the experimental artifact workflow. + * Generates Agent Skills and slash commands for the experimental artifact workflow. * Creates .claude/skills/ directory with SKILL.md files following Agent Skills spec. + * Creates .claude/commands/opsx/ directory with slash command files. */ async function artifactExperimentalSetupCommand(): Promise { - const spinner = ora('Setting up experimental artifact workflow skills...').start(); + const spinner = ora('Setting up experimental artifact workflow...').start(); try { const projectRoot = process.cwd(); const skillsDir = path.join(projectRoot, '.claude', 'skills'); + const commandsDir = path.join(projectRoot, '.claude', 'commands', 'opsx'); // Get skill templates const newChangeSkill = getNewChangeSkillTemplate(); const continueChangeSkill = getContinueChangeSkillTemplate(); const applyChangeSkill = getApplyChangeSkillTemplate(); + // Get command templates + const newCommand = getOpsxNewCommandTemplate(); + const continueCommand = getOpsxContinueCommandTemplate(); + const applyCommand = getOpsxApplyCommandTemplate(); + // Create skill directories and SKILL.md files const skills = [ { template: newChangeSkill, dirName: 'openspec-new-change' }, @@ -700,7 +707,7 @@ async function artifactExperimentalSetupCommand(): Promise { { template: applyChangeSkill, dirName: 'openspec-apply-change' }, ]; - const createdFiles: string[] = []; + const createdSkillFiles: string[] = []; for (const { template, dirName } of skills) { const skillDir = path.join(skillsDir, dirName); @@ -717,31 +724,69 @@ ${template.instructions} // Write the skill file await FileSystemUtils.writeFile(skillFile, skillContent); - createdFiles.push(path.relative(projectRoot, skillFile)); + createdSkillFiles.push(path.relative(projectRoot, skillFile)); + } + + // Create slash command files + const commands = [ + { template: newCommand, fileName: 'new.md' }, + { template: continueCommand, fileName: 'continue.md' }, + { template: applyCommand, fileName: 'apply.md' }, + ]; + + const createdCommandFiles: string[] = []; + + for (const { template, fileName } of commands) { + const commandFile = path.join(commandsDir, fileName); + + // Generate command content with YAML frontmatter + const commandContent = `--- +name: ${template.name} +description: ${template.description} +category: ${template.category} +tags: [${template.tags.join(', ')}] +--- + +${template.content} +`; + + // Write the command file + await FileSystemUtils.writeFile(commandFile, commandContent); + createdCommandFiles.push(path.relative(projectRoot, commandFile)); } spinner.succeed('Experimental artifact workflow setup complete!'); // Print success message console.log(); - console.log(chalk.bold('๐Ÿงช Experimental Artifact Workflow Skills Created')); + console.log(chalk.bold('๐Ÿงช Experimental Artifact Workflow Setup Complete')); + console.log(); + console.log(chalk.bold('Skills Created:')); + for (const file of createdSkillFiles) { + console.log(chalk.green(' โœ“ ' + file)); + } console.log(); - for (const file of createdFiles) { + console.log(chalk.bold('Slash Commands Created:')); + for (const file of createdCommandFiles) { 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(' ' + chalk.cyan('Skills') + ' work automatically in compatible editors:'); + console.log(' โ€ข Claude Code - Auto-detected, ready to use'); + console.log(' โ€ข Cursor - Enable in Settings โ†’ Rules โ†’ Import Settings'); + console.log(' โ€ข 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 "'); console.log(' โ€ข "Continue working on this change"'); + console.log(' โ€ข "Implement the tasks for this change"'); console.log(); - console.log(' Claude will automatically use the appropriate skill.'); + console.log(' ' + chalk.cyan('Slash Commands') + ' for explicit invocation:'); + console.log(' โ€ข /opsx:new - Start a new change'); + console.log(' โ€ข /opsx:continue - Create the next artifact'); + console.log(' โ€ข /opsx:apply - Implement tasks'); console.log(); console.log(chalk.yellow('๐Ÿ’ก This is an experimental feature.')); console.log(' Feedback welcome at: https://github.com/Fission-AI/OpenSpec/issues'); diff --git a/src/core/templates/skill-templates.ts b/src/core/templates/skill-templates.ts index 7622ba01..23011dd7 100644 --- a/src/core/templates/skill-templates.ts +++ b/src/core/templates/skill-templates.ts @@ -319,3 +319,324 @@ This skill supports the "actions on a change" model: - **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly` }; } + +// ----------------------------------------------------------------------------- +// Slash Command Templates +// ----------------------------------------------------------------------------- + +export interface CommandTemplate { + name: string; + description: string; + category: string; + tags: string[]; + content: string; +} + +/** + * Template for /opsx:new slash command + */ +export function getOpsxNewCommandTemplate(): CommandTemplate { + return { + name: 'OPSX: New', + description: 'Start a new change using the experimental artifact workflow (OPSX)', + category: 'Workflow', + tags: ['workflow', 'artifacts', 'experimental'], + content: `Start a new change using the experimental artifact-driven approach. + +**Input**: The argument after \`/opsx:new\` is the change name (kebab-case), OR a description of what the user wants to build. + +**Steps** + +1. **If no 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 "" + \`\`\` + This creates a scaffolded change at \`openspec/changes//\`. + +3. **Show the artifact status** + \`\`\`bash + openspec status --change "" + \`\`\` + 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 "" + \`\`\` + 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? Run \`/opsx:continue\` or just describe what this change is about and I'll draft the proposal." + +**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 using \`/opsx:continue\` instead` + }; +} + +/** + * Template for /opsx:continue slash command + */ +export function getOpsxContinueCommandTemplate(): CommandTemplate { + return { + name: 'OPSX: Continue', + description: 'Continue working on a change - create the next artifact (Experimental)', + category: 'Workflow', + tags: ['workflow', 'artifacts', 'experimental'], + content: `Continue working on a change by creating the next artifact. + +**Input**: Optionally specify \`--change \` after \`/opsx:continue\`. 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 "" --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 --change "" --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 "" + \`\`\` + +**Output** + +After each invocation, show: +- Which artifact was created +- Current progress (N/M complete) +- What artifacts are now unlocked +- Prompt: "Run \`/opsx:continue\` to create the next artifact" + +**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//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 /opsx:apply slash command + */ +export function getOpsxApplyCommandTemplate(): CommandTemplate { + return { + name: 'OPSX: Apply', + description: 'Implement tasks from an OpenSpec change (Experimental)', + category: 'Workflow', + tags: ['workflow', 'artifacts', 'experimental'], + content: `Implement tasks from an OpenSpec change. + +**Input**: Optionally specify \`--change \` after \`/opsx:apply\`. 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 "" --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 \`/opsx:continue\` + - 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: + +Working on task 3/7: +[...implementation happening...] +โœ“ Task complete + +Working on task 4/7: +[...implementation happening...] +โœ“ Task complete +\`\`\` + +**Output On Completion** + +\`\`\` +## Implementation Complete + +**Change:** +**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:** +**Progress:** 4/7 tasks complete + +### Issue Encountered + + +**Options:** +1.