Skip to content

fix(commands): self-register slash commands via config hook (1.0.10) - #17

Merged
charfeng1 merged 2 commits into
masterfrom
fix/self-register-slash-commands
May 25, 2026
Merged

fix(commands): self-register slash commands via config hook (1.0.10)#17
charfeng1 merged 2 commits into
masterfrom
fix/self-register-slash-commands

Conversation

@charfeng1

@charfeng1 charfeng1 commented May 25, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the silently-broken slash-command auto-install. On current opencode, /ralph-loop, /cancel-ralph, and /help don't appear in autocomplete because the plugin writes its .md files to ~/.config/opencode/command/ (singular) but opencode reads from ~/.config/opencode/commands/ (plural). Verified live on 1.15.10.

Fix

Switch to the supported plugin-side registration path: the config hook on Hooks. The plugin receives the runtime config object and mutates input.command to add its entries. opencode merges them on load. No filesystem state, no path conventions to drift.

return {
  config: async (input) => {
    input.command = input.command ?? {};
    for (const [name, def] of Object.entries(RALPH_COMMANDS)) {
      if (!input.command[name]) input.command[name] = def;
    }
  },
  // ...
};

User-defined entries in opencode.json take precedence — we only set defaults when absent.

Also fixed

Skills auto-copy was hitting the same singular/plural bug — corrected from skill/ to skills/.

