-
Notifications
You must be signed in to change notification settings - Fork 1.7k
fix(opencode): use plural commands/ directory to match OpenCode convention #760
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fsilvaortiz
wants to merge
3
commits into
Fission-AI:main
Choose a base branch
from
fsilvaortiz:fix/748-opencode-commands-directory
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+292
−24
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
c58d3c8
fix(opencode): use plural `commands/` directory to match OpenCode con…
fsilvaortiz f2ece90
fix(legacy): detect both opsx-* and openspec-* patterns, auto-cleanup…
fsilvaortiz 692244b
chore: update task description to reflect dual-pattern support
fsilvaortiz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@fission-ai/openspec": patch | ||
| --- | ||
|
|
||
| fix: OpenCode adapter now uses `.opencode/commands/` (plural) to match OpenCode's official directory convention. Fixes #748. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
openspec/changes/fix-opencode-commands-directory/.openspec.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| schema: spec-driven | ||
| created: 2026-02-25 |
48 changes: 48 additions & 0 deletions
48
openspec/changes/fix-opencode-commands-directory/design.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| ## Context | ||
|
|
||
| The OpenCode adapter in `src/core/command-generation/adapters/opencode.ts` currently generates command files at `.opencode/command/opsx-<id>.md` (singular `command`). OpenCode's official documentation uses `.opencode/commands/` (plural), and every other adapter in the codebase follows the plural convention for commands directories. The legacy cleanup module in `src/core/legacy-cleanup.ts` also references the singular form for detecting old artifacts. | ||
|
|
||
| ## Goals / Non-Goals | ||
|
|
||
| **Goals:** | ||
| - Align the OpenCode adapter path with OpenCode's official `.opencode/commands/` convention | ||
| - Add the old singular path `.opencode/command/` to legacy cleanup so existing installations are properly cleaned | ||
| - Update documentation to reflect the corrected path | ||
| - Update test assertions to match the new path | ||
|
|
||
| **Non-Goals:** | ||
| - Changing the OpenCode skill path (`.opencode/skills/`) — already correct | ||
| - Modifying any other adapter's directory structure | ||
| - Adding migration prompts or interactive upgrade flows | ||
|
|
||
| ## Decisions | ||
|
|
||
| ### 1. Direct path rename in adapter | ||
|
|
||
| **Decision:** Change `path.join('.opencode', 'command', ...)` to `path.join('.opencode', 'commands', ...)` in the adapter's `getFilePath` method. | ||
|
|
||
| **Rationale:** This is a single-line change that aligns with the established pattern across all other adapters. No abstraction or indirection needed. | ||
|
|
||
| **Alternatives considered:** | ||
| - Add a configuration option for the directory name — rejected as over-engineering for a bug fix | ||
| - Keep singular and add plural as alias — rejected as it creates ambiguity about which is canonical | ||
|
|
||
| ### 2. Legacy cleanup via existing constant map | ||
|
|
||
| **Decision:** Update the `LEGACY_SLASH_COMMAND_PATHS` entry for `'opencode'` from `'.opencode/command/openspec-*.md'` to `'.opencode/command/opsx-*.md'` (the old singular path becomes the legacy pattern) and ensure the new path is handled by the current command generation pipeline. | ||
|
|
||
| **Rationale:** The existing legacy cleanup infrastructure uses `LEGACY_SLASH_COMMAND_PATHS` as an explicit lookup. The old singular-path pattern already matches the legacy format (`openspec-*` prefix from the old SlashCommandRegistry era). The current command generation uses the `opsx-*` prefix, so we also need to add a legacy pattern for `opsx-*` files in the old singular directory. | ||
|
|
||
| **Alternatives considered:** | ||
| - Add a separate migration script — rejected; the existing legacy cleanup mechanism handles this scenario | ||
|
|
||
| ### 3. Documentation update | ||
|
|
||
| **Decision:** Update the `docs/supported-tools.md` table entry for OpenCode from `.opencode/command/opsx-<id>.md` to `.opencode/commands/opsx-<id>.md`. | ||
|
|
||
| **Rationale:** Documentation must match the actual generated paths. | ||
|
|
||
| ## Risks / Trade-offs | ||
|
|
||
| - **[Existing installations have files at old path]** → Mitigated by legacy cleanup detecting `.opencode/command/` artifacts. On next `openspec init`, old files are cleaned up and new files written to `.opencode/commands/`. | ||
| - **[Users referencing old path in custom scripts]** → Low risk. The old path was incorrect per OpenCode's specification, so custom references were already misaligned. |
26 changes: 26 additions & 0 deletions
26
openspec/changes/fix-opencode-commands-directory/proposal.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| ## Why | ||
|
|
||
| The OpenCode adapter uses `.opencode/command/` (singular) for its commands directory, but OpenCode's official documentation specifies `.opencode/commands/` (plural). Every other adapter in the codebase also uses plural directory names (`.claude/commands/`, `.cursor/commands/`, `.factory/commands/`, etc.). This inconsistency was introduced in Oct 2025 without documented rationale. Fixes [#748](https://github.com/Fission-AI/OpenSpec/issues/748). | ||
|
|
||
| ## What Changes | ||
|
|
||
| - OpenCode adapter path changes from `.opencode/command/` to `.opencode/commands/` | ||
| - Legacy cleanup adds `.opencode/command/` (old singular path) for backward compatibility | ||
| - Documentation updated to reflect the new plural path | ||
|
|
||
| ## Capabilities | ||
|
|
||
| ### New Capabilities | ||
|
|
||
| _None._ | ||
|
|
||
| ### Modified Capabilities | ||
|
|
||
| - `command-generation`: OpenCode adapter path changes from singular `command/` to plural `commands/` to match OpenCode's official directory convention | ||
|
|
||
| ## Impact | ||
|
|
||
| - `src/core/command-generation/adapters/opencode.ts` — adapter path | ||
| - `src/core/legacy-cleanup.ts` — legacy cleanup pattern + add old singular path | ||
| - `docs/supported-tools.md` — documentation table | ||
| - `test/core/command-generation/adapters.test.ts` — test assertion |
63 changes: 63 additions & 0 deletions
63
openspec/changes/fix-opencode-commands-directory/specs/command-generation/spec.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| ## MODIFIED Requirements | ||
|
|
||
| ### Requirement: ToolCommandAdapter interface | ||
|
|
||
| The system SHALL define a `ToolCommandAdapter` interface for per-tool formatting. | ||
|
|
||
| #### Scenario: Adapter interface structure | ||
|
|
||
| - **WHEN** implementing a tool adapter | ||
| - **THEN** `ToolCommandAdapter` SHALL require: | ||
| - `toolId`: string identifier matching `AIToolOption.value` | ||
| - `getFilePath(commandId: string)`: returns file path for command (relative from project root, or absolute for global-scoped tools like Codex) | ||
| - `formatFile(content: CommandContent)`: returns complete file content with frontmatter | ||
|
|
||
| #### Scenario: Claude adapter formatting | ||
|
|
||
| - **WHEN** formatting a command for Claude Code | ||
| - **THEN** the adapter SHALL output YAML frontmatter with `name`, `description`, `category`, `tags` fields | ||
| - **AND** file path SHALL follow pattern `.claude/commands/opsx/<id>.md` | ||
|
|
||
| #### Scenario: Cursor adapter formatting | ||
|
|
||
| - **WHEN** formatting a command for Cursor | ||
| - **THEN** the adapter SHALL output YAML frontmatter with `name` as `/opsx-<id>`, `id`, `category`, `description` fields | ||
| - **AND** file path SHALL follow pattern `.cursor/commands/opsx-<id>.md` | ||
|
|
||
| #### Scenario: Windsurf adapter formatting | ||
|
|
||
| - **WHEN** formatting a command for Windsurf | ||
| - **THEN** the adapter SHALL output YAML frontmatter with `name`, `description`, `category`, `tags` fields | ||
| - **AND** file path SHALL follow pattern `.windsurf/workflows/opsx-<id>.md` | ||
|
|
||
| #### Scenario: OpenCode adapter formatting | ||
|
|
||
| - **WHEN** formatting a command for OpenCode | ||
| - **THEN** the adapter SHALL output YAML frontmatter with `description` field | ||
| - **AND** file path SHALL follow pattern `.opencode/commands/opsx-<id>.md` using `path.join('.opencode', 'commands', ...)` for cross-platform compatibility | ||
| - **AND** the adapter SHALL transform colon-based command references (`/opsx:name`) to hyphen-based (`/opsx-name`) in the body | ||
|
|
||
| ## ADDED Requirements | ||
|
|
||
| ### Requirement: Legacy cleanup for renamed OpenCode command directory | ||
|
|
||
| The legacy cleanup module SHALL detect and remove old OpenCode command files from the previous singular `.opencode/command/` directory path. | ||
|
|
||
| #### Scenario: Detect old singular-path OpenCode command files | ||
|
|
||
| - **WHEN** running legacy artifact detection on a project with files matching `.opencode/command/opsx-*.md` or `.opencode/command/openspec-*.md` | ||
| - **THEN** the system SHALL include those files in the legacy slash command files list via `LEGACY_SLASH_COMMAND_PATHS` | ||
| - **AND** `LegacySlashCommandPattern.pattern` SHALL accept `string | string[]` to support multiple glob patterns per tool | ||
|
|
||
| #### Scenario: Clean up old OpenCode command files on init | ||
|
|
||
| - **WHEN** a user runs `openspec init` in a project with old `.opencode/command/` artifacts | ||
| - **THEN** the system SHALL remove the old files | ||
| - **AND** generate new command files at `.opencode/commands/` | ||
|
|
||
| #### Scenario: Auto-cleanup legacy artifacts in non-interactive mode | ||
|
|
||
| - **WHEN** a user runs `openspec init` in non-interactive mode (e.g., CI) and legacy artifacts are detected | ||
| - **THEN** the system SHALL auto-cleanup legacy artifacts without requiring `--force` | ||
| - **AND** legacy slash command files (100% OpenSpec-managed) SHALL be removed | ||
| - **AND** config file cleanup SHALL only remove OpenSpec markers (never delete user files) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| ## 1. Adapter Fix | ||
|
|
||
| - [x] 1.1 Update `src/core/command-generation/adapters/opencode.ts`: change `path.join('.opencode', 'command', ...)` to `path.join('.opencode', 'commands', ...)` and update the JSDoc comment | ||
|
|
||
| ## 2. Legacy Cleanup | ||
|
|
||
| - [x] 2.1 Update `src/core/legacy-cleanup.ts`: update the `'opencode'` entry in `LEGACY_SLASH_COMMAND_PATHS` to detect both `opsx-*.md` and `openspec-*.md` patterns at `.opencode/command/` for backward compatibility | ||
|
|
||
| ## 3. Documentation | ||
|
|
||
| - [x] 3.1 Update `docs/supported-tools.md`: change OpenCode command path from `.opencode/command/opsx-<id>.md` to `.opencode/commands/opsx-<id>.md` | ||
|
|
||
| ## 4. Tests | ||
|
|
||
| - [x] 4.1 Update `test/core/command-generation/adapters.test.ts`: change the OpenCode file path assertion from `path.join('.opencode', 'command', 'opsx-explore.md')` to `path.join('.opencode', 'commands', 'opsx-explore.md')` | ||
|
|
||
| ## 5. Changeset | ||
|
|
||
| - [x] 5.1 Create a changeset file (`.changeset/fix-opencode-commands-directory.md`) with a patch bump describing the path fix |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also account for non-interactive
openspec inithere? Right now legacy artifacts cause init to exit unless--forceis set. That could surprise existing/commandusers in CI. Either auto-handle this rename migration or call out the one-time--forcerequirement in release notes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call —
handleLegacyCleanupnow auto-cleans in non-interactive mode instead of aborting with exit 1. Legacy slash commands are 100% OpenSpec-managed, and config file cleanup only removes markers (never deletes files), so auto-cleanup is safe without--force.Added a test verifying that
openspec init --tools opencodein non-interactive mode cleans up.opencode/command/legacy files and generates new ones at.opencode/commands/. Spec updated with the new scenario.