diff --git a/src/commands/artifact-workflow.ts b/src/commands/artifact-workflow.ts index a3e2770e..b6b9c8e5 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 { getExploreSkillTemplate, getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getFfChangeSkillTemplate, getSyncSpecsSkillTemplate, getArchiveChangeSkillTemplate, getVerifyChangeSkillTemplate, getOpsxExploreCommandTemplate, getOpsxNewCommandTemplate, getOpsxContinueCommandTemplate, getOpsxApplyCommandTemplate, getOpsxFfCommandTemplate, getOpsxSyncCommandTemplate, getOpsxArchiveCommandTemplate, getOpsxVerifyCommandTemplate } from '../core/templates/skill-templates.js'; +import { getExploreSkillTemplate, getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getFfChangeSkillTemplate, getSyncSpecsSkillTemplate, getArchiveChangeSkillTemplate, getBulkArchiveChangeSkillTemplate, getVerifyChangeSkillTemplate, getOpsxExploreCommandTemplate, getOpsxNewCommandTemplate, getOpsxContinueCommandTemplate, getOpsxApplyCommandTemplate, getOpsxFfCommandTemplate, getOpsxSyncCommandTemplate, getOpsxArchiveCommandTemplate, getOpsxBulkArchiveCommandTemplate, getOpsxVerifyCommandTemplate } from '../core/templates/skill-templates.js'; import { FileSystemUtils } from '../utils/file-system.js'; import { promptForConfig, serializeConfig, isExitPromptError } from '../core/config-prompts.js'; import { readProjectConfig } from '../core/project-config.js'; @@ -818,6 +818,7 @@ async function artifactExperimentalSetupCommand(): Promise { const ffChangeSkill = getFfChangeSkillTemplate(); const syncSpecsSkill = getSyncSpecsSkillTemplate(); const archiveChangeSkill = getArchiveChangeSkillTemplate(); + const bulkArchiveChangeSkill = getBulkArchiveChangeSkillTemplate(); const verifyChangeSkill = getVerifyChangeSkillTemplate(); // Get command templates @@ -828,6 +829,7 @@ async function artifactExperimentalSetupCommand(): Promise { const ffCommand = getOpsxFfCommandTemplate(); const syncCommand = getOpsxSyncCommandTemplate(); const archiveCommand = getOpsxArchiveCommandTemplate(); + const bulkArchiveCommand = getOpsxBulkArchiveCommandTemplate(); const verifyCommand = getOpsxVerifyCommandTemplate(); // Create skill directories and SKILL.md files @@ -839,6 +841,7 @@ async function artifactExperimentalSetupCommand(): Promise { { template: ffChangeSkill, dirName: 'openspec-ff-change' }, { template: syncSpecsSkill, dirName: 'openspec-sync-specs' }, { template: archiveChangeSkill, dirName: 'openspec-archive-change' }, + { template: bulkArchiveChangeSkill, dirName: 'openspec-bulk-archive-change' }, { template: verifyChangeSkill, dirName: 'openspec-verify-change' }, ]; @@ -871,6 +874,7 @@ ${template.instructions} { template: ffCommand, fileName: 'ff.md' }, { template: syncCommand, fileName: 'sync.md' }, { template: archiveCommand, fileName: 'archive.md' }, + { template: bulkArchiveCommand, fileName: 'bulk-archive.md' }, { template: verifyCommand, fileName: 'verify.md' }, ]; diff --git a/src/core/templates/skill-templates.ts b/src/core/templates/skill-templates.ts index 3008b82f..8e705f1c 100644 --- a/src/core/templates/skill-templates.ts +++ b/src/core/templates/skill-templates.ts @@ -1634,6 +1634,252 @@ All artifacts complete. All tasks complete. }; } +/** + * Template for openspec-bulk-archive-change skill + * For archiving multiple completed changes at once + */ +export function getBulkArchiveChangeSkillTemplate(): SkillTemplate { + return { + name: 'openspec-bulk-archive-change', + description: 'Archive multiple completed changes at once. Use when archiving several parallel changes.', + instructions: `Archive multiple completed changes in a single operation. + +This skill allows you to batch-archive changes, handling spec conflicts intelligently by checking the codebase to determine what's actually implemented. + +**Input**: None required (prompts for selection) + +**Steps** + +1. **Get active changes** + + Run \`openspec list --json\` to get all active changes. + + If no active changes exist, inform user and stop. + +2. **Prompt for change selection** + + Use **AskUserQuestion tool** with multi-select to let user choose changes: + - Show each change with its schema + - Include an option for "All changes" + - Allow any number of selections (1+ works, 2+ is the typical use case) + + **IMPORTANT**: Do NOT auto-select. Always let the user choose. + +3. **Batch validation - gather status for all selected changes** + + For each selected change, collect: + + a. **Artifact status** - Run \`openspec status --change "" --json\` + - Parse \`schemaName\` and \`artifacts\` list + - Note which artifacts are \`done\` vs other states + + b. **Task completion** - Read \`openspec/changes//tasks.md\` + - Count \`- [ ]\` (incomplete) vs \`- [x]\` (complete) + - If no tasks file exists, note as "No tasks" + + c. **Delta specs** - Check \`openspec/changes//specs/\` directory + - List which capability specs exist + - For each, extract requirement names (lines matching \`### Requirement: \`) + +4. **Detect spec conflicts** + + Build a map of \`capability -> [changes that touch it]\`: + + \`\`\` + auth -> [change-a, change-b] <- CONFLICT (2+ changes) + api -> [change-c] <- OK (only 1 change) + \`\`\` + + A conflict exists when 2+ selected changes have delta specs for the same capability. + +5. **Resolve conflicts agentically** + + **For each conflict**, investigate the codebase: + + a. **Read the delta specs** from each conflicting change to understand what each claims to add/modify + + b. **Search the codebase** for implementation evidence: + - Look for code implementing requirements from each delta spec + - Check for related files, functions, or tests + + c. **Determine resolution**: + - If only one change is actually implemented -> sync that one's specs + - If both implemented -> apply in chronological order (older first, newer overwrites) + - If neither implemented -> skip spec sync, warn user + + d. **Record resolution** for each conflict: + - Which change's specs to apply + - In what order (if both) + - Rationale (what was found in codebase) + +6. **Show consolidated status table** + + Display a table summarizing all changes: + + \`\`\` + | Change | Artifacts | Tasks | Specs | Conflicts | Status | + |---------------------|-----------|-------|---------|-----------|--------| + | schema-management | Done | 5/5 | 2 delta | None | Ready | + | project-config | Done | 3/3 | 1 delta | None | Ready | + | add-oauth | Done | 4/4 | 1 delta | auth (!) | Ready* | + | add-verify-skill | 1 left | 2/5 | None | None | Warn | + \`\`\` + + For conflicts, show the resolution: + \`\`\` + * Conflict resolution: + - auth spec: Will apply add-oauth then add-jwt (both implemented, chronological order) + \`\`\` + + For incomplete changes, show warnings: + \`\`\` + Warnings: + - add-verify-skill: 1 incomplete artifact, 3 incomplete tasks + \`\`\` + +7. **Confirm batch operation** + + Use **AskUserQuestion tool** with a single confirmation: + + - "Archive N changes?" with options based on status + - Options might include: + - "Archive all N changes" + - "Archive only N ready changes (skip incomplete)" + - "Cancel" + + If there are incomplete changes, make clear they'll be archived with warnings. + +8. **Execute archive for each confirmed change** + + Process changes in the determined order (respecting conflict resolution): + + a. **Sync specs** if delta specs exist: + - Use the openspec-sync-specs approach (agent-driven intelligent merge) + - For conflicts, apply in resolved order + - Track if sync was done + + b. **Perform the archive**: + \`\`\`bash + mkdir -p openspec/changes/archive + mv openspec/changes/ openspec/changes/archive/YYYY-MM-DD- + \`\`\` + + c. **Track outcome** for each change: + - Success: archived successfully + - Failed: error during archive (record error) + - Skipped: user chose not to archive (if applicable) + +9. **Display summary** + + Show final results: + + \`\`\` + ## Bulk Archive Complete + + Archived 3 changes: + - schema-management-cli -> archive/2026-01-19-schema-management-cli/ + - project-config -> archive/2026-01-19-project-config/ + - add-oauth -> archive/2026-01-19-add-oauth/ + + Skipped 1 change: + - add-verify-skill (user chose not to archive incomplete) + + Spec sync summary: + - 4 delta specs synced to main specs + - 1 conflict resolved (auth: applied both in chronological order) + \`\`\` + + If any failures: + \`\`\` + Failed 1 change: + - some-change: Archive directory already exists + \`\`\` + +**Conflict Resolution Examples** + +Example 1: Only one implemented +\`\`\` +Conflict: specs/auth/spec.md touched by [add-oauth, add-jwt] + +Checking add-oauth: +- Delta adds "OAuth Provider Integration" requirement +- Searching codebase... found src/auth/oauth.ts implementing OAuth flow + +Checking add-jwt: +- Delta adds "JWT Token Handling" requirement +- Searching codebase... no JWT implementation found + +Resolution: Only add-oauth is implemented. Will sync add-oauth specs only. +\`\`\` + +Example 2: Both implemented +\`\`\` +Conflict: specs/api/spec.md touched by [add-rest-api, add-graphql] + +Checking add-rest-api (created 2026-01-10): +- Delta adds "REST Endpoints" requirement +- Searching codebase... found src/api/rest.ts + +Checking add-graphql (created 2026-01-15): +- Delta adds "GraphQL Schema" requirement +- Searching codebase... found src/api/graphql.ts + +Resolution: Both implemented. Will apply add-rest-api specs first, +then add-graphql specs (chronological order, newer takes precedence). +\`\`\` + +**Output On Success** + +\`\`\` +## Bulk Archive Complete + +Archived N changes: +- -> archive/YYYY-MM-DD-/ +- -> archive/YYYY-MM-DD-/ + +Spec sync summary: +- N delta specs synced to main specs +- No conflicts (or: M conflicts resolved) +\`\`\` + +**Output On Partial Success** + +\`\`\` +## Bulk Archive Complete (partial) + +Archived N changes: +- -> archive/YYYY-MM-DD-/ + +Skipped M changes: +- (user chose not to archive incomplete) + +Failed K changes: +- : Archive directory already exists +\`\`\` + +**Output When No Changes** + +\`\`\` +## No Changes to Archive + +No active changes found. Use \`/opsx:new\` to create a new change. +\`\`\` + +**Guardrails** +- Allow any number of changes (1+ is fine, 2+ is the typical use case) +- Always prompt for selection, never auto-select +- Detect spec conflicts early and resolve by checking codebase +- When both changes are implemented, apply specs in chronological order +- Skip spec sync only when implementation is missing (warn user) +- Show clear per-change status before confirming +- Use single confirmation for entire batch +- Track and report all outcomes (success/skip/fail) +- Preserve .openspec.yaml when moving to archive +- Archive directory target uses current date: YYYY-MM-DD- +- If archive target exists, fail that change but continue with others` + }; +} + /** * Template for /opsx:sync slash command */ @@ -2103,6 +2349,253 @@ Target archive directory already exists. }; } +/** + * Template for /opsx:bulk-archive slash command + */ +export function getOpsxBulkArchiveCommandTemplate(): CommandTemplate { + return { + name: 'OPSX: Bulk Archive', + description: 'Archive multiple completed changes at once', + category: 'Workflow', + tags: ['workflow', 'archive', 'experimental', 'bulk'], + content: `Archive multiple completed changes in a single operation. + +This skill allows you to batch-archive changes, handling spec conflicts intelligently by checking the codebase to determine what's actually implemented. + +**Input**: None required (prompts for selection) + +**Steps** + +1. **Get active changes** + + Run \`openspec list --json\` to get all active changes. + + If no active changes exist, inform user and stop. + +2. **Prompt for change selection** + + Use **AskUserQuestion tool** with multi-select to let user choose changes: + - Show each change with its schema + - Include an option for "All changes" + - Allow any number of selections (1+ works, 2+ is the typical use case) + + **IMPORTANT**: Do NOT auto-select. Always let the user choose. + +3. **Batch validation - gather status for all selected changes** + + For each selected change, collect: + + a. **Artifact status** - Run \`openspec status --change "" --json\` + - Parse \`schemaName\` and \`artifacts\` list + - Note which artifacts are \`done\` vs other states + + b. **Task completion** - Read \`openspec/changes//tasks.md\` + - Count \`- [ ]\` (incomplete) vs \`- [x]\` (complete) + - If no tasks file exists, note as "No tasks" + + c. **Delta specs** - Check \`openspec/changes//specs/\` directory + - List which capability specs exist + - For each, extract requirement names (lines matching \`### Requirement: \`) + +4. **Detect spec conflicts** + + Build a map of \`capability -> [changes that touch it]\`: + + \`\`\` + auth -> [change-a, change-b] <- CONFLICT (2+ changes) + api -> [change-c] <- OK (only 1 change) + \`\`\` + + A conflict exists when 2+ selected changes have delta specs for the same capability. + +5. **Resolve conflicts agentically** + + **For each conflict**, investigate the codebase: + + a. **Read the delta specs** from each conflicting change to understand what each claims to add/modify + + b. **Search the codebase** for implementation evidence: + - Look for code implementing requirements from each delta spec + - Check for related files, functions, or tests + + c. **Determine resolution**: + - If only one change is actually implemented -> sync that one's specs + - If both implemented -> apply in chronological order (older first, newer overwrites) + - If neither implemented -> skip spec sync, warn user + + d. **Record resolution** for each conflict: + - Which change's specs to apply + - In what order (if both) + - Rationale (what was found in codebase) + +6. **Show consolidated status table** + + Display a table summarizing all changes: + + \`\`\` + | Change | Artifacts | Tasks | Specs | Conflicts | Status | + |---------------------|-----------|-------|---------|-----------|--------| + | schema-management | Done | 5/5 | 2 delta | None | Ready | + | project-config | Done | 3/3 | 1 delta | None | Ready | + | add-oauth | Done | 4/4 | 1 delta | auth (!) | Ready* | + | add-verify-skill | 1 left | 2/5 | None | None | Warn | + \`\`\` + + For conflicts, show the resolution: + \`\`\` + * Conflict resolution: + - auth spec: Will apply add-oauth then add-jwt (both implemented, chronological order) + \`\`\` + + For incomplete changes, show warnings: + \`\`\` + Warnings: + - add-verify-skill: 1 incomplete artifact, 3 incomplete tasks + \`\`\` + +7. **Confirm batch operation** + + Use **AskUserQuestion tool** with a single confirmation: + + - "Archive N changes?" with options based on status + - Options might include: + - "Archive all N changes" + - "Archive only N ready changes (skip incomplete)" + - "Cancel" + + If there are incomplete changes, make clear they'll be archived with warnings. + +8. **Execute archive for each confirmed change** + + Process changes in the determined order (respecting conflict resolution): + + a. **Sync specs** if delta specs exist: + - Use the openspec-sync-specs approach (agent-driven intelligent merge) + - For conflicts, apply in resolved order + - Track if sync was done + + b. **Perform the archive**: + \`\`\`bash + mkdir -p openspec/changes/archive + mv openspec/changes/ openspec/changes/archive/YYYY-MM-DD- + \`\`\` + + c. **Track outcome** for each change: + - Success: archived successfully + - Failed: error during archive (record error) + - Skipped: user chose not to archive (if applicable) + +9. **Display summary** + + Show final results: + + \`\`\` + ## Bulk Archive Complete + + Archived 3 changes: + - schema-management-cli -> archive/2026-01-19-schema-management-cli/ + - project-config -> archive/2026-01-19-project-config/ + - add-oauth -> archive/2026-01-19-add-oauth/ + + Skipped 1 change: + - add-verify-skill (user chose not to archive incomplete) + + Spec sync summary: + - 4 delta specs synced to main specs + - 1 conflict resolved (auth: applied both in chronological order) + \`\`\` + + If any failures: + \`\`\` + Failed 1 change: + - some-change: Archive directory already exists + \`\`\` + +**Conflict Resolution Examples** + +Example 1: Only one implemented +\`\`\` +Conflict: specs/auth/spec.md touched by [add-oauth, add-jwt] + +Checking add-oauth: +- Delta adds "OAuth Provider Integration" requirement +- Searching codebase... found src/auth/oauth.ts implementing OAuth flow + +Checking add-jwt: +- Delta adds "JWT Token Handling" requirement +- Searching codebase... no JWT implementation found + +Resolution: Only add-oauth is implemented. Will sync add-oauth specs only. +\`\`\` + +Example 2: Both implemented +\`\`\` +Conflict: specs/api/spec.md touched by [add-rest-api, add-graphql] + +Checking add-rest-api (created 2026-01-10): +- Delta adds "REST Endpoints" requirement +- Searching codebase... found src/api/rest.ts + +Checking add-graphql (created 2026-01-15): +- Delta adds "GraphQL Schema" requirement +- Searching codebase... found src/api/graphql.ts + +Resolution: Both implemented. Will apply add-rest-api specs first, +then add-graphql specs (chronological order, newer takes precedence). +\`\`\` + +**Output On Success** + +\`\`\` +## Bulk Archive Complete + +Archived N changes: +- -> archive/YYYY-MM-DD-/ +- -> archive/YYYY-MM-DD-/ + +Spec sync summary: +- N delta specs synced to main specs +- No conflicts (or: M conflicts resolved) +\`\`\` + +**Output On Partial Success** + +\`\`\` +## Bulk Archive Complete (partial) + +Archived N changes: +- -> archive/YYYY-MM-DD-/ + +Skipped M changes: +- (user chose not to archive incomplete) + +Failed K changes: +- : Archive directory already exists +\`\`\` + +**Output When No Changes** + +\`\`\` +## No Changes to Archive + +No active changes found. Use \`/opsx:new\` to create a new change. +\`\`\` + +**Guardrails** +- Allow any number of changes (1+ is fine, 2+ is the typical use case) +- Always prompt for selection, never auto-select +- Detect spec conflicts early and resolve by checking codebase +- When both changes are implemented, apply specs in chronological order +- Skip spec sync only when implementation is missing (warn user) +- Show clear per-change status before confirming +- Use single confirmation for entire batch +- Track and report all outcomes (success/skip/fail) +- Preserve .openspec.yaml when moving to archive +- Archive directory target uses current date: YYYY-MM-DD- +- If archive target exists, fail that change but continue with others` + }; +} + /** * Template for /opsx:verify slash command */