Test plan

  • npm run typecheck clean
  • npm test — 24/24 passing
  • Loader-safety simulation: 3 function exports, RALPH_COMMANDS is module-internal (not exported, doesn't appear in Object.values(module))
  • Verified pattern works on goal-loop sandbox (sibling plugin in a feature branch) — slash commands appeared in autocomplete with zero command block in opencode.json
  • CI: Test (Node 20), Test (Node 22) green
  • After merge + publish: install 1.0.10 from npm, confirm /ralph-loop appears in autocomplete without manually editing ~/.config/opencode/commands/

Bumps

1.0.91.0.10

Summary by CodeRabbit

  • Chores

    • Version bump to 1.0.10
  • New Features

    • Slash-command templates now auto-register at startup for seamless command availability
    • Centralized command definitions added for clearer command behavior and state handling
  • Refactor

    • First-run setup streamlined: only plugin skill assets are installed (command/markdown files are no longer copied)

Review Change Stack

Slash commands were silently broken on current opencode: the auto-copy
shipped them to ~/.config/opencode/command/ (singular), but opencode
reads from .../commands/ (plural). `/ralph-loop` etc. never appeared in
autocomplete unless the user manually copied the files.

Switch to opencode's supported pattern for plugin-defined commands:
mutate `input.command` from the `config` hook. opencode merges the
plugin-provided dict into the runtime config, so the slash commands
appear with zero filesystem state and no path conventions to drift.

- New `config` hook adds `ralph-loop`, `cancel-ralph`, `help` defaults.
  User-defined entries in opencode.json take precedence (we only set
  when absent).
- `setupSkillsAndCommands` renamed to `setupSkills` (commands no longer
  copied to disk).
- Skills target dir corrected from `skill/` (singular, dead) to
  `skills/` (plural, what opencode actually scans).
- commands/*.md files retained in the package for now — they're the
  same content as the inline templates, useful as documentation.

Tests: 24 passing, typecheck clean. Loader-safe: 3 function exports
(default, parseState, serializeState); RALPH_COMMANDS is module-internal.
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3097917b-c56a-496d-b4d5-eda2542a6f39

📥 Commits

Reviewing files that changed from the base of the PR and between 23ffbbb and 72d1a1b.

📒 Files selected for processing (2)
  • src/commands.ts
  • src/index.ts

📝 Walkthrough

Walkthrough

Setup now copies only skill directories, command metadata moves to src/commands.ts, and the plugin registers missing slash commands during the async config hook. Package version updated to 1.0.10.

Changes

Setup and Command Configuration Refactor

Layer / File(s) Summary
Command definitions module
src/commands.ts
Adds STATE_* constants, RalphCommandDef interface, and RALPH_COMMANDS registry with templates for /ralph-loop, /cancel-ralph, and /help.
Skills setup function
src/index.ts
Introduces setupSkills() which copies only skill directories (derived from Object.keys(RALPH_COMMANDS)) into ~/.config/opencode/skills/, ignoring filesystem errors.
Runtime registration and release
src/index.ts, package.json
Plugin startup calls setupSkills(), adds an async config hook that fills missing input.command entries from RALPH_COMMANDS, and bumps package version to 1.0.10.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code and moved the commands home,
Skills tucked in folders, no stray docs to comb,
A config hook seeds the slashes in place,
Version bumped softly — a warm carrot embrace! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: self-registering slash commands via a config hook, with the version bump included for context.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/self-register-slash-commands

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates the plugin to version 1.0.10 and refactors the registration of slash commands by moving from file-based copying to an inline configuration using a new config hook. The review feedback focuses on improving maintainability and compliance with opencode 1.15+ plugin standards, specifically by suggesting that constants and command definitions be moved to a sibling module to keep the entrypoint clean of non-function exports. Additionally, it is recommended to derive the skills list dynamically and use constants for the state file path within command templates.

Comment thread src/index.ts Outdated
}
if (!existsSync(pluginSkillsDir)) return;

const skills = ["ralph-loop", "cancel-ralph", "help"];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The list of skills is currently hardcoded and duplicates the keys defined in RALPH_COMMANDS. To improve maintainability and ensure that new commands automatically have their corresponding skills set up, consider deriving this list dynamically from the command definitions. Note that per opencode plugin rules, RALPH_COMMANDS and any derived constants should be moved to a sibling module rather than being defined in src/index.ts.

Suggested change
const skills = ["ralph-loop", "cancel-ralph", "help"];
const skills = Object.keys(RALPH_COMMANDS);
References
  1. In opencode 1.15+ plugins, the entrypoint module (e.g., src/index.ts) must only export functions or objects shaped as { server: <function> }. Exporting other types such as RegExps, constants, or plain objects will trigger a 'Plugin export is not a function' error. Move non-function values to sibling modules and import them without re-exporting.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 72d1a1bsetupSkills() now derives the list via Object.keys(RALPH_COMMANDS) so adding a new command auto-includes its skill dir.

Comment thread src/index.ts Outdated
// on the config-block entry). Mutating input.command via the config hook
// is opencode's supported way to self-register slash commands from a
// plugin — no file copying, no path conventions.
const RALPH_COMMANDS = {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Following the general rule for opencode plugins, non-function values like RALPH_COMMANDS should be moved to a sibling module (e.g., src/commands.ts) and imported without re-exporting. This keeps the entrypoint clean and avoids potential issues with the plugin loader, which expects only functions to be exported from the main module. Additionally, this would be a good place to unify the help content, as it is currently duplicated between this template and the help tool implementation.

References
  1. In opencode 1.15+ plugins, the entrypoint module (e.g., src/index.ts) must only export functions or objects shaped as { server: <function> }. Exporting other types such as RegExps, constants, or plain objects will trigger a 'Plugin export is not a function' error. Move non-function values to sibling modules and import them without re-exporting.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 72d1a1bRALPH_COMMANDS moved to src/commands.ts and imported. Entrypoint stays loader-safe.

Comment thread src/index.ts Outdated
Create the state file in the project directory:

\`\`\`bash
mkdir -p .opencode && cat > .opencode/ralph-loop.local.md << 'EOF'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The state file path is hardcoded in the template. Consider using the STATE_FILENAME constant to ensure consistency across the codebase. This also applies to lines 101, 103, and 132. Note that per opencode plugin rules, this constant should be imported from a sibling module to keep the entrypoint clean of non-function exports.

Suggested change
mkdir -p .opencode && cat > .opencode/ralph-loop.local.md << 'EOF'
mkdir -p .opencode && cat > .opencode/${STATE_FILENAME} << 'EOF'
References
  1. In opencode 1.15+ plugins, the entrypoint module (e.g., src/index.ts) must only export functions or objects shaped as { server: <function> }. Exporting other types such as RegExps, constants, or plain objects will trigger a 'Plugin export is not a function' error. Move non-function values to sibling modules and import them without re-exporting.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 72d1a1b — templates now interpolate STATE_DIR/STATE_FILENAME from src/commands.ts instead of hardcoding the path.

…g module

- Move RALPH_COMMANDS (non-function const) out of src/index.ts into
  src/commands.ts. Keeps the plugin entrypoint loader-safe.
- Derive setupSkills() skill list from Object.keys(RALPH_COMMANDS) so
  adding a new command auto-includes its skill directory.
- Replace hardcoded .opencode/ralph-loop.local.md paths in templates
  with STATE_DIR/STATE_FILENAME-derived STATE_PATH.

Addresses Gemini review comments on #17 (lines 44, 64, 72).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@charfeng1

Copy link
Copy Markdown
Owner Author

AI-reviewer loop converged at 72d1a1b:

  • CI: ✅ Node 20 + Node 22 pass; CodeRabbit pass.
  • CodeRabbit: 🎉 No actionable comments on 72d1a1b.
  • Gemini: 3 medium-priority inline comments addressed in 72d1a1b:
    • src/index.ts:44setupSkills() derives skill list from Object.keys(RALPH_COMMANDS).
    • src/index.ts:64RALPH_COMMANDS moved to src/commands.ts (entrypoint stays loader-safe).
    • src/index.ts:72 → templates now interpolate STATE_DIR/STATE_FILENAME instead of hardcoded paths.

Ready for merge — 1.0.10 will auto-publish via OIDC.

@charfeng1
charfeng1 merged commit 2ef2386 into master May 25, 2026
4 checks passed
@charfeng1
charfeng1 deleted the fix/self-register-slash-commands branch May 25, 2026 08:31
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.

1 participant