Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .changeset/calm-amps-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@fission-ai/openspec": minor
---

### New Features

- **Amp support**: select `amp` during init to install OpenSpec workflows as project skills under `.agents/skills/`.
22 changes: 15 additions & 7 deletions docs-lab/reference/supported-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The id goes to `openspec init --tools <id>` to skip the picker ([CLI](cli.md)).
| Tool | `--tools` id | Skills | Skill invocation | Commands | Command invocation |
|---|---|---|---|---|---|
| Amazon Q Developer | `amazon-q` | `.amazonq/skills/` | `/openspec-apply-change` | `.amazonq/prompts/` | `@opsx-apply` |
| Amp | `amp` | `.agents/skills/` | `/openspec-apply-change` | none | none |
| Antigravity | `antigravity` | `.agents/skills/` | `/openspec-apply-change` | `.agents/workflows/` | `/opsx-apply` |
| Auggie (Augment CLI) | `auggie` | `.augment/skills/` | `/openspec-apply-change` | `.augment/commands/` | `/opsx-apply` |
| Bob Shell | `bob` | `.bob/skills/` | `/openspec-apply-change` | `.bob/commands/` | `/opsx-apply` |
Expand Down Expand Up @@ -62,15 +63,22 @@ The id goes to `openspec init --tools <id>` to skip the picker ([CLI](cli.md)).

A tool not listed here behaves exactly as its row reads.

### Amp

- **Project skills**: Amp reads OpenSpec skills from `.agents/skills/`.
- **No command files**: Amp runs skills directly, so init skips command generation.
- **Shared folder**: Amp shares `.agents/skills/` with Antigravity, Codex, Zed Agent,
and the `agents` target. OpenSpec writes the skill tree once.

### Antigravity

- **Current folder**: Antigravity v1.20.5 and later read workspace skills and
workflows from `.agents/`.
- **Legacy folder**: after OpenSpec writes replacements, it removes equivalent
generated files from `.agent/`. Custom files and changed generated files stay in
`.agent/` for you to review.
- **Shared skills**: Antigravity shares `.agents/skills/` with Codex, Zed Agent, and
the `agents` target. OpenSpec writes that skill tree once while still writing
- **Shared skills**: Antigravity shares `.agents/skills/` with Amp, Codex, Zed Agent,
and the `agents` target. OpenSpec writes that skill tree once while still writing
Antigravity commands to `.agents/workflows/`.

### Cline
Expand All @@ -88,10 +96,10 @@ Skills stay in `.cline/skills/`.
describes both interfaces.
- **No command files**: Codex runs skills directly, so init skips commands even when
delivery includes them and prints `Commands skipped for: codex (uses skills)`.
- **Shared folder**: Codex skills land in `.agents/skills/`, the same tree Antigravity,
Zed Agent, and the `agents` target use. Selecting more than one keeps a single
compatible tree, and its handoffs spell both `$openspec-*` and `/openspec-*` when
Codex owns it.
- **Shared folder**: Codex skills land in `.agents/skills/`, the same tree Amp,
Antigravity, Zed Agent, and the `agents` target use. Selecting more than one keeps a
single compatible tree, and its handoffs spell both `$openspec-*` and `/openspec-*`
when Codex owns it.
- **Legacy path**: skills installed under `.codex/skills/` by older versions are
migrated on the next `openspec update`.

Expand Down Expand Up @@ -134,7 +142,7 @@ init prints this reminder after install.
assistant is not listed. The init picker's search box finds it by `universal`,
`other`, `generic`, `custom`, `proprietary`, `unlisted`, `unsupported`,
`vendor-neutral`, or `agents.md`.
- **Alongside other targets**: Antigravity, Codex, Zed Agent, and this target share
- **Alongside other targets**: Amp, Antigravity, Codex, Zed Agent, and this target share
one physical skill tree. OpenSpec records one writer in `.openspec-target` and
writes the tree once per run. Each tool's separate command files are still
generated.
Expand Down
2 changes: 2 additions & 0 deletions openspec/changes/add-amp-support/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2025-12-29
31 changes: 31 additions & 0 deletions openspec/changes/add-amp-support/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Why

Amp reads project skills from `.agents/skills/`, but OpenSpec does not list Amp in its tool picker or accept `amp` through `--tools`. Amp users can select the universal `.agents` target, but only if they already know how Amp discovers skills.

## What Changes

- Add Amp as a supported skills-only tool with `amp` as its tool id.
- Generate Amp's OpenSpec skills through the existing shared `.agents/skills/` pipeline.
- Detect Amp projects from `.amp/` and recognize Amp-owned OpenSpec skill trees during update.
- Document Amp's paths and invocation syntax in the docs-lab supported-tools reference.

## Capabilities

### New Capabilities

_None._

### Modified Capabilities

- `ai-tool-paths`: define Amp's shared Agent Skills path and skills-only behavior.

## Impact

- `src/core/config.ts`: add the Amp tool metadata.
- `test/core/init.test.ts`, `test/core/update.test.ts`, and `test/core/available-tools.test.ts`: cover generation, refresh, and detection.
- `docs-lab/reference/supported-tools.md`: add Amp to the support matrix and shared-folder notes.

## Non-Goals

- Adding an Amp command adapter. Amp's supported project extension surface is Agent Skills.
- Adding a second Amp-specific skill generator or template set.
28 changes: 28 additions & 0 deletions openspec/changes/add-amp-support/specs/ai-tool-paths/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## ADDED Requirements

### Requirement: Amp skills integration

OpenSpec SHALL expose Amp as a supported skills-only tool that uses Amp's project Agent Skills directory.

#### Scenario: Selecting Amp

- **WHEN** the user selects Amp in `openspec init` or passes `--tools amp`
- **THEN** OpenSpec SHALL generate the active profile's skills under `.agents/skills/`
- **AND** the generated skills SHALL use `/openspec-<skill>` references
- **AND** OpenSpec SHALL NOT generate command files for Amp

#### Scenario: Detecting an Amp project

- **WHEN** a project contains an `.amp/` directory
- **THEN** OpenSpec SHALL detect Amp as an available tool

#### Scenario: Updating an Amp-owned skill tree

- **GIVEN** `.agents/skills/.openspec-target` names `amp`
- **WHEN** `openspec update` runs
- **THEN** OpenSpec SHALL refresh the Amp skill tree through the shared skill generator

#### Scenario: Sharing the Agent Skills directory

- **WHEN** Amp is selected with another tool that writes `.agents/skills/`
- **THEN** OpenSpec SHALL write one compatible skill tree rather than letting the tools overwrite each other
20 changes: 20 additions & 0 deletions openspec/changes/add-amp-support/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## 1. Tool support

- [x] 1.1 Add Amp to `AI_TOOLS` as a skills-only `.agents` target.
- [x] 1.2 Detect Amp projects from `.amp/` and preserve shared-root ownership.

## 2. Documentation

- [x] 2.1 Add Amp to the docs-lab supported-tools matrix.
- [x] 2.2 Document Amp's skills-only and shared-folder behavior.

## 3. Tests

- [x] 3.1 Cover Amp detection from `.amp/` and `.openspec-target`.
- [x] 3.2 Cover init generation, Agent Skills frontmatter, invocation syntax, and command skipping.
- [x] 3.3 Cover update of an Amp-owned shared skill tree.

## 4. Verification

