Add explicit .helpOption('-h, --help') call to the Commander program configuration so the help option is explicitly defined rather than relying on Commander.js lazy initialization. Add corresponding test to pin the behavior.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Move three pure helpers from src/shared/ into @oh-my-opencode/model-core so the package can stand alone without depending on plugin internals:
- buildModelCapabilitiesSnapshotFromModelsDev + fetchModelCapabilitiesSnapshot (models.dev normalization)
- parseModelSuggestion (cross-provider ProviderModelNotFoundError suggestion extraction)
- resolveActualContextLimit (Anthropic GA 1M context override)
Split provider-model-id-transform into two variants exposed by model-core:
- transformModelForProvider keeps the runtime dash to dot Anthropic rewrite used by the SDK
- transformModelForProviderDisplay preserves hyphenated Anthropic IDs so the installer writes registry-compatible model strings, fixing the ProviderModelNotFoundError fresh installs hit when the dotted form leaks into the config
src/shared/* and src/cli/provider-model-id-transform.ts collapse to re-export shims that point at the new core modules. Stale src/shared/{known-variants,model-capability-aliases,model-capability-guardrails,model-capability-heuristics}.ts re-export files plus the duplicated context-limit-resolver test are removed in favor of the canonical model-core copies.
Tests: bun test packages/model-core src/shared/model-capabilities-cache.test.ts src/cli/provider-model-id-transform.test.ts
Discord support and other guidance still tell users 'bunx oh-my-opencode setup', but the CLI only registers 'install'. Running the recommended command produces:
error: unknown command 'setup'
which dead-ends users who are trying to refresh missing skills (hyperplan, etc.) after upgrading. Commander.js supports first-class aliases, so declaring '.alias("setup")' on the install command makes both names route to the same action without any code duplication. Help output now reads 'Usage: oh-my-opencode install|setup [options]', which is the right discovery hint for both newcomers and users following older docs.
Regression test parses cli-program.ts and asserts the install command block contains '.alias("setup")' before its '.action(' so a future refactor of the CLI cannot silently drop the alias.
Keep the built-in lsp MCP registered even when the submodule CLI artifact is missing.
The fallback command initializes the lsp-tools-mcp submodule, prefers the source CLI without dirtying the checkout with dist output, and keeps npm build as a last resort when Bun cannot run the source entrypoint.
Plan: plans/fix-lsp-mcp-missing-cli.md
Previously the tui-plugin-config check returned PASS when the server
plugin (oh-my-openagent in opencode.json) was missing but the TUI plugin
entry was present in tui.json. The plugin can't function with only half
of the registration — the server side handles tool dispatch, hook
execution, and SDK integration; the TUI side only ships the sidebar.
Now we emit a clear warning with the fix suggestion.
Addresses cubic-dev-ai's review on PR #4048 (severity 3/10).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The doctor command reported "unknown" for the plugin version whenever the
package was installed somewhere outside the config dir or XDG cache dir
(for example, when running via `bunx oh-my-openagent`, which keeps the
plugin in its own ephemeral cache). The existing scan only checked
`node_modules/<package>/package.json` under those two well-known
directories, so it silently returned a null `loadedVersion`.
Add a `require.resolve("<pkg>/package.json")` fallback that uses Node's
own module resolution to locate whichever copy of the plugin is actually
loaded. The fallback only runs when neither candidate path exists, so
existing behavior is preserved for the common case.
Closes#3822
The plugin ships two module entries: the server plugin (registered in
opencode.json) and the TUI plugin (./tui subpath, registered in tui.json).
opencode's TUI loader reads tui.json, not opencode.json, so a fresh install
that only writes opencode.json leaves the Roles - Models sidebar section
and TUI-only commands unloaded.
Commit 19e8cab717 fixed the install flow, but existing users who installed
before that won't have tui.json populated and have no signal that anything
is wrong. This adds a doctor check that detects the mismatch and emits a
clear warning with a one-line fix suggestion (re-run the installer or add
the entry manually).
The check is a soft warning, not a fatal blocker, and is pure (no side
effects, no auto-write). It accepts:
- canonical and legacy package names in either config
- file: URLs pointing at a local checkout of our package (opencode-tui
loads the ./tui subpath via package.json exports for those entries)
Status matrix:
- both registered -> pass
- server registered, TUI missing -> warn
- TUI provided via file: entry -> pass
- neither registered -> skip (plugin not installed at all)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Keep prompt reservations briefly after successful dispatch so rapid idle/message/error transitions cannot inject the same follow-up twice.
Route all production session prompt calls through the shared gate, restore skipped background resume state, release holds after abort/recovery paths, and preserve Ralph/ULW loop state when a dispatch is deferred.
Add regression coverage for session routing, static prompt route auditing, team-mode live messaging, model suggestion retries, call-omo-agent reuse, background parent wakes, runtime fallback, compaction recovery, Atlas, and Ralph/ULW loops.
bun-install.ts streamToText() was reachable from the plugin bundle via
the cli/config-manager barrel re-export. Replace with the WHATWG
standard `new Response(stream).text()` pattern which works
identically in Bun and Node and avoids the last raw Bun.* runtime
call in dist/index.js.
picocolors emits ANSI escape codes when FORCE_COLOR is set (GitHub
Actions default), so the literal toContain('status: active') assertion
fails against the wrapped 'status: \x1b[36mactive\x1b[39m' output.
Reuse the existing stripAnsi helper from src/cli/doctor/format-shared.ts
in the test before assertion.
Reproduced locally with FORCE_COLOR=1 bun test src/cli/boulder/formatter.test.ts.
After SIGTERM/SIGKILL escalation, the stdout stream may not close immediately on all platforms. The unconditional await on outputPromise could then hang indefinitely, defeating the bounded process lifetime guarantee. Race outputPromise against a short follow-up timeout to ensure getOpenCodeVersion always returns within a bounded time.
Refs #3766