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>
The same output.trim() bug fixed in PR #3909 for doctor exists in the
installer's opencode-binary.ts. Without this fix, `bunx oh-my-opencode
install` would store polluted Electron stdout (e.g., `00:24:25.202 >
app starting { version: '1.14.33', packaged: true }`) as the OpenCode
version in config, breaking downstream version-dependent logic.
- Extract extractSemverFromOutput to src/shared/extract-semver.ts
(precedent: spawn-with-windows-hide is in shared because used by
both doctor and installer)
- src/cli/doctor/checks/system-binary.ts now imports from shared and
re-exports for backward compat
- src/cli/config-manager/opencode-binary.ts uses the shared helper
with `?? output.trim()` fallback to preserve legacy behavior on
non-semver-shaped successful outputs (e.g., custom builds)
- Add 4 installer regression tests covering: clean semver, polluted
Electron stdout (regression for #3765 installer caller), fallback
for non-semver, null when no binary on PATH
Refs #3765
The Electron-based OpenCode build leaks log lines like
`00:24:25.202 > app starting { version: '1.14.33', packaged: true }`
into stdout, so `getOpenCodeVersion` was returning the entire log
line as the 'version'. `compareVersions` then split that string on
'.' and produced a nonsensical numeric array (e.g., `[0, 0, 14, 0]`),
which it judged as < the minimum 1.4.0. Result: doctor incorrectly
flagged OpenCode 1.14.x as below the minimum required version.
Replace the raw `stdout.trim()` return with a small
`extractSemverFromOutput` helper that runs a semver-shaped regex
across the output. A negative lookbehind `(?<![\d:])` skips the
milliseconds segment of timestamps (e.g., `25.202` in
`00:24:25.202`), so the parser locks onto the real version token.
Adds 12 unit tests covering plain semver, v-prefix, pre-release,
build metadata, the Electron regression, timestamp-only stdout,
and various invalid inputs.
Fixes#3765
Updates the canonical Anthropic Opus model in every fallback chain
(sisyphus, oracle, prometheus, metis, momus, visual-engineering,
ultrabrain, deep, artistry, unspecified-high), the unspecified-high
category default, the think-mode HIGH_VARIANT_MAP, the Claude Code
alias map, the claude-thinking legacy alias, the context-limit GA
regex, and event.ts fallback strings.
Widens supportsCachedAnthropicLimit to accept both claude-*-4-6 and
claude-*-4-7 so the 1M context cache still applies across the bump.
Regenerates the bundled model-capabilities snapshot from models.dev
and the model-fallback snapshot to match the new source output.
Doctor's comment-checker probe only checked system PATH and the
package binary while the runtime resolution path checks the
lazy-download cache first. Aligned the doctor resolution order
with runtime so the cached binary is recognized as installed.
🤖 Generated with OhMyOpenCode assistance
https://github.com/code-yeongyu/oh-my-opencode
Doctor checks spawned subprocesses (gh, opencode, sg) without timeouts,
causing the process to hang indefinitely if any binary was stuck.
The OS would then SIGKILL the process (exit code 137).
- Add spawnWithTimeout utility with 10s per-spawn timeout and proper
cleanup (timer cleared on success, proc.exited awaited after kill)
- Capture both stdout and stderr to preserve gh auth status behavior
- Add 30s overall doctor command timeout with JSON-mode support
- Distinguish timeout errors from other failures in runner
- Update all doctor check subprocess calls to use timeouts
Doctor checks spawned subprocesses (gh, opencode, sg) without timeouts,
causing the process to hang indefinitely if any binary was stuck.
The OS would then SIGKILL the process (exit code 137).
- Add spawnWithTimeout utility with 10s per-spawn timeout
- Add 30s overall doctor command timeout with graceful error message
- Update all doctor check subprocess calls to use timeouts
Update doctor checks to use centralized constants from plugin-identity:
- PACKAGE_NAME now references PUBLISHED_PACKAGE_NAME
- System check uses PLUGIN_NAME and LEGACY_PLUGIN_NAME for plugin
registration validation and legacy name detection
- Formatters updated for consistency
🤖 Generated with assistance of OhMyOpenCode
Update systemLoadedVersion check to detect installs under both
oh-my-opencode and oh-my-openagent package names. Adds package
candidate selection logic for dual-published packages.
🤖 Generated with assistance of OhMyOpenCode
Extract hardcoded GPT apply_patch permission logic into a reusable module
to ensure consistent behavior across all agents. This prevents GPT models
from using the unreliable apply_patch tool while allowing other models.
- Add gpt-apply-patch-guard.ts with GPT_APPLY_PATCH_GUIDANCE and getGptApplyPatchPermission
- Update Hephaestus agent to use centralized permission logic
- Update Sisyphus-Junior agent to use centralized permission logic
- Update all GPT prompt builders to reference shared guidance constant
🤖 Generated with assistance of OhMyOpenCode
The doctor's 'Model override uses unavailable provider' check only
looked at providers from ~/.cache/opencode/models.json (built-in
providers from models.dev). Custom OpenAI-compatible providers defined
in the user's opencode.json (under the 'provider' key) were not
included, causing false-positive warnings.
Now loadAvailableModelsFromCache() also reads provider names from
~/.config/opencode/opencode.json and ~/.config/opencode/opencode.jsonc,
merging them with the cache providers. This eliminates the false
positive while preserving real warnings for truly unknown providers.
7 new tests cover: cache-only, custom-only, merged, deduplicated,
JSONC variant, and malformed config resilience.
Fixes#3199
- Move mock.module() calls from top-level into importFreshSystemModule()
- Restore real modules in afterAll to prevent cross-test contamination
- Reset mocks before each test
🤖 GENERATED WITH ASSISTANCE OF OhMyOpenCode
- Add afterAll(() => { mock.restore() }) to 52 test files missing cleanup
- Rewrite create-tool-guard-hooks.test.ts to use spyOn instead of barrel mock
- Fix skill-mcp-manager OAuth tests with missing mockTokens/mockLogin definitions
- Fix start-work hook: show worktree active block on resume with existing worktree_path
- Extract createWorktreeActiveBlock to worktree-block.ts to avoid circular import
- Replace 80-line isolated test runner CI config with single `bun test` command