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.
Files changed (307) hide show
  1. package/.agents/command/omomomo.md +1 -1
  2. package/.agents/skills/omomomo/SKILL.md +1 -1
  3. package/.opencode/command/omomomo.md +1 -1
  4. package/README.ja.md +6 -6
  5. package/README.ko.md +6 -6
  6. package/README.md +8 -8
  7. package/README.ru.md +6 -6
  8. package/README.zh-cn.md +6 -6
  9. package/dist/agents/atlas/agent.d.ts +7 -6
  10. package/dist/agents/gpt-prompt-identity.d.ts +3 -0
  11. package/dist/agents/sisyphus/gpt-5-5.d.ts +1 -1
  12. package/dist/agents/sisyphus/gpt-task-system-guide.d.ts +1 -0
  13. package/dist/agents/sisyphus/index.d.ts +5 -0
  14. package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
  15. package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
  16. package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
  17. package/dist/agents/sisyphus-junior/gpt-5-5.d.ts +2 -2
  18. package/dist/agents/sisyphus-junior/index.d.ts +2 -0
  19. package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
  20. package/dist/agents/types.d.ts +2 -2
  21. package/dist/cli/index.js +781 -215
  22. package/dist/cli-node/index.js +781 -215
  23. package/dist/config/index.d.ts +1 -1
  24. package/dist/config/schema/agent-overrides.d.ts +48 -48
  25. package/dist/config/schema/browser-automation.d.ts +1 -0
  26. package/dist/config/schema/categories.d.ts +6 -6
  27. package/dist/config/schema/codegraph.d.ts +2 -0
  28. package/dist/config/schema/commands.d.ts +1 -3
  29. package/dist/config/schema/default-mode.d.ts +1 -1
  30. package/dist/config/schema/fallback-models.d.ts +5 -5
  31. package/dist/config/schema/goal.d.ts +7 -0
  32. package/dist/config/schema/hooks.d.ts +1 -1
  33. package/dist/config/schema/oh-my-opencode-config.d.ts +56 -58
  34. package/dist/config/schema/ralph-loop.d.ts +1 -1
  35. package/dist/config/schema.d.ts +1 -1
  36. package/dist/create-hooks.d.ts +1 -1
  37. package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
  38. package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
  39. package/dist/features/builtin-commands/types.d.ts +1 -1
  40. package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
  41. package/dist/hooks/codegraph-bootstrap/hook.d.ts +3 -2
  42. package/dist/hooks/goal/command-arguments.d.ts +13 -0
  43. package/dist/hooks/goal/controller.d.ts +26 -0
  44. package/dist/hooks/goal/index.d.ts +23 -0
  45. package/dist/hooks/goal/prompt.d.ts +3 -0
  46. package/dist/hooks/goal/store.d.ts +8 -0
  47. package/dist/hooks/goal/tools.d.ts +10 -0
  48. package/dist/hooks/goal/types.d.ts +93 -0
  49. package/dist/hooks/goal/validation.d.ts +4 -0
  50. package/dist/hooks/index.d.ts +1 -0
  51. package/dist/hooks/keyword-detector/hook.d.ts +1 -2
  52. package/dist/hooks/ralph-loop/types.d.ts +1 -1
  53. package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +1 -1
  54. package/dist/hooks/start-work/notepad-scaffold.d.ts +10 -0
  55. package/dist/index.js +5398 -5109
  56. package/dist/oh-my-opencode.schema.json +34 -19
  57. package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
  58. package/dist/plugin/chat-message/types.d.ts +23 -10
  59. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
  60. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
  61. package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
  62. package/dist/plugin/stop-continuation.d.ts +2 -2
  63. package/dist/shared/omo-process-sweep.d.ts +18 -0
  64. package/dist/shared/system-directive.d.ts +0 -1
  65. package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
  66. package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
  67. package/dist/skills/ulw-plan/SKILL.md +31 -8
  68. package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
  69. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  70. package/dist/skills/ulw-research/SKILL.md +3 -3
  71. package/dist/testing/create-plugin-module.d.ts +1 -0
  72. package/dist/tools/delegate-task/types.d.ts +0 -1
  73. package/dist/tui.js +138 -80
  74. package/package.json +14 -14
  75. package/packages/git-bash-mcp/dist/cli.js +54 -8
  76. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +60 -0
  77. package/packages/lsp-core/src/lsp/client.ts +14 -1
  78. package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +2 -2
  79. package/packages/lsp-core/src/mcp.ts +9 -0
  80. package/packages/lsp-daemon/dist/cli.js +319 -51
  81. package/packages/lsp-daemon/dist/client.js +136 -48
  82. package/packages/lsp-daemon/dist/daemon-client.d.ts +0 -10
  83. package/packages/lsp-daemon/dist/daemon-client.js +4 -34
  84. package/packages/lsp-daemon/dist/daemon-failure-result.d.ts +3 -0
  85. package/packages/lsp-daemon/dist/daemon-failure-result.js +38 -0
  86. package/packages/lsp-daemon/dist/daemon-request-error.d.ts +14 -0
  87. package/packages/lsp-daemon/dist/daemon-request-error.js +26 -0
  88. package/packages/lsp-daemon/dist/daemon-server.js +5 -1
  89. package/packages/lsp-daemon/dist/index.js +137 -48
  90. package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
  91. package/packages/lsp-daemon/dist/proxy.js +1 -0
  92. package/packages/lsp-daemon/dist/version-reap.d.ts +26 -0
  93. package/packages/lsp-daemon/dist/version-reap.js +182 -0
  94. package/packages/lsp-tools-mcp/dist/cli.js +61 -10
  95. package/packages/lsp-tools-mcp/dist/lsp/manager.js +6 -1
  96. package/packages/lsp-tools-mcp/dist/mcp.js +61 -10
  97. package/packages/lsp-tools-mcp/dist/tools.js +6 -1
  98. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  99. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +416 -25
  100. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  101. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  102. package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +22 -2
  103. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
  104. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +638 -267
  105. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +178 -89
  106. package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
  107. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +3 -1
  108. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +3 -1
  109. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +98 -32
  110. package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +5 -0
  111. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +15 -6
  112. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +6 -2
  113. package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +1 -0
  114. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
  115. package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +148 -0
  116. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +31 -2
  117. package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +1 -1
  118. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +65 -1
  119. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -7
  120. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +1 -1
  121. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +57 -3
  122. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +41 -0
  123. package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +109 -8
  124. package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +2 -2
  125. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +43 -0
  126. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  127. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  128. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  129. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  130. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  131. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  132. package/packages/omo-codex/plugin/components/lsp/AGENTS.md +1 -0
  133. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  134. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +80 -40
  135. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  136. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  137. package/packages/omo-codex/plugin/components/rules/dist/cli.js +11 -0
  138. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  139. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  140. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +11 -0
  141. package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +60 -0
  142. package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
  143. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +147 -60
  144. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  145. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  146. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +3 -71
  147. package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
  148. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +198 -23
  149. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
  150. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +6 -5
  151. package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
  152. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  153. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  154. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  155. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  156. package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +10 -10
  157. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +2 -2
  158. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +2 -2
  159. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
  160. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +1 -1
  161. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +2 -2
  162. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
  163. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
  164. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +1 -1
  165. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  166. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  167. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +31 -8
  168. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
  169. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  170. package/packages/omo-codex/plugin/components/ultrawork/src/codex-hook.ts +1 -1
  171. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook-trigger-policy.test.ts +42 -0
  172. package/packages/omo-codex/plugin/components/ulw-loop/README.md +6 -4
  173. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.d.ts +21 -0
  174. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +64 -0
  175. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +15 -2
  176. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.js +1 -1
  177. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +2 -1
  178. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
  179. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +3 -3
  180. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.d.ts +3 -0
  181. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +85 -0
  182. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +0 -1
  183. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -33
  184. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +998 -593
  185. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +1 -0
  186. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.d.ts +1 -1
  187. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.js +2 -0
  188. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +6 -0
  189. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.d.ts +1 -0
  190. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.js +3 -0
  191. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +1 -0
  192. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +4 -0
  193. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +1 -0
  194. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +10 -2
  195. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +1 -1
  196. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.d.ts +15 -0
  197. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +84 -0
  198. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +5 -0
  199. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +81 -0
  200. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +7 -59
  201. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +12 -4
  202. package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.d.ts +9 -0
  203. package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.js +122 -0
  204. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  205. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  206. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +8 -6
  207. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +84 -0
  208. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +13 -2
  209. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-arg-parser.ts +1 -1
  210. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +1 -1
  211. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +3 -3
  212. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +49 -0
  213. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +13 -45
  214. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +1 -0
  215. package/packages/omo-codex/plugin/components/ulw-loop/src/constants.ts +2 -0
  216. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +7 -0
  217. package/packages/omo-codex/plugin/components/ulw-loop/src/goal-status.ts +4 -0
  218. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +4 -1
  219. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +20 -2
  220. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +1 -1
  221. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +125 -0
  222. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +59 -0
  223. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +6 -58
  224. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +12 -4
  225. package/packages/omo-codex/plugin/components/ulw-loop/src/validation-batch.ts +122 -0
  226. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +76 -0
  227. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-checkpoint-continuation.test.ts +111 -0
  228. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-steering-batch.test.ts +67 -0
  229. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-validation-batch.test.ts +57 -0
  230. package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +1 -0
  231. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +152 -0
  232. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +29 -0
  233. package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch-checkpoint.test.ts +147 -0
  234. package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch.test.ts +73 -0
  235. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  236. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  237. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  238. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  239. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  240. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  241. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  242. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  243. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  244. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  245. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  246. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +2 -2
  247. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  248. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +2 -2
  249. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  250. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +2 -2
  251. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  252. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  253. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  254. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  255. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  256. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  257. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  258. package/packages/omo-codex/plugin/package-lock.json +41 -41
  259. package/packages/omo-codex/plugin/package.json +1 -1
  260. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
  261. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
  262. package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
  263. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +3 -18
  264. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +43 -0
  265. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
  266. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +8 -6
  267. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +31 -8
  268. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
  269. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  270. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +11 -11
  271. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +26 -0
  272. package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +41 -0
  273. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
  274. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
  275. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
  276. package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
  277. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
  278. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +3 -18
  279. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
  280. package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
  281. package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
  282. package/packages/omo-codex/scripts/install-dist/install-local.mjs +401 -22
  283. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
  284. package/packages/omo-codex/scripts/install-marketplace-cache.test.mjs +4 -1
  285. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
  286. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
  287. package/packages/shared-skills/skills/ulw-plan/SKILL.md +31 -8
  288. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
  289. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  290. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
  291. package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
  292. package/dist/skills/ultraresearch/SKILL.md +0 -10
  293. package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
  294. package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
  295. package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
  296. package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
  297. package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
  298. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
  299. package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
  300. package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
  301. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
  302. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
  303. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
  304. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
  305. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
  306. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
  307. /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 clientForwardingDone = forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
