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,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // components/codegraph/src/cli.ts
4
- import { realpathSync as realpathSync6 } from "node:fs";
5
- import { basename as basename5, resolve as resolve6 } from "node:path";
4
+ import { realpathSync as realpathSync7 } from "node:fs";
5
+ import { basename as basename5, resolve as resolve8 } from "node:path";
6
6
  import { stderr as processStderr4 } from "node:process";
7
7
  import { fileURLToPath as fileURLToPath4 } from "node:url";
8
8
 
9
9
  // components/codegraph/src/hook.ts
10
10
  import { execFile as execFile4, spawn } from "node:child_process";
11
11
  import { homedir as homedir13 } from "node:os";
12
- import { join as join13 } from "node:path";
12
+ import { join as join14 } from "node:path";
13
13
  import {
14
14
  cwd as processCwd2,
15
15
  env as processEnv2,
@@ -56,6 +56,7 @@ var SAFE_AMBIENT_ENV_KEYS = new Set([
56
56
  var SAFE_CODEGRAPH_RUNTIME_ENV_KEYS = new Set([
57
57
  "CODEGRAPH_ALLOW_UNSAFE_NODE",
58
58
  "CODEGRAPH_BIN",
59
+ "CODEGRAPH_DAEMON_IDLE_TIMEOUT_MS",
59
60
  "CODEGRAPH_FAKE_LOG",
60
61
  "CODEGRAPH_NO_DAEMON",
61
62
  "CODEGRAPH_NODE_BIN",
@@ -67,7 +68,7 @@ function buildCodegraphEnv(options = {}) {
67
68
  const homeDir = options.homeDir ?? homedir();
68
69
  return {
69
70
  [CODEGRAPH_INSTALL_DIR_ENV]: join(homeDir, ".omo", "codegraph"),
70
- [CODEGRAPH_NO_DAEMON_ENV]: "1",
71
+ ...options.daemon === true ? {} : { [CODEGRAPH_NO_DAEMON_ENV]: "1" },
71
72
  [CODEGRAPH_NO_DOWNLOAD_ENV]: "1",
72
73
  [CODEGRAPH_TELEMETRY_ENV]: "0",
73
74
  [DO_NOT_TRACK_ENV]: "1"
@@ -98,8 +99,9 @@ function buildCodegraphChildEnv(options = {}) {
98
99
  import { homedir as homedir5 } from "node:os";
99
100
 
100
101
  // ../../utils/src/codegraph/workspace.ts
102
+ import { execFileSync } from "node:child_process";
101
103
  import { appendFileSync, existsSync as existsSync2, lstatSync as lstatSync2, mkdirSync, realpathSync as realpathSync3, readFileSync as readFileSync2, statSync, symlinkSync } from "node:fs";
102
- import { join as join5 } from "node:path";
104
+ import { dirname, join as join5, resolve as resolve3 } from "node:path";
103
105
 
104
106
  // ../../utils/src/codegraph/paths.ts
105
107
  import { createHash } from "node:crypto";
@@ -305,12 +307,52 @@ function prepareCodegraphWorkspace(workspace, options = {}) {
305
307
  }
306
308
  }
307
309
  function ensureCodegraphGitignored(workspace) {
308
- const gitDir = join5(workspace, ".git");
309
- if (!existsSync2(gitDir))
310
+ const gitMarkerPath = join5(workspace, ".git");
311
+ if (!existsSync2(gitMarkerPath))
310
312
  return false;
311
- const excludePath = join5(gitDir, "info", "exclude");
312
313
  try {
313
- mkdirSync(join5(gitDir, "info"), { recursive: true });
314
+ const isWorktree = execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
315
+ cwd: workspace,
316
+ encoding: "utf8",
317
+ shell: false,
318
+ stdio: ["ignore", "pipe", "ignore"],
319
+ timeout: 5000,
320
+ windowsHide: true
321
+ }).trim();
322
+ if (isWorktree !== "true")
323
+ return false;
324
+ const gitTopLevel = execFileSync("git", ["rev-parse", "--show-toplevel"], {
325
+ cwd: workspace,
326
+ encoding: "utf8",
327
+ shell: false,
328
+ stdio: ["ignore", "pipe", "ignore"],
329
+ timeout: 5000,
330
+ windowsHide: true
331
+ }).trim();
332
+ if (gitTopLevel.length === 0 || realpathSync3.native(gitTopLevel) !== realpathSync3.native(workspace))
333
+ return false;
334
+ const gitDir = execFileSync("git", ["rev-parse", "--absolute-git-dir"], {
335
+ cwd: workspace,
336
+ encoding: "utf8",
337
+ shell: false,
338
+ stdio: ["ignore", "pipe", "ignore"],
339
+ timeout: 5000,
340
+ windowsHide: true
341
+ }).trim();
342
+ if (gitDir.length === 0 || !gitMarkerOwnsGitDir(gitMarkerPath, workspace, gitDir))
343
+ return false;
344
+ const gitExcludePath = execFileSync("git", ["rev-parse", "--git-path", "info/exclude"], {
345
+ cwd: workspace,
346
+ encoding: "utf8",
347
+ shell: false,
348
+ stdio: ["ignore", "pipe", "ignore"],
349
+ timeout: 5000,
350
+ windowsHide: true
351
+ }).trim();
352
+ if (gitExcludePath.length === 0)
353
+ return false;
354
+ const excludePath = resolve3(workspace, gitExcludePath);
355
+ mkdirSync(dirname(excludePath), { recursive: true });
314
356
  const existing = existsSync2(excludePath) ? readFileSync2(excludePath, "utf8") : "";
315
357
  if (existing.split(/\r?\n/).includes(".codegraph"))
316
358
  return true;
@@ -319,12 +361,38 @@ function ensureCodegraphGitignored(workspace) {
319
361
  `}.codegraph
320
362
  `);
321
363
  return true;
322
- } catch (error) {
323
- if (error instanceof Error)
324
- return false;
325
- throw error;
364
+ } catch {
365
+ return false;
326
366
  }
327
367
  }
368
+ function gitMarkerOwnsGitDir(gitMarkerPath, workspace, gitDir) {
369
+ const markerStat = lstatSync2(gitMarkerPath);
370
+ if (markerStat.isSymbolicLink())
371
+ return false;
372
+ const resolvedGitDir = realpathSync3.native(gitDir);
373
+ if (markerStat.isDirectory())
374
+ return realpathSync3.native(gitMarkerPath) === resolvedGitDir;
375
+ if (!markerStat.isFile())
376
+ return false;
377
+ const markerMatch = /^gitdir:\s*(.+)\s*$/i.exec(readFileSync2(gitMarkerPath, "utf8").trim());
378
+ if (markerMatch?.[1] === undefined)
379
+ return false;
380
+ if (realpathSync3.native(resolve3(workspace, markerMatch[1])) !== resolvedGitDir)
381
+ return false;
382
+ const backlinkPath = join5(resolvedGitDir, "gitdir");
383
+ if (existsSync2(backlinkPath)) {
384
+ const backlink = readFileSync2(backlinkPath, "utf8").trim();
385
+ return backlink.length > 0 && realpathSync3.native(resolve3(resolvedGitDir, backlink)) === realpathSync3.native(gitMarkerPath);
386
+ }
387
+ const coreWorktree = execFileSync("git", ["config", "--file", join5(resolvedGitDir, "config"), "--get", "core.worktree"], {
388
+ encoding: "utf8",
389
+ shell: false,
390
+ stdio: ["ignore", "pipe", "ignore"],
391
+ timeout: 5000,
392
+ windowsHide: true
393
+ }).trim();
394
+ return coreWorktree.length > 0 && realpathSync3.native(resolve3(resolvedGitDir, coreWorktree)) === realpathSync3.native(workspace);
395
+ }
328
396
 
329
397
  // ../../utils/src/codegraph/guidance.ts
330
398
  var CODEGRAPH_UNINITIALIZED_PATTERN = /CodeGraph not initialized in ([\s\S]*?)\.\s*Run ['`]codegraph init['`] in that project first\./i;
@@ -332,6 +400,9 @@ var ANSI_ESCAPE_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
332
400
  var CODEGRAPH_STATUS_PROJECT_PATTERN = /^.*?\bProject:\s*(.+?)\s*$/im;
333
401
  var CODEGRAPH_STATUS_UNINITIALIZED_PATTERN = /^.*?\bNot initialized\s*$/im;
334
402
  var CODEGRAPH_INIT_HINT_PATTERN = /Run\s+["'`]codegraph init["'`]\s+(?:in that project first|to initialize)\.?/i;
403
+ var CODEGRAPH_PROJECT_NOT_INDEXED_PATTERN = /The project at (.+?) isn't indexed with codegraph\b/i;
404
+ var CODEGRAPH_NO_PROJECT_LOADED_PATTERN = /No CodeGraph project is loaded for this session\./i;
405
+ var CODEGRAPH_NO_PROJECT_SEARCHED_FROM_PATTERN = /^Searched for a \.codegraph\/ directory starting from:\s*(.+?)\s*$/im;
335
406
  function getCodegraphUninitializedProject(input) {
336
407
  const output = textFromUnknown(input.toolOutput);
337
408
  if (!isCodegraphTool(input.toolName))
@@ -372,6 +443,16 @@ function extractProjectPath(output) {
372
443
  const uninitializedProject = uninitializedMatch?.[1]?.trim();
373
444
  if (uninitializedProject && uninitializedProject.length > 0)
374
445
  return uninitializedProject;
446
+ const notIndexedMatch = normalizedOutput.match(CODEGRAPH_PROJECT_NOT_INDEXED_PATTERN);
447
+ const notIndexedProject = notIndexedMatch?.[1]?.trim();
448
+ if (notIndexedProject && notIndexedProject.length > 0)
449
+ return notIndexedProject;
450
+ if (CODEGRAPH_NO_PROJECT_LOADED_PATTERN.test(normalizedOutput)) {
451
+ const searchedFromMatch = normalizedOutput.match(CODEGRAPH_NO_PROJECT_SEARCHED_FROM_PATTERN);
452
+ const searchedFromProject = searchedFromMatch?.[1]?.trim();
453
+ if (searchedFromProject && searchedFromProject.length > 0)
454
+ return searchedFromProject;
455
+ }
375
456
  if (!looksLikeCodegraphUninitializedOutput(normalizedOutput))
376
457
  return null;
377
458
  const statusMatch = normalizedOutput.match(CODEGRAPH_STATUS_PROJECT_PATTERN);
@@ -416,7 +497,7 @@ function isRecord(value) {
416
497
  import { existsSync as existsSync3 } from "node:fs";
417
498
  import { homedir as homedir6 } from "node:os";
418
499
  import { spawnSync } from "node:child_process";
419
- import { basename as basename2, dirname, join as join7 } from "node:path";
500
+ import { basename as basename2, dirname as dirname2, join as join7 } from "node:path";
420
501
  import { createRequire } from "node:module";
421
502
 
422
503
  // ../../utils/src/runtime/which.ts
@@ -603,7 +684,7 @@ function defaultProvisionedBin(homeDir, fileExists) {
603
684
  function resolveBundledShim(requireResolve, fileExists) {
604
685
  try {
605
686
  const packageJson = requireResolve(`${CODEGRAPH_PACKAGE}/package.json`);
606
- const packageRoot = dirname(packageJson);
687
+ const packageRoot = dirname2(packageJson);
607
688
  const candidates = [join7(packageRoot, "bin", "codegraph.js"), join7(packageRoot, "npm-shim.js")];
608
689
  return candidates.find((candidate) => fileExists(candidate)) ?? null;
609
690
  } catch (error) {
@@ -1530,8 +1611,9 @@ function parseJsoncSafe(content) {
1530
1611
  var HARNESS_IDS = ["codex", "opencode", "omo"];
1531
1612
  var SETTING_HARNESS_SUPPORT = {
1532
1613
  "codegraph.auto_provision": HARNESS_IDS,
1614
+ "codegraph.daemon": ["codex", "opencode"],
1533
1615
  "codegraph.enabled": HARNESS_IDS,
1534
- "codegraph.excluded_roots": ["codex"],
1616
+ "codegraph.excluded_roots": ["codex", "opencode"],
1535
1617
  "codegraph.install_dir": HARNESS_IDS,
1536
1618
  "codegraph.telemetry": HARNESS_IDS,
1537
1619
  "codegraph.watch_debounce_ms": ["opencode", "omo"]
@@ -1548,6 +1630,7 @@ var BUILT_IN_DEFAULTS = {
1548
1630
  var HARNESS_BLOCK_KEYS = HARNESS_IDS.map((harness) => `[${harness}]`);
1549
1631
  var CODEGRAPH_SETTING_KEYS = [
1550
1632
  "auto_provision",
1633
+ "daemon",
1551
1634
  "enabled",
1552
1635
  "excluded_roots",
1553
1636
  "install_dir",
@@ -1621,6 +1704,10 @@ function setCodegraphSetting(config, key, value) {
1621
1704
  if (typeof value === "boolean")
1622
1705
  config.auto_provision = value;
1623
1706
  return;
1707
+ case "daemon":
1708
+ if (typeof value === "boolean")
1709
+ config.daemon = value;
1710
+ return;
1624
1711
  case "enabled":
1625
1712
  if (typeof value === "boolean")
1626
1713
  config.enabled = value;
@@ -1819,14 +1906,14 @@ function buildEnvOverrides(harness, env, warnings, merge) {
1819
1906
 
1820
1907
  // ../../utils/src/omo-config/resolve.ts
1821
1908
  import { existsSync as existsSync4 } from "node:fs";
1822
- import { dirname as dirname2, isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve3 } from "node:path";
1909
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve4 } from "node:path";
1823
1910
  function containsPath(parent, child) {
1824
1911
  const pathToChild = relative(parent, child);
1825
1912
  return pathToChild === "" || !pathToChild.startsWith("..") && !isAbsolute2(pathToChild);
1826
1913
  }
1827
1914
  function findProjectConfigPathsNearestFirst(cwd, homeDir) {
1828
- const startDir = resolve3(cwd);
1829
- const stopBeforeDir = containsPath(resolve3(homeDir), startDir) ? resolve3(homeDir) : null;
1915
+ const startDir = resolve4(cwd);
1916
+ const stopBeforeDir = containsPath(resolve4(homeDir), startDir) ? resolve4(homeDir) : null;
1830
1917
  const paths = [];
1831
1918
  let currentDir = startDir;
1832
1919
  while (true) {
@@ -1836,7 +1923,7 @@ function findProjectConfigPathsNearestFirst(cwd, homeDir) {
1836
1923
  if (existsSync4(configPath)) {
1837
1924
  paths.push(configPath);
1838
1925
  }
1839
- const parentDir = dirname2(currentDir);
1926
+ const parentDir = dirname3(currentDir);
1840
1927
  if (parentDir === currentDir)
1841
1928
  break;
1842
1929
  currentDir = parentDir;
@@ -1844,7 +1931,7 @@ function findProjectConfigPathsNearestFirst(cwd, homeDir) {
1844
1931
  return paths;
1845
1932
  }
1846
1933
  function resolveOmoConfigPaths(options) {
1847
- const globalPath = join8(resolve3(options.homeDir), ".omo", "config.jsonc");
1934
+ const globalPath = join8(resolve4(options.homeDir), ".omo", "config.jsonc");
1848
1935
  const projectPathsFarthestFirst = findProjectConfigPathsNearestFirst(options.cwd, options.homeDir).reverse();
1849
1936
  return [
1850
1937
  { path: globalPath, scope: "global" },
@@ -1940,77 +2027,131 @@ function isNonFatalCodegraphGcError(error) {
1940
2027
 
1941
2028
  // components/codegraph/src/hook-sweep.ts
1942
2029
  import { fileURLToPath } from "node:url";
1943
- // ../../utils/src/codegraph/process-sweeper.ts
2030
+ // ../../utils/src/process-sweep/sweeper.ts
1944
2031
  import { existsSync as existsSync7, mkdirSync as mkdirSync2, statSync as statSync2, utimesSync, writeFileSync as writeFileSync2 } from "node:fs";
1945
2032
  import { homedir as homedir10 } from "node:os";
1946
- import { dirname as dirname3, join as join10 } from "node:path";
2033
+ import { dirname as dirname5, join as join11 } from "node:path";
1947
2034
 
1948
- // ../../utils/src/codegraph/process-exec.ts
1949
- import { execFile } from "node:child_process";
1950
-
1951
- // ../../utils/src/codegraph/process-match.ts
1952
- import { posix, win32 } from "node:path";
1953
- var SERVE_WRAPPER_SUFFIX = "/components/codegraph/dist/serve.js";
1954
- var UPSTREAM_PACKAGE_SEGMENT = "/@colbymchenry/codegraph/";
1955
- function parsePosixProcessTable(output) {
1956
- const processes = [];
1957
- for (const line of output.split(/\r?\n/)) {
1958
- const match = /^\s*(\d+)\s+(\d+)\s+(.+?)\s*$/.exec(line);
1959
- if (match === null)
1960
- continue;
1961
- const pid = Number(match[1]);
1962
- const ppid = Number(match[2]);
1963
- const command = match[3];
1964
- if (!isValidProcessId(pid) || !Number.isInteger(ppid) || ppid < 0 || command === undefined)
1965
- continue;
1966
- processes.push({ command, pid, ppid });
2035
+ // ../../utils/src/codegraph/daemon-lock.ts
2036
+ import { readFileSync as readFileSync4, realpathSync as realpathSync4 } from "node:fs";
2037
+ import { dirname as dirname4, join as join9, resolve as resolve5 } from "node:path";
2038
+ function parseDaemonLock(raw) {
2039
+ const trimmed = raw.trim();
2040
+ if (trimmed.length === 0)
2041
+ return null;
2042
+ try {
2043
+ const parsed = JSON.parse(trimmed);
2044
+ if (typeof parsed === "object" && parsed !== null && "pid" in parsed) {
2045
+ const pid = parsed.pid;
2046
+ if (typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0) {
2047
+ const record = parsed;
2048
+ return {
2049
+ pid,
2050
+ ...typeof record["socketPath"] === "string" ? { socketPath: record["socketPath"] } : {},
2051
+ ...typeof record["startedAt"] === "number" ? { startedAt: record["startedAt"] } : {},
2052
+ ...typeof record["version"] === "string" ? { version: record["version"] } : {}
2053
+ };
2054
+ }
2055
+ }
2056
+ return null;
2057
+ } catch (error) {
2058
+ if (!(error instanceof SyntaxError))
2059
+ throw error;
1967
2060
  }
1968
- return processes;
2061
+ const legacyPid = Number(trimmed);
2062
+ if (Number.isSafeInteger(legacyPid) && legacyPid > 0)
2063
+ return { pid: legacyPid };
2064
+ return null;
1969
2065
  }
1970
- function parseWindowsProcessTable(output) {
1971
- const parsed = parseJson(output);
1972
- const entries = Array.isArray(parsed) ? parsed : parsed === undefined ? [] : [parsed];
1973
- const processes = [];
1974
- for (const entry of entries) {
1975
- if (!isRecord3(entry))
1976
- continue;
1977
- const pid = numberField(entry, "ProcessId");
1978
- const ppid = numberField(entry, "ParentProcessId");
1979
- const command = stringField(entry, "CommandLine");
1980
- if (pid === undefined || ppid === undefined || command === undefined || command.trim().length === 0)
2066
+ function daemonLockCandidates(projectRoot) {
2067
+ const dirs = new Set;
2068
+ const resolved = resolve5(projectRoot);
2069
+ collectAncestors(resolved, dirs);
2070
+ collectAncestors(realpathIfPossible2(resolved), dirs);
2071
+ return [...dirs].map((dir) => join9(dir, ".codegraph", "daemon.pid"));
2072
+ }
2073
+ function evaluateDaemonStaleness(pid, projectRoot) {
2074
+ let sawLock = false;
2075
+ for (const lockPath of daemonLockCandidates(projectRoot)) {
2076
+ const raw = readLockIfPresent(lockPath);
2077
+ if (raw === undefined)
1981
2078
  continue;
1982
- processes.push({ command, pid, ppid });
2079
+ if (raw === null)
2080
+ return { stale: false, reason: "lock-unreadable" };
2081
+ sawLock = true;
2082
+ const lock = parseDaemonLock(raw);
2083
+ if (lock === null)
2084
+ return { stale: false, reason: "lock-unparseable" };
2085
+ if (lock.pid === pid)
2086
+ return { stale: false, reason: "lock-pid-match" };
2087
+ }
2088
+ return sawLock ? { stale: true, reason: "lock-pid-mismatch" } : { stale: true, reason: "lock-absent" };
2089
+ }
2090
+ function readLockIfPresent(lockPath) {
2091
+ try {
2092
+ return readFileSync4(lockPath, "utf8");
2093
+ } catch (error) {
2094
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
2095
+ return;
2096
+ if (error instanceof Error && "code" in error && error.code === "ENOTDIR")
2097
+ return;
2098
+ return null;
1983
2099
  }
1984
- return processes;
1985
2100
  }
1986
- function selectZombieCodegraphProcesses(processes, options) {
1987
- const platform = options.platform ?? process.platform;
1988
- const livePids = new Set(processes.map((processInfo) => processInfo.pid));
1989
- const roots = normalizeRoots(options.ownedRoots, platform);
1990
- const zombies = [];
1991
- for (const processInfo of processes) {
1992
- const match = matchOwnedCodegraphCommand(processInfo.command, roots, platform);
1993
- if (match === null)
1994
- continue;
1995
- if (!isOrphaned(processInfo, livePids))
1996
- continue;
1997
- zombies.push({ ...processInfo, matchedRoot: match.root, matchKind: match.kind });
2101
+ function collectAncestors(start, output) {
2102
+ let current = start;
2103
+ for (;; ) {
2104
+ output.add(current);
2105
+ const parent = dirname4(current);
2106
+ if (parent === current)
2107
+ return;
2108
+ current = parent;
1998
2109
  }
1999
- return zombies;
2000
2110
  }
2001
- function matchOwnedCodegraphCommand(command, roots, platform) {
2002
- const normalizedCommand = normalizeForComparison2(command, platform);
2003
- for (const root of roots) {
2004
- if (root.length === 0)
2111
+ function realpathIfPossible2(path) {
2112
+ try {
2113
+ return realpathSync4(path);
2114
+ } catch (error) {
2115
+ if (error instanceof Error)
2116
+ return path;
2117
+ throw error;
2118
+ }
2119
+ }
2120
+
2121
+ // ../../utils/src/process-sweep/command-match.ts
2122
+ import { posix, win32 } from "node:path";
2123
+ function splitCommandTokens(command) {
2124
+ const tokens = [];
2125
+ let current = "";
2126
+ let quote = null;
2127
+ let tokenStarted = false;
2128
+ for (const char of command) {
2129
+ if (quote !== null) {
2130
+ if (char === quote) {
2131
+ quote = null;
2132
+ } else {
2133
+ current += char;
2134
+ }
2135
+ continue;
2136
+ }
2137
+ if (char === '"' || char === "'") {
2138
+ quote = char;
2139
+ tokenStarted = true;
2140
+ continue;
2141
+ }
2142
+ if (/\s/.test(char)) {
2143
+ if (tokenStarted || current.length > 0) {
2144
+ tokens.push(current);
2145
+ current = "";
2146
+ tokenStarted = false;
2147
+ }
2005
2148
  continue;
2006
- const serveWrapper = `${root}${SERVE_WRAPPER_SUFFIX}`;
2007
- if (hasExecutableToken(normalizedCommand, serveWrapper))
2008
- return { kind: "serve-wrapper", root };
2009
- if (upstreamPackagePathIsUnderRoot(normalizedCommand, root)) {
2010
- return { kind: "upstream-codegraph", root };
2011
2149
  }
2150
+ current += char;
2012
2151
  }
2013
- return null;
2152
+ if (tokenStarted || current.length > 0)
2153
+ tokens.push(current);
2154
+ return tokens;
2014
2155
  }
2015
2156
  function hasExecutableToken(command, expectedPath) {
2016
2157
  let searchFrom = 0;
@@ -2026,24 +2167,19 @@ function hasExecutableToken(command, expectedPath) {
2026
2167
  }
2027
2168
  }
2028
2169
  function tokenLooksExecutable(command, tokenStart) {
2029
- const prefix = command.slice(0, tokenStart).trimEnd();
2170
+ let prefix = command.slice(0, tokenStart).trimEnd();
2030
2171
  if (prefix.length === 0)
2031
2172
  return true;
2032
- const previousTokenStart = findTokenStart(prefix, prefix.length - 1);
2033
- const previousToken = prefix.slice(previousTokenStart);
2034
- const executableName = previousToken.split("/").at(-1) ?? previousToken;
2035
- return /^node\d*(\.exe)?$/.test(executableName) || /^bun(\.exe)?$/.test(executableName);
2036
- }
2037
- function upstreamPackagePathIsUnderRoot(command, root) {
2038
- let searchFrom = 0;
2039
2173
  for (;; ) {
2040
- const packageIndex = command.indexOf(UPSTREAM_PACKAGE_SEGMENT, searchFrom);
2041
- if (packageIndex < 0)
2174
+ const previousTokenStart = findTokenStart(prefix, prefix.length - 1);
2175
+ const previousToken = prefix.slice(previousTokenStart);
2176
+ if (!previousToken.startsWith("-")) {
2177
+ const executableName = previousToken.split("/").at(-1) ?? previousToken;
2178
+ return /^node\d*(\.exe)?$/i.test(executableName) || /^bun(\.exe)?$/i.test(executableName);
2179
+ }
2180
+ prefix = prefix.slice(0, previousTokenStart).trimEnd();
2181
+ if (prefix.length === 0)
2042
2182
  return false;
2043
- const tokenStart = findTokenStart(command, packageIndex);
2044
- if (command.slice(tokenStart).startsWith(`${root}/`) && tokenLooksExecutable(command, tokenStart))
2045
- return true;
2046
- searchFrom = packageIndex + UPSTREAM_PACKAGE_SEGMENT.length;
2047
2183
  }
2048
2184
  }
2049
2185
  function findTokenStart(command, index) {
@@ -2077,6 +2213,39 @@ function normalizeForComparison2(value, platform) {
2077
2213
  const normalized = value.replaceAll("\\", "/").replace(/\/+$/, "");
2078
2214
  return platform === "win32" ? normalized.toLowerCase() : normalized;
2079
2215
  }
2216
+
2217
+ // ../../utils/src/process-sweep/process-table.ts
2218
+ function parsePosixProcessTable(output) {
2219
+ const processes = [];
2220
+ for (const line of output.split(/\r?\n/)) {
2221
+ const match = /^\s*(\d+)\s+(\d+)\s+(.+?)\s*$/.exec(line);
2222
+ if (match === null)
2223
+ continue;
2224
+ const pid = Number(match[1]);
2225
+ const ppid = Number(match[2]);
2226
+ const command = match[3];
2227
+ if (!isValidProcessId(pid) || !Number.isInteger(ppid) || ppid < 0 || command === undefined)
2228
+ continue;
2229
+ processes.push({ command, pid, ppid });
2230
+ }
2231
+ return processes;
2232
+ }
2233
+ function parseWindowsProcessTable(output) {
2234
+ const parsed = parseJson(output);
2235
+ const entries = Array.isArray(parsed) ? parsed : parsed === undefined ? [] : [parsed];
2236
+ const processes = [];
2237
+ for (const entry of entries) {
2238
+ if (!isRecord3(entry))
2239
+ continue;
2240
+ const pid = numberField(entry, "ProcessId");
2241
+ const ppid = numberField(entry, "ParentProcessId");
2242
+ const command = stringField(entry, "CommandLine");
2243
+ if (pid === undefined || ppid === undefined || command === undefined || command.trim().length === 0)
2244
+ continue;
2245
+ processes.push({ command, pid, ppid });
2246
+ }
2247
+ return processes;
2248
+ }
2080
2249
  function isOrphaned(processInfo, livePids) {
2081
2250
  return processInfo.ppid === 1 || !livePids.has(processInfo.ppid);
2082
2251
  }
@@ -2104,13 +2273,113 @@ function isRecord3(value) {
2104
2273
  return typeof value === "object" && value !== null;
2105
2274
  }
2106
2275
 
2107
- // ../../utils/src/codegraph/process-exec.ts
2108
- function enumerateCodegraphProcesses(platform = process.platform) {
2276
+ // ../../utils/src/process-sweep/codegraph-family.ts
2277
+ var SERVE_WRAPPER_SUFFIX = "/components/codegraph/dist/serve.js";
2278
+ var UPSTREAM_PACKAGE_SEGMENT = "/@colbymchenry/codegraph/";
2279
+ function selectZombieCodegraphProcesses(processes, options) {
2280
+ const platform = options.platform ?? process.platform;
2281
+ const livePids = new Set(processes.map((processInfo) => processInfo.pid));
2282
+ const roots = normalizeRoots(options.ownedRoots, platform);
2283
+ const zombies = [];
2284
+ for (const processInfo of processes) {
2285
+ const daemon = matchDaemonCommand(processInfo.command, roots, platform);
2286
+ if (daemon !== null) {
2287
+ if (!isOrphaned(processInfo, livePids))
2288
+ continue;
2289
+ zombies.push({
2290
+ ...processInfo,
2291
+ daemonProjectRoot: daemon.projectRoot,
2292
+ matchedRoot: daemon.root,
2293
+ matchKind: "upstream-daemon"
2294
+ });
2295
+ continue;
2296
+ }
2297
+ const match = matchOwnedCodegraphCommand(processInfo.command, roots, platform);
2298
+ if (match === null)
2299
+ continue;
2300
+ if (!isOrphaned(processInfo, livePids))
2301
+ continue;
2302
+ zombies.push({ ...processInfo, matchedRoot: match.root, matchKind: match.kind });
2303
+ }
2304
+ return zombies;
2305
+ }
2306
+ var STANDALONE_LAUNCHER_SUFFIXES = ["/bin/codegraph", "/bin/codegraph.exe"];
2307
+ var BUNDLE_SCRIPT_SUFFIX = "/lib/dist/bin/codegraph.js";
2308
+ function matchDaemonCommand(command, roots, platform) {
2309
+ const projectRoot = extractDaemonProjectRoot(splitCommandTokens(command));
2310
+ if (projectRoot === null)
2311
+ return null;
2312
+ const normalizedCommand = normalizeForComparison2(command, platform);
2313
+ for (const root of roots) {
2314
+ if (root.length === 0)
2315
+ continue;
2316
+ if (upstreamPackagePathIsUnderRoot(normalizedCommand, root))
2317
+ return { projectRoot, root };
2318
+ for (const suffix of STANDALONE_LAUNCHER_SUFFIXES) {
2319
+ if (hasExecutableToken(normalizedCommand, `${root}${suffix}`))
2320
+ return { projectRoot, root };
2321
+ }
2322
+ if (hasExecutableToken(normalizedCommand, `${root}${BUNDLE_SCRIPT_SUFFIX}`))
2323
+ return { projectRoot, root };
2324
+ }
2325
+ return null;
2326
+ }
2327
+ function extractDaemonProjectRoot(tokens) {
2328
+ if (!tokens.includes("serve") || !tokens.includes("--mcp"))
2329
+ return null;
2330
+ const pathIndex = tokens.indexOf("--path");
2331
+ if (pathIndex < 0)
2332
+ return null;
2333
+ const value = tokens[pathIndex + 1];
2334
+ if (value === undefined || value.length === 0 || value.startsWith("--"))
2335
+ return null;
2336
+ return value;
2337
+ }
2338
+ function matchOwnedCodegraphCommand(command, roots, platform) {
2339
+ const normalizedCommand = normalizeForComparison2(command, platform);
2340
+ for (const root of roots) {
2341
+ if (root.length === 0)
2342
+ continue;
2343
+ const serveWrapper = `${root}${SERVE_WRAPPER_SUFFIX}`;
2344
+ if (hasExecutableToken(normalizedCommand, serveWrapper))
2345
+ return { kind: "serve-wrapper", root };
2346
+ if (upstreamPackagePathIsUnderRoot(normalizedCommand, root)) {
2347
+ return { kind: "upstream-codegraph", root };
2348
+ }
2349
+ }
2350
+ return null;
2351
+ }
2352
+ function upstreamPackagePathIsUnderRoot(command, root) {
2353
+ let searchFrom = 0;
2354
+ for (;; ) {
2355
+ const packageIndex = command.indexOf(UPSTREAM_PACKAGE_SEGMENT, searchFrom);
2356
+ if (packageIndex < 0)
2357
+ return false;
2358
+ const tokenStart = findTokenStart(command, packageIndex);
2359
+ if (command.slice(tokenStart).startsWith(`${root}/`) && tokenLooksExecutable(command, tokenStart))
2360
+ return true;
2361
+ searchFrom = packageIndex + UPSTREAM_PACKAGE_SEGMENT.length;
2362
+ }
2363
+ }
2364
+
2365
+ // ../../utils/src/process-sweep/exec.ts
2366
+ import { execFile } from "node:child_process";
2367
+ function enumerateProcesses(platform = process.platform) {
2109
2368
  return platform === "win32" ? enumerateWindowsProcesses() : enumeratePosixProcesses();
2110
2369
  }
2111
- function createDefaultCodegraphProcessKiller(platform = process.platform) {
2370
+ function createDefaultProcessKiller(platform = process.platform) {
2112
2371
  return platform === "win32" ? createWindowsKiller() : createPosixKiller();
2113
2372
  }
2373
+ function defaultIsProcessAlive(pid) {
2374
+ try {
2375
+ process.kill(pid, 0);
2376
+ return true;
2377
+ } catch (error) {
2378
+ if (!(error instanceof Error))
2379
+ throw error;
2380
+ return processKillErrorMeansAlive(error);
2381
+ }
2382
+ }
2114
2383
  function enumeratePosixProcesses() {
2115
2384
  return execFileText("ps", ["-eo", "pid=,ppid=,command="]).then(parsePosixProcessTable);
2116
2385
  }
@@ -2124,16 +2393,7 @@ function enumerateWindowsProcesses() {
2124
2393
  }
2125
2394
  function createPosixKiller() {
2126
2395
  return {
2127
- isAlive: (pid) => {
2128
- try {
2129
- process.kill(pid, 0);
2130
- return true;
2131
- } catch (error) {
2132
- if (!(error instanceof Error))
2133
- throw error;
2134
- return processKillErrorMeansAlive(error);
2135
- }
2136
- },
2396
+ isAlive: defaultIsProcessAlive,
2137
2397
  kill: (pid) => {
2138
2398
  process.kill(pid, "SIGKILL");
2139
2399
  return Promise.resolve();
@@ -2146,16 +2406,7 @@ function createPosixKiller() {
2146
2406
  }
2147
2407
  function createWindowsKiller() {
2148
2408
  return {
2149
- isAlive: (pid) => {
2150
- try {
2151
- process.kill(pid, 0);
2152
- return true;
2153
- } catch (error) {
2154
- if (!(error instanceof Error))
2155
- throw error;
2156
- return processKillErrorMeansAlive(error);
2157
- }
2158
- },
2409
+ isAlive: defaultIsProcessAlive,
2159
2410
  kill: (pid) => execFileVoid("taskkill.exe", ["/PID", String(pid), "/T", "/F"]),
2160
2411
  terminate: (pid) => execFileVoid("taskkill.exe", ["/PID", String(pid), "/T"])
2161
2412
  };
@@ -2185,10 +2436,10 @@ function processKillErrorMeansAlive(error) {
2185
2436
  return false;
2186
2437
  }
2187
2438
 
2188
- // ../../utils/src/codegraph/process-roots.ts
2189
- import { existsSync as existsSync6, readdirSync as readdirSync2, realpathSync as realpathSync4 } from "node:fs";
2439
+ // ../../utils/src/process-sweep/roots.ts
2440
+ import { existsSync as existsSync6, readdirSync as readdirSync2, realpathSync as realpathSync5 } from "node:fs";
2190
2441
  import { homedir as homedir9 } from "node:os";
2191
- import { join as join9, resolve as resolve4 } from "node:path";
2442
+ import { join as join10, resolve as resolve6 } from "node:path";
2192
2443
  function discoverCodegraphOwnedRoots(options = {}) {
2193
2444
  const env = options.env ?? process.env;
2194
2445
  const homeDir = options.homeDir ?? env["HOME"] ?? env["USERPROFILE"] ?? homedir9();
@@ -2198,33 +2449,33 @@ function discoverCodegraphOwnedRoots(options = {}) {
2198
2449
  addRoot(roots, options.pluginRoot);
2199
2450
  for (const root of options.extraRoots ?? [])
2200
2451
  addRoot(roots, root);
2201
- for (const root of readCodexPluginCacheRoots(options.codexHome ?? env["CODEX_HOME"] ?? join9(homeDir, ".codex"))) {
2452
+ for (const root of readCodexPluginCacheRoots(options.codexHome ?? env["CODEX_HOME"] ?? join10(homeDir, ".codex"))) {
2202
2453
  addRoot(roots, root);
2203
2454
  }
2204
2455
  return [...roots];
2205
2456
  }
2206
2457
  function readCodexPluginCacheRoots(codexHome) {
2207
- const cacheRoot = join9(codexHome, "plugins", "cache");
2458
+ const cacheRoot = join10(codexHome, "plugins", "cache");
2208
2459
  if (!existsSync6(cacheRoot))
2209
2460
  return [];
2210
2461
  const roots = [];
2211
2462
  for (const publisher of safeReadDir(cacheRoot)) {
2212
2463
  if (!OMO_CODEX_PLUGIN_CACHE_PUBLISHERS.has(publisher))
2213
2464
  continue;
2214
- const omoRoot = join9(cacheRoot, publisher, "omo");
2465
+ const omoRoot = join10(cacheRoot, publisher, "omo");
2215
2466
  if (!existsSync6(omoRoot))
2216
2467
  continue;
2217
2468
  for (const version of safeReadDir(omoRoot))
2218
- roots.push(join9(omoRoot, version));
2469
+ roots.push(join10(omoRoot, version));
2219
2470
  }
2220
2471
  return roots;
2221
2472
  }
2222
2473
  function addRoot(roots, root) {
2223
2474
  if (root === undefined || root.trim().length === 0)
2224
2475
  return;
2225
- const resolved = resolve4(root);
2476
+ const resolved = resolve6(root);
2226
2477
  roots.add(resolved);
2227
- roots.add(realpathIfPossible2(resolved));
2478
+ roots.add(realpathIfPossible3(resolved));
2228
2479
  }
2229
2480
  function safeReadDir(path) {
2230
2481
  try {
@@ -2235,12 +2486,12 @@ function safeReadDir(path) {
2235
2486
  throw error;
2236
2487
  }
2237
2488
  }
2238
- function realpathIfPossible2(path) {
2489
+ function realpathIfPossible3(path) {
2239
2490
  try {
2240
- return realpathSync4(path);
2491
+ return realpathSync5(path);
2241
2492
  } catch (error) {
2242
2493
  if (error instanceof Error)
2243
- return resolve4(path);
2494
+ return resolve6(path);
2244
2495
  throw error;
2245
2496
  }
2246
2497
  }
@@ -2252,70 +2503,109 @@ function isNonFatalFsError(error) {
2252
2503
  }
2253
2504
  var OMO_CODEX_PLUGIN_CACHE_PUBLISHERS = new Set(["sisyphuslabs"]);
2254
2505
 
2255
- // ../../utils/src/codegraph/process-sweeper.ts
2506
+ // ../../utils/src/process-sweep/sweeper.ts
2256
2507
  var DEFAULT_GRACE_MS = 2000;
2257
2508
  var DEFAULT_THROTTLE_MS = 60 * 60 * 1000;
2258
- var SWEEP_STAMP_FILE = "zombie-sweep.stamp";
2259
- async function sweepCodegraphZombies(options = {}) {
2260
- const homeDir = options.homeDir ?? options.env?.["HOME"] ?? options.env?.["USERPROFILE"] ?? homedir10();
2261
- const stampFile = join10(codegraphDataRoot(homeDir), SWEEP_STAMP_FILE);
2509
+ async function runProcessFamilySweep(config, options) {
2262
2510
  const nowMs = options.nowMs ?? Date.now();
2263
2511
  const dryRun = options.dryRun === true;
2264
- const ownedRoots = options.ownedRoots ?? discoverCodegraphOwnedRoots(options);
2265
- if (options.force !== true && isSweepThrottled(stampFile, nowMs, options.throttleMs ?? DEFAULT_THROTTLE_MS)) {
2266
- return emptyResult("throttled", dryRun, ownedRoots, stampFile);
2512
+ if (options.force !== true && isSweepThrottled(config.stampFile, nowMs, options.throttleMs ?? DEFAULT_THROTTLE_MS)) {
2513
+ return { action: "throttled", candidates: [], dryRun, failed: [], killed: [], spared: [], stampFile: config.stampFile };
2267
2514
  }
2268
2515
  try {
2269
- const provider = options.processProvider ?? (() => enumerateCodegraphProcesses(options.platform));
2270
- const candidates = selectZombieCodegraphProcesses(await provider(), {
2271
- ownedRoots,
2272
- ...options.platform === undefined ? {} : { platform: options.platform }
2273
- });
2274
- const result = dryRun ? { failed: [], killed: [] } : await killCandidates(candidates, options.killer ?? createDefaultCodegraphProcessKiller(options.platform), options);
2516
+ const plan = await config.collect();
2517
+ const { failed, killed } = dryRun ? { failed: [], killed: [] } : await killTargets(plan.killList, options.killer ?? createDefaultProcessKiller(options.platform), options, config.familyLabel);
2275
2518
  if (!dryRun)
2276
- writeSweepStamp(stampFile, nowMs);
2277
- return { action: "swept", candidates, dryRun, failed: result.failed, killed: result.killed, ownedRoots, stampFile };
2519
+ writeSweepStamp(config.stampFile, nowMs);
2520
+ return {
2521
+ action: "swept",
2522
+ candidates: plan.candidates,
2523
+ dryRun,
2524
+ failed,
2525
+ killed,
2526
+ spared: plan.spared,
2527
+ stampFile: config.stampFile
2528
+ };
2278
2529
  } catch (error) {
2279
- options.log?.(`CodeGraph zombie sweep skipped: ${error instanceof Error ? error.message : String(error)}`);
2280
- return emptyResult("failed", dryRun, ownedRoots, stampFile);
2530
+ options.log?.(`${config.familyLabel} skipped: ${error instanceof Error ? error.message : String(error)}`);
2531
+ return { action: "failed", candidates: [], dryRun, failed: [], killed: [], spared: [], stampFile: config.stampFile };
2281
2532
  }
2282
2533
  }
2283
- async function killCandidates(candidates, killer, options) {
2534
+ var CODEGRAPH_SWEEP_STAMP_FILE = "zombie-sweep.stamp";
2535
+ async function sweepCodegraphZombies(options = {}) {
2536
+ const homeDir = options.homeDir ?? options.env?.["HOME"] ?? options.env?.["USERPROFILE"] ?? homedir10();
2537
+ const stampFile = join11(codegraphDataRoot(homeDir), CODEGRAPH_SWEEP_STAMP_FILE);
2538
+ const ownedRoots = options.ownedRoots ?? discoverCodegraphOwnedRoots(options);
2539
+ const result = await runProcessFamilySweep({
2540
+ familyLabel: "CodeGraph zombie sweep",
2541
+ stampFile,
2542
+ collect: async () => {
2543
+ const provider = options.processProvider ?? (() => enumerateProcesses(options.platform));
2544
+ const candidates = selectZombieCodegraphProcesses(await provider(), {
2545
+ ownedRoots,
2546
+ ...options.platform === undefined ? {} : { platform: options.platform }
2547
+ });
2548
+ const { killList, spared } = partitionByDaemonStaleness(candidates, options.log);
2549
+ return { candidates, killList, spared };
2550
+ }
2551
+ }, options);
2552
+ return { ...result, ownedRoots };
2553
+ }
2554
+ function partitionByDaemonStaleness(candidates, log) {
2555
+ const killList = [];
2556
+ const spared = [];
2557
+ for (const candidate of candidates) {
2558
+ if (candidate.matchKind !== "upstream-daemon") {
2559
+ killList.push(candidate);
2560
+ continue;
2561
+ }
2562
+ const staleness = evaluateDaemonStaleness(candidate.pid, candidate.daemonProjectRoot ?? candidate.matchedRoot);
2563
+ if (staleness.stale) {
2564
+ log?.(`CodeGraph zombie sweep sweeping stale daemon pid ${candidate.pid} (${staleness.reason})`);
2565
+ killList.push(candidate);
2566
+ continue;
2567
+ }
2568
+ log?.(`CodeGraph zombie sweep spared live daemon pid ${candidate.pid} (${staleness.reason})`);
2569
+ spared.push(candidate);
2570
+ }
2571
+ return { killList, spared };
2572
+ }
2573
+ async function killTargets(targets, killer, options, familyLabel) {
2284
2574
  const failed = [];
2285
2575
  const killed = [];
2286
- for (const candidate of candidates) {
2287
- const terminated = await safelyTerminate(candidate.pid, killer, failed, options.log);
2576
+ for (const target of targets) {
2577
+ const terminated = await safelyTerminate(target.pid, killer, failed, options.log, familyLabel);
2288
2578
  if (!terminated)
2289
2579
  continue;
2290
2580
  await delay(options.graceMs ?? DEFAULT_GRACE_MS);
2291
- if (!await killer.isAlive(candidate.pid)) {
2292
- killed.push(candidate);
2581
+ if (!await killer.isAlive(target.pid)) {
2582
+ killed.push(target);
2293
2583
  continue;
2294
2584
  }
2295
- if (await safelyKill(candidate.pid, killer, failed, options.log))
2296
- killed.push(candidate);
2585
+ if (await safelyKill(target.pid, killer, failed, options.log, familyLabel))
2586
+ killed.push(target);
2297
2587
  }
2298
2588
  return { failed, killed };
2299
2589
  }
2300
- async function safelyTerminate(pid, killer, failed, log) {
2590
+ async function safelyTerminate(pid, killer, failed, log, familyLabel) {
2301
2591
  try {
2302
2592
  await killer.terminate(pid);
2303
2593
  return true;
2304
2594
  } catch (error) {
2305
2595
  const message = error instanceof Error ? error.message : String(error);
2306
2596
  failed.push({ error: message, pid, stage: "terminate" });
2307
- log?.(`CodeGraph zombie sweep failed to terminate pid ${pid}: ${message}`);
2597
+ log?.(`${familyLabel} failed to terminate pid ${pid}: ${message}`);
2308
2598
  return false;
2309
2599
  }
2310
2600
  }
2311
- async function safelyKill(pid, killer, failed, log) {
2601
+ async function safelyKill(pid, killer, failed, log, familyLabel) {
2312
2602
  try {
2313
2603
  await killer.kill(pid);
2314
2604
  return true;
2315
2605
  } catch (error) {
2316
2606
  const message = error instanceof Error ? error.message : String(error);
2317
2607
  failed.push({ error: message, pid, stage: "kill" });
2318
- log?.(`CodeGraph zombie sweep failed to kill pid ${pid}: ${message}`);
2608
+ log?.(`${familyLabel} failed to kill pid ${pid}: ${message}`);
2319
2609
  return false;
2320
2610
  }
2321
2611
  }
@@ -2325,15 +2615,12 @@ function isSweepThrottled(stampFile, nowMs, throttleMs) {
2325
2615
  return nowMs - statSync2(stampFile).mtimeMs < throttleMs;
2326
2616
  }
2327
2617
  function writeSweepStamp(stampFile, nowMs) {
2328
- mkdirSync2(dirname3(stampFile), { recursive: true });
2618
+ mkdirSync2(dirname5(stampFile), { recursive: true });
2329
2619
  writeFileSync2(stampFile, `${new Date(nowMs).toISOString()}
2330
2620
  `);
2331
2621
  const stampDate = new Date(nowMs);
2332
2622
  utimesSync(stampFile, stampDate, stampDate);
2333
2623
  }
2334
- function emptyResult(action, dryRun, ownedRoots, stampFile) {
2335
- return { action, candidates: [], dryRun, failed: [], killed: [], ownedRoots, stampFile };
2336
- }
2337
2624
  function delay(ms) {
2338
2625
  if (ms <= 0)
2339
2626
  return Promise.resolve();
@@ -2361,56 +2648,55 @@ function defaultPluginRoot() {
2361
2648
  import { execFile as execFile3 } from "node:child_process";
2362
2649
  import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync3 } from "node:fs";
2363
2650
  import { homedir as homedir12 } from "node:os";
2364
- import { extname, join as join12 } from "node:path";
2651
+ import { extname, join as join13 } from "node:path";
2365
2652
  import { cwd as processCwd, env as processEnv, execPath as processExecPath, stderr as processStderr } from "node:process";
2366
2653
 
2367
- // ../../utils/src/codegraph/provision.ts
2368
- import { createHash as createHash2, randomUUID } from "node:crypto";
2369
- import { execFile as execFile2 } from "node:child_process";
2370
- import { chmod, mkdir, readdir, readFile, rename, rm, rmdir, stat, writeFile } from "node:fs/promises";
2371
- import { existsSync as existsSync8 } from "node:fs";
2372
- import { homedir as homedir11, hostname } from "node:os";
2373
- import { basename as basename3, join as join11 } from "node:path";
2374
- import { promisify } from "node:util";
2375
-
2376
2654
  // ../../utils/src/codegraph/manifest.ts
2655
+ var CODEGRAPH_PINNED_VERSION = "1.4.1";
2377
2656
  var CODEGRAPH_PROVISION_MANIFEST = {
2378
2657
  assets: {
2379
2658
  "darwin-arm64": {
2380
2659
  executableName: "codegraph",
2381
- sha256: "95bb27bf6382b69659e158e0c04d71cc394778951e1317d582be7807e7866908",
2382
- url: "https://github.com/colbymchenry/codegraph/releases/download/v1.0.1/codegraph-darwin-arm64.tar.gz"
2660
+ sha256: "4a679ae5a5cb9fff900dd59bb786da6a581b7f68f4cf713bdedd137e347d34dc",
2661
+ url: "https://github.com/colbymchenry/codegraph/releases/download/v1.4.1/codegraph-darwin-arm64.tar.gz"
2383
2662
  },
2384
2663
  "darwin-x64": {
2385
2664
  executableName: "codegraph",
2386
- sha256: "3311cc1d1f0f0ad742709b6a43d8a9187b1ef0af0dd30e0b58008dc673e29478",
2387
- url: "https://github.com/colbymchenry/codegraph/releases/download/v1.0.1/codegraph-darwin-x64.tar.gz"
2665
+ sha256: "436f96943cfd926ea6d0a8454f18833d21254d5fd9b3d224317b1426132def95",
2666
+ url: "https://github.com/colbymchenry/codegraph/releases/download/v1.4.1/codegraph-darwin-x64.tar.gz"
2388
2667
  },
2389
2668
  "linux-arm64": {
2390
2669
  executableName: "codegraph",
2391
- sha256: "e16f612bc96c2ebccd04574cbed500c9939147c80666ad6bb024398dff7992ae",
2392
- url: "https://github.com/colbymchenry/codegraph/releases/download/v1.0.1/codegraph-linux-arm64.tar.gz"
2670
+ sha256: "0d62c5eb2722f8d19d20f7a1bd974445e18d5294cb59be116a0c3d55ce87591f",
2671
+ url: "https://github.com/colbymchenry/codegraph/releases/download/v1.4.1/codegraph-linux-arm64.tar.gz"
2393
2672
  },
2394
2673
  "linux-x64": {
2395
2674
  executableName: "codegraph",
2396
- sha256: "d45a068f44596a85c7ba7d0ef924eaf7103fbbf3cafbeb668127daff60a52228",
2397
- url: "https://github.com/colbymchenry/codegraph/releases/download/v1.0.1/codegraph-linux-x64.tar.gz"
2675
+ sha256: "fb585ff5018d6faaa46d282b61f4f689bc7967ed8a1b467a5c556dd7ced9b542",
2676
+ url: "https://github.com/colbymchenry/codegraph/releases/download/v1.4.1/codegraph-linux-x64.tar.gz"
2398
2677
  },
2399
2678
  "win32-arm64": {
2400
2679
  executableName: "codegraph.cmd",
2401
- sha256: "8d57ced73b24d35f758f2ede2318e80e1d7241987f37a999e3d80edb6fddf961",
2402
- url: "https://registry.npmjs.org/@colbymchenry/codegraph-win32-arm64/-/codegraph-win32-arm64-1.0.1.tgz"
2680
+ sha256: "e2a2a28c802a79804c7df203afa50bd461309c6c180ce3f76079fdc7cddc7697",
2681
+ url: "https://registry.npmjs.org/@colbymchenry/codegraph-win32-arm64/-/codegraph-win32-arm64-1.4.1.tgz"
2403
2682
  },
2404
2683
  "win32-x64": {
2405
2684
  executableName: "codegraph.cmd",
2406
- sha256: "52607fe73b05e741fd1087da2ceca9d3c8f565e36bf1a7070600bdbdf3931e32",
2407
- url: "https://registry.npmjs.org/@colbymchenry/codegraph-win32-x64/-/codegraph-win32-x64-1.0.1.tgz"
2685
+ sha256: "4f08700fda5f4a03ad5b2956135c5788d739a351b3433db2b5820e5d5224c30d",
2686
+ url: "https://registry.npmjs.org/@colbymchenry/codegraph-win32-x64/-/codegraph-win32-x64-1.4.1.tgz"
2408
2687
  }
2409
2688
  },
2410
- version: "1.0.1"
2689
+ version: CODEGRAPH_PINNED_VERSION
2411
2690
  };
2412
2691
 
2413
2692
  // ../../utils/src/codegraph/provision.ts
2693
+ import { createHash as createHash2, randomUUID } from "node:crypto";
2694
+ import { execFile as execFile2 } from "node:child_process";
2695
+ import { chmod, mkdir, readdir, readFile, rename, rm, rmdir, stat, writeFile } from "node:fs/promises";
2696
+ import { existsSync as existsSync8 } from "node:fs";
2697
+ import { homedir as homedir11, hostname } from "node:os";
2698
+ import { basename as basename3, join as join12 } from "node:path";
2699
+ import { promisify } from "node:util";
2414
2700
  var DEFAULT_LOCK_WAIT_MS = 5000;
2415
2701
  var DEFAULT_LOCK_STALE_MS = 120000;
2416
2702
  var DEFAULT_DOWNLOAD_TIMEOUT_MS = 60000;
@@ -2419,10 +2705,10 @@ function platformKey() {
2419
2705
  return `${process.platform}-${process.arch}`;
2420
2706
  }
2421
2707
  function markerPath(installDir, version) {
2422
- return join11(installDir, ".provisioned", `codegraph-${version}.json`);
2708
+ return join12(installDir, ".provisioned", `codegraph-${version}.json`);
2423
2709
  }
2424
2710
  function defaultInstallDir() {
2425
- return join11(homedir11(), ".omo", "codegraph");
2711
+ return join12(homedir11(), ".omo", "codegraph");
2426
2712
  }
2427
2713
  function sha256(bytes) {
2428
2714
  return createHash2("sha256").update(bytes).digest("hex");
@@ -2442,7 +2728,7 @@ async function removeEmptyDirectory(path) {
2442
2728
  }
2443
2729
  }
2444
2730
  function sleep(ms) {
2445
- return new Promise((resolve5) => setTimeout(resolve5, ms));
2731
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
2446
2732
  }
2447
2733
  async function defaultDownloader(asset, timeoutMs = DEFAULT_DOWNLOAD_TIMEOUT_MS) {
2448
2734
  const response = await fetch(asset.url, { signal: AbortSignal.timeout(timeoutMs) });
@@ -2456,7 +2742,7 @@ function forcedBadChecksumOptions(options) {
2456
2742
  const key = options.platformKey ?? platformKey();
2457
2743
  return {
2458
2744
  downloader: async () => new TextEncoder().encode("checksum mismatch"),
2459
- installDir: options.installDir ?? join11(options.lockDir, "codegraph-force-bad-checksum"),
2745
+ installDir: options.installDir ?? join12(options.lockDir, "codegraph-force-bad-checksum"),
2460
2746
  manifest: {
2461
2747
  assets: {
2462
2748
  [key]: { executableName: process.platform === "win32" ? "codegraph.cmd" : "codegraph", sha256: "0000", url: "memory://bad" }
@@ -2484,7 +2770,7 @@ async function readMarker(path) {
2484
2770
  }
2485
2771
  async function acquireLock(lockPath, waitMs, staleMs) {
2486
2772
  const startedAt = Date.now();
2487
- await mkdir(join11(lockPath, ".."), { recursive: true });
2773
+ await mkdir(join12(lockPath, ".."), { recursive: true });
2488
2774
  while (Date.now() - startedAt <= waitMs) {
2489
2775
  try {
2490
2776
  await mkdir(lockPath);
@@ -2509,14 +2795,14 @@ async function installExtractedBundle(extractDir, installDir, executableName) {
2509
2795
  const roots = await readdir(extractDir);
2510
2796
  if (roots.length !== 1)
2511
2797
  throw new Error(`CodeGraph archive should contain one root directory, found ${roots.length}`);
2512
- const bundleDir = join11(extractDir, roots[0] ?? "");
2798
+ const bundleDir = join12(extractDir, roots[0] ?? "");
2513
2799
  const bundleEntries = await readdir(bundleDir);
2514
2800
  await mkdir(installDir, { recursive: true });
2515
2801
  for (const entry of bundleEntries) {
2516
- await rm(join11(installDir, entry), { force: true, recursive: true });
2517
- await rename(join11(bundleDir, entry), join11(installDir, entry));
2802
+ await rm(join12(installDir, entry), { force: true, recursive: true });
2803
+ await rename(join12(bundleDir, entry), join12(installDir, entry));
2518
2804
  }
2519
- const destination = join11(installDir, "bin", executableName);
2805
+ const destination = join12(installDir, "bin", executableName);
2520
2806
  if (!existsSync8(destination))
2521
2807
  throw new Error(`CodeGraph archive did not contain bin/${executableName}`);
2522
2808
  await chmod(destination, 493);
@@ -2524,9 +2810,9 @@ async function installExtractedBundle(extractDir, installDir, executableName) {
2524
2810
  }
2525
2811
  async function installAsset(layout) {
2526
2812
  const { asset, downloader, installDir, version } = layout;
2527
- const stagingDir = join11(installDir, ".staging", randomUUID());
2528
- const archivePath = join11(stagingDir, basename3(asset.url));
2529
- const extractDir = join11(stagingDir, "extract");
2813
+ const stagingDir = join12(installDir, ".staging", randomUUID());
2814
+ const archivePath = join12(stagingDir, basename3(asset.url));
2815
+ const extractDir = join12(stagingDir, "extract");
2530
2816
  try {
2531
2817
  await mkdir(extractDir, { recursive: true });
2532
2818
  const bytes = await downloader(asset);
@@ -2540,13 +2826,13 @@ async function installAsset(layout) {
2540
2826
  await writeFile(archivePath, bytes);
2541
2827
  await extractTarGz(archivePath, extractDir);
2542
2828
  const destination = await installExtractedBundle(extractDir, installDir, asset.executableName);
2543
- await mkdir(join11(installDir, ".provisioned"), { recursive: true });
2829
+ await mkdir(join12(installDir, ".provisioned"), { recursive: true });
2544
2830
  await writeFile(markerPath(installDir, version), `${JSON.stringify({ binPath: destination, version })}
2545
2831
  `);
2546
2832
  return destination;
2547
2833
  } finally {
2548
2834
  await rm(stagingDir, { force: true, recursive: true });
2549
- await removeEmptyDirectory(join11(installDir, ".staging"));
2835
+ await removeEmptyDirectory(join12(installDir, ".staging"));
2550
2836
  }
2551
2837
  }
2552
2838
  async function ensureCodegraphProvisioned(options) {
@@ -2559,7 +2845,7 @@ async function ensureCodegraphProvisioned(options) {
2559
2845
  const existing = await readMarker(marker);
2560
2846
  if (existing !== null)
2561
2847
  return { binPath: existing, provisioned: true };
2562
- const lockPath = join11(options.lockDir, `codegraph-${hostname()}.lock`);
2848
+ const lockPath = join12(options.lockDir, `codegraph-${hostname()}.lock`);
2563
2849
  const release = await acquireLock(lockPath, options.lockWaitMs ?? DEFAULT_LOCK_WAIT_MS, options.lockStaleMs ?? DEFAULT_LOCK_STALE_MS);
2564
2850
  if (release === null)
2565
2851
  return { error: "timed out waiting for codegraph provisioning lock", provisioned: false };
@@ -2585,7 +2871,7 @@ async function ensureCodegraphProvisioned(options) {
2585
2871
 
2586
2872
  // components/codegraph/src/session-start-worker.ts
2587
2873
  var SESSION_START_CWD_ENV = "OMO_CODEGRAPH_SESSION_START_CWD";
2588
- var CODEGRAPH_VERSION = "1.0.1";
2874
+ var CODEGRAPH_VERSION = CODEGRAPH_PINNED_VERSION;
2589
2875
  var COMMAND_TIMEOUT_MS = 60000;
2590
2876
  var WINDOWS_CMD_EXTENSIONS = new Set([".bat", ".cmd"]);
2591
2877
  var WINDOWS_NODE_SCRIPT_EXTENSIONS = new Set([".cjs", ".js", ".mjs"]);
@@ -2649,15 +2935,15 @@ async function resolveOrProvisionCommand(deps, config, env, homeDir, nodeSupport
2649
2935
  return { kind: "unsupported-node" };
2650
2936
  if (config.auto_provision === false)
2651
2937
  return { error: "codegraph binary unavailable and auto_provision is disabled", kind: "unavailable", source: resolved.source };
2652
- const installDir = trustedInstallDir ?? join12(homeDir, ".omo", "codegraph");
2653
- const provisioned = await deps.ensureProvisioned({ installDir, lockDir: join12(installDir, ".locks"), version: CODEGRAPH_VERSION });
2938
+ const installDir = trustedInstallDir ?? join13(homeDir, ".omo", "codegraph");
2939
+ const provisioned = await deps.ensureProvisioned({ installDir, lockDir: join13(installDir, ".locks"), version: CODEGRAPH_VERSION });
2654
2940
  if (!provisioned.provisioned || provisioned.binPath === undefined) {
2655
2941
  return { error: provisioned.error ?? "provisioning did not produce a binary", kind: "unavailable", source: resolved.source };
2656
2942
  }
2657
2943
  return { kind: "resolved", resolution: { argsPrefix: [], command: provisioned.binPath, exists: true, source: "provisioned" } };
2658
2944
  }
2659
2945
  function codegraphEnvForConfig(config, homeDir) {
2660
- const env = buildCodegraphEnv({ homeDir });
2946
+ const env = buildCodegraphEnv({ daemon: false, homeDir });
2661
2947
  return config.trustedCodegraphInstallDir === undefined ? env : { ...env, CODEGRAPH_INSTALL_DIR: config.trustedCodegraphInstallDir };
2662
2948
  }
2663
2949
  function canUseResolvedCommand(resolved, nodeSupport) {
@@ -2718,9 +3004,9 @@ function resolveCodegraphCommandInvocation(command, args, platform = process.pla
2718
3004
  return { args: ["/d", "/s", "/c", command, ...args], command: "cmd.exe" };
2719
3005
  }
2720
3006
  function appendOutcome(homeDir, outcome) {
2721
- const logDir = join12(homeDir, ".omo", "codegraph");
3007
+ const logDir = join13(homeDir, ".omo", "codegraph");
2722
3008
  mkdirSync3(logDir, { recursive: true });
2723
- appendFileSync2(join12(logDir, "session-start.jsonl"), `${JSON.stringify({ ...outcome, timestamp: new Date().toISOString() })}
3009
+ appendFileSync2(join13(logDir, "session-start.jsonl"), `${JSON.stringify({ ...outcome, timestamp: new Date().toISOString() })}
2724
3010
  `);
2725
3011
  }
2726
3012
  function safeLogOutcome(logOutcome, outcome) {
@@ -2737,7 +3023,7 @@ function safeLogOutcome(logOutcome, outcome) {
2737
3023
  function provisionedBinFromInstallDir(installDir) {
2738
3024
  if (installDir === undefined)
2739
3025
  return null;
2740
- const candidate = join12(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
3026
+ const candidate = join13(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
2741
3027
  return existsSync9(candidate) ? candidate : null;
2742
3028
  }
2743
3029
  function resolveExitCode(error) {
@@ -2798,6 +3084,7 @@ async function executeCodegraphSessionStartHook(options = {}) {
2798
3084
  return { action: "skipped-excluded", exitCode: 0 };
2799
3085
  }
2800
3086
  const isInitialized = await (options.statusProbe ?? isCodegraphProjectInitialized)({
3087
+ daemon: config.codegraph?.daemon === true,
2801
3088
  env,
2802
3089
  homeDir,
2803
3090
  projectRoot,
@@ -2828,7 +3115,7 @@ async function isCodegraphProjectInitialized(options) {
2828
3115
  return false;
2829
3116
  const invocation = resolveCodegraphCommandInvocation(resolved.command, [...resolved.argsPrefix, "status", "--json"]);
2830
3117
  const codegraphEnv = {
2831
- ...buildCodegraphEnv({ homeDir: options.homeDir }),
3118
+ ...buildCodegraphEnv({ daemon: options.daemon, homeDir: options.homeDir }),
2832
3119
  ...options.trustedCodegraphInstallDir === undefined ? {} : { CODEGRAPH_INSTALL_DIR: options.trustedCodegraphInstallDir }
2833
3120
  };
2834
3121
  const status = await runStatusProbe(options.projectRoot, invocation.command, invocation.args, buildCodegraphChildEnv({ ambientEnv: options.env, codegraphEnv, runtimeEnv: options.env }));
@@ -2870,7 +3157,7 @@ function codegraphStatusSaysInitialized(stdout) {
2870
3157
  function provisionedBinFromInstallDir2(installDir) {
2871
3158
  if (installDir === undefined)
2872
3159
  return null;
2873
- return join13(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
3160
+ return join14(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
2874
3161
  }
2875
3162
  async function executeCodegraphPostToolUseHook(options = {}) {
2876
3163
  const env = options.env ?? processEnv2;
@@ -2960,9 +3247,9 @@ function defaultWorkerCliPath() {
2960
3247
  }
2961
3248
 
2962
3249
  // components/codegraph/src/serve.ts
2963
- import { existsSync as existsSync10, realpathSync as realpathSync5 } from "node:fs";
3250
+ import { existsSync as existsSync10, realpathSync as realpathSync6 } from "node:fs";
2964
3251
  import { homedir as homedir14 } from "node:os";
2965
- import { basename as basename4, join as join14, resolve as resolve5 } from "node:path";
3252
+ import { basename as basename4, join as join15, resolve as resolve7 } from "node:path";
2966
3253
  import {
2967
3254
  cwd as processCwd3,
2968
3255
  env as processEnv3,
@@ -3023,7 +3310,7 @@ ${body}` : `${body}
3023
3310
  await writeChunk(output, payload);
3024
3311
  }
3025
3312
  function writeChunk(output, chunk) {
3026
- return new Promise((resolve5, reject) => {
3313
+ return new Promise((resolve7, reject) => {
3027
3314
  let settled = false;
3028
3315
  const onError = (error) => {
3029
3316
  if (settled)
@@ -3043,7 +3330,7 @@ function writeChunk(output, chunk) {
3043
3330
  return;
3044
3331
  }
3045
3332
  output.removeListener("error", onError);
3046
- resolve5();
3333
+ resolve7();
3047
3334
  });
3048
3335
  } catch (error) {
3049
3336
  output.removeListener("error", onError);
@@ -3135,16 +3422,35 @@ function bufferFromChunk(chunk) {
3135
3422
 
3136
3423
  // ../../mcp-stdio-core/src/server.ts
3137
3424
  var DEFAULT_IDLE_TIMEOUT_MS = 10 * 60000;
3425
+ var DEFAULT_PARENT_POLL_INTERVAL_MS = 30000;
3138
3426
  var noopLog = () => {};
3427
+ function isProcessAlive(pid) {
3428
+ try {
3429
+ process.kill(pid, 0);
3430
+ return true;
3431
+ } catch (error) {
3432
+ return !hasErrorCode(error, "ESRCH");
3433
+ }
3434
+ }
3139
3435
  async function runJsonRpcStdioServer(config) {
3140
3436
  const log = config.log ?? noopLog;
3141
3437
  const idleTimeoutMs = config.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
3142
- const idleTimer = createIdleTimer(idleTimeoutMs, log, config.onIdleTimeout);
3438
+ let isClosed = false;
3439
+ const idleTimer = createIdleTimer(idleTimeoutMs, log, () => {
3440
+ isClosed = true;
3441
+ config.onIdleTimeout?.();
3442
+ });
3443
+ const watchdog = createParentWatchdog(config.parentWatchdog, (parentPid, pollIntervalMs) => {
3444
+ isClosed = true;
3445
+ log("parent_exit", { parent_pid: parentPid, poll_interval_ms: pollIntervalMs });
3446
+ config.onParentExit?.();
3447
+ config.input.destroy();
3448
+ });
3143
3449
  log("stdio_started", { cwd: process.cwd(), idle_timeout_ms: idleTimeoutMs });
3144
3450
  idleTimer.arm();
3145
3451
  try {
3146
3452
  for await (const message of readStdioJsonRpcMessages(config.input)) {
3147
- if (idleTimer.closed())
3453
+ if (isClosed)
3148
3454
  break;
3149
3455
  idleTimer.arm();
3150
3456
  if (message.kind === "parse_error") {
@@ -3155,8 +3461,12 @@ async function runJsonRpcStdioServer(config) {
3155
3461
  if (!await handleRequest(message, config, log))
3156
3462
  break;
3157
3463
  }
3464
+ } catch (error) {
3465
+ if (!(isClosed && hasErrorCode(error, "ERR_STREAM_PREMATURE_CLOSE")))
3466
+ throw error;
3158
3467
  } finally {
3159
3468
  idleTimer.clear();
3469
+ watchdog.clear();
3160
3470
  log("stdio_stopped");
3161
3471
  }
3162
3472
  }
@@ -3212,9 +3522,33 @@ function isTerminalOutputError(error) {
3212
3522
  return false;
3213
3523
  return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
3214
3524
  }
3215
- function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3525
+ function hasErrorCode(error, code) {
3526
+ return error instanceof Error && "code" in error && error.code === code;
3527
+ }
3528
+ function createParentWatchdog(config, onDeadParent) {
3529
+ if (config === undefined)
3530
+ return { clear: () => {} };
3531
+ const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_PARENT_POLL_INTERVAL_MS;
3532
+ if (pollIntervalMs <= 0)
3533
+ return { clear: () => {} };
3534
+ const parentPid = config.parentPid ?? process.ppid;
3535
+ const probeAlive = config.probeAlive ?? isProcessAlive;
3536
+ let fired = false;
3537
+ const timer = setInterval(() => {
3538
+ if (fired || probeAlive(parentPid))
3539
+ return;
3540
+ fired = true;
3541
+ onDeadParent(parentPid, pollIntervalMs);
3542
+ }, pollIntervalMs);
3543
+ timer.unref();
3544
+ return {
3545
+ clear: () => {
3546
+ clearInterval(timer);
3547
+ }
3548
+ };
3549
+ }
3550
+ function createIdleTimer(idleTimeoutMs, log, onTimeout) {
3216
3551
  let timer = null;
3217
- let isClosed = false;
3218
3552
  return {
3219
3553
  arm: () => {
3220
3554
  if (timer !== null)
@@ -3222,9 +3556,8 @@ function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3222
3556
  if (idleTimeoutMs <= 0)
3223
3557
  return;
3224
3558
  timer = setTimeout(() => {
3225
- isClosed = true;
3226
3559
  log("idle_timeout", { idle_timeout_ms: idleTimeoutMs });
3227
- onIdleTimeout?.();
3560
+ onTimeout();
3228
3561
  }, idleTimeoutMs);
3229
3562
  timer.unref();
3230
3563
  },
@@ -3233,8 +3566,7 @@ function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3233
3566
  return;
3234
3567
  clearTimeout(timer);
3235
3568
  timer = null;
3236
- },
3237
- closed: () => isClosed
3569
+ }
3238
3570
  };
3239
3571
  }
3240
3572
  // components/codegraph/src/serve-invocation.ts
@@ -3267,6 +3599,7 @@ class CodegraphBridgeStdioError extends Error {
3267
3599
  var CODEGRAPH_NODE_DESCRIPTION = "Inspect one named symbol or file. In symbol mode, includeCode=true includes leaf-symbol source when available. Container symbols such as classes, interfaces, structs, enums, modules, and namespaces return structural outlines with member lists by design. For container source, request a specific member symbol or use file mode with symbolsOnly=false plus offset/limit.";
3268
3600
  var CODEGRAPH_NODE_INCLUDE_CODE_DESCRIPTION = "Symbol mode: include leaf-symbol source when available. Container symbols such as classes, interfaces, structs, enums, modules, and namespaces intentionally return structural outlines with members; request a specific member symbol or use file mode with symbolsOnly=false plus offset/limit for source.";
3269
3601
  var CODEGRAPH_CONTAINER_OUTLINE_GUIDANCE = "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.";
3602
+ var SIGKILL_ESCALATION_MS = 2000;
3270
3603
  async function runBridgedCodegraphProcess(command, args, options) {
3271
3604
  const invocation = resolveServeProcessInvocation(command, args);
3272
3605
  const child = spawn2(invocation.command, invocation.args, {
@@ -3292,17 +3625,24 @@ async function runBridgedCodegraphProcess(command, args, options) {
3292
3625
  resolveExit(signal === null ? 0 : 1);
3293
3626
  });
3294
3627
  });
3295
- const clientForwardingDone = forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
3296
- defaultResponseMode = mode;
3297
- });
3298
- const responseForwardingDone = forwardCodegraphToClient(childOutput, options.output, pendingResponses, () => defaultResponseMode);
3299
- const bridgeDone = Promise.all([clientForwardingDone, responseForwardingDone]);
3300
- const childAndResponsesDone = Promise.all([childExit, responseForwardingDone]).then(([exitCode]) => exitCode);
3301
3628
  const destroyChildPipes = () => {
3302
3629
  childInput.destroy();
3303
3630
  childOutput.destroy();
3304
3631
  };
3305
3632
  childExit.then(destroyChildPipes, destroyChildPipes);
3633
+ let parentWatchdogFired = false;
3634
+ const parentWatchdog = createParentWatchdog(options.parentWatchdog, () => {
3635
+ parentWatchdogFired = true;
3636
+ options.input.destroy();
3637
+ destroyChildPipes();
3638
+ terminateCodegraphChild(child);
3639
+ });
3640
+ const clientForwardingDone = forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
3641
+ defaultResponseMode = mode;
3642
+ }, () => parentWatchdogFired);
3643
+ const responseForwardingDone = forwardCodegraphToClient(childOutput, options.output, pendingResponses, () => defaultResponseMode, () => parentWatchdogFired);
3644
+ const bridgeDone = Promise.all([clientForwardingDone, responseForwardingDone]);
3645
+ const childAndResponsesDone = Promise.all([childExit, responseForwardingDone]).then(([exitCode]) => exitCode);
3306
3646
  try {
3307
3647
  return await Promise.race([childAndResponsesDone, bridgeDone.then(() => childExit)]);
3308
3648
  } catch (error) {
@@ -3313,39 +3653,66 @@ async function runBridgedCodegraphProcess(command, args, options) {
3313
3653
  return;
3314
3654
  });
3315
3655
  throw error;
3656
+ } finally {
3657
+ parentWatchdog.clear();
3316
3658
  }
3317
3659
  }
3318
- async function forwardClientToCodegraph(input, childInput, pendingResponses, setDefaultResponseMode) {
3319
- for await (const message of readStdioJsonRpcMessages(input)) {
3320
- if (message.kind === "parse_error") {
3321
- continue;
3322
- }
3323
- const responseMode = message.responseMode;
3324
- setDefaultResponseMode(responseMode);
3325
- const key = responseModeKey(message.payload);
3326
- if (key !== null) {
3327
- pendingResponses.set(key, {
3328
- method: jsonRpcMethod(message.payload),
3329
- responseMode,
3330
- toolName: jsonRpcToolName(message.payload)
3331
- });
3660
+ function terminateCodegraphChild(child) {
3661
+ if (child.exitCode !== null || child.signalCode !== null)
3662
+ return;
3663
+ child.kill("SIGTERM");
3664
+ const escalation = setTimeout(() => {
3665
+ if (child.exitCode === null && child.signalCode === null)
3666
+ child.kill("SIGKILL");
3667
+ }, SIGKILL_ESCALATION_MS);
3668
+ escalation.unref();
3669
+ }
3670
+ function isWatchdogTeardownError(error) {
3671
+ if (!(error instanceof Error) || !("code" in error))
3672
+ return false;
3673
+ return error.code === "ERR_STREAM_PREMATURE_CLOSE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END" || error.code === "EPIPE";
3674
+ }
3675
+ async function forwardClientToCodegraph(input, childInput, pendingResponses, setDefaultResponseMode, tolerateWatchdogClose) {
3676
+ try {
3677
+ for await (const message of readStdioJsonRpcMessages(input)) {
3678
+ if (message.kind === "parse_error") {
3679
+ continue;
3680
+ }
3681
+ const responseMode = message.responseMode;
3682
+ setDefaultResponseMode(responseMode);
3683
+ const key = responseModeKey(message.payload);
3684
+ if (key !== null) {
3685
+ pendingResponses.set(key, {
3686
+ method: jsonRpcMethod(message.payload),
3687
+ responseMode,
3688
+ toolName: jsonRpcToolName(message.payload)
3689
+ });
3690
+ }
3691
+ await writeLine(childInput, JSON.stringify(message.payload));
3332
3692
  }
3333
- await writeLine(childInput, JSON.stringify(message.payload));
3693
+ childInput.end();
3694
+ } catch (error) {
3695
+ if (!(tolerateWatchdogClose() && isWatchdogTeardownError(error)))
3696
+ throw error;
3334
3697
  }
3335
- childInput.end();
3336
3698
  }
3337
- async function forwardCodegraphToClient(childOutput, output, pendingResponses, defaultResponseMode) {
3338
- for await (const message of readStdioJsonRpcMessages(childOutput)) {
3339
- if (message.kind === "parse_error") {
3340
- await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
3341
- continue;
3699
+ async function forwardCodegraphToClient(childOutput, output, pendingResponses, defaultResponseMode, tolerateWatchdogClose) {
3700
+ try {
3701
+ for await (const message of readStdioJsonRpcMessages(childOutput)) {
3702
+ if (message.kind === "parse_error") {
3703
+ await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
3704
+ continue;
3705
+ }
3706
+ const key = responseModeKey(message.payload);
3707
+ const pendingResponse = key === null ? undefined : pendingResponses.get(key);
3708
+ const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
3709
+ if (key !== null)
3710
+ pendingResponses.delete(key);
3711
+ await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
3342
3712
  }
3343
- const key = responseModeKey(message.payload);
3344
- const pendingResponse = key === null ? undefined : pendingResponses.get(key);
3345
- const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
3346
- if (key !== null)
3347
- pendingResponses.delete(key);
3348
- await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
3713
+ } catch (error) {
3714
+ if (!(tolerateWatchdogClose() && isWatchdogTeardownError(error)))
3715
+ throw error;
3349
3716
  }
3350
3717
  }
3351
3718
  function responseModeKey(payload) {
@@ -3477,7 +3844,8 @@ async function runUnavailableCodegraphMcpServer(options) {
3477
3844
  serverVersion: options.serverVersion
3478
3845
  },
3479
3846
  input: options.input,
3480
- output: options.output
3847
+ output: options.output,
3848
+ parentWatchdog: options.parentWatchdog ?? {}
3481
3849
  });
3482
3850
  }
3483
3851
  async function handleUnavailableCodegraphMcpRequest(input, options) {
@@ -3521,7 +3889,7 @@ var CODEGRAPH_DISABLED_HINT = `CodeGraph MCP skipped: disabled by OMO SOT config
3521
3889
  `;
3522
3890
  var CODEGRAPH_EXCLUDED_HINT = `CodeGraph MCP skipped: project excluded by OMO CodeGraph policy.
3523
3891
  `;
3524
- var CODEGRAPH_VERSION2 = "1.0.1";
3892
+ var CODEGRAPH_VERSION2 = CODEGRAPH_PINNED_VERSION;
3525
3893
  var PROJECT_CWD_ENV_KEYS = ["OMO_CODEGRAPH_PROJECT_CWD", SESSION_START_CWD_ENV, "PWD"];
3526
3894
  async function runCodegraphServe(options = {}) {
3527
3895
  const env = options.env ?? processEnv3;
@@ -3569,7 +3937,7 @@ async function runCodegraphServe(options = {}) {
3569
3937
  return runUnavailableMcp(buildCodegraphNodeSkipHint(nodeSupport), options);
3570
3938
  }
3571
3939
  const runProcess = options.runProcess ?? runBridgedCodegraphProcess;
3572
- const codegraphEnv = codegraphEnvForConfig2(trustedInstallDir, homeDir, options.buildEnv);
3940
+ const codegraphEnv = codegraphEnvForConfig2(trustedInstallDir, homeDir, codegraphConfig.daemon === true, options.buildEnv);
3573
3941
  const mergedEnv = buildCodegraphChildEnv({ ambientEnv: env, codegraphEnv, runtimeEnv: env });
3574
3942
  return runProcess(resolution.command, [...resolution.argsPrefix, "serve", "--mcp"], {
3575
3943
  cwd: projectCwd,
@@ -3577,7 +3945,8 @@ async function runCodegraphServe(options = {}) {
3577
3945
  input: options.stdin ?? processStdin2,
3578
3946
  output: options.stdout ?? processStdout2,
3579
3947
  stderr: options.stderr ?? processStderr3,
3580
- stdio: "pipe"
3948
+ stdio: "pipe",
3949
+ parentWatchdog: options.parentWatchdog ?? {}
3581
3950
  });
3582
3951
  }
3583
3952
  async function runUnavailableMcp(reason, options) {
@@ -3586,7 +3955,8 @@ async function runUnavailableMcp(reason, options) {
3586
3955
  input: options.stdin ?? processStdin2,
3587
3956
  output: options.stdout ?? processStdout2,
3588
3957
  reason,
3589
- serverVersion: CODEGRAPH_VERSION2
3958
+ serverVersion: CODEGRAPH_VERSION2,
3959
+ parentWatchdog: options.parentWatchdog ?? {}
3590
3960
  });
3591
3961
  return 0;
3592
3962
  }
@@ -3595,10 +3965,10 @@ async function provisionMissingCodegraph(options) {
3595
3965
  return null;
3596
3966
  if (options.config.auto_provision === false)
3597
3967
  return null;
3598
- const installDir = options.trustedInstallDir ?? join14(options.homeDir, ".omo", "codegraph");
3968
+ const installDir = options.trustedInstallDir ?? join15(options.homeDir, ".omo", "codegraph");
3599
3969
  const result = await options.ensureProvisioned({
3600
3970
  installDir,
3601
- lockDir: join14(installDir, ".locks"),
3971
+ lockDir: join15(installDir, ".locks"),
3602
3972
  version: CODEGRAPH_VERSION2
3603
3973
  });
3604
3974
  if (!result.provisioned || result.binPath === undefined)
@@ -3615,8 +3985,8 @@ function shouldSkipResolvedCommand(resolution, commandExists) {
3615
3985
  function looksLikePath2(command) {
3616
3986
  return command.includes("/") || command.includes("\\");
3617
3987
  }
3618
- function codegraphEnvForConfig2(trustedInstallDir, homeDir, buildEnv) {
3619
- const env = { ...buildEnv?.({ homeDir }) ?? buildCodegraphEnv({ homeDir }), [CODEGRAPH_NO_DAEMON_ENV]: "1" };
3988
+ function codegraphEnvForConfig2(trustedInstallDir, homeDir, daemon, buildEnv) {
3989
+ const env = buildEnv?.({ daemon, homeDir }) ?? buildCodegraphEnv({ daemon, homeDir });
3620
3990
  return trustedInstallDir === undefined ? env : { ...env, CODEGRAPH_INSTALL_DIR: trustedInstallDir };
3621
3991
  }
3622
3992
  function resolveProjectCwd(env, fallback) {
@@ -3624,16 +3994,16 @@ function resolveProjectCwd(env, fallback) {
3624
3994
  const candidate = env[key]?.trim();
3625
3995
  if (candidate === undefined || candidate.length === 0)
3626
3996
  continue;
3627
- const resolved = resolve5(candidate);
3997
+ const resolved = resolve7(candidate);
3628
3998
  if (existsSync10(resolved))
3629
3999
  return resolved;
3630
4000
  }
3631
- return resolve5(fallback);
4001
+ return resolve7(fallback);
3632
4002
  }
3633
4003
  function provisionedBinFromInstallDir3(installDir) {
3634
4004
  if (installDir === undefined)
3635
4005
  return null;
3636
- const candidate = join14(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
4006
+ const candidate = join15(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
3637
4007
  return existsSync10(candidate) ? candidate : null;
3638
4008
  }
3639
4009
  async function runCodegraphServeCli() {
@@ -3653,7 +4023,7 @@ function isDirectInvocation(argvPath) {
3653
4023
  const moduleName = basename4(modulePath);
3654
4024
  if (moduleName !== "serve.js" && moduleName !== "serve.ts")
3655
4025
  return false;
3656
- return realpathSync5(resolve5(argvPath)) === realpathSync5(modulePath);
4026
+ return realpathSync6(resolve7(argvPath)) === realpathSync6(modulePath);
3657
4027
  }
3658
4028
 
3659
4029
  // components/codegraph/src/sweep-cli.ts
@@ -3700,6 +4070,7 @@ function writeSweepReport(stdout, result) {
3700
4070
  failed: result.failed,
3701
4071
  killed: result.killed.map(formatProcess),
3702
4072
  ownedRoots: result.ownedRoots,
4073
+ spared: result.spared.map(formatProcess),
3703
4074
  stampFile: result.stampFile
3704
4075
  })}
3705
4076
  `);
@@ -3763,7 +4134,7 @@ function isDirectInvocation2(argvPath) {
3763
4134
  const moduleName = basename5(modulePath);
3764
4135
  if (moduleName !== "cli.js" && moduleName !== "cli.ts")
3765
4136
  return false;
3766
- return realpathSync6(resolve6(argvPath)) === realpathSync6(modulePath);
4137
+ return realpathSync7(resolve8(argvPath)) === realpathSync7(modulePath);
3767
4138
  }
3768
4139
  export {
3769
4140
  runCodegraphCli