Compare commits

...
Author SHA1 Message Date
Tabish Bidiwale 39b9b8d692 Merge branch 'main' into fix-archive-command-arguments 2025-10-16 10:36:01 +11:00
Tabish Bidiwale a850398843 refactor: improve archive slash command argument handling instructions
Update the archive slash command template to provide clearer guidance on how
to handle change IDs from arguments versus conversation context. The new
instructions better distinguish between explicit argument-provided IDs and
contextual references, and provide more explicit failure modes.
2025-10-15 16:01:30 +11:00
Tabish Bidiwale 810f12fd43 feat: add argument support to archive slash command
Add $ARGUMENTS placeholder to the /openspec:archive slash command to allow explicit change ID specification, improving safety and matching CLI behavior.

Changes:
- Add $ARGUMENTS to archive command frontmatter in OpenCode configurator
- Update archive command template with argument validation steps
- Add rewriteArchiveFile method to handle managed section updates
- Update AGENTS.md documentation for archive command usage

This change makes it possible for users to run `/openspec:archive <change-id>` instead of relying on context inference, reducing the risk of archiving the wrong change.
2025-10-15 15:20:36 +11:00
6 changed files with 126 additions and 7 deletions
+3 -2
View File
@@ -60,7 +60,8 @@ Track these steps as TODOs and complete them one by one.
After deployment, create separate PR to:
- Move `changes/[name]/` → `changes/archive/YYYY-MM-DD-[name]/`
- Update `specs/` if capabilities changed
- Use `openspec archive [change] --skip-specs --yes` for tooling-only changes
- Use `openspec archive <change-id> --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly)
- OpenCode slash command: `/openspec:archive <change-id>` passes the change ID via arguments—fail fast if the ID is missing or invalid
- Run `openspec validate --strict` to confirm the archived change passes checks
## Before Any Task
@@ -97,7 +98,7 @@ openspec list --specs # List specifications
openspec show [item] # Display change or spec
openspec diff [change] # Show spec differences
openspec validate [item] # Validate changes or specs
openspec archive [change] [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
openspec archive <change-id> [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
# Project management
openspec init [path] # Initialize OpenSpec
@@ -0,0 +1,17 @@
# Add Archive Command Arguments
## Why
The `/openspec:archive` slash command currently lacks argument support, forcing the AI to infer which change to archive from conversation context or by listing all changes. This creates a safety risk where the wrong proposal could be archived if the context is ambiguous or multiple changes exist. Users expect to specify the change ID explicitly, matching the behavior of the CLI command `openspec archive <id>`.
## What Changes
- Add `$ARGUMENTS` placeholder to the OpenCode archive slash command frontmatter (matching existing pattern for proposal command)
- Update archive command template steps to validate the specific change ID argument when provided
- Note: Codex, GitHub Copilot, and Amazon Q already have `$ARGUMENTS` for archive; Claude/Cursor/Windsurf/Kilocode don't support arguments
## Impact
- Affected specs: `cli-update` (slash command generation logic)
- Affected code:
- `src/core/configurators/slash/opencode.ts` (add `$ARGUMENTS` to archive frontmatter)
- `src/core/templates/slash-command-templates.ts` (archive template steps for argument validation)
- Breaking: No - this is additive functionality that makes the command safer
- User-facing: Yes - OpenCode users will be able to pass the change ID as an argument: `/openspec:archive <change-id>`
@@ -0,0 +1,34 @@
# CLI Update Specification Delta
## MODIFIED Requirements
### Requirement: Slash Command Updates
The update command SHALL refresh existing slash command files for configured tools without creating new ones, and ensure the OpenCode archive command accepts change ID arguments.
#### Scenario: Updating slash commands for OpenCode
- **WHEN** `.opencode/command/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
- **AND** ensure the archive command includes `$ARGUMENTS` placeholder in frontmatter for accepting change ID arguments
## ADDED Requirements
### Requirement: Archive Command Argument Support
The archive slash command template SHALL support optional change ID arguments for tools that support `$ARGUMENTS` placeholder.
#### Scenario: Archive command with change ID argument
- **WHEN** a user invokes `/openspec:archive <change-id>` with a change ID
- **THEN** the template SHALL instruct the AI to validate the provided change ID against `openspec list`
- **AND** use the provided change ID for archiving if valid
- **AND** fail fast if the provided change ID doesn't match an archivable change
#### Scenario: Archive command without argument (backward compatibility)
- **WHEN** a user invokes `/openspec:archive` without providing a change ID
- **THEN** the template SHALL instruct the AI to identify the change ID from context or by running `openspec list`
- **AND** proceed with the existing behavior (maintaining backward compatibility)
#### Scenario: OpenCode archive template generation
- **WHEN** generating the OpenCode archive slash command file
- **THEN** include the `$ARGUMENTS` placeholder in the frontmatter
- **AND** wrap it in a clear structure like `<ChangeId>\n $ARGUMENTS\n</ChangeId>` to indicate the expected argument
- **AND** include validation steps in the template body to check if the change ID is valid
@@ -0,0 +1,21 @@
# Implementation Tasks
## 1. Update OpenCode Configurator
- [x] 1.1 Add `$ARGUMENTS` placeholder to OpenCode archive frontmatter (matching the proposal pattern)
- [x] 1.2 Format it as `<ChangeId>\n $ARGUMENTS\n</ChangeId>` or similar structure for clarity
- [x] 1.3 Ensure `updateExisting` rewrites the archive frontmatter/body so `$ARGUMENTS` persists after `openspec update`
## 2. Update Slash Command Templates
- [x] 2.1 Modify archive steps to validate change ID argument when provided via `$ARGUMENTS`
- [x] 2.2 Keep backward compatibility - allow inferring from context if no argument provided
- [x] 2.3 Add step to validate the change ID exists using `openspec list` before archiving
## 3. Update Documentation
- [x] 3.1 Update AGENTS.md archive examples to show argument usage
- [x] 3.2 Document that OpenCode now supports `/openspec:archive <change-id>`
## 4. Validation and Testing
- [ ] 4.1 Run `openspec update` to regenerate OpenCode slash commands
- [ ] 4.2 Manually test with OpenCode using `/openspec:archive <change-id>`
- [ ] 4.3 Test backward compatibility (archive command without arguments)
- [ ] 4.4 Run `openspec validate --strict` to ensure no issues
+41 -1
View File
@@ -1,5 +1,7 @@
import { SlashCommandConfigurator } from "./base.js";
import { SlashCommandId } from "../../templates/index.js";
import { FileSystemUtils } from "../../../utils/file-system.js";
import { OPENSPEC_MARKERS } from "../../config.js";
const FILE_PATHS: Record<SlashCommandId, string> = {
proposal: ".opencode/command/openspec-proposal.md",
@@ -24,7 +26,11 @@ description: Implement an approved OpenSpec change and keep tasks in sync.
archive: `---
agent: build
description: Archive a deployed OpenSpec change and update specs.
---`,
---
<ChangeId>
$ARGUMENTS
</ChangeId>
`,
};
export class OpenCodeSlashCommandConfigurator extends SlashCommandConfigurator {
@@ -38,4 +44,38 @@ export class OpenCodeSlashCommandConfigurator extends SlashCommandConfigurator {
protected getFrontmatter(id: SlashCommandId): string | undefined {
return FRONTMATTER[id];
}
async generateAll(projectPath: string, _openspecDir: string): Promise<string[]> {
const createdOrUpdated = await super.generateAll(projectPath, _openspecDir);
await this.rewriteArchiveFile(projectPath);
return createdOrUpdated;
}
async updateExisting(projectPath: string, _openspecDir: string): Promise<string[]> {
const updated = await super.updateExisting(projectPath, _openspecDir);
const rewroteArchive = await this.rewriteArchiveFile(projectPath);
if (rewroteArchive && !updated.includes(FILE_PATHS.archive)) {
updated.push(FILE_PATHS.archive);
}
return updated;
}
private async rewriteArchiveFile(projectPath: string): Promise<boolean> {
const archivePath = FileSystemUtils.joinPath(projectPath, FILE_PATHS.archive);
if (!await FileSystemUtils.fileExists(archivePath)) {
return false;
}
const body = this.getBody("archive");
const frontmatter = this.getFrontmatter("archive");
const sections: string[] = [];
if (frontmatter) {
sections.push(frontmatter.trim());
}
sections.push(`${OPENSPEC_MARKERS.start}\n${body}\n${OPENSPEC_MARKERS.end}`);
await FileSystemUtils.writeFile(archivePath, sections.join("\n") + "\n");
return true;
}
}
+10 -4
View File
@@ -33,12 +33,18 @@ const applyReferences = `**Reference**
- Use \`openspec show <id> --json --deltas-only\` if you need additional context from the proposal while implementing.`;
const archiveSteps = `**Steps**
1. Identify the requested change ID (via the prompt or \`openspec list\`).
2. Run \`openspec archive <id> --yes\` to let the CLI move the change and apply spec updates without prompts (use \`--skip-specs\` only for tooling-only work).
3. Review the command output to confirm the target specs were updated and the change landed in \`changes/archive/\`.
4. Validate with \`openspec validate --strict\` and inspect with \`openspec show <id>\` if anything looks off.`;
1. Determine the change ID to archive:
- If this prompt already includes a specific change ID (for example inside a \`<ChangeId>\` block populated by slash-command arguments), use that value after trimming whitespace.
- If the conversation references a change loosely (for example by title or summary), run \`openspec list\` to surface likely IDs, share the relevant candidates, and confirm which one the user intends.
- Otherwise, review the conversation, run \`openspec list\`, and ask the user which change to archive; wait for a confirmed change ID before proceeding.
- If you still cannot identify a single change ID, stop and tell the user you cannot archive anything yet.
2. Validate the change ID by running \`openspec list\` (or \`openspec show <id>\`) and stop if the change is missing, already archived, or otherwise not ready to archive.
3. Run \`openspec archive <id> --yes\` so the CLI moves the change and applies spec updates without prompts (use \`--skip-specs\` only for tooling-only work).
4. Review the command output to confirm the target specs were updated and the change landed in \`changes/archive/\`.
5. Validate with \`openspec validate --strict\` and inspect with \`openspec show <id>\` if anything looks off.`;
const archiveReferences = `**Reference**
- Use \`openspec list\` to confirm change IDs before archiving.
- Inspect refreshed specs with \`openspec list --specs\` and address any validation issues before handing off.`;
export const slashCommandBodies: Record<SlashCommandId, string> = {