67
- defaultResponseMode = mode;
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
- for await (const message of readStdioJsonRpcMessages(input)) {
99
- if (message.kind === "parse_error") {
100
- continue;
101
- }
102
- const responseMode = message.responseMode;
103
- setDefaultResponseMode(responseMode);
104
- const key = responseModeKey(message.payload);
105
- if (key !== null) {
106
- pendingResponses.set(key, {
107
- method: jsonRpcMethod(message.payload),
108
- responseMode,
109
- toolName: jsonRpcToolName(message.payload),
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
- await writeLine(childInput, JSON.stringify(message.payload));
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
- for await (const message of readStdioJsonRpcMessages(childOutput)) {
124
- if (message.kind === "parse_error") {
125
- await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
126
- continue;
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
- const key = responseModeKey(message.payload);
129
- const pendingResponse = key === null ? undefined : pendingResponses.get(key);
130
- const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
131
- if (key !== null) pendingResponses.delete(key);
132
- await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
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 = "1.0.1";
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
- buildEnv: ((options: { readonly homeDir: string }) => Record<string, string>) | undefined,
206
+ daemon: boolean,
207
+ buildEnv: ((options: { readonly daemon: boolean; readonly homeDir: string }) => Record<string, string>) | undefined,
199
208
  ): Record<string, string> {
200
- const env = { ...(buildEnv?.({ homeDir }) ?? buildCodegraphEnv({ homeDir })), [CODEGRAPH_NO_DAEMON_ENV]: "1" };
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 = "1.0.1";
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
- const env = buildCodegraphEnv({ homeDir });
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", "ultraresearch", "run", "clones", "repo");
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.0.1' } } }) + '\\n');",
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
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { tools: [{ name: 'codegraph_search' }, { name: 'codegraph_node' }, { name: 'codegraph_explore' }, { name: 'codegraph_callers' }] } }) + '\\n');",
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.0.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");
@@ -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.0.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
  ]);