How the OpenCode Plugin Registers Commands in the i-have-adhd Project
The OpenCode plugin registers commands by exposing its skill directory through the config hook in .opencode/plugins/i-have-adhd.mjs, which makes the /i-have-adhd command automatically available alongside an optional always-on mode that injects ADHD-friendly rules into every system prompt.
The i-have-adhd repository provides an OpenCode plugin that enhances AI interactions with ADHD-friendly formatting rules. Understanding how this OpenCode plugin registers its commands reveals the architecture behind OpenCode's plugin system, which uses skill discovery and runtime hooks to extend functionality. The plugin implements two distinct mechanisms: static skill registration for command availability and dynamic system prompt transformation for persistent behavior modification.
Skill Discovery and Command Registration
The primary mechanism for OpenCode plugin command registration occurs in the config hook, where the plugin declares its capabilities to the OpenCode runtime. Unlike traditional command registration that explicitly maps functions to CLI commands, this plugin leverages OpenCode's skill discovery system.
The Config Hook Implementation
In .opencode/plugins/i-have-adhd.mjs (lines 45-54), the plugin modifies the configuration object to include its skills directory:
config.skills = config.skills || {};
config.skills.paths = config.skills.paths || [];
if (!config.skills.paths.includes(skillsDir)) config.skills.paths.push(skillsDir);
This code appends the repository's skills folder to config.skills.paths, making the skill automatically discoverable by the generic skill tool. According to the i-have-adhd source code, adding this path enables the automatically generated /i-have-adhd command, which loads the ruleset defined in skills/i-have-adhd/SKILL.md. The OpenCode runtime scans these registered paths to build available slash commands without requiring explicit command definitions in the plugin code.
Runtime Behavior via System Prompt Transformation
Beyond static command registration, the plugin implements an experimental hook for persistent behavior modification. This secondary mechanism does not register new commands but transforms existing interactions.
The Always-On Hook
The plugin implements experimental.chat.system.transform (lines 55-78) to intercept and modify system prompts before each turn:
'experimental.chat.system.transform': async (_input, output) => { … }
This hook checks for a flag file at ~/.config/opencode/.i-have-adhd-always. When present, the hook reads the contents of skills/i-have-adhd/SKILL.md, strips the frontmatter using the rulesetBody() helper (lines 35-43), and prepends the ruleset to the system prompt. This implementation mirrors the "always-on" behavior found in Claude Code, allowing the ADHD-friendly formatting rules to persist across all interactions without requiring the explicit /i-have-adhd command.
Flag File Location and Management
The plugin constructs the flag file path from the user's XDG configuration directory or defaults to ~/.config (lines 27-33). The specific path opencode/.i-have-adhd-always determines whether the always-on mode is active. Toggling this mode requires only creating or removing this file, making it a stateless configuration mechanism that persists across sessions.
Practical Usage Examples
To utilize the registered commands and features provided by the plugin:
# Enable the plugin by adding it to opencode.json
# { "plugin": ["./.opencode/plugins/i-have-adhd.mjs"] }
# Use the automatically registered command
$ opencode chat "/i-have-adhd"
# Enable always-on mode (injects rules every turn)
$ touch ~/.config/opencode/.i-have-adhd-always
# Disable always-on mode permanently
$ rm ~/.config/opencode/.i-have-adhd-always
Programmatic usage via the OpenCode SDK:
import OpenCode from 'opencode';
const oc = new OpenCode();
await oc.loadPlugin('./.opencode/plugins/i-have-adhd.mjs');
// The registered command is now available
const resp = await oc.chat('/i-have-adhd');
console.log(resp);
Summary
- The plugin registers the
/i-have-adhdcommand by appending itsskillsdirectory toconfig.skills.pathsin theconfighook (lines 45-54). - Skill discovery happens automatically when OpenCode scans the registered paths and finds
skills/i-have-adhd/SKILL.md. - The
experimental.chat.system.transformhook (lines 55-78) provides optional always-on behavior by injecting rules into every system prompt. - State management occurs through a flag file at
~/.config/opencode/.i-have-adhd-always, checked on each turn. - The
rulesetBody()helper function (lines 35-43) processes the skill markdown to remove frontmatter before injection.
Frequently Asked Questions
How does the OpenCode plugin make the /i-have-adhd command available?
The plugin adds its local skills directory to config.skills.paths during the config hook execution in .opencode/plugins/i-have-adhd.mjs. OpenCode's runtime automatically generates slash commands for any valid SKILL.md files found in these registered paths, creating the /i-have-adhd command without explicit command mapping code.
What is the difference between using /i-have-adhd and always-on mode?
The /i-have-adhd command loads the skill ruleset for a single interaction when explicitly invoked, while always-on mode uses the experimental.chat.system.transform hook to prepend the ADHD-friendly rules to every system prompt automatically. The latter requires the flag file at ~/.config/opencode/.i-have-adhd-always to be present and persists across all chat turns until disabled.
Where does the OpenCode plugin look for the always-on flag file?
According to lines 27-33 of the plugin source, the code constructs the path using the XDG configuration directory (or defaulting to ~/.config) and appends opencode/.i-have-adhd-always. The plugin checks this specific location during each chat turn to determine whether to inject the ruleset.
Can I adapt this registration pattern for other custom skills?
Yes. The pattern demonstrated in .opencode/plugins/i-have-adhd.mjs is reusable: export an async function that returns an object with a config hook to register skill paths, and optionally implement experimental.chat.system.transform for persistent prompt modification. Any directory containing a properly formatted SKILL.md file can be registered using the same config.skills.paths.push() mechanism.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →