oh-my-opencode 4.18.2 → 4.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/command/omomomo.md +1 -1
- package/.agents/skills/omomomo/SKILL.md +1 -1
- package/.opencode/command/omomomo.md +1 -1
- package/README.ja.md +6 -6
- package/README.ko.md +6 -6
- package/README.md +8 -8
- package/README.ru.md +6 -6
- package/README.zh-cn.md +6 -6
- package/dist/agents/atlas/agent.d.ts +7 -6
- package/dist/agents/gpt-prompt-identity.d.ts +3 -0
- package/dist/agents/sisyphus/gpt-5-5.d.ts +1 -1
- package/dist/agents/sisyphus/gpt-task-system-guide.d.ts +1 -0
- package/dist/agents/sisyphus/index.d.ts +5 -0
- package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
- package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
- package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
- package/dist/agents/sisyphus-junior/gpt-5-5.d.ts +2 -2
- package/dist/agents/sisyphus-junior/index.d.ts +2 -0
- package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
- package/dist/agents/types.d.ts +2 -2
- package/dist/cli/index.js +781 -215
- package/dist/cli-node/index.js +781 -215
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schema/agent-overrides.d.ts +48 -48
- package/dist/config/schema/browser-automation.d.ts +1 -0
- package/dist/config/schema/categories.d.ts +6 -6
- package/dist/config/schema/codegraph.d.ts +2 -0
- package/dist/config/schema/commands.d.ts +1 -3
- package/dist/config/schema/default-mode.d.ts +1 -1
- package/dist/config/schema/fallback-models.d.ts +5 -5
- package/dist/config/schema/goal.d.ts +7 -0
- package/dist/config/schema/hooks.d.ts +1 -1
- package/dist/config/schema/oh-my-opencode-config.d.ts +56 -58
- package/dist/config/schema/ralph-loop.d.ts +1 -1
- package/dist/config/schema.d.ts +1 -1
- package/dist/create-hooks.d.ts +1 -1
- package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
- package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
- package/dist/hooks/codegraph-bootstrap/hook.d.ts +3 -2
- package/dist/hooks/goal/command-arguments.d.ts +13 -0
- package/dist/hooks/goal/controller.d.ts +26 -0
- package/dist/hooks/goal/index.d.ts +23 -0
- package/dist/hooks/goal/prompt.d.ts +3 -0
- package/dist/hooks/goal/store.d.ts +8 -0
- package/dist/hooks/goal/tools.d.ts +10 -0
- package/dist/hooks/goal/types.d.ts +93 -0
- package/dist/hooks/goal/validation.d.ts +4 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/keyword-detector/hook.d.ts +1 -2
- package/dist/hooks/ralph-loop/types.d.ts +1 -1
- package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +1 -1
- package/dist/hooks/start-work/notepad-scaffold.d.ts +10 -0
- package/dist/index.js +5398 -5109
- package/dist/oh-my-opencode.schema.json +34 -19
- package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
- package/dist/plugin/chat-message/types.d.ts +23 -10
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
- package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
- package/dist/plugin/stop-continuation.d.ts +2 -2
- package/dist/shared/omo-process-sweep.d.ts +18 -0
- package/dist/shared/system-directive.d.ts +0 -1
- package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
- package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
- package/dist/skills/ulw-plan/SKILL.md +31 -8
- package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
- package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/dist/skills/ulw-research/SKILL.md +3 -3
- package/dist/testing/create-plugin-module.d.ts +1 -0
- package/dist/tools/delegate-task/types.d.ts +0 -1
- package/dist/tui.js +138 -80
- package/package.json +14 -14
- package/packages/git-bash-mcp/dist/cli.js +54 -8
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +60 -0
- package/packages/lsp-core/src/lsp/client.ts +14 -1
- package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +2 -2
- package/packages/lsp-core/src/mcp.ts +9 -0
- package/packages/lsp-daemon/dist/cli.js +319 -51
- package/packages/lsp-daemon/dist/client.js +136 -48
- package/packages/lsp-daemon/dist/daemon-client.d.ts +0 -10
- package/packages/lsp-daemon/dist/daemon-client.js +4 -34
- package/packages/lsp-daemon/dist/daemon-failure-result.d.ts +3 -0
- package/packages/lsp-daemon/dist/daemon-failure-result.js +38 -0
- package/packages/lsp-daemon/dist/daemon-request-error.d.ts +14 -0
- package/packages/lsp-daemon/dist/daemon-request-error.js +26 -0
- package/packages/lsp-daemon/dist/daemon-server.js +5 -1
- package/packages/lsp-daemon/dist/index.js +137 -48
- package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
- package/packages/lsp-daemon/dist/proxy.js +1 -0
- package/packages/lsp-daemon/dist/version-reap.d.ts +26 -0
- package/packages/lsp-daemon/dist/version-reap.js +182 -0
- package/packages/lsp-tools-mcp/dist/cli.js +61 -10
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +6 -1
- package/packages/lsp-tools-mcp/dist/mcp.js +61 -10
- package/packages/lsp-tools-mcp/dist/tools.js +6 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +416 -25
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +22 -2
- package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +638 -267
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +178 -89
- package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
- package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +3 -1
- package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +3 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +98 -32
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +5 -0
- package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +15 -6
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +6 -2
- package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +1 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +148 -0
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +31 -2
- package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +65 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -7
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +57 -3
- package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +41 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +109 -8
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +2 -2
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +43 -0
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/AGENTS.md +1 -0
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +80 -40
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +11 -0
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +11 -0
- package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +60 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +147 -60
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +3 -71
- package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +198 -23
- package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +6 -5
- package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +10 -10
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +31 -8
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/components/ultrawork/src/codex-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook-trigger-policy.test.ts +42 -0
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +6 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.d.ts +21 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +64 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +15 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.d.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +85 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +0 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -33
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +998 -593
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.js +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.js +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +10 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.d.ts +15 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +84 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +5 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +81 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +7 -59
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +12 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.d.ts +9 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.js +122 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +8 -6
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +84 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +13 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-arg-parser.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +49 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +13 -45
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/constants.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/goal-status.ts +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +4 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +20 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +125 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +59 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +6 -58
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +12 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/validation-batch.ts +122 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +76 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-checkpoint-continuation.test.ts +111 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-steering-batch.test.ts +67 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-validation-batch.test.ts +57 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +152 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +29 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch-checkpoint.test.ts +147 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch.test.ts +73 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +2 -2
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +2 -2
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +2 -2
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +41 -41
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +3 -18
- package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +43 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +8 -6
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +31 -8
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +11 -11
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +26 -0
- package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +41 -0
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +3 -18
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
- package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +401 -22
- package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
- package/packages/omo-codex/scripts/install-marketplace-cache.test.mjs +4 -1
- package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +31 -8
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
- package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
- package/dist/skills/ultraresearch/SKILL.md +0 -10
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
- package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
- package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/agents/openai.yaml +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
2
2
|
import type { Readable, Writable } from "node:stream";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
createParentWatchdog,
|
|
5
6
|
errorResponse,
|
|
6
7
|
isPlainRecord,
|
|
7
8
|
jsonRpcId,
|
|
@@ -35,6 +36,8 @@ const CODEGRAPH_NODE_INCLUDE_CODE_DESCRIPTION =
|
|
|
35
36
|
const CODEGRAPH_CONTAINER_OUTLINE_GUIDANCE =
|
|
36
37
|
"Container symbols intentionally return structural outlines with members. For source, request a specific member symbol or call codegraph_node in file mode with symbolsOnly=false plus offset/limit around the symbol location.";
|
|
37
38
|
|
|
39
|
+
const SIGKILL_ESCALATION_MS = 2_000;
|
|
40
|
+
|
|
38
41
|
export async function runBridgedCodegraphProcess(
|
|
39
42
|
command: string,
|
|
40
43
|
args: readonly string[],
|
|
@@ -63,22 +66,45 @@ export async function runBridgedCodegraphProcess(
|
|
|
63
66
|
resolveExit(signal === null ? 0 : 1);
|
|
64
67
|
});
|
|
65
68
|
});
|
|
66
|
-
const
|
|
67
|
-
|
|
69
|
+
const destroyChildPipes = (): void => {
|
|
70
|
+
childInput.destroy();
|
|
71
|
+
childOutput.destroy();
|
|
72
|
+
};
|
|
73
|
+
void childExit.then(destroyChildPipes, destroyChildPipes);
|
|
74
|
+
// Parent-liveness watchdog: when the parent dies while holding our stdio
|
|
75
|
+
// open, the client stream never sees EOF, so the only settle path is to
|
|
76
|
+
// drop the client input, close the child pipes, and terminate the child.
|
|
77
|
+
// The bridge then resolves through the normal child-exit path, and the
|
|
78
|
+
// destroyed input releases its read handle so the process can actually
|
|
79
|
+
// exit (same teardown pattern as runJsonRpcStdioServer's own watchdog).
|
|
80
|
+
// In daemon mode the child is the upstream lightweight proxy, so killing
|
|
81
|
+
// it on parent death is correct; the detached daemon is a separate process
|
|
82
|
+
// and is never touched.
|
|
83
|
+
let parentWatchdogFired = false;
|
|
84
|
+
const parentWatchdog = createParentWatchdog(options.parentWatchdog, () => {
|
|
85
|
+
parentWatchdogFired = true;
|
|
86
|
+
options.input.destroy();
|
|
87
|
+
destroyChildPipes();
|
|
88
|
+
terminateCodegraphChild(child);
|
|
68
89
|
});
|
|
90
|
+
const clientForwardingDone = forwardClientToCodegraph(
|
|
91
|
+
options.input,
|
|
92
|
+
childInput,
|
|
93
|
+
pendingResponses,
|
|
94
|
+
(mode) => {
|
|
95
|
+
defaultResponseMode = mode;
|
|
96
|
+
},
|
|
97
|
+
() => parentWatchdogFired,
|
|
98
|
+
);
|
|
69
99
|
const responseForwardingDone = forwardCodegraphToClient(
|
|
70
100
|
childOutput,
|
|
71
101
|
options.output,
|
|
72
102
|
pendingResponses,
|
|
73
103
|
() => defaultResponseMode,
|
|
104
|
+
() => parentWatchdogFired,
|
|
74
105
|
);
|
|
75
106
|
const bridgeDone = Promise.all([clientForwardingDone, responseForwardingDone]);
|
|
76
107
|
const childAndResponsesDone = Promise.all([childExit, responseForwardingDone]).then(([exitCode]) => exitCode);
|
|
77
|
-
const destroyChildPipes = (): void => {
|
|
78
|
-
childInput.destroy();
|
|
79
|
-
childOutput.destroy();
|
|
80
|
-
};
|
|
81
|
-
void childExit.then(destroyChildPipes, destroyChildPipes);
|
|
82
108
|
try {
|
|
83
109
|
return await Promise.race([childAndResponsesDone, bridgeDone.then(() => childExit)]);
|
|
84
110
|
} catch (error) {
|
|
@@ -86,32 +112,64 @@ export async function runBridgedCodegraphProcess(
|
|
|
86
112
|
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
87
113
|
await childExit.catch(() => undefined);
|
|
88
114
|
throw error;
|
|
115
|
+
} finally {
|
|
116
|
+
parentWatchdog.clear();
|
|
89
117
|
}
|
|
90
118
|
}
|
|
91
119
|
|
|
120
|
+
function terminateCodegraphChild(child: ChildProcess): void {
|
|
121
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
122
|
+
child.kill("SIGTERM");
|
|
123
|
+
const escalation = setTimeout(() => {
|
|
124
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
125
|
+
}, SIGKILL_ESCALATION_MS);
|
|
126
|
+
escalation.unref();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Stream errors raised by the watchdog's own teardown (input destroy, child
|
|
130
|
+
// pipe destroy) while a forwarder is parked on that stream.
|
|
131
|
+
function isWatchdogTeardownError(error: unknown): boolean {
|
|
132
|
+
if (!(error instanceof Error) || !("code" in error)) return false;
|
|
133
|
+
return (
|
|
134
|
+
error.code === "ERR_STREAM_PREMATURE_CLOSE" ||
|
|
135
|
+
error.code === "ERR_STREAM_DESTROYED" ||
|
|
136
|
+
error.code === "ERR_STREAM_WRITE_AFTER_END" ||
|
|
137
|
+
error.code === "EPIPE"
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
92
141
|
async function forwardClientToCodegraph(
|
|
93
142
|
input: Readable,
|
|
94
143
|
childInput: Writable,
|
|
95
144
|
pendingResponses: Map<string, PendingClientResponse>,
|
|
96
145
|
setDefaultResponseMode: (mode: StdioJsonRpcResponseMode) => void,
|
|
146
|
+
tolerateWatchdogClose: () => boolean,
|
|
97
147
|
): Promise<void> {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
148
|
+
try {
|
|
149
|
+
for await (const message of readStdioJsonRpcMessages(input)) {
|
|
150
|
+
if (message.kind === "parse_error") {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const responseMode = message.responseMode;
|
|
154
|
+
setDefaultResponseMode(responseMode);
|
|
155
|
+
const key = responseModeKey(message.payload);
|
|
156
|
+
if (key !== null) {
|
|
157
|
+
pendingResponses.set(key, {
|
|
158
|
+
method: jsonRpcMethod(message.payload),
|
|
159
|
+
responseMode,
|
|
160
|
+
toolName: jsonRpcToolName(message.payload),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
await writeLine(childInput, JSON.stringify(message.payload));
|
|
111
164
|
}
|
|
112
|
-
|
|
165
|
+
childInput.end();
|
|
166
|
+
} catch (error) {
|
|
167
|
+
// The parent watchdog destroys the client input and child pipes while
|
|
168
|
+
// this forwarder is parked on a read or write; that teardown is
|
|
169
|
+
// self-inflicted, so settle quietly and let the child-exit path report
|
|
170
|
+
// the outcome.
|
|
171
|
+
if (!(tolerateWatchdogClose() && isWatchdogTeardownError(error))) throw error;
|
|
113
172
|
}
|
|
114
|
-
childInput.end();
|
|
115
173
|
}
|
|
116
174
|
|
|
117
175
|
async function forwardCodegraphToClient(
|
|
@@ -119,17 +177,25 @@ async function forwardCodegraphToClient(
|
|
|
119
177
|
output: Writable,
|
|
120
178
|
pendingResponses: Map<string, PendingClientResponse>,
|
|
121
179
|
defaultResponseMode: () => StdioJsonRpcResponseMode,
|
|
180
|
+
tolerateWatchdogClose: () => boolean,
|
|
122
181
|
): Promise<void> {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
182
|
+
try {
|
|
183
|
+
for await (const message of readStdioJsonRpcMessages(childOutput)) {
|
|
184
|
+
if (message.kind === "parse_error") {
|
|
185
|
+
await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const key = responseModeKey(message.payload);
|
|
189
|
+
const pendingResponse = key === null ? undefined : pendingResponses.get(key);
|
|
190
|
+
const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
|
|
191
|
+
if (key !== null) pendingResponses.delete(key);
|
|
192
|
+
await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
|
|
127
193
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
194
|
+
} catch (error) {
|
|
195
|
+
// The parent watchdog destroys the child pipes while this forwarder is
|
|
196
|
+
// parked on a read; that close is self-inflicted, so settle quietly and
|
|
197
|
+
// let the child-exit path report the outcome.
|
|
198
|
+
if (!(tolerateWatchdogClose() && isWatchdogTeardownError(error))) throw error;
|
|
133
199
|
}
|
|
134
200
|
}
|
|
135
201
|
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
runJsonRpcStdioServer,
|
|
8
8
|
successResponse,
|
|
9
9
|
type JsonRpcResponse,
|
|
10
|
+
type ParentWatchdogConfig,
|
|
10
11
|
} from "../../../../../mcp-stdio-core/src/index.ts";
|
|
11
12
|
|
|
12
13
|
export interface UnavailableCodegraphMcpOptions {
|
|
@@ -14,6 +15,9 @@ export interface UnavailableCodegraphMcpOptions {
|
|
|
14
15
|
readonly output: Writable;
|
|
15
16
|
readonly reason: string;
|
|
16
17
|
readonly serverVersion: string;
|
|
18
|
+
// Test seam: production callers leave this unset so the watchdog uses its
|
|
19
|
+
// defaults (parentPid = process.ppid, 30s poll).
|
|
20
|
+
readonly parentWatchdog?: ParentWatchdogConfig;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
interface UnavailableCodegraphMcpHandlerOptions {
|
|
@@ -32,6 +36,7 @@ export async function runUnavailableCodegraphMcpServer(
|
|
|
32
36
|
},
|
|
33
37
|
input: options.input,
|
|
34
38
|
output: options.output,
|
|
39
|
+
parentWatchdog: options.parentWatchdog ?? {},
|
|
35
40
|
});
|
|
36
41
|
}
|
|
37
42
|
|
|
@@ -13,10 +13,11 @@ import type { Readable, Writable } from "node:stream";
|
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
-
CODEGRAPH_NO_DAEMON_ENV,
|
|
17
16
|
buildCodegraphChildEnv,
|
|
18
17
|
buildCodegraphEnv,
|
|
19
18
|
} from "../../../../../utils/src/codegraph/env.ts";
|
|
19
|
+
import type { ParentWatchdogConfig } from "../../../../../mcp-stdio-core/src/index.ts";
|
|
20
|
+
import { CODEGRAPH_PINNED_VERSION } from "../../../../../utils/src/codegraph/manifest.ts";
|
|
20
21
|
import {
|
|
21
22
|
buildCodegraphNodeSkipHint,
|
|
22
23
|
evaluateCodegraphNodeSupport,
|
|
@@ -48,6 +49,7 @@ export interface CodegraphServeProcessOptions {
|
|
|
48
49
|
readonly output: Writable;
|
|
49
50
|
readonly stderr: CodegraphServeStderr;
|
|
50
51
|
readonly stdio: ServeStdio;
|
|
52
|
+
readonly parentWatchdog?: ParentWatchdogConfig;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
export type CodegraphServeProcessRunner = (
|
|
@@ -64,7 +66,7 @@ export interface CodegraphServeStderr {
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
export interface RunCodegraphServeOptions {
|
|
67
|
-
readonly buildEnv?: (options: { readonly homeDir: string }) => Record<string, string>;
|
|
69
|
+
readonly buildEnv?: (options: { readonly daemon: boolean; readonly homeDir: string }) => Record<string, string>;
|
|
68
70
|
readonly commandExists?: (filePath: string) => boolean;
|
|
69
71
|
readonly config?: CodexOmoConfig;
|
|
70
72
|
readonly cwd?: string;
|
|
@@ -77,6 +79,10 @@ export interface RunCodegraphServeOptions {
|
|
|
77
79
|
readonly runProcess?: CodegraphServeProcessRunner;
|
|
78
80
|
readonly stderr?: CodegraphServeStderr;
|
|
79
81
|
readonly ensureProvisioned?: CodegraphProvisioner;
|
|
82
|
+
// Test seam: production callers leave this unset so the bridge and the
|
|
83
|
+
// unavailable facade use the watchdog defaults (parentPid = process.ppid,
|
|
84
|
+
// 30s poll).
|
|
85
|
+
readonly parentWatchdog?: ParentWatchdogConfig;
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
const CODEGRAPH_SKIP_HINT =
|
|
@@ -84,7 +90,7 @@ const CODEGRAPH_SKIP_HINT =
|
|
|
84
90
|
const CODEGRAPH_DISABLED_HINT =
|
|
85
91
|
"CodeGraph MCP skipped: disabled by OMO SOT config. Set [codex].codegraph.enabled=true to enable it.\n";
|
|
86
92
|
const CODEGRAPH_EXCLUDED_HINT = "CodeGraph MCP skipped: project excluded by OMO CodeGraph policy.\n";
|
|
87
|
-
const CODEGRAPH_VERSION =
|
|
93
|
+
const CODEGRAPH_VERSION = CODEGRAPH_PINNED_VERSION;
|
|
88
94
|
const PROJECT_CWD_ENV_KEYS = ["OMO_CODEGRAPH_PROJECT_CWD", SESSION_START_CWD_ENV, "PWD"] as const;
|
|
89
95
|
|
|
90
96
|
export async function runCodegraphServe(options: RunCodegraphServeOptions = {}): Promise<number> {
|
|
@@ -136,7 +142,7 @@ export async function runCodegraphServe(options: RunCodegraphServeOptions = {}):
|
|
|
136
142
|
}
|
|
137
143
|
|
|
138
144
|
const runProcess = options.runProcess ?? runBridgedCodegraphProcess;
|
|
139
|
-
const codegraphEnv = codegraphEnvForConfig(trustedInstallDir, homeDir, options.buildEnv);
|
|
145
|
+
const codegraphEnv = codegraphEnvForConfig(trustedInstallDir, homeDir, codegraphConfig.daemon === true, options.buildEnv);
|
|
140
146
|
const mergedEnv = buildCodegraphChildEnv({ ambientEnv: env, codegraphEnv, runtimeEnv: env });
|
|
141
147
|
return runProcess(resolution.command, [...resolution.argsPrefix, "serve", "--mcp"], {
|
|
142
148
|
cwd: projectCwd,
|
|
@@ -145,6 +151,7 @@ export async function runCodegraphServe(options: RunCodegraphServeOptions = {}):
|
|
|
145
151
|
output: options.stdout ?? processStdout,
|
|
146
152
|
stderr: options.stderr ?? processStderr,
|
|
147
153
|
stdio: "pipe",
|
|
154
|
+
parentWatchdog: options.parentWatchdog ?? {},
|
|
148
155
|
});
|
|
149
156
|
}
|
|
150
157
|
|
|
@@ -155,6 +162,7 @@ async function runUnavailableMcp(reason: string, options: RunCodegraphServeOptio
|
|
|
155
162
|
output: options.stdout ?? processStdout,
|
|
156
163
|
reason,
|
|
157
164
|
serverVersion: CODEGRAPH_VERSION,
|
|
165
|
+
parentWatchdog: options.parentWatchdog ?? {},
|
|
158
166
|
});
|
|
159
167
|
return 0;
|
|
160
168
|
}
|
|
@@ -195,9 +203,10 @@ function looksLikePath(command: string): boolean {
|
|
|
195
203
|
function codegraphEnvForConfig(
|
|
196
204
|
trustedInstallDir: string | undefined,
|
|
197
205
|
homeDir: string,
|
|
198
|
-
|
|
206
|
+
daemon: boolean,
|
|
207
|
+
buildEnv: ((options: { readonly daemon: boolean; readonly homeDir: string }) => Record<string, string>) | undefined,
|
|
199
208
|
): Record<string, string> {
|
|
200
|
-
const env =
|
|
209
|
+
const env = buildEnv?.({ daemon, homeDir }) ?? buildCodegraphEnv({ daemon, homeDir });
|
|
201
210
|
return trustedInstallDir === undefined ? env : { ...env, CODEGRAPH_INSTALL_DIR: trustedInstallDir };
|
|
202
211
|
}
|
|
203
212
|
|
|
@@ -6,6 +6,7 @@ import { cwd as processCwd, env as processEnv, execPath as processExecPath, stde
|
|
|
6
6
|
|
|
7
7
|
import { getCodexOmoConfig } from "../../../shared/src/config-loader.ts";
|
|
8
8
|
import { buildCodegraphChildEnv, buildCodegraphEnv } from "../../../../../utils/src/codegraph/env.ts";
|
|
9
|
+
import { CODEGRAPH_PINNED_VERSION } from "../../../../../utils/src/codegraph/manifest.ts";
|
|
9
10
|
import { evaluateCodegraphNodeSupport, type CodegraphNodeSupport } from "../../../../../utils/src/codegraph/node-support.ts";
|
|
10
11
|
import { ensureCodegraphProvisioned } from "../../../../../utils/src/codegraph/provision.ts";
|
|
11
12
|
import {
|
|
@@ -25,7 +26,7 @@ import type {
|
|
|
25
26
|
|
|
26
27
|
export const SESSION_START_CWD_ENV = "OMO_CODEGRAPH_SESSION_START_CWD";
|
|
27
28
|
|
|
28
|
-
const CODEGRAPH_VERSION =
|
|
29
|
+
const CODEGRAPH_VERSION = CODEGRAPH_PINNED_VERSION;
|
|
29
30
|
const COMMAND_TIMEOUT_MS = 60_000;
|
|
30
31
|
const WINDOWS_CMD_EXTENSIONS = new Set([".bat", ".cmd"]);
|
|
31
32
|
const WINDOWS_NODE_SCRIPT_EXTENSIONS = new Set([".cjs", ".js", ".mjs"]);
|
|
@@ -127,7 +128,10 @@ async function resolveOrProvisionCommand(
|
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
function codegraphEnvForConfig(config: CodegraphBootstrapConfig, homeDir: string): Record<string, string> {
|
|
130
|
-
|
|
131
|
+
// The detached indexing worker must NEVER engage the upstream daemon, even
|
|
132
|
+
// when the user opted into daemon mode for interactive MCP sessions: a
|
|
133
|
+
// daemon-backed init/sync would outlive and race the worker's lifecycle.
|
|
134
|
+
const env = buildCodegraphEnv({ daemon: false, homeDir });
|
|
131
135
|
return config.trustedCodegraphInstallDir === undefined ? env : { ...env, CODEGRAPH_INSTALL_DIR: config.trustedCodegraphInstallDir };
|
|
132
136
|
}
|
|
133
137
|
|
|
@@ -61,6 +61,7 @@ function writeSweepReport(stdout: HookStdout, result: SweepCodegraphZombiesResul
|
|
|
61
61
|
failed: result.failed,
|
|
62
62
|
killed: result.killed.map(formatProcess),
|
|
63
63
|
ownedRoots: result.ownedRoots,
|
|
64
|
+
spared: result.spared.map(formatProcess),
|
|
64
65
|
stampFile: result.stampFile,
|
|
65
66
|
})}\n`);
|
|
66
67
|
}
|
|
@@ -21,7 +21,7 @@ describe("CodeGraph SessionStart exclusion policy", () => {
|
|
|
21
21
|
const stdout: string[] = [];
|
|
22
22
|
const spawned: WorkerSpawnInvocation[] = [];
|
|
23
23
|
const stateRoot = createAllowedWorkspace("codegraph-omo-state");
|
|
24
|
-
const workspace = join(stateRoot, ".omo", "
|
|
24
|
+
const workspace = join(stateRoot, ".omo", "ulw-research", "run", "clones", "repo");
|
|
25
25
|
mkdirSync(workspace, { recursive: true });
|
|
26
26
|
|
|
27
27
|
try {
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
import { executeCodegraphSessionStartHook, type WorkerSpawnInvocation } from "../src/hook.ts";
|
|
9
|
+
|
|
10
|
+
const pluginRoot = resolve(fileURLToPath(new URL("../../..", import.meta.url)));
|
|
11
|
+
|
|
12
|
+
// Observed verbatim from a REAL codegraph 1.4.1 binary running `status --json` (exit 0)
|
|
13
|
+
// against a project store built by a REAL codegraph 1.0.1 binary
|
|
14
|
+
// (see .omo/evidence/codegraph-daemon-process-hygiene/task-2-store-upgrade.txt).
|
|
15
|
+
const MIGRATED_1_0_1_STORE_STATUS_JSON = JSON.stringify({
|
|
16
|
+
initialized: true,
|
|
17
|
+
version: "1.4.1",
|
|
18
|
+
projectPath: "/private/tmp/cg-task2/fixture",
|
|
19
|
+
indexPath: "/private/tmp/cg-task2/fixture/.codegraph",
|
|
20
|
+
lastIndexed: "2026-07-21T04:28:09.593Z",
|
|
21
|
+
fileCount: 2,
|
|
22
|
+
nodeCount: 5,
|
|
23
|
+
edgeCount: 4,
|
|
24
|
+
dbSizeBytes: 159744,
|
|
25
|
+
backend: "node-sqlite",
|
|
26
|
+
journalMode: "wal",
|
|
27
|
+
nodesByKind: { file: 2, function: 3 },
|
|
28
|
+
languages: ["typescript"],
|
|
29
|
+
pendingChanges: { added: 0, modified: 0, removed: 0 },
|
|
30
|
+
worktreeMismatch: null,
|
|
31
|
+
index: {
|
|
32
|
+
builtWithVersion: "1.0.1",
|
|
33
|
+
builtWithExtractionVersion: 24,
|
|
34
|
+
currentExtractionVersion: 24,
|
|
35
|
+
reindexRecommended: false,
|
|
36
|
+
state: null,
|
|
37
|
+
pendingRefs: 0,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
function createAllowedWorkspace(prefix: string): string {
|
|
42
|
+
return mkdtempSync(join(pluginRoot, `.tmp-${prefix}-`));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// On win32 execFile cannot run a #!/bin/sh script, so emit a codegraph.cmd batch file instead;
|
|
46
|
+
// resolveCodegraphCommandInvocation wraps .cmd in `cmd.exe /d /s /c`, exactly like the real codegraph.cmd.
|
|
47
|
+
function createFakeCodegraphBin(scripts: { readonly posix: string; readonly win32: string }): { readonly binPath: string; readonly dir: string } {
|
|
48
|
+
const dir = mkdtempSync(join(tmpdir(), "omo-codegraph-fake-bin-"));
|
|
49
|
+
if (process.platform === "win32") {
|
|
50
|
+
const binPath = join(dir, "codegraph.cmd");
|
|
51
|
+
writeFileSync(binPath, scripts.win32);
|
|
52
|
+
return { binPath, dir };
|
|
53
|
+
}
|
|
54
|
+
const binPath = join(dir, "codegraph");
|
|
55
|
+
writeFileSync(binPath, scripts.posix, { mode: 0o755 });
|
|
56
|
+
chmodSync(binPath, 0o755);
|
|
57
|
+
return { binPath, dir };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Keep the probe hermetic on POSIX but viable on win32: resolveCodegraphCommandInvocation wraps
|
|
61
|
+
// the fake .cmd in `cmd.exe /d /s /c`, and Windows can only resolve cmd.exe when the child env
|
|
62
|
+
// carries the real system PATH (System32) plus SystemRoot (both whitelisted in SAFE_AMBIENT_ENV_KEYS).
|
|
63
|
+
// A POSIX-only PATH makes the probe spawn fail, so the hook reports not-initialized and spawns.
|
|
64
|
+
function probeEnv(homeDir: string, binPath: string): Record<string, string> {
|
|
65
|
+
if (process.platform === "win32") {
|
|
66
|
+
const env: Record<string, string> = {
|
|
67
|
+
HOME: homeDir,
|
|
68
|
+
OMO_CODEGRAPH_BIN: binPath,
|
|
69
|
+
};
|
|
70
|
+
if (process.env["PATH"] !== undefined) env["PATH"] = process.env["PATH"];
|
|
71
|
+
if (process.env["SystemRoot"] !== undefined) env["SystemRoot"] = process.env["SystemRoot"];
|
|
72
|
+
return env;
|
|
73
|
+
}
|
|
74
|
+
return { HOME: homeDir, OMO_CODEGRAPH_BIN: binPath, PATH: "/usr/bin:/bin" };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describe("CodeGraph SessionStart hook with a 1.0.1-era project store under the 1.4.1 binary", () => {
|
|
78
|
+
it("#given a real 1.4.1 status payload for a migrated 1.0.1 store #when SessionStart probes via the default probe #then it stays silent without spawning a re-init worker", async () => {
|
|
79
|
+
// given
|
|
80
|
+
const stdout: string[] = [];
|
|
81
|
+
const spawned: WorkerSpawnInvocation[] = [];
|
|
82
|
+
const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-upgrade-home-"));
|
|
83
|
+
// The payload is a single JSON line with no cmd metacharacters (% ^ & < > |), so a plain echo is safe.
|
|
84
|
+
const fake = createFakeCodegraphBin({
|
|
85
|
+
posix: `#!/bin/sh\nprintf '%s\\n' '${MIGRATED_1_0_1_STORE_STATUS_JSON}'\n`,
|
|
86
|
+
win32: `@echo off\r\n@echo ${MIGRATED_1_0_1_STORE_STATUS_JSON}\r\n`,
|
|
87
|
+
});
|
|
88
|
+
const workspace = createAllowedWorkspace("codegraph-upgrade-workspace");
|
|
89
|
+
mkdirSync(join(workspace, ".codegraph"), { recursive: true });
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// when
|
|
93
|
+
const result = await executeCodegraphSessionStartHook({
|
|
94
|
+
config: { codegraph: { enabled: true }, sources: [], warnings: [] },
|
|
95
|
+
cwd: workspace,
|
|
96
|
+
env: probeEnv(homeDir, fake.binPath),
|
|
97
|
+
stdin: Readable.from(["{}"]),
|
|
98
|
+
stdout: { write: (chunk) => stdout.push(chunk) },
|
|
99
|
+
spawnWorker: (invocation) => spawned.push(invocation),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// then
|
|
103
|
+
expect(result).toEqual({ action: "skipped-initialized", exitCode: 0 });
|
|
104
|
+
expect(spawned).toEqual([]);
|
|
105
|
+
expect(stdout.join("")).toBe("");
|
|
106
|
+
} finally {
|
|
107
|
+
rmSync(homeDir, { recursive: true, force: true });
|
|
108
|
+
rmSync(fake.dir, { recursive: true, force: true });
|
|
109
|
+
rmSync(workspace, { recursive: true, force: true, maxRetries: 10, retryDelay: 500 });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("#given the status probe hangs past its 2s timeout #when SessionStart fires #then it exits zero promptly and spawns at most one bounded worker", async () => {
|
|
114
|
+
// given
|
|
115
|
+
const stdout: string[] = [];
|
|
116
|
+
const spawned: WorkerSpawnInvocation[] = [];
|
|
117
|
+
const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-slow-home-"));
|
|
118
|
+
const fake = createFakeCodegraphBin({
|
|
119
|
+
posix: "#!/bin/sh\nsleep 30\n",
|
|
120
|
+
win32: "@echo off\r\nset /p _probe_hang=\r\n",
|
|
121
|
+
});
|
|
122
|
+
const workspace = createAllowedWorkspace("codegraph-slow-probe-workspace");
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
// when
|
|
126
|
+
const startedAt = Date.now();
|
|
127
|
+
const result = await executeCodegraphSessionStartHook({
|
|
128
|
+
config: { codegraph: { enabled: true }, sources: [], warnings: [] },
|
|
129
|
+
cwd: workspace,
|
|
130
|
+
env: probeEnv(homeDir, fake.binPath),
|
|
131
|
+
stdin: Readable.from(["{}"]),
|
|
132
|
+
stdout: { write: (chunk) => stdout.push(chunk) },
|
|
133
|
+
spawnWorker: (invocation) => spawned.push(invocation),
|
|
134
|
+
});
|
|
135
|
+
const elapsedMs = Date.now() - startedAt;
|
|
136
|
+
|
|
137
|
+
// then
|
|
138
|
+
expect(result.exitCode).toBe(0);
|
|
139
|
+
expect(result.action).toBe("spawned");
|
|
140
|
+
expect(spawned).toHaveLength(1);
|
|
141
|
+
expect(elapsedMs).toBeLessThan(15_000);
|
|
142
|
+
} finally {
|
|
143
|
+
rmSync(homeDir, { recursive: true, force: true });
|
|
144
|
+
rmSync(fake.dir, { recursive: true, force: true });
|
|
145
|
+
rmSync(workspace, { recursive: true, force: true, maxRetries: 10, retryDelay: 500 });
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import { chmodSync, writeFileSync } from "node:fs";
|
|
2
2
|
|
|
3
|
+
export const CODEGRAPH_141_DEFAULT_TOOLS: readonly Record<string, unknown>[] = [
|
|
4
|
+
{
|
|
5
|
+
name: "codegraph_explore",
|
|
6
|
+
description:
|
|
7
|
+
"PRIMARY TOOL — call FIRST for almost any question OR before an edit: how does X work, architecture, a bug, where/what is X, surveying an area, or the symbols you are about to change. Returns the verbatim source of the relevant symbols grouped by file in ONE capped call (Read-equivalent — treat the shown source as already Read; do NOT re-open those files), plus the call path among them. Query can be a natural-language question OR a bag of symbol/file names. Usually the ONLY call you need — more accurate context, in far fewer tokens and round-trips than a search/Read/Grep loop.",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
query: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description:
|
|
14
|
+
'Symbol names, file names, or short code terms to explore (e.g., "AuthService loginUser session-manager", "GraphTraverser BFS impact traversal.ts"). For a flow question, name the symbols spanning the flow (e.g. "mutateElement renderScene"). A natural-language question works too — no prior codegraph_search needed.',
|
|
15
|
+
},
|
|
16
|
+
maxFiles: {
|
|
17
|
+
type: "number",
|
|
18
|
+
description: "Maximum number of files to include source code from (default: 12)",
|
|
19
|
+
default: 12,
|
|
20
|
+
},
|
|
21
|
+
projectPath: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description:
|
|
24
|
+
"Absolute path to the project to query (or any directory inside it) — codegraph uses the nearest .codegraph/ index at or above that path. Omit to use this session's default project. Pass it to query a second codebase, or when the server root has no index of its own (e.g. a monorepo where only sub-projects are indexed, so there is no default project).",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
required: ["query"],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
3
32
|
export function writeFakeNewlineCodegraph(filePath: string): void {
|
|
4
33
|
writeFileSync(
|
|
5
34
|
filePath,
|
|
@@ -12,10 +41,10 @@ export function writeFakeNewlineCodegraph(filePath: string): void {
|
|
|
12
41
|
"rl.on('line', (line) => {",
|
|
13
42
|
" const request = JSON.parse(line);",
|
|
14
43
|
" if (request.method === 'initialize') {",
|
|
15
|
-
" process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { capabilities: { tools: { listChanged: false } }, protocolVersion: request.params.protocolVersion, serverInfo: { name: 'codegraph', version: '1.
|
|
44
|
+
" process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { capabilities: { tools: { listChanged: false } }, protocolVersion: request.params.protocolVersion, serverInfo: { name: 'codegraph', version: '1.4.1' } } }) + '\\n');",
|
|
16
45
|
" }",
|
|
17
46
|
" if (request.method === 'tools/list') {",
|
|
18
|
-
|
|
47
|
+
` process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { tools: ${JSON.stringify(CODEGRAPH_141_DEFAULT_TOOLS)} } }) + '\\n');`,
|
|
19
48
|
" }",
|
|
20
49
|
"});",
|
|
21
50
|
].join("\n"),
|
|
@@ -43,7 +43,7 @@ describe("CodeGraph component runtime package metadata", () => {
|
|
|
43
43
|
const files = packageJson.files ?? [];
|
|
44
44
|
|
|
45
45
|
// then
|
|
46
|
-
expect(optionalDependencies["@colbymchenry/codegraph"]).toBe("1.
|
|
46
|
+
expect(optionalDependencies["@colbymchenry/codegraph"]).toBe("1.4.1");
|
|
47
47
|
expect(files).toContain("LICENSE");
|
|
48
48
|
expect(files).toContain("NODE-RUNTIME-LICENSES.md");
|
|
49
49
|
expect(files).toContain("NOTICE");
|
package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { mkdirSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { PassThrough, Writable } from "node:stream";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
|
+
import { isProcessAlive } from "../../../../../mcp-stdio-core/src/index.ts";
|
|
7
8
|
import { runCodegraphServe } from "../src/serve.ts";
|
|
8
9
|
import {
|
|
9
10
|
frameMcpRequest,
|
|
10
11
|
writeFakeExitingCodegraph,
|
|
12
|
+
writeFakeHeldOpenCodegraph,
|
|
11
13
|
} from "./mcp-bridge-fixtures.ts";
|
|
12
14
|
|
|
13
15
|
const componentRoot = realpathSync(
|
|
@@ -66,4 +68,66 @@ describe("runCodegraphServe MCP bridge lifecycle", () => {
|
|
|
66
68
|
rmSync(tempRoot, { recursive: true, force: true });
|
|
67
69
|
}
|
|
68
70
|
});
|
|
71
|
+
|
|
72
|
+
it("#given a held-open Codegraph child and a dead parent #when the bridge watchdog polls #then the child is terminated and the bridge settles through the child-exit path", async () => {
|
|
73
|
+
// given
|
|
74
|
+
const tempRoot = mkdtempSync(
|
|
75
|
+
join(componentRoot, ".tmp-codegraph-orphan-"),
|
|
76
|
+
);
|
|
77
|
+
const projectRoot = join(tempRoot, "project");
|
|
78
|
+
const fakeCodegraph = join(tempRoot, "codegraph-held-open.cjs");
|
|
79
|
+
const childPidFile = join(tempRoot, "child.pid");
|
|
80
|
+
// Client stdio is held open and never sees EOF, so the parent-liveness
|
|
81
|
+
// watchdog is the only settle path. The probe reports the parent alive
|
|
82
|
+
// until the child has started, making the poll fire deterministically.
|
|
83
|
+
const input = new PassThrough();
|
|
84
|
+
const output = new PassThrough();
|
|
85
|
+
output.resume();
|
|
86
|
+
let childPid: number | undefined;
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
mkdirSync(projectRoot, { recursive: true });
|
|
90
|
+
writeFakeHeldOpenCodegraph(fakeCodegraph);
|
|
91
|
+
const run = runCodegraphServe({
|
|
92
|
+
cwd: tempRoot,
|
|
93
|
+
env: {
|
|
94
|
+
CODEGRAPH_ALLOW_UNSAFE_NODE: "1",
|
|
95
|
+
CODEGRAPH_FAKE_LOG: childPidFile,
|
|
96
|
+
OMO_CODEGRAPH_BIN: fakeCodegraph,
|
|
97
|
+
OMO_CODEGRAPH_PROJECT_CWD: projectRoot,
|
|
98
|
+
PATH: process.env["PATH"],
|
|
99
|
+
},
|
|
100
|
+
nodeVersion: "22.14.0",
|
|
101
|
+
buildEnv: () => ({}),
|
|
102
|
+
resolve: () => ({
|
|
103
|
+
argsPrefix: [],
|
|
104
|
+
command: fakeCodegraph,
|
|
105
|
+
exists: true,
|
|
106
|
+
source: "env",
|
|
107
|
+
}),
|
|
108
|
+
stderr: { write: () => undefined },
|
|
109
|
+
stdin: input,
|
|
110
|
+
stdout: output,
|
|
111
|
+
parentWatchdog: {
|
|
112
|
+
pollIntervalMs: 10,
|
|
113
|
+
probeAlive: () => !existsSync(childPidFile),
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// when
|
|
118
|
+
const exitCode = await run;
|
|
119
|
+
|
|
120
|
+
// then
|
|
121
|
+
expect(exitCode).not.toBe(0);
|
|
122
|
+
childPid = Number(readFileSync(childPidFile, "utf8"));
|
|
123
|
+
expect(isProcessAlive(childPid)).toBe(false);
|
|
124
|
+
} finally {
|
|
125
|
+
if (childPid !== undefined && isProcessAlive(childPid)) {
|
|
126
|
+
process.kill(childPid, "SIGKILL");
|
|
127
|
+
}
|
|
128
|
+
input.destroy();
|
|
129
|
+
output.destroy();
|
|
130
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
69
133
|
});
|
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import { runCodegraphServe } from "../src/serve.ts";
|
|
8
8
|
import {
|
|
9
9
|
arrayProperty,
|
|
10
|
+
CODEGRAPH_141_DEFAULT_TOOLS,
|
|
10
11
|
findTool,
|
|
11
12
|
frameMcpRequest,
|
|
12
13
|
parseMcpBodies,
|
|
@@ -86,19 +87,14 @@ describe("runCodegraphServe MCP protocol bridge", () => {
|
|
|
86
87
|
result: {
|
|
87
88
|
capabilities: { tools: { listChanged: false } },
|
|
88
89
|
protocolVersion: "2025-06-18",
|
|
89
|
-
serverInfo: { name: "codegraph", version: "1.
|
|
90
|
+
serverInfo: { name: "codegraph", version: "1.4.1" },
|
|
90
91
|
},
|
|
91
92
|
},
|
|
92
93
|
{
|
|
93
94
|
id: 2,
|
|
94
95
|
jsonrpc: "2.0",
|
|
95
96
|
result: {
|
|
96
|
-
tools:
|
|
97
|
-
{ name: "codegraph_search" },
|
|
98
|
-
{ name: "codegraph_node" },
|
|
99
|
-
{ name: "codegraph_explore" },
|
|
100
|
-
{ name: "codegraph_callers" },
|
|
101
|
-
],
|
|
97
|
+
tools: CODEGRAPH_141_DEFAULT_TOOLS,
|
|
102
98
|
},
|
|
103
99
|
},
|
|
104
100
|
]);
|