diff --git a/openspec/specs/ai-tool-paths/spec.md b/openspec/specs/ai-tool-paths/spec.md index 4394812570..1b02b9c506 100644 --- a/openspec/specs/ai-tool-paths/spec.md +++ b/openspec/specs/ai-tool-paths/spec.md @@ -32,10 +32,16 @@ The `AI_TOOLS` array SHALL include `skillsDir` for tools that support the Agent - **WHEN** looking up the `cursor` tool - **THEN** `skillsDir` SHALL be `.cursor` -#### Scenario: Windsurf paths defined +#### Scenario: Devin Desktop paths defined -- **WHEN** looking up the `windsurf` tool -- **THEN** `skillsDir` SHALL be `.windsurf` +- **WHEN** looking up the `devin` tool +- **THEN** `skillsDir` SHALL be `.devin` + +#### Scenario: Legacy Windsurf tool ID + +- **WHEN** initializing with `openspec init --tools windsurf` +- **THEN** the `windsurf` alias SHALL resolve to `devin` +- **AND** when skill delivery is enabled, skills SHALL be generated under `.devin/skills/`, not `.windsurf/skills/` #### Scenario: Kimi Code paths defined diff --git a/openspec/specs/cli-artifact-workflow/spec.md b/openspec/specs/cli-artifact-workflow/spec.md index 2b82647027..a0f75859d1 100644 --- a/openspec/specs/cli-artifact-workflow/spec.md +++ b/openspec/specs/cli-artifact-workflow/spec.md @@ -208,8 +208,8 @@ The system SHALL support an `apply` block in schema definitions that controls wh #### Scenario: Schema without apply block - **WHEN** a schema has no `apply` block -- **THEN** the system requires all artifacts to exist before apply is available -- **AND** uses default instruction: "All artifacts complete. Proceed with implementation." +- **THEN** the system requires all non-skipped artifacts to exist before apply is available +- **AND** once those artifacts exist, uses default instruction: "All required artifacts complete. Proceed with implementation." ### Requirement: Apply Instructions Command @@ -275,23 +275,24 @@ The `artifact-experimental-setup` command SHALL accept a `--tool ` flag ### Requirement: Output messaging -The setup command SHALL display clear output about what was generated. +The `openspec init` command SHALL display clear output about what was generated. #### Scenario: Show target tool in output -- **WHEN** setup command runs successfully -- **THEN** output includes the target tool name (e.g., "Setting up for Cursor...") +- **WHEN** initialization creates or refreshes a tool configuration +- **THEN** output includes the tool name under `Created:` or `Refreshed:`, respectively #### Scenario: Show generated paths -- **WHEN** setup command completes -- **THEN** output lists all generated skill file paths -- **AND** lists all generated command file paths (if applicable) +- **WHEN** initialization generates skills or commands +- **THEN** output summarizes their counts and destination directories +- **AND** only reports the types enabled by the selected profile and delivery mode #### Scenario: Show skipped commands message -- **WHEN** command generation is skipped due to missing adapter -- **THEN** output includes message: "Command generation skipped - no adapter for " +- **WHEN** initialization skips command generation due to a missing adapter +- **THEN** output includes message: "Commands skipped for: (no adapter)" +- **AND** `` lists the skipped tool IDs separated by commas ### Requirement: Status JSON provides planning context The status command SHALL provide machine-readable planning context for changes. diff --git a/openspec/specs/cli-change/spec.md b/openspec/specs/cli-change/spec.md index 46866f98a9..2809209609 100644 --- a/openspec/specs/cli-change/spec.md +++ b/openspec/specs/cli-change/spec.md @@ -37,19 +37,29 @@ The system SHALL provide a `change` command with subcommands for displaying, lis ### Requirement: Legacy Compatibility -The system SHALL maintain backward compatibility with the existing `list` command while showing deprecation notices. +The system SHALL retain `openspec change list` as a deprecated alias for listing active changes and direct users to `openspec list`. #### Scenario: Legacy list command -- **WHEN** executing `openspec list` -- **THEN** display current list of changes (existing behavior) -- **AND** show deprecation notice: "Note: 'openspec list' is deprecated. Use 'openspec change list' instead." +- **WHEN** executing `openspec change list` +- **THEN** display the current list of active changes on stdout +- **AND** write `Warning: "openspec change list" is deprecated. Use "openspec list".` to stderr + +#### Scenario: Legacy list with JSON output + +- **WHEN** executing `openspec change list --json` +- **THEN** output the active changes as a JSON array on stdout +- **AND** write the deprecation warning to stderr without corrupting the JSON output -#### Scenario: Legacy list with --all flag +#### Scenario: Unsupported legacy list flag -- **WHEN** executing `openspec list --all` -- **THEN** display all changes (existing behavior) -- **AND** show same deprecation notice +- **WHEN** executing `openspec change list --all` +- **THEN** reject the unknown option with a nonzero exit code + +#### Scenario: Preferred list command + +- **WHEN** executing `openspec list` +- **THEN** display the current list of active changes without a deprecation warning ### Requirement: Interactive show selection diff --git a/test/cli-e2e/basic.test.ts b/test/cli-e2e/basic.test.ts index 1db7e33a76..096bfa3472 100644 --- a/test/cli-e2e/basic.test.ts +++ b/test/cli-e2e/basic.test.ts @@ -86,6 +86,40 @@ describe('openspec CLI e2e basics', () => { expectJsonOnlyOutput(result); }); + describe('legacy change list compatibility', () => { + it.each([ + { args: [], output: 'c1\n' }, + { args: ['--long'], output: 'c1: Test Change [deltas 1]\n' }, + ])('preserves text output with $args and warns on stderr', async ({ args, output }) => { + const projectDir = await prepareFixture('tmp-init'); + const result = await runCLI(['change', 'list', ...args], { cwd: projectDir }); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toBe(output); + expect(result.stderr).toContain('Warning: "openspec change list" is deprecated. Use "openspec list".'); + }); + + it('preserves JSON output and warns on stderr', async () => { + const projectDir = await prepareFixture('tmp-init'); + const result = await runCLI(['change', 'list', '--json'], { cwd: projectDir }); + + expect(result.exitCode).toBe(0); + expect(JSON.parse(result.stdout)).toEqual([ + { id: 'c1', title: 'Test Change', deltaCount: 1, taskStatus: { total: 0, completed: 0 } }, + ]); + expect(result.stderr).toContain('Warning: "openspec change list" is deprecated. Use "openspec list".'); + }); + + it('rejects the unsupported --all option', async () => { + const projectDir = await prepareFixture('tmp-init'); + const result = await runCLI(['change', 'list', '--all'], { cwd: projectDir }); + + expect(result.exitCode).toBe(1); + expect(result.stdout).toBe(''); + expect(result.stderr).toContain("error: unknown option '--all'"); + }); + }); + it('keeps schemas --json free of spinner output', async () => { const projectDir = await prepareFixture('tmp-init'); const result = await runCLI(['schemas', '--json'], { cwd: projectDir });