fix(commands): self-register slash commands via config hook (1.0.10) - #17
Conversation
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.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughSetup now copies only skill directories, command metadata moves to ChangesSetup and Command Configuration Refactor
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
There was a problem hiding this comment.
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.
| } | ||
| if (!existsSync(pluginSkillsDir)) return; | ||
|
|
||
| const skills = ["ralph-loop", "cancel-ralph", "help"]; |
There was a problem hiding this comment.
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.
| const skills = ["ralph-loop", "cancel-ralph", "help"]; | |
| const skills = Object.keys(RALPH_COMMANDS); |
References
- 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.
There was a problem hiding this comment.
Fixed in 72d1a1b — setupSkills() now derives the list via Object.keys(RALPH_COMMANDS) so adding a new command auto-includes its skill dir.
| // 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 = { |
There was a problem hiding this comment.
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
- 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.
There was a problem hiding this comment.
Fixed in 72d1a1b — RALPH_COMMANDS moved to src/commands.ts and imported. Entrypoint stays loader-safe.
| Create the state file in the project directory: | ||
|
|
||
| \`\`\`bash | ||
| mkdir -p .opencode && cat > .opencode/ralph-loop.local.md << 'EOF' |
There was a problem hiding this comment.
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.
| mkdir -p .opencode && cat > .opencode/ralph-loop.local.md << 'EOF' | |
| mkdir -p .opencode && cat > .opencode/${STATE_FILENAME} << 'EOF' |
References
- 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.
There was a problem hiding this comment.
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>
|
AI-reviewer loop converged at
Ready for merge — 1.0.10 will auto-publish via OIDC. |
Summary
Fixes the silently-broken slash-command auto-install. On current opencode,
/ralph-loop,/cancel-ralph, and/helpdon't appear in autocomplete because the plugin writes its.mdfiles 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
confighook onHooks. The plugin receives the runtime config object and mutatesinput.commandto add its entries. opencode merges them on load. No filesystem state, no path conventions to drift.User-defined entries in
opencode.jsontake precedence — we only set defaults when absent.Also fixed
Skills auto-copy was hitting the same singular/plural bug — corrected from
skill/toskills/.Test plan
npm run typecheckcleannpm test— 24/24 passingRALPH_COMMANDSis module-internal (not exported, doesn't appear inObject.values(module))commandblock in opencode.json/ralph-loopappears in autocomplete without manually editing~/.config/opencode/commands/Bumps
1.0.9→1.0.10Summary by CodeRabbit
Chores
New Features
Refactor