First off, thanks for taking the time to contribute! This document provides guidelines and instructions for contributing to oh-my-opencode.
- Code of Conduct
- Language Policy
- Getting Started
- Development Environment
- Credentials & Isolation
- Project Structure
- Development Workflow
- Making Changes
- QA Discipline
- Pull Request Process
- Publishing
- Getting Help
Be respectful, inclusive, and constructive. We're all here to make better tools together.
English is the primary language for all communications in this repository.
This includes:
- Issues and bug reports
- Pull requests and code reviews
- Documentation and comments
- Discussions and community interactions
- Global Accessibility: English allows contributors from all regions to collaborate effectively
- Consistency: A single language keeps discussions organized and searchable
- Open Source Best Practice: Most successful open-source projects use English as the lingua franca
If English isn't your first language, don't worry! We value your contributions regardless of perfect grammar. You can:
- Use translation tools to help compose messages
- Ask for help from other community members
- Focus on clear, simple communication rather than perfect prose
- Bun 1.3.12 (CI-pinned) - The only package manager for the workspace itself
- Node 24 - Required for vendored packages (lsp-tools-mcp, lsp-daemon, git-bash-mcp) and the Codex plugin build via npm
- git - Version control
- tmux (optional) - Enables the
interactive_bashtool and Team Mode visualization
# Clone the repository (with the frontend provenance submodules)
git clone --recurse-submodules https://github.com/code-yeongyu/oh-my-openagent.git
cd oh-my-openagent
# If you cloned without --recurse-submodules, initialize them now (non-fatal offline):
git submodule update --init --recursive
# Install dependencies (bun only - never use npm/yarn)
bun install
# Build the project (materializes the frontend third-party references from the submodules)
bun run buildThe frontend skill's brand / taste / ui-ux references are third-party content kept ONLY as pinned git submodules under
packages/shared-skills/upstreams/. The repo contains zero committed copies; the build materializes them intopackages/shared-skills/skills/frontend/references/(gitignored locally, shipped in the published package).script/agent/setup.shruns the submodule init + materialize for you (both non-fatal, so an offline checkout still builds, just without the brand refs).
After making changes, you can test your local build in OpenCode:
-
Build the project:
bun run build
-
Update your OpenCode config (
~/.config/opencode/opencode.jsonoropencode.jsonc):Built dist (after
bun run build):{ "plugin": ["file:///absolute/path/to/oh-my-openagent/dist/index.js"] }Source mode (no build needed):
{ "plugin": ["file:///absolute/path/to/oh-my-openagent/packages/omo-opencode/src/index.ts"] }The path must be absolute and contain a recognizable project name plus
(src|dist)/index.(ts|js). A relativefile://./...path will not be detected by the installer.Note: Remove
"oh-my-openagent"or"oh-my-opencode"from the plugin array if they exist, to avoid conflicts with the npm version. -
Restart OpenCode to load the changes.
-
Verify the plugin is loaded by checking for OmO agent availability or startup messages.
The cross-harness one-command bootstrap is the single source of truth for all development environments.
script/agent/setup.shverifies Bun, Node, and git, warns if tmux is missing, runsbun install, initializes the frontend provenance submodules and materializes their references (both non-fatal so an offline checkout still builds), and builds whendist/index.jsis missing orOMO_AGENT_FORCE_BUILD=1is set.script/agent/cleanup.shremoves regenerable transients by default. Pass--deepto also dropdist/andnode_modules/; it leaves thepackages/shared-skills/upstreams/submodules in place.script/agent/cleanup-hook.shlaunches cleanup from Claude CodeSessionEndwithout blocking shutdown.
All harnesses delegate to these scripts:
| Harness | Wiring |
|---|---|
| GitHub Codespaces / VS Code Dev Containers | .devcontainer/devcontainer.json runs postCreateCommand: script/agent/setup.sh on .devcontainer/Dockerfile (Node 24 + Bun 1.3.12 + tmux) |
| Plain Docker | script/agent/docker-dev.sh builds the Dockerfile and opens a shell |
| Cursor cloud agents | .cursor/environment.json install runs setup on environment creation |
| Claude Code | .claude/settings.json SessionStart hook runs setup; SessionEnd hook launches cleanup |
| Codex App (local environments) | .codex/setup.sh runs at project root on worktree creation |
| OpenCode (this plugin's own harness) | reads AGENTS.md + CLAUDE.md (a symlink); run script/agent/setup.sh directly |
The single source of truth is the script/agent/ dev-environment contract: setup.sh, cleanup.sh, and harness launchers such as cleanup-hook.sh. Maintenance means keeping those scripts, harness wiring files, and pinned Dockerfile versions in sync.
.env.example is the committed injection point. Copy it to .env (gitignored) once and fill in your keys:
ANTHROPIC_API_KEYOPENAI_API_KEYOPENCODE_SERVER_PASSWORD(optional)
Both setup.sh and qa-sandbox.sh auto-source .env, so credentials are set once per machine and never prompted again.
For QA isolation, run:
source script/agent/qa-sandbox.shThis exports an isolated, throwaway environment with its own XDG_* directories and a fresh CODEX_HOME under a mktemp directory. It also sets OPENCODE_DISABLE_AUTOUPDATE=1 and OPENCODE_DISABLE_MODELS_FETCH=1. QA never reads or writes the host's real ~/.config/opencode or ~/.codex. This mirrors the conventions used by the opencode-qa and codex-qa skills.
For containerized environments (Codespaces, Dev Containers, Docker), see .devcontainer/README.md. It documents injecting provider credentials (via .env, Codespaces secrets, or remoteEnv) and bind-mounting your ~/.codex, ~/.claude, and ~/.config/opencode config into the container so OpenCode, Codex, and Claude Code all work inside it.
The repository is a monorepo with layered packages under packages/.
oh-my-opencode/
├── packages/
│ ├── omo-opencode/ # OpenCode Ultimate edition adapter and build entry
│ │ └── src/
│ │ ├── index.ts # Thin wrapper default-exporting PluginModule via createPluginModule()
│ │ ├── plugin-config.ts # JSONC multi-level config (Zod v4)
│ │ ├── agents/ # agent factories (Sisyphus, Hephaestus, Oracle, ...)
│ │ ├── hooks/ # lifecycle hooks, 5-tier composition (see AGENTS.md for current counts)
│ │ ├── tools/ # native tool dirs, config-gated (LSP via MCP, ast-grep via skill)
│ │ ├── mcp/ # built-in MCPs: remote (websearch, context7, grep_app) + local stdio (lsp, codegraph)
│ │ ├── features/ # feature modules (background-agent, skill-loader, tmux, MCP-OAuth, boulder-state, monitor, ...)
│ │ ├── config/ # Zod v4 schema system
│ │ ├── shared/ # Cross-cutting utilities
│ │ ├── cli/ # CLI: install, run, doctor, mcp-oauth, boulder, ulw-loop (Commander.js)
│ │ ├── plugin/ # OpenCode hook handlers + 5-tier hook composition
│ │ └── plugin-handlers/ # 6-phase config loading pipeline
│ ├── omo-codex/ # Codex Light edition / lazycodex
│ ├── utils/ # Core package
│ ├── model-core/ # Core package
│ ├── prompts-core/ # Core package
│ ├── rules-engine/ # Core package
│ ├── agents-md-core/ # Core package
│ ├── comment-checker-core/ # Core package
│ ├── hashline-core/ # Core package
│ ├── boulder-state/ # Core package
│ ├── telemetry-core/ # Core package
│ ├── lsp-core/ # Core package
│ ├── mcp-stdio-core/ # Core package
│ ├── tmux-core/ # Core package
│ ├── claude-code-compat-core/ # Core package
│ ├── skills-loader-core/ # Core package
│ ├── mcp-client-core/ # Core package
│ ├── openclaw-core/ # Core package
│ ├── team-core/ # Core package
│ ├── delegate-core/ # Core package
│ ├── lsp-tools-mcp/ # MCP package
│ ├── lsp-daemon/ # MCP package
│ ├── git-bash-mcp/ # MCP package
│ ├── shared-skills/ # Cross-harness SKILL.md bundle
│ └── web/ # Marketing site (Next.js 15 + Cloudflare Workers)
└── dist/ # Build output (ESM + .d.ts)
The multi-harness refactor is in progress. See ROADMAP.md for the current state.
# Type check only
bun run typecheck
# Full build (ESM + TypeScript declarations + JSON schema)
bun run build
# Clean build output
bun run clean
# Rebuild from scratch
bun run clean && bun run build
# Build schema only (after modifying packages/omo-opencode/src/config/schema/)
bun run build:schema
# Run the root Bun test suite
bun test
# Run the Codex Light compatibility suite
bun run test:codexTests are co-located as *.test.ts files and follow a given/when/then style.
| Convention | Rule |
|---|---|
| Package Manager | Bun only (bun run, bun build, bunx) |
| Types | Use bun-types, not @types/node |
| Directory Naming | kebab-case (ast-grep/, claude-code-hooks/) |
| File Operations | Never use bash commands (mkdir/touch/rm) for file creation in code |
| Tool Structure | index.ts (barrel), types.ts, constants.ts, and concern-split implementation files named after what they do. Generic catch-all dump modules are banned (see .omo/rules/file-size-architectural-smell.md). |
| Hook Pattern | createXXXHook(deps) function naming |
| Exports | Barrel pattern (export * from "./module" in index.ts) |
Anti-Patterns (Do Not Do):
- Using npm/yarn instead of bun
- Using
@types/nodeinstead ofbun-types - Suppressing TypeScript errors with
as any,@ts-ignore,@ts-expect-error - Generic AI-generated comment bloat
- Direct
bun publish(use GitHub Actions only) - Local version modifications in
package.json
- Create a new
.tsfile inpackages/omo-opencode/src/agents/ - Export a factory
createXyzAgent(model): AgentConfigwhereAgentConfigis imported from@opencode-ai/sdk - Set the static
.modeproperty on the factory to"primary","subagent", or"all" - Add the factory to the
agentSourcesrecord inpackages/omo-opencode/src/agents/builtin-agents.ts - Add the new name to the
BuiltinAgentNameunion inpackages/omo-opencode/src/agents/types.tsAND toBuiltinAgentNameSchema(plusOverridableAgentNameSchemaif it should be user-overridable) inpackages/omo-opencode/src/config/schema/agent-names.ts. The schema enum is whatbuild:schemaemits, so updating onlytypes.tswill not change the published JSON schema. - Run
bun run build:schemato regenerate the JSON schema - Special agents (Sisyphus, Hephaestus, Atlas, Prometheus) have dedicated wiring under
packages/omo-opencode/src/agents/builtin-agents/; a plain subagent only needs theagentSourcesentry from step 4
// packages/omo-opencode/src/agents/my-agent.ts
import type { AgentConfig } from "@opencode-ai/sdk";
export function createMyAgent(model: string): AgentConfig {
return {
name: "my-agent",
model,
description: "Description of what this agent does",
prompt: `Your agent's system prompt here`,
temperature: 0.1,
// ... other config
};
}
createMyAgent.mode = "subagent" as const;- Create a new directory in
packages/omo-opencode/src/hooks/(kebab-case) - Implement
createXyzHook(deps)returning an object keyed by OpenCode hook names (for example"tool.execute.before","chat.message","event") whose values are(input, output) => voidhandlers - Re-export from
packages/omo-opencode/src/hooks/index.ts - Wire the hook into the matching tier composer in
packages/omo-opencode/src/plugin/hooks/create-*-hooks.ts(Session / ToolGuard / Transform / Continuation / Skill) viasafeHook() - Add the hook name to
HookNameSchemainpackages/omo-opencode/src/config/schema/hooks.ts
// packages/omo-opencode/src/hooks/my-hook/index.ts
export function createMyHook(deps: { logger: Logger }) {
return {
"chat.message": async (input: ChatMessageInput, output: ChatMessageOutput) => {
// Hook logic here
},
};
}- Create a new directory in
packages/omo-opencode/src/tools/<name>/ - Export a factory
createXyzTool(ctx): ToolDefinitionbuilt withtool({...})from@opencode-ai/plugin - Register the factory in the
ToolRegistryFactoriestype anddefaultToolRegistryFactoriesrecord inpackages/omo-opencode/src/plugin/tool-registry-factories.ts - Wire it into
createCoreTools()intool-registry-core-tools.tsfor always-on tools, or a gated record intool-registry-gated-tools.tsspread inpackages/omo-opencode/src/plugin/tool-registry.ts - Export from
packages/omo-opencode/src/tools/index.ts
- Create a config factory in
packages/omo-opencode/src/mcp/<name>.tsreturning aRemoteMcpConfig(type"remote", url) orLocalMcpConfig(type"local", command) - Register it inside
createBuiltinMcps()inpackages/omo-opencode/src/mcp/index.ts - Add the MCP name to
McpNameSchemainpackages/omo-opencode/src/mcp/types.ts - Document in README if it requires external setup
Any change to packages/omo-opencode (the OpenCode side) must be QA'd with the opencode-qa skill. Any change to packages/omo-codex (the Codex Light side) must be QA'd with the codex-qa skill. Record QA evidence under .omo/evidence/<date>-<slug>/.
"It typechecks" or "bun test is green" is not QA. You must drive the real harness and record the observed behavior.
- Fork the repository and create your branch from
dev - Make changes following the conventions above
- Build and test locally:
bun run typecheck # Ensure no type errors bun run build # Ensure build succeeds bun test # Run the root test suite bun run test:codex # Run the Codex Light compatibility suite
- Test in OpenCode using the local build method described above
- Commit with clear, descriptive messages:
- Use present tense ("Add feature" not "Added feature")
- Reference issues if applicable ("Fix #123")
- Push to your fork and create a Pull Request
- Describe your changes clearly in the PR description
- Code follows project conventions
-
bun run typecheckpasses -
bun run buildsucceeds -
bun testpasses -
bun run test:codexpasses (if Codex-side changed) - Tested locally with OpenCode
- QA evidence recorded under
.omo/evidence/(if harness-connected changes) - Updated documentation if needed (README, AGENTS.md)
- No version changes in
package.json
Important: Publishing is handled exclusively through GitHub Actions.
- Never run
bun publishdirectly (OIDC provenance issues) - Never modify
package.jsonversion locally - Maintainers use GitHub Actions workflow_dispatch:
gh workflow run publish -f bump=patch # or minor/major
- Project Knowledge: Check
AGENTS.mdfor detailed project documentation - Code Patterns: Review existing implementations in
packages/omo-opencode/src/ - Issues: Open an issue for bugs or feature requests
- Discussions: Start a discussion for questions or ideas
Thank you for contributing to Oh My OpenCode! Your efforts help make AI-assisted coding better for everyone.