Skip to content

fix(opencode): use commands/ directory (plural) to match OpenCode docs#2453

Open
marcusburghardt wants to merge 2 commits intogithub:mainfrom
marcusburghardt:fix/opencode-commands-dir
Open

fix(opencode): use commands/ directory (plural) to match OpenCode docs#2453
marcusburghardt wants to merge 2 commits intogithub:mainfrom
marcusburghardt:fix/opencode-commands-dir

Conversation

@marcusburghardt
Copy link
Copy Markdown

@marcusburghardt marcusburghardt commented May 5, 2026

Description

Align the OpenCode integration's command directory with OpenCode's
documented standard: .opencode/commands/ (plural) instead of
.opencode/command/ (singular), while maintaining full backward
compatibility with existing projects.

OpenCode's documentation (https://opencode.ai/docs/commands/) uses
commands/ as the canonical directory name. The OpenCode runtime
supports both via a {command,commands} glob, so the singular form
works but is outdated. OpenSpec already fixed this in
Fission-AI/OpenSpec#748.

Changes

Commit 1 — directory rename:

  • commands_subdir: "command""commands"
  • registrar_config.dir: ".opencode/command"".opencode/commands"
  • Test constant updates in test_integration_opencode.py and
    test_integration_subcommand.py

Commit 2 — backward compatibility (legacy_dir):

  • Add legacy_dir: ".opencode/command" to OpenCode's registrar_config
  • Add _resolve_agent_dir() to CommandRegistrar that falls back to
    legacy_dir when the canonical directory does not exist, emitting a
    deprecation warning directing users to specify integration upgrade
  • Apply the fallback in register_commands(),
    register_commands_for_all_agents(),
    register_commands_for_non_skill_agents(), and
    unregister_commands()
  • The legacy_dir mechanism is opt-in: integrations that do not
    declare it get no fallback and no behavioral change

Backward compatibility

Existing projects with .opencode/command/ (singular) continue
working without changes:

  • Extension/preset registration: _resolve_agent_dir() detects
    the legacy directory and registers commands there, with a warning
  • Command cleanup: unregister_commands() finds and removes
    commands from the legacy directory
  • Upgrade path: specify integration upgrade opencode writes
    new commands to .opencode/commands/ and removes stale files from
    .opencode/command/ via manifest diff (end-to-end test included)
  • New installs: setup() always writes to .opencode/commands/

Previously deployed Speckit commands in .opencode/command/ remain
functional. Users can migrate by running:

specify integration upgrade opencode

Or manually remove only the Speckit-owned orphans:

rm .opencode/command/speckit.*.md

Testing

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

Ran specify init /tmp/speckit-test --integration opencode --script sh
and verified commands are created in .opencode/commands/ (plural)
with no .opencode/command/ (singular) directory created.

Also ran uv run python -m pytest tests/test_agent_config_consistency.py
(24/24 pass) and all 105 affected tests pass.

New tests added

  • test_registrar_config_has_legacy_dir — config has legacy_dir field
  • test_legacy_dir_extension_registration — extensions register in
    legacy dir with deprecation warning
  • test_legacy_dir_unregister — unregister finds commands in legacy dir
  • test_canonical_dir_preferred_over_legacy — canonical dir takes
    precedence when both exist, no warning emitted
  • test_setup_writes_to_canonical_dir — new installs use canonical path
  • test_upgrade_migrates_opencode_legacy_dir — end-to-end: upgrade
    moves commands from legacy to canonical, removes stale files

AI Disclosure

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

This fix was identified through codebase analysis using OpenCode
(claude-opus-4-6). The source changes, _resolve_agent_dir() helper,
and all tests were authored with AI assistance. All tests were run
and verified locally.

OpenCode documentation (https://opencode.ai/docs/commands/) uses
.opencode/commands/ (plural) as the canonical command directory.
The OpenCode runtime supports both .opencode/command/ and
.opencode/commands/ via a {command,commands} glob, but the
singular form was the original convention and is now outdated.

Update the OpenCode integration to write to .opencode/commands/
instead of .opencode/command/, aligning with the documented
standard and the OpenSpec fix (Fission-AI/OpenSpec#748).

Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
Assisted-by: OpenCode (claude-opus-4-6)
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

This PR updates the OpenCode integration to use OpenCode’s documented .opencode/commands/ directory instead of the legacy singular .opencode/command/, and adjusts the related tests accordingly. The change fits the integration layer by aligning Spec Kit’s generated command paths with the current OpenCode convention.

Changes:

  • Switches the OpenCode integration’s command output directory from command to commands.
  • Updates the registrar path for OpenCode command registration to the plural directory.
  • Refreshes OpenCode integration and integration-switch tests to assert the new path.
Show a summary per file
File Description
src/specify_cli/integrations/opencode/__init__.py Updates OpenCode integration config and registrar paths to the plural commands directory.
tests/integrations/test_integration_opencode.py Updates Opencode integration test constants to match the new directory layout.
tests/integrations/test_integration_subcommand.py Updates integration-switch extension assertions to expect .opencode/commands/.

Copilot's findings

Tip

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

Comments suppressed due to low confidence (1)

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

  • This path change also breaks cleanup of commands that were previously registered for OpenCode. Extension/preset registries only store command names, and unregister_commands() reconstructs the file path from the current AGENT_CONFIGS entry; after this change it will try to delete .opencode/commands/... and leave legacy .opencode/command/... files behind. Because OpenCode still loads both directories, removed/disabled/upgraded commands can remain active.
    registrar_config = {
        "dir": ".opencode/commands",
  • Files reviewed: 3/3 changed files
  • Comments generated: 2

Comment thread src/specify_cli/integrations/opencode/__init__.py
Comment thread tests/integrations/test_integration_subcommand.py
@mnriem
Copy link
Copy Markdown
Collaborator

mnriem commented May 5, 2026

Please support both models so we are not breaking existing installs. If the old one is detected we should warn the user that they need to upgrade their code agent integration (see https://github.github.com/spec-kit/reference/integrations.html#upgrade-an-integration)?

Copy link
Copy Markdown
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

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

See comments above

…ctory migration

Add _resolve_agent_dir() to CommandRegistrar that checks a
legacy_dir fallback when the canonical directory does not exist.
When legacy_dir is found, a deprecation warning directs users to
run "specify integration upgrade" to migrate.

The OpenCode integration declares legacy_dir: ".opencode/command"
so that extension and preset registration, as well as command
cleanup, continue working for projects that have not yet migrated
to .opencode/commands/.

The legacy_dir mechanism is opt-in: integrations that do not
declare it get no fallback and no behavioral change.

Add end-to-end test verifying that "specify integration upgrade
opencode" migrates commands from legacy .opencode/command/ to
canonical .opencode/commands/ and removes stale files.

Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
Assisted-by: OpenCode (claude-opus-4-6)
@marcusburghardt
Copy link
Copy Markdown
Author

See comments above

Thanks for the feedback @mnriem . I addressed the comments in fe32984 and updated the PR description to reflect the last changes . Essentially introduced an optional legacy_dir config to ensure a backward compatibility. This could also be used by other integrations whenever necessary without impacting existing integrations.

I also noticed there was no end-to-end test to simulate the upgrate workflow mentioned in https://github.github.com/spec-kit/reference/integrations.html#upgrade-an-integration so also introduce it.

@marcusburghardt marcusburghardt requested a review from mnriem May 6, 2026 06:06
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