- [x] 4.1 Validate `add-amp-support` in strict mode.
- [x] 4.2 Run targeted tests, lint, build, and the full test suite.
1 change: 1 addition & 0 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface AIToolOption {

export const AI_TOOLS: AIToolOption[] = [
{ name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer', skillsDir: '.amazonq', requiresIdeRestart: true },
{ name: 'Amp', value: 'amp', available: true, successLabel: 'Amp', skillsDir: '.agents', detectionPaths: ['.amp', '.agents/skills'] },
// Antigravity moved workspace skills and workflows from `.agent` to the
// shared `.agents` root in v1.20.5. Detection keys off `.agent` and
// `.agents/workflows` rather than the bare `.agents` root: that root is
Expand Down
13 changes: 13 additions & 0 deletions test/core/available-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ describe('available-tools', () => {
expect(tools[0].skillsDir).toBe('.agents');
});

it('should detect Amp from its project configuration directory', async () => {
await fs.mkdir(path.join(testDir, '.amp'), { recursive: true });

expect(getAvailableTools(testDir).map((tool) => tool.value)).toEqual(['amp']);
});

it('should detect Antigravity from .agents/workflows', async () => {
await fs.mkdir(path.join(testDir, '.agents', 'workflows'), { recursive: true });

Expand Down Expand Up @@ -203,6 +209,13 @@ describe('available-tools', () => {
expect(getAvailableTools(testDir).map((tool) => tool.value)).toEqual(['zed']);
});

it('should use the shared-root marker to detect a configured Amp target', async () => {
await fs.mkdir(path.join(testDir, '.agents', 'skills'), { recursive: true });
await fs.writeFile(path.join(testDir, '.agents', 'skills', '.openspec-target'), 'amp\n');

expect(getAvailableTools(testDir).map((tool) => tool.value)).toEqual(['amp']);
});

it('should preserve a global tool while reconciling a shared project root', async () => {
const sharedSkills = path.join(testDir, '.agents', 'skills');
const globalSkill = path.join(
Expand Down
28 changes: 28 additions & 0 deletions test/core/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,34 @@ describe('InitCommand', () => {
).toBe(true);
});

it('should support Amp through its shared Agent Skills directory', async () => {
saveGlobalConfig({
featureFlags: {},
profile: 'core',
delivery: 'both',
});

await new InitCommand({ tools: 'amp', force: true }).execute(testDir);

const skillsDir = path.join(testDir, '.agents', 'skills');
const skillFile = path.join(skillsDir, 'openspec-explore', 'SKILL.md');
expect(await fileExists(skillFile)).toBe(true);
expect(await fs.readFile(path.join(skillsDir, '.openspec-target'), 'utf-8')).toBe('amp\n');

const skillContent = await fs.readFile(skillFile, 'utf-8');
expect(skillContent).toContain('name: openspec-explore');
expect(skillContent).toContain('/openspec-');
expect(skillContent).not.toContain('/opsx:');
expect(await directoryExists(path.join(testDir, '.agents', 'commands'))).toBe(false);

const logCalls = vi.mocked(console.log).mock.calls.flat().map(String);
expect(
logCalls.some(
(entry) => entry.includes('Commands skipped for: amp') && entry.includes('(no adapter)')
)
).toBe(true);
});

it('should install MiniMax Code skills only in the user-home target', async () => {
saveGlobalConfig({
featureFlags: {},
Expand Down
15 changes: 15 additions & 0 deletions test/core/update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,21 @@ metadata:
).toBe(true);
});

it('should refresh an Amp-owned shared skill tree', async () => {
await new InitCommand({ tools: 'amp', force: true }).execute(testDir);
const skillsDir = path.join(testDir, '.agents', 'skills');
const skillFile = path.join(skillsDir, 'openspec-propose', 'SKILL.md');
await fs.writeFile(skillFile, 'stale Amp skill');

await new UpdateCommand({ force: true }).execute(testDir);

expect(await fs.readFile(path.join(skillsDir, '.openspec-target'), 'utf-8')).toBe('amp\n');
const updated = await fs.readFile(skillFile, 'utf-8');
expect(updated).toContain('name: openspec-propose');
expect(updated).toContain('/openspec-apply-change');
expect(updated).not.toContain('stale Amp skill');
});

it('should refresh Antigravity workflows without rewriting Codex-owned shared skills', async () => {
await new InitCommand({ tools: 'antigravity,codex', force: true }).execute(testDir);

Expand Down
Loading