diff --git a/src/commands/artifact-workflow.ts b/src/commands/artifact-workflow.ts index 1debc5ea..550752f4 100644 --- a/src/commands/artifact-workflow.ts +++ b/src/commands/artifact-workflow.ts @@ -28,7 +28,7 @@ import { type SchemaInfo, } from '../core/artifact-graph/index.js'; import { createChange, validateChangeName } from '../utils/change-utils.js'; -import { getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getOpsxNewCommandTemplate, getOpsxContinueCommandTemplate, getOpsxApplyCommandTemplate } from '../core/templates/skill-templates.js'; +import { getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getFfChangeSkillTemplate, getOpsxNewCommandTemplate, getOpsxContinueCommandTemplate, getOpsxApplyCommandTemplate, getOpsxFfCommandTemplate } from '../core/templates/skill-templates.js'; import { FileSystemUtils } from '../utils/file-system.js'; // ----------------------------------------------------------------------------- @@ -796,17 +796,20 @@ async function artifactExperimentalSetupCommand(): Promise { const newChangeSkill = getNewChangeSkillTemplate(); const continueChangeSkill = getContinueChangeSkillTemplate(); const applyChangeSkill = getApplyChangeSkillTemplate(); + const ffChangeSkill = getFfChangeSkillTemplate(); // Get command templates const newCommand = getOpsxNewCommandTemplate(); const continueCommand = getOpsxContinueCommandTemplate(); const applyCommand = getOpsxApplyCommandTemplate(); + const ffCommand = getOpsxFfCommandTemplate(); // 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' }, + { template: ffChangeSkill, dirName: 'openspec-ff-change' }, ]; const createdSkillFiles: string[] = []; @@ -834,6 +837,7 @@ ${template.instructions} { template: newCommand, fileName: 'new.md' }, { template: continueCommand, fileName: 'continue.md' }, { template: applyCommand, fileName: 'apply.md' }, + { template: ffCommand, fileName: 'ff.md' }, ]; const createdCommandFiles: string[] = []; @@ -889,6 +893,7 @@ ${template.content} console.log(' • /opsx:new - Start a new change'); console.log(' • /opsx:continue - Create the next artifact'); console.log(' • /opsx:apply - Implement tasks'); + console.log(' • /opsx:ff - Fast-forward: create all artifacts at once'); 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/artifact-graph/instruction-loader.ts b/src/core/artifact-graph/instruction-loader.ts index 4432e512..92b6de2c 100644 --- a/src/core/artifact-graph/instruction-loader.ts +++ b/src/core/artifact-graph/instruction-loader.ts @@ -99,6 +99,8 @@ export interface ChangeStatus { schemaName: string; /** Whether all artifacts are complete */ isComplete: boolean; + /** Artifact IDs required before apply phase (from schema's apply.requires) */ + applyRequires: string[]; /** Status of each artifact */ artifacts: ArtifactStatus[]; } @@ -252,6 +254,10 @@ function getUnlockedArtifacts(graph: ArtifactGraph, artifactId: string): string[ * @returns Formatted change status */ export function formatChangeStatus(context: ChangeContext): ChangeStatus { + // Load schema to get apply phase configuration + const schema = resolveSchema(context.schemaName); + const applyRequires = schema.apply?.requires ?? schema.artifacts.map(a => a.id); + const artifacts = context.graph.getAllArtifacts(); const ready = new Set(context.graph.getNextArtifacts(context.completed)); const blocked = context.graph.getBlocked(context.completed); @@ -290,6 +296,7 @@ export function formatChangeStatus(context: ChangeContext): ChangeStatus { changeName: context.changeName, schemaName: context.schemaName, isComplete: context.graph.isComplete(context.completed), + applyRequires, artifacts: artifactStatuses, }; } diff --git a/src/core/templates/skill-templates.ts b/src/core/templates/skill-templates.ts index f27ee4bf..d5a0a9a8 100644 --- a/src/core/templates/skill-templates.ts +++ b/src/core/templates/skill-templates.ts @@ -360,6 +360,101 @@ This skill supports the "actions on a change" model: }; } +/** + * Template for openspec-ff-change skill + * Fast-forward through artifact creation + */ +export function getFfChangeSkillTemplate(): SkillTemplate { + return { + name: 'openspec-ff-change', + description: 'Fast-forward through OpenSpec artifact creation. Use when the user wants to quickly create all artifacts needed for implementation without stepping through each one individually.', + instructions: `Fast-forward through artifact creation - generate everything needed to start implementation in one go. + +**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 "" + \`\`\` + This creates a scaffolded change at \`openspec/changes//\`. + +3. **Get the artifact build order** + \`\`\`bash + openspec status --change "" --json + \`\`\` + Parse the JSON to get: + - \`applyRequires\`: array of artifact IDs needed before implementation (e.g., \`["tasks"]\`) + - \`artifacts\`: list of all artifacts with their status and dependencies + +4. **Create artifacts in sequence until apply-ready** + + Use the **TodoWrite tool** to track progress through the artifacts. + + Loop through artifacts in dependency order (artifacts with no pending dependencies first): + + a. **For each artifact that is \`ready\` (dependencies satisfied)**: + - Get instructions: + \`\`\`bash + openspec instructions --change "" --json + \`\`\` + - The instructions JSON includes: + - \`template\`: The template content to use + - \`instruction\`: Schema-specific guidance for this artifact type + - \`outputPath\`: Where to write the artifact + - \`dependencies\`: Completed artifacts to read for context + - Read any completed dependency files for context + - Create the artifact file following the schema's \`instruction\` + - Show brief progress: "✓ Created " + + b. **Continue until all \`applyRequires\` artifacts are complete** + - After creating each artifact, re-run \`openspec status --change "" --json\` + - Check if every artifact ID in \`applyRequires\` has \`status: "done"\` in the artifacts array + - Stop when all \`applyRequires\` artifacts are done + + c. **If an artifact requires user input** (unclear context): + - Use **AskUserQuestion tool** to clarify + - Then continue with creation + +5. **Show final status** + \`\`\`bash + openspec status --change "" + \`\`\` + +**Output** + +After completing all artifacts, summarize: +- Change name and location +- List of artifacts created with brief descriptions +- What's ready: "All artifacts created! Ready for implementation." +- Prompt: "Run \`/opsx:apply\` or ask me to implement to start working on the tasks." + +**Artifact Creation Guidelines** + +- Follow the \`instruction\` field from \`openspec instructions\` for each artifact type +- The schema defines what each artifact should contain - follow it +- Read dependency artifacts for context before creating new ones +- Use the \`template\` as a starting point, filling in based on context + +**Guardrails** +- Create ALL artifacts needed for implementation (as defined by schema's \`apply.requires\`) +- Always read dependency artifacts before creating a new one +- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum +- If a change with that name already exists, suggest continuing that change instead +- Verify each artifact file exists after writing before proceeding to next` + }; +} + // ----------------------------------------------------------------------------- // Slash Command Templates // ----------------------------------------------------------------------------- @@ -719,3 +814,100 @@ 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` }; } + + +/** + * Template for /opsx:ff slash command + */ +export function getOpsxFfCommandTemplate(): CommandTemplate { + return { + name: 'OPSX: Fast Forward', + description: 'Create a change and generate all artifacts needed for implementation in one go', + category: 'Workflow', + tags: ['workflow', 'artifacts', 'experimental'], + content: `Fast-forward through artifact creation - generate everything needed to start implementation. + +**Input**: The argument after \`/opsx:ff\` 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. **Get the artifact build order** + \`\`\`bash + openspec status --change "" --json + \`\`\` + Parse the JSON to get: + - \`applyRequires\`: array of artifact IDs needed before implementation (e.g., \`["tasks"]\`) + - \`artifacts\`: list of all artifacts with their status and dependencies + +4. **Create artifacts in sequence until apply-ready** + + Use the **TodoWrite tool** to track progress through the artifacts. + + Loop through artifacts in dependency order (artifacts with no pending dependencies first): + + a. **For each artifact that is \`ready\` (dependencies satisfied)**: + - Get instructions: + \`\`\`bash + openspec instructions --change "" --json + \`\`\` + - The instructions JSON includes: + - \`template\`: The template content to use + - \`instruction\`: Schema-specific guidance for this artifact type + - \`outputPath\`: Where to write the artifact + - \`dependencies\`: Completed artifacts to read for context + - Read any completed dependency files for context + - Create the artifact file following the schema's \`instruction\` + - Show brief progress: "✓ Created " + + b. **Continue until all \`applyRequires\` artifacts are complete** + - After creating each artifact, re-run \`openspec status --change "" --json\` + - Check if every artifact ID in \`applyRequires\` has \`status: "done"\` in the artifacts array + - Stop when all \`applyRequires\` artifacts are done + + c. **If an artifact requires user input** (unclear context): + - Use **AskUserQuestion tool** to clarify + - Then continue with creation + +5. **Show final status** + \`\`\`bash + openspec status --change "" + \`\`\` + +**Output** + +After completing all artifacts, summarize: +- Change name and location +- List of artifacts created with brief descriptions +- What's ready: "All artifacts created! Ready for implementation." +- Prompt: "Run \`/opsx:apply\` to start implementing." + +**Artifact Creation Guidelines** + +- Follow the \`instruction\` field from \`openspec instructions\` for each artifact type +- The schema defines what each artifact should contain - follow it +- Read dependency artifacts for context before creating new ones +- Use the \`template\` as a starting point, filling in based on context + +**Guardrails** +- Create ALL artifacts needed for implementation (as defined by schema's \`apply.requires\`) +- Always read dependency artifacts before creating a new one +- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum +- If a change with that name already exists, ask if user wants to continue it or create a new one +- Verify each artifact file exists after writing before proceeding to next` + }; +}