Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions openspec/changes/add-amp-support/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## Why
Amp is a popular AI coding agent from Sourcegraph that uses Skills (`.agents/skills/`) to extend its capabilities. Today OpenSpec can scaffold slash commands for many IDEs but not Amp, so Amp users cannot run the proposal/apply/archive flows from their workflow.

## What Changes
- Add Amp as a selectable native tool in `openspec init` so it creates `.agents/skills/openspec-proposal/SKILL.md`, `.agents/skills/openspec-apply/SKILL.md`, and `.agents/skills/openspec-archive/SKILL.md` with YAML frontmatter containing `name` and `description` fields plus the standard OpenSpec-managed body.
- Ensure `openspec update` refreshes the body of any existing Amp skills inside `.agents/skills/` without creating missing files, mirroring the behavior of other tools.
- Share e2e/template coverage confirming the generator writes the proper directory, filename casing, and frontmatter format so Amp picks up the skills.

## Impact
- Affected specs: `specs/cli-init`, `specs/cli-update`
- Expected code: CLI init/update tool registries, slash-command configurator, associated tests
9 changes: 9 additions & 0 deletions openspec/changes/add-amp-support/specs/cli-init/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## MODIFIED Requirements
### Requirement: Slash Command Configuration
The init command SHALL generate slash command files for supported editors using shared templates.

#### Scenario: Generating slash commands for Amp
- **WHEN** the user selects Amp during initialization
- **THEN** create `.agents/skills/openspec-proposal/SKILL.md`, `.agents/skills/openspec-apply/SKILL.md`, and `.agents/skills/openspec-archive/SKILL.md`
- **AND** ensure each file begins with YAML frontmatter that contains `name: <skill name>` and `description: <stage summary>` fields followed by the shared OpenSpec workflow instructions wrapped in managed markers
- **AND** populate the skill body with the same proposal/apply/archive guidance used for other tools so Amp behaves like other AI coding assistants while pointing to the `.agents/skills/` directory
8 changes: 8 additions & 0 deletions openspec/changes/add-amp-support/specs/cli-update/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## 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 Amp
- **WHEN** `.agents/skills/` contains `openspec-proposal/SKILL.md`, `openspec-apply/SKILL.md`, and `openspec-archive/SKILL.md`
- **THEN** refresh the OpenSpec-managed portion of each file so the skill copy matches other tools while preserving the existing `name` and `description` frontmatter
- **AND** skip creating any missing skill files during update, mirroring the behavior for other IDEs
13 changes: 13 additions & 0 deletions openspec/changes/add-amp-support/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## 1. CLI init support
- [x] 1.1 Surface Amp in the native-tool picker (interactive + `--tools`) so it toggles alongside other IDEs.
- [x] 1.2 Generate `.agents/skills/openspec-proposal/SKILL.md`, `.agents/skills/openspec-apply/SKILL.md`, and `.agents/skills/openspec-archive/SKILL.md` with YAML frontmatter containing `name` and `description` fields for each stage and wrap the body in OpenSpec markers.
- [x] 1.3 Confirm workspace scaffolding covers missing directory creation and re-run scenarios so repeated init refreshes the managed block.

## 2. CLI update support
- [x] 2.1 Detect existing Amp skill files during `openspec update` and refresh only the managed body, skipping creation when files are missing.
- [x] 2.2 Ensure update logic preserves the `name` and `description` frontmatter block exactly as written by init, including case and spacing, and refreshes body templates alongside other tools.

## 3. Templates and tests
- [x] 3.1 Add AmpSlashCommandConfigurator that reuses the shared proposal/apply/archive templates but targets `.agents/skills/` and includes the proper frontmatter.
- [x] 3.2 Register the configurator in the slash command registry.
- [x] 3.3 Expand automated coverage (unit or integration) verifying init and update produce the expected file paths and frontmatter + body markers for Amp.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## Why

The base class `SlashCommandConfigurator` has a semantic mismatch with the actual tool ecosystem. Different AI tools use different terminology for their integration files:
- Claude/Cursor/Windsurf: "slash commands" or "workflows"
- Amp: "skills" (`.agents/skills/`)
- Codex/GitHub Copilot: "prompts"
- Gemini: "commands" (TOML format)

The current naming misleadingly implies these configurators only handle slash commands, when they actually configure "OpenSpec workflow operations" (proposal/apply/archive) across diverse tool formats.

## What Changes

- Rename `SlashCommandConfigurator` → `WorkflowConfigurator` in `src/core/configurators/slash/base.ts`
- Rename `SlashCommandRegistry` → `WorkflowRegistry` in `src/core/configurators/slash/registry.ts`
- Rename `SlashCommandTarget` → `WorkflowTarget` interface
- Rename `SlashCommandId` → `WorkflowId` type in templates
- Rename directory `src/core/configurators/slash/` → `src/core/configurators/workflow/`
- Export old names as type aliases for backward compatibility
- Update all configurator subclasses and consumers to use new names
- Remove explanatory documentation workarounds added during Amp implementation

