Skip to content

fix(opencode): use plural commands directory per official docs#2495

Open
tinesoft wants to merge 1 commit into
github:mainfrom
tinesoft:fix/opencode-commands
Open

fix(opencode): use plural commands directory per official docs#2495
tinesoft wants to merge 1 commit into
github:mainfrom
tinesoft:fix/opencode-commands

Conversation

@tinesoft
Copy link
Copy Markdown
Contributor

@tinesoft tinesoft commented May 8, 2026

Description

opencode's official documentation specifies .opencode/commands/ (plural) as the directory for custom slash commands
(see https://opencode.ai/docs/commands/). The integration was using the old singular form .opencode/command, which
still works for backwards compatibility but is no longer the recommended convention.

This updates commands_subdir and registrar_config["dir"] in the opencode integration, along with the corresponding
test constants.

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest
  • Tested with a sample project (if applicable)

All 23 opencode integration tests pass (uv run python -m pytest tests/integrations/test_integration_opencode.py -q).

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (describe below)

Code changes and documentation research generated by Claude Code (Claude Sonnet 4.6). The AI verified the correct
directory name against the official opencode docs and confirmed backwards compatibility before making the change.

Copilot AI review requested due to automatic review settings May 8, 2026 05:53
@tinesoft tinesoft requested a review from mnriem as a code owner May 8, 2026 05:53
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Opencode integration to follow Opencode’s current convention of using the plural .opencode/commands/ directory for custom slash commands, aligning Spec Kit’s generated paths and registrar configuration with official Opencode docs.

Changes:

  • Change Opencode integration commands_subdir from commandcommands.
  • Change Opencode registrar output directory from .opencode/command.opencode/commands.
  • Update Opencode integration test constants to match the new directory naming.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/specify_cli/integrations/opencode/__init__.py Updates Opencode integration config and registrar directory to .opencode/commands.
tests/integrations/test_integration_opencode.py Updates test expectations for the new Opencode commands directory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/specify_cli/integrations/opencode/__init__.py
Comment thread tests/integrations/test_integration_opencode.py
The official opencode documentation specifies that custom commands should now be placed in a directory named `.opencode/commands`.
Although previus path is still supported, it is recommended to use the plural form to ensure compatibility with future updates and to follow the standard convention. (See <https://opencode.ai/docs/config/#precedence-order>)

Updated commands_subdir and registrar dir from `.opencode/command` to `.opencode/commands` to match opencode's canonical configuration.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Comments suppressed due to low confidence (2)

src/specify_cli/integrations/opencode/init.py:26

  • shutil.rmtree(legacy) can raise (e.g., permissions, transient FS errors, or if the path is a symlink). Because this runs during setup(), a failure here will abort the install even though the new .opencode/commands files were created. Handle errors explicitly (and treat symlinks specially: either refuse to touch them or unlink the symlink itself) so setup is robust and doesn’t risk acting on unexpected filesystem types.
    legacy = project_root / ".opencode" / "command"
    if not legacy.is_dir():
        return 0
    count = sum(1 for _ in legacy.iterdir())
    shutil.rmtree(legacy)
    return count

src/specify_cli/integrations/opencode/init.py:56

  • The PR description says this is a directory-name update and notes the old singular directory “still works for backwards compatibility”, but setup() now actively removes the legacy directory. If intentional, this behavior change should be called out explicitly in the PR description/release notes; otherwise consider dropping the deletion or making it opt-in to preserve backwards compatibility expectations.
    def setup(
        self,
        project_root: Path,
        manifest: IntegrationManifest,
        parsed_options: dict[str, Any] | None = None,
        **opts: Any,
    ) -> list[Path]:
        """Install commands and remove any legacy `.opencode/command` directory."""
        created = super().setup(project_root, manifest, parsed_options=parsed_options, **opts)
        _migrate_legacy_command_dir(project_root)
        return created
  • Files reviewed: 2/2 changed files
  • Comments generated: 2

Comment on lines +14 to +19
"""Remove the legacy `.opencode/command` directory.

Called after setup() has already written canonical files to
`.opencode/commands/`. The legacy directory only ever contained
spec-kit-managed files, so it is safe to remove.
Returns the number of entries that were in the legacy directory.
Comment on lines +67 to +91
def test_removes_legacy_command_dir(self, tmp_path):
legacy = tmp_path / ".opencode" / "command"
legacy.mkdir(parents=True)
(legacy / "speckit.specify.md").write_text("old content")

removed = _migrate_legacy_command_dir(tmp_path)

assert removed == 1
assert not legacy.exists()

def test_no_op_when_no_legacy_dir(self, tmp_path):
removed = _migrate_legacy_command_dir(tmp_path)
assert removed == 0

def test_setup_removes_legacy_dir(self, tmp_path):
"""OpencodeIntegration.setup() cleans up legacy .opencode/command/."""
legacy = tmp_path / ".opencode" / "command"
legacy.mkdir(parents=True)
(legacy / "speckit.specify.md").write_text("old content")

i = get_integration("opencode")
m = IntegrationManifest("opencode", tmp_path)
i.setup(tmp_path, m)

assert not legacy.exists()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants