Compare commits

...
Author SHA1 Message Date
Tabish Bidiwale e82eef9969 Fix package name to use scoped package @fission-ai/openspec 2025-10-13 19:37:37 +11:00
Tabish Bidiwale 6bfc82b60a Add changeset for Amazon Q Developer integration 2025-10-13 19:28:42 +11:00
Brian AndersonandBrian Anderson 56d57da119 Amazon Q Developer integration (#160)
* feat: add Amazon Q Developer CLI integration

- Add AmazonQSlashCommandConfigurator for .amazonq/prompts/ support
- Register Amazon Q in SlashCommandRegistry and AI_TOOLS config
- Generate slash commands compatible with Amazon Q CLI (@-syntax)
- Update README.md with Amazon Q Developer in tools table

* test: add Amazon Q Developer integration tests

- Add init tests for Amazon Q prompt file creation and configuration detection
- Add update tests for Amazon Q prompt refresh and missing file handling
- Follow same test patterns as GitHub Copilot integration
- Verify .amazonq/prompts/ directory structure and file content

---------

Co-authored-by: Brian Anderson <brian@popsofviolet.com>
2025-10-12 18:17:01 +11:00
github-actions[bot]andTabish Bidiwale f56189a8f7 chore(release): version packages (#158)
* Version Packages

* chore: trigger CI

* chore: trigger CI again

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tabish Bidiwale <tabishbidiwale@gmail.com>
2025-10-12 15:06:46 +11:00
Tabish Bidiwale d7e0ce85e5 Add changeset for init wizard Enter key improvements (#157) 2025-10-12 14:59:47 +11:00
Tabish Bidiwale eb0d50c094 feat: improve init wizard Enter key behavior (#156)
Update the tool selection wizard so pressing Enter on a highlighted tool
automatically selects it before proceeding to the review step. This aligns
with common CLI expectations where Enter confirms the highlighted item.

Changes:
- Add logic to select the currently highlighted tool when Enter is pressed
- Update help text to clarify that Enter selects the highlighted tool
- Maintain Space key for toggling multiple selections

This reduces friction during onboarding by matching user expectations,
especially for users who navigate to a tool and press Enter without
first toggling it with Space.
2025-10-12 14:53:44 +11:00
github-actions[bot]andTabish Bidiwale c482f1b47a chore(release): version packages (#150)
* chore: trigger CI

* chore: trigger CI

* Version Packages

---------

Co-authored-by: Tabish Bidiwale <tabishbidiwale@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-11 11:47:37 +11:00
Tabish Bidiwale 2ae0484ac7 chore: add changeset for cross-platform fixes release (#147)
Add changeset for patch release including fixes for joinPath behavior and slash command path resolution across platforms.
2025-10-11 11:27:26 +11:00
13 changed files with 231 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@fission-ai/openspec": minor
---
Add Amazon Q Developer CLI integration. OpenSpec now supports Amazon Q Developer with automatic prompt generation in `.amazonq/prompts/` directory, allowing you to use OpenSpec slash commands with Amazon Q's @-syntax.
+12
View File
@@ -1,5 +1,17 @@
# @fission-ai/openspec
## 0.10.0
### Minor Changes
- d7e0ce8: Improve init wizard Enter key behavior to allow proceeding through prompts more naturally
## 0.9.2
### Patch Changes
- 2ae0484: Fix cross-platform path handling issues. This release includes fixes for joinPath behavior and slash command path resolution to ensure OpenSpec works correctly across all platforms.
## 0.9.1
### Patch Changes
+1
View File
@@ -97,6 +97,7 @@ These tools have built-in OpenSpec commands. Select the OpenSpec integration whe
| **Windsurf** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.windsurf/workflows/`) |
| **Codex** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (global: `~/.codex/prompts`, auto-installed) |
| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.github/prompts/`) |
| **Amazon Q Developer** | `@openspec-proposal`, `@openspec-apply`, `@openspec-archive` (`.amazonq/prompts/`) |
Kilo Code discovers team workflows automatically. Save the generated files under `.kilocode/workflows/` and trigger them from the command palette with `/openspec-proposal.md`, `/openspec-apply.md`, or `/openspec-archive.md`.
@@ -0,0 +1,14 @@
## Why
- Users frequently scroll to a tool and press Enter without toggling it, resulting in no configuration changes.
- The current workflow deviates from common CLI expectations where Enter confirms the highlighted item.
- Aligning behavior with user expectations reduces friction during onboarding.
## What Changes
- Update the init wizard so pressing Enter on a highlighted tool selects it before moving to the review step.
- Adjust interactive instructions to clarify Enter selects the current tool and Space still toggles selections.
- Refresh specs to capture the clarified behavior for the interactive menu.
## Impact
- Users who press Enter without toggling now configure the highlighted tool instead of exiting with no selections.
- Spacebar multi-select support remains unchanged for power users.
- Documentation better reflects how the wizard behaves.
@@ -0,0 +1,10 @@
## MODIFIED Requirements
### Requirement: Interactive Mode
The command SHALL provide an interactive menu for AI tool selection with clear navigation instructions.
#### Scenario: Displaying interactive menu
- **WHEN** run in fresh or extend mode
- **THEN** present a looping select menu that lets users toggle tools with Space and review selections with Enter
- **AND** when Enter is pressed on a highlighted selectable tool that is not already selected, automatically add it to the selection before moving to review so the highlighted tool is configured
- **AND** label already configured tools with "(already configured)" while keeping disabled options marked "coming soon"
- **AND** change the prompt copy in extend mode to "Which AI tools would you like to add or refresh?"
- **AND** display inline instructions clarifying that Space toggles tools and Enter selects the highlighted tool before reviewing selections
@@ -0,0 +1,8 @@
## 1. Implementation
- [x] Update the tool selection wizard to auto-select the highlighted tool when Enter is pressed without prior toggles.
- [x] Refresh inline instructions copy so Enter behavior is clear.
- [x] Adjust or add tests if needed to cover the new selection flow.
## 2. Validation
- [x] Run `pnpm run build`.
- [x] Run `pnpm test` (or targeted suite) if applicable.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@fission-ai/openspec",
"version": "0.9.1",
"version": "0.10.0",
"description": "AI-native system for spec-driven development",
"keywords": [
"openspec",
+1
View File
@@ -24,5 +24,6 @@ export const AI_TOOLS: AIToolOption[] = [
{ name: 'Windsurf', value: 'windsurf', available: true, successLabel: 'Windsurf' },
{ name: 'Codex', value: 'codex', available: true, successLabel: 'Codex' },
{ name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot' },
{ name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer' },
{ name: 'AGENTS.md (works with Amp, VS Code, …)', value: 'agents', available: false, successLabel: 'your AGENTS.md-compatible assistant' }
];
+51
View File
@@ -0,0 +1,51 @@
import { SlashCommandConfigurator } from './base.js';
import { SlashCommandId } from '../../templates/index.js';
const FILE_PATHS: Record<SlashCommandId, string> = {
proposal: '.amazonq/prompts/openspec-proposal.md',
apply: '.amazonq/prompts/openspec-apply.md',
archive: '.amazonq/prompts/openspec-archive.md'
};
const FRONTMATTER: Record<SlashCommandId, string> = {
proposal: `---
description: Scaffold a new OpenSpec change and validate strictly.
---
The user has requested the following change proposal. Use the openspec instructions to create their change proposal.
<UserRequest>
$ARGUMENTS
</UserRequest>`,
apply: `---
description: Implement an approved OpenSpec change and keep tasks in sync.
---
The user wants to apply the following change. Use the openspec instructions to implement the approved change.
<ChangeId>
$ARGUMENTS
</ChangeId>`,
archive: `---
description: Archive a deployed OpenSpec change and update specs.
---
The user wants to archive the following deployed change. Use the openspec instructions to archive the change and update specs.
<ChangeId>
$ARGUMENTS
</ChangeId>`
};
export class AmazonQSlashCommandConfigurator extends SlashCommandConfigurator {
readonly toolId = 'amazon-q';
readonly isAvailable = true;
protected getRelativePath(id: SlashCommandId): string {
return FILE_PATHS[id];
}
protected getFrontmatter(id: SlashCommandId): string {
return FRONTMATTER[id];
}
}
+3
View File
@@ -6,6 +6,7 @@ import { KiloCodeSlashCommandConfigurator } from './kilocode.js';
import { OpenCodeSlashCommandConfigurator } from './opencode.js';
import { CodexSlashCommandConfigurator } from './codex.js';
import { GitHubCopilotSlashCommandConfigurator } from './github-copilot.js';
import { AmazonQSlashCommandConfigurator } from './amazon-q.js';
export class SlashCommandRegistry {
private static configurators: Map<string, SlashCommandConfigurator> = new Map();
@@ -18,6 +19,7 @@ export class SlashCommandRegistry {
const opencode = new OpenCodeSlashCommandConfigurator();
const codex = new CodexSlashCommandConfigurator();
const githubCopilot = new GitHubCopilotSlashCommandConfigurator();
const amazonQ = new AmazonQSlashCommandConfigurator();
this.configurators.set(claude.toolId, claude);
this.configurators.set(cursor.toolId, cursor);
@@ -26,6 +28,7 @@ export class SlashCommandRegistry {
this.configurators.set(opencode.toolId, opencode);
this.configurators.set(codex.toolId, codex);
this.configurators.set(githubCopilot.toolId, githubCopilot);
this.configurators.set(amazonQ.toolId, amazonQ);
}
static register(configurator: SlashCommandConfigurator): void {
+11 -1
View File
@@ -220,6 +220,16 @@ const toolSelectionWizard = createPrompt<string[], ToolWizardConfig>(
}
if (isEnterKey(key)) {
const current = config.choices[cursor];
if (
current &&
current.selectable &&
!selectedSet.has(current.value)
) {
const next = new Set(selected);
next.add(current.value);
updateSelected(next);
}
setStep('review');
setError(null);
return;
@@ -298,7 +308,7 @@ const toolSelectionWizard = createPrompt<string[], ToolWizardConfig>(
lines.push(PALETTE.white(config.baseMessage));
lines.push(
PALETTE.midGray(
'Use ↑/↓ to move · Space to toggle · Enter to review selections.'
'Use ↑/↓ to move · Space to toggle · Enter selects highlighted tool and reviews.'
)
);
lines.push('');
+48
View File
@@ -570,6 +570,54 @@ describe('InitCommand', () => {
);
expect(githubCopilotChoice.configured).toBe(true);
});
it('should create Amazon Q Developer prompt files with templates', async () => {
queueSelections('amazon-q', DONE);
await initCommand.execute(testDir);
const proposalPath = path.join(
testDir,
'.amazonq/prompts/openspec-proposal.md'
);
const applyPath = path.join(
testDir,
'.amazonq/prompts/openspec-apply.md'
);
const archivePath = path.join(
testDir,
'.amazonq/prompts/openspec-archive.md'
);
expect(await fileExists(proposalPath)).toBe(true);
expect(await fileExists(applyPath)).toBe(true);
expect(await fileExists(archivePath)).toBe(true);
const proposalContent = await fs.readFile(proposalPath, 'utf-8');
expect(proposalContent).toContain('---');
expect(proposalContent).toContain('description: Scaffold a new OpenSpec change and validate strictly.');
expect(proposalContent).toContain('$ARGUMENTS');
expect(proposalContent).toContain('<!-- OPENSPEC:START -->');
expect(proposalContent).toContain('**Guardrails**');
const applyContent = await fs.readFile(applyPath, 'utf-8');
expect(applyContent).toContain('---');
expect(applyContent).toContain('description: Implement an approved OpenSpec change and keep tasks in sync.');
expect(applyContent).toContain('$ARGUMENTS');
expect(applyContent).toContain('<!-- OPENSPEC:START -->');
});
it('should mark Amazon Q Developer as already configured during extend mode', async () => {
queueSelections('amazon-q', DONE, 'amazon-q', DONE);
await initCommand.execute(testDir);
await initCommand.execute(testDir);
const secondRunArgs = mockPrompt.mock.calls[1][0];
const amazonQChoice = secondRunArgs.choices.find(
(choice: any) => choice.value === 'amazon-q'
);
expect(amazonQChoice.configured).toBe(true);
});
});
describe('error handling', () => {
+66
View File
@@ -377,6 +377,72 @@ Old body
await expect(FileSystemUtils.fileExists(ghArchive)).resolves.toBe(false);
});
it('should refresh existing Amazon Q Developer prompts', async () => {
const aqPath = path.join(
testDir,
'.amazonq/prompts/openspec-apply.md'
);
await fs.mkdir(path.dirname(aqPath), { recursive: true });
const initialContent = `---
description: Implement an approved OpenSpec change and keep tasks in sync.
---
The user wants to apply the following change. Use the openspec instructions to implement the approved change.
<ChangeId>
$ARGUMENTS
</ChangeId>
<!-- OPENSPEC:START -->
Old body
<!-- OPENSPEC:END -->`;
await fs.writeFile(aqPath, initialContent);
const consoleSpy = vi.spyOn(console, 'log');
await updateCommand.execute(testDir);
const updatedContent = await fs.readFile(aqPath, 'utf-8');
expect(updatedContent).toContain('**Guardrails**');
expect(updatedContent).toContain('<!-- OPENSPEC:START -->');
expect(updatedContent).toContain('<!-- OPENSPEC:END -->');
expect(updatedContent).not.toContain('Old body');
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining('.amazonq/prompts/openspec-apply.md')
);
consoleSpy.mockRestore();
});
it('should not create missing Amazon Q Developer prompts on update', async () => {
const aqApply = path.join(
testDir,
'.amazonq/prompts/openspec-apply.md'
);
// Only create apply; leave proposal and archive missing
await fs.mkdir(path.dirname(aqApply), { recursive: true });
await fs.writeFile(
aqApply,
'---\ndescription: Old\n---\n\nThe user wants to apply the following change.\n\n<ChangeId>\n $ARGUMENTS\n</ChangeId>\n<!-- OPENSPEC:START -->\nOld\n<!-- OPENSPEC:END -->'
);
await updateCommand.execute(testDir);
const aqProposal = path.join(
testDir,
'.amazonq/prompts/openspec-proposal.md'
);
const aqArchive = path.join(
testDir,
'.amazonq/prompts/openspec-archive.md'
);
// Confirm they weren't created by update
await expect(FileSystemUtils.fileExists(aqProposal)).resolves.toBe(false);
await expect(FileSystemUtils.fileExists(aqArchive)).resolves.toBe(false);
});
it('should preserve Windsurf content outside markers during update', async () => {
const wsPath = path.join(
testDir,