## Impact

- Affected specs: `cli-init`, `cli-update` (terminology in scenarios)
- Affected code: All files in `src/core/configurators/slash/`, template exports, init/update command implementations
- Breaking changes: None if aliases are maintained; internal refactor only
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## MODIFIED Requirements

### Requirement: Slash Command Configuration

The init command SHALL generate workflow configuration files for supported editors using shared templates.

#### Scenario: Generating workflow files for Claude Code

- **WHEN** the user selects Claude Code during initialization
- **THEN** create `.claude/commands/openspec/proposal.md`, `.claude/commands/openspec/apply.md`, and `.claude/commands/openspec/archive.md`
- **AND** populate each file from shared templates so command text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage

#### Scenario: Generating workflow files for Amp

- **WHEN** the user selects Amp during initialization
- **THEN** create `.agents/skills/openspec-proposal/SKILL.md`, `.agents/skills/openspec-apply/SKILL.md`, and `.agents/skills/openspec-archive/SKILL.md`
- **AND** populate each file with YAML frontmatter containing `name` and `description` fields
- **AND** wrap the body in OpenSpec markers so `openspec update` can refresh the content
- **AND** each template includes instructions for the relevant OpenSpec workflow stage

#### Scenario: Generating workflow files for Codex

- **WHEN** the user selects Codex during initialization
- **THEN** create global prompt files at `~/.codex/prompts/openspec-proposal.md`, `~/.codex/prompts/openspec-apply.md`, and `~/.codex/prompts/openspec-archive.md` (or under `$CODEX_HOME/prompts` if set)
- **AND** populate each file from shared templates that map the first numbered placeholder (`$1`) to the primary user input (e.g., change identifier or question text)
- **AND** wrap the generated content in OpenSpec markers so `openspec update` can refresh the prompts without touching surrounding custom notes

#### Scenario: Generating workflow files for GitHub Copilot

- **WHEN** the user selects GitHub Copilot during initialization
- **THEN** create `.github/prompts/openspec-proposal.prompt.md`, `.github/prompts/openspec-apply.prompt.md`, and `.github/prompts/openspec-archive.prompt.md`
- **AND** populate each file with YAML frontmatter containing a `description` field that summarizes the workflow stage
- **AND** include `$ARGUMENTS` placeholder to capture user input
- **AND** wrap the shared template body with OpenSpec markers so `openspec update` can refresh the content
- **AND** each template includes instructions for the relevant OpenSpec workflow stage

#### Scenario: Generating workflow files for Gemini CLI

- **WHEN** the user selects Gemini CLI during initialization
- **THEN** create `.gemini/commands/openspec/proposal.toml`, `.gemini/commands/openspec/apply.toml`, and `.gemini/commands/openspec/archive.toml`
- **AND** populate each file as TOML that sets a stage-specific `description = "<summary>"` and a multi-line `prompt = """` block with the shared OpenSpec template
- **AND** wrap the OpenSpec managed markers (`<!-- OPENSPEC:START -->` / `<!-- OPENSPEC:END -->`) inside the `prompt` value so `openspec update` can safely refresh the body between markers without touching the TOML framing
- **AND** ensure the workflow copy matches the existing proposal/apply/archive templates used by other tools
30 changes: 30 additions & 0 deletions openspec/changes/refactor-workflow-configurator-naming/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## 1. Core renaming

- [ ] 1.1 Rename `SlashCommandConfigurator` → `WorkflowConfigurator` in `src/core/configurators/slash/base.ts`
- [ ] 1.2 Rename `SlashCommandTarget` → `WorkflowTarget` interface in `base.ts`
- [ ] 1.3 Rename `SlashCommandRegistry` → `WorkflowRegistry` in `registry.ts`
- [ ] 1.4 Rename `SlashCommandId` → `WorkflowId` in `src/core/templates/slash-command-templates.ts`
- [ ] 1.5 Rename directory `src/core/configurators/slash/` → `src/core/configurators/workflow/`

## 2. Compatibility aliases

- [ ] 2.1 Export `SlashCommandConfigurator` as alias for `WorkflowConfigurator`
- [ ] 2.2 Export `SlashCommandTarget` as alias for `WorkflowTarget`
- [ ] 2.3 Export `SlashCommandRegistry` as alias for `WorkflowRegistry`
- [ ] 2.4 Export `SlashCommandId` as alias for `WorkflowId`
- [ ] 2.5 Add deprecation JSDoc comments to all aliases

## 3. Update consumers

- [ ] 3.1 Update all configurator subclasses to extend `WorkflowConfigurator`
- [ ] 3.2 Update init command to use `WorkflowRegistry`
- [ ] 3.3 Update update command to use `WorkflowRegistry`
- [ ] 3.4 Update template exports in `src/core/templates/index.ts`
- [ ] 3.5 Remove workaround documentation from `amp.ts` explaining the naming mismatch

## 4. Tests and validation

- [ ] 4.1 Update existing unit tests to use new names
- [ ] 4.2 Verify backward compatibility by testing alias imports
- [ ] 4.3 Run full test suite to ensure no regressions
- [ ] 4.4 Run typecheck and lint
1 change: 1 addition & 0 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface AIToolOption {

export const AI_TOOLS: AIToolOption[] = [
{ name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer' },
{ name: 'Amp', value: 'amp', available: true, successLabel: 'Amp' },
{ name: 'Antigravity', value: 'antigravity', available: true, successLabel: 'Antigravity' },
{ name: 'Auggie (Augment CLI)', value: 'auggie', available: true, successLabel: 'Auggie' },
{ name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code' },
Expand Down
39 changes: 39 additions & 0 deletions src/core/configurators/slash/amp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { SlashCommandConfigurator } from './base.js';
import { SlashCommandId } from '../../templates/index.js';
import { getSkillBody, getSkillMetadata, SkillId } from '../../templates/skill-templates.js';

/**
* AmpSlashCommandConfigurator generates Amp "skills" for OpenSpec workflows.
*
* Amp uses Skills (not slash commands) located in .agents/skills/<skill-name>/SKILL.md.
* Each skill file contains YAML frontmatter with `name` and `description` fields,
* followed by the OpenSpec workflow instructions wrapped in managed markers.
*
* Despite extending SlashCommandConfigurator (a historical naming choice),
* this configurator generates Amp-native skill files that Amp discovers and
* presents to users as available skills.
*/

const FILE_PATHS: Record<SlashCommandId, string> = {
proposal: '.agents/skills/openspec-proposal/SKILL.md',
apply: '.agents/skills/openspec-apply/SKILL.md',
archive: '.agents/skills/openspec-archive/SKILL.md'
};

export class AmpSlashCommandConfigurator extends SlashCommandConfigurator {
readonly toolId = 'amp';
readonly isAvailable = true;

protected getRelativePath(id: SlashCommandId): string {
return FILE_PATHS[id];
}

protected getFrontmatter(id: SlashCommandId): string | undefined {
const metadata = getSkillMetadata(id as SkillId);
return `---\nname: ${metadata.name}\ndescription: ${metadata.description}\n---`;
}

protected getBody(id: SlashCommandId): string {
return getSkillBody(id as SkillId).trim();
}
}
3 changes: 3 additions & 0 deletions src/core/configurators/slash/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { QwenSlashCommandConfigurator } from './qwen.js';
import { RooCodeSlashCommandConfigurator } from './roocode.js';
import { AntigravitySlashCommandConfigurator } from './antigravity.js';
import { IflowSlashCommandConfigurator } from './iflow.js';
import { AmpSlashCommandConfigurator } from './amp.js';

export class SlashCommandRegistry {
private static configurators: Map<string, SlashCommandConfigurator> = new Map();
Expand All @@ -44,6 +45,7 @@ export class SlashCommandRegistry {
const roocode = new RooCodeSlashCommandConfigurator();
const antigravity = new AntigravitySlashCommandConfigurator();
const iflow = new IflowSlashCommandConfigurator();
const amp = new AmpSlashCommandConfigurator();

this.configurators.set(claude.toolId, claude);
this.configurators.set(codeBuddy.toolId, codeBuddy);
Expand All @@ -65,6 +67,7 @@ export class SlashCommandRegistry {
this.configurators.set(roocode.toolId, roocode);
this.configurators.set(antigravity.toolId, antigravity);
this.configurators.set(iflow.toolId, iflow);
this.configurators.set(amp.toolId, amp);
}

static register(configurator: SlashCommandConfigurator): void {
Expand Down
10 changes: 10 additions & 0 deletions src/core/templates/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { clineTemplate } from './cline-template.js';
import { costrictTemplate } from './costrict-template.js';
import { agentsRootStubTemplate } from './agents-root-stub.js';
import { getSlashCommandBody, SlashCommandId } from './slash-command-templates.js';
import { getSkillBody, getSkillMetadata, SkillId, SkillMetadata } from './skill-templates.js';

export interface Template {
path: string;
Expand Down Expand Up @@ -44,7 +45,16 @@ export class TemplateManager {
static getSlashCommandBody(id: SlashCommandId): string {
return getSlashCommandBody(id);
}

static getSkillBody(id: SkillId): string {
return getSkillBody(id);
}

static getSkillMetadata(id: SkillId): SkillMetadata {
return getSkillMetadata(id);
}
}

export { ProjectContext } from './project-template.js';
export type { SlashCommandId } from './slash-command-templates.js';
export type { SkillId, SkillMetadata } from './skill-templates.js';
Loading