oh-my-opencode 4.19.0 → 4.19.2

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 (310) 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 +6 -6
  7. package/README.ru.md +6 -6
  8. package/README.zh-cn.md +6 -6
  9. package/dist/agents/atlas/agent.d.ts +5 -3
  10. package/dist/agents/explore.d.ts +4 -2
  11. package/dist/agents/gpt-prompt-identity.d.ts +3 -0
  12. package/dist/agents/hephaestus/agent.d.ts +4 -2
  13. package/dist/agents/librarian.d.ts +4 -2
  14. package/dist/agents/metis.d.ts +4 -2
  15. package/dist/agents/momus.d.ts +3 -2
  16. package/dist/agents/multimodal-looker.d.ts +4 -2
  17. package/dist/agents/oracle.d.ts +4 -2
  18. package/dist/agents/sisyphus/claude-opus-5.d.ts +33 -0
  19. package/dist/agents/sisyphus/gpt-5-5.d.ts +1 -1
  20. package/dist/agents/sisyphus/gpt-task-system-guide.d.ts +1 -0
  21. package/dist/agents/sisyphus/index.d.ts +2 -0
  22. package/dist/agents/sisyphus-agent-factory.d.ts +5 -2
  23. package/dist/agents/sisyphus-junior/agent.d.ts +4 -1
  24. package/dist/agents/sisyphus-junior/gpt-5-5.d.ts +2 -2
  25. package/dist/agents/types.d.ts +2 -2
  26. package/dist/cli/index.js +23577 -19269
  27. package/dist/cli-node/index.js +23577 -19269
  28. package/dist/config/schema/agent-names.d.ts +26 -26
  29. package/dist/config/schema/agent-overrides.d.ts +400 -400
  30. package/dist/config/schema/browser-automation.d.ts +2 -2
  31. package/dist/config/schema/categories.d.ts +36 -36
  32. package/dist/config/schema/codegraph.d.ts +2 -0
  33. package/dist/config/schema/commands.d.ts +2 -2
  34. package/dist/config/schema/dynamic-context-pruning.d.ts +1 -1
  35. package/dist/config/schema/experimental.d.ts +1 -1
  36. package/dist/config/schema/fallback-models.d.ts +25 -25
  37. package/dist/config/schema/hooks.d.ts +43 -43
  38. package/dist/config/schema/internal/permission.d.ts +9 -9
  39. package/dist/config/schema/keyword-detector.d.ts +6 -6
  40. package/dist/config/schema/oh-my-opencode-config.d.ts +404 -402
  41. package/dist/config/schema/tmux.d.ts +6 -6
  42. package/dist/create-hooks.d.ts +35 -35
  43. package/dist/features/background-agent/spawner/tmux-callback-invoker.d.ts +11 -0
  44. package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
  45. package/dist/features/claude-tasks/types.d.ts +4 -4
  46. package/dist/features/team-mode/team-runtime/shutdown-helpers.d.ts +1 -1
  47. package/dist/features/team-mode/team-runtime/shutdown-test-fixtures.d.ts +4 -4
  48. package/dist/features/team-mode/tools/lifecycle-test-fixture.d.ts +42 -42
  49. package/dist/features/tmux-subagent/environment-eligibility.d.ts +2 -0
  50. package/dist/features/tmux-subagent/index.d.ts +1 -0
  51. package/dist/features/tmux-subagent/tracked-session-state.d.ts +1 -0
  52. package/dist/features/tui-sidebar/snapshot-schema.d.ts +6 -6
  53. package/dist/help/schema/acp.d.ts +3 -3
  54. package/dist/help/schema/doctor.d.ts +4 -4
  55. package/dist/help/schema/status.d.ts +3 -3
  56. package/dist/hooks/codegraph-bootstrap/hook.d.ts +3 -2
  57. package/dist/hooks/goal/types.d.ts +5 -5
  58. package/dist/hooks/non-interactive-env/constants.d.ts +4 -1
  59. package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +1 -1
  60. package/dist/hooks/start-work/notepad-scaffold.d.ts +10 -0
  61. package/dist/hooks/start-work/parse-user-request.d.ts +2 -0
  62. package/dist/hooks/start-work/worktree-block.d.ts +5 -0
  63. package/dist/index.js +27370 -21301
  64. package/dist/mcp/types.d.ts +3 -3
  65. package/dist/oh-my-opencode.schema.json +11 -0
  66. package/dist/plugin/chat-message/loop-commands.d.ts +1 -0
  67. package/dist/plugin/command-execute-before.d.ts +3 -3
  68. package/dist/plugin/hooks/create-core-hooks.d.ts +25 -25
  69. package/dist/shared/omo-process-sweep.d.ts +18 -0
  70. package/dist/shared/tmux/tmux-utils/environment.d.ts +1 -1
  71. package/dist/shared/tmux/tmux-utils.d.ts +1 -1
  72. package/dist/shared/typescript-native-source-parser.d.ts +7 -0
  73. package/dist/skills/coding-agent-sessions/SKILL.md +5 -2
  74. package/dist/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  75. package/dist/skills/coding-agent-sessions/references/senpi.md +15 -5
  76. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
  77. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
  78. package/dist/skills/debugging/SKILL.md +2 -1
  79. package/dist/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
  80. package/dist/skills/programming/SKILL.md +11 -1
  81. package/dist/skills/programming/references/logging.md +6 -1
  82. package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
  83. package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.ts +75 -11
  84. package/dist/skills/start-work/SKILL.md +12 -2
  85. package/dist/skills/ulw-plan/SKILL.md +21 -2
  86. package/dist/skills/ulw-plan/references/full-workflow.md +12 -1
  87. package/dist/skills/ulw-plan/references/intent-clear.md +1 -1
  88. package/dist/testing/create-plugin-module.d.ts +1 -0
  89. package/dist/tools/task/types.d.ts +10 -10
  90. package/dist/tui.js +23442 -19182
  91. package/package.json +14 -14
  92. package/packages/git-bash-mcp/dist/cli.js +54 -8
  93. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +62 -2
  94. package/packages/lsp-core/src/lsp/client.ts +14 -1
  95. package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +2 -2
  96. package/packages/lsp-core/src/mcp.ts +9 -0
  97. package/packages/lsp-daemon/dist/cli.js +319 -51
  98. package/packages/lsp-daemon/dist/client.js +136 -48
  99. package/packages/lsp-daemon/dist/daemon-client.d.ts +0 -10
  100. package/packages/lsp-daemon/dist/daemon-client.js +4 -34
  101. package/packages/lsp-daemon/dist/daemon-failure-result.d.ts +3 -0
  102. package/packages/lsp-daemon/dist/daemon-failure-result.js +38 -0
  103. package/packages/lsp-daemon/dist/daemon-request-error.d.ts +14 -0
  104. package/packages/lsp-daemon/dist/daemon-request-error.js +26 -0
  105. package/packages/lsp-daemon/dist/daemon-server.js +5 -1
  106. package/packages/lsp-daemon/dist/index.js +137 -48
  107. package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
  108. package/packages/lsp-daemon/dist/proxy.js +1 -0
  109. package/packages/lsp-daemon/dist/version-reap.d.ts +26 -0
  110. package/packages/lsp-daemon/dist/version-reap.js +182 -0
  111. package/packages/lsp-tools-mcp/dist/cli.js +61 -10
  112. package/packages/lsp-tools-mcp/dist/lsp/manager.js +6 -1
  113. package/packages/lsp-tools-mcp/dist/mcp.js +61 -10
  114. package/packages/lsp-tools-mcp/dist/tools.js +6 -1
  115. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  116. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +72 -12
  117. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  118. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  119. package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +22 -2
  120. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +779 -385
  121. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +302 -173
  122. package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
  123. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +3 -1
  124. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +23 -42
  125. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +98 -32
  126. package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +5 -0
  127. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +17 -9
  128. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +18 -25
  129. package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +1 -0
  130. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +5 -0
  131. package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +176 -0
  132. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +6 -1
  133. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +31 -2
  134. package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +1 -1
  135. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +6 -0
  136. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +65 -1
  137. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -7
  138. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +2 -1
  139. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +57 -3
  140. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +42 -0
  141. package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +122 -10
  142. package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +2 -2
  143. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +101 -64
  144. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  145. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  146. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  147. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  148. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  149. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  150. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  151. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +80 -40
  152. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  153. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  154. package/packages/omo-codex/plugin/components/rules/dist/cli.js +11 -0
  155. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  156. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  157. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +11 -0
  158. package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +60 -0
  159. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +1 -1
  160. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  161. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  162. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  163. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  164. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  165. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  166. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +2 -2
  167. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +2 -2
  168. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
  169. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +1 -1
  170. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +2 -2
  171. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
  172. package/packages/omo-codex/plugin/components/ultrawork/directive.md +12 -7
  173. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +1 -1
  174. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  175. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  176. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +12 -7
  177. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +21 -2
  178. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +12 -1
  179. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +1 -1
  180. package/packages/omo-codex/plugin/components/ultrawork/src/codex-hook.ts +1 -1
  181. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook-trigger-policy.test.ts +42 -0
  182. package/packages/omo-codex/plugin/components/ulw-loop/README.md +6 -4
  183. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +12 -7
  184. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.d.ts +21 -0
  185. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +64 -0
  186. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +15 -2
  187. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.js +1 -1
  188. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +2 -1
  189. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
  190. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +3 -3
  191. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.d.ts +3 -0
  192. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +85 -0
  193. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +0 -1
  194. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -33
  195. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +998 -593
  196. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +1 -0
  197. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.d.ts +1 -1
  198. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.js +2 -0
  199. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +6 -0
  200. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.d.ts +1 -0
  201. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.js +3 -0
  202. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +1 -0
  203. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +4 -0
  204. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +1 -0
  205. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +10 -2
  206. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +1 -1
  207. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.d.ts +15 -0
  208. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +84 -0
  209. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +5 -0
  210. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +81 -0
  211. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +7 -59
  212. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +12 -4
  213. package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.d.ts +9 -0
  214. package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.js +122 -0
  215. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  216. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  217. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +2 -1
  218. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +8 -6
  219. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +84 -0
  220. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +13 -2
  221. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-arg-parser.ts +1 -1
  222. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +1 -1
  223. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +3 -3
  224. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +49 -0
  225. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +13 -45
  226. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +1 -0
  227. package/packages/omo-codex/plugin/components/ulw-loop/src/constants.ts +2 -0
  228. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +7 -0
  229. package/packages/omo-codex/plugin/components/ulw-loop/src/goal-status.ts +4 -0
  230. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +4 -1
  231. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +20 -2
  232. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +1 -1
  233. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +125 -0
  234. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +59 -0
  235. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +6 -58
  236. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +12 -4
  237. package/packages/omo-codex/plugin/components/ulw-loop/src/validation-batch.ts +122 -0
  238. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +76 -0
  239. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-checkpoint-continuation.test.ts +111 -0
  240. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-steering-batch.test.ts +67 -0
  241. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-validation-batch.test.ts +57 -0
  242. package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +1 -0
  243. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +152 -0
  244. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +29 -0
  245. package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch-checkpoint.test.ts +147 -0
  246. package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch.test.ts +73 -0
  247. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  248. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  249. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  250. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  251. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  252. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  253. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  254. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  255. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  256. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  257. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  258. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +2 -2
  259. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  260. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +2 -2
  261. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  262. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +2 -2
  263. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  264. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  265. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  266. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  267. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  268. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  269. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  270. package/packages/omo-codex/plugin/package-lock.json +41 -41
  271. package/packages/omo-codex/plugin/package.json +1 -1
  272. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +44 -0
  273. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +5 -2
  274. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  275. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/senpi.md +15 -5
  276. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
  277. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
  278. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +2 -1
  279. package/packages/omo-codex/plugin/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
  280. package/packages/omo-codex/plugin/skills/programming/SKILL.md +11 -1
  281. package/packages/omo-codex/plugin/skills/programming/references/logging.md +6 -1
  282. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +75 -11
  283. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +12 -2
  284. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +12 -7
  285. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +2 -1
  286. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +8 -6
  287. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +21 -2
  288. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +12 -1
  289. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +1 -1
  290. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +9 -9
  291. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +26 -0
  292. package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +41 -0
  293. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +22 -0
  294. package/packages/omo-codex/scripts/install-dist/install-local.mjs +49 -5
  295. package/packages/omo-codex/scripts/install-marketplace-cache.test.mjs +4 -1
  296. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +5 -2
  297. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  298. package/packages/shared-skills/skills/coding-agent-sessions/references/senpi.md +15 -5
  299. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
  300. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
  301. package/packages/shared-skills/skills/debugging/SKILL.md +2 -1
  302. package/packages/shared-skills/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
  303. package/packages/shared-skills/skills/programming/SKILL.md +11 -1
  304. package/packages/shared-skills/skills/programming/references/logging.md +6 -1
  305. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
  306. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +75 -11
  307. package/packages/shared-skills/skills/start-work/SKILL.md +12 -2
  308. package/packages/shared-skills/skills/ulw-plan/SKILL.md +21 -2
  309. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +12 -1
  310. package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +1 -1
@@ -1,15 +1,14 @@
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 resolve7 } from "node:path";
4
+ import { realpathSync as realpathSync7 } from "node:fs";
5
+ import { basename as basename5, resolve as resolve9 } 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
- import { execFile as execFile4, spawn } from "node:child_process";
10
+ import { spawn as spawn2 } from "node:child_process";
11
11
  import { homedir as homedir13 } from "node:os";
12
- import { join as join13 } from "node:path";
13
12
  import {
14
13
  cwd as processCwd2,
15
14
  env as processEnv2,
@@ -56,6 +55,7 @@ var SAFE_AMBIENT_ENV_KEYS = new Set([
56
55
  var SAFE_CODEGRAPH_RUNTIME_ENV_KEYS = new Set([
57
56
  "CODEGRAPH_ALLOW_UNSAFE_NODE",
58
57
  "CODEGRAPH_BIN",
58
+ "CODEGRAPH_DAEMON_IDLE_TIMEOUT_MS",
59
59
  "CODEGRAPH_FAKE_LOG",
60
60
  "CODEGRAPH_NO_DAEMON",
61
61
  "CODEGRAPH_NODE_BIN",
@@ -67,7 +67,7 @@ function buildCodegraphEnv(options = {}) {
67
67
  const homeDir = options.homeDir ?? homedir();
68
68
  return {
69
69
  [CODEGRAPH_INSTALL_DIR_ENV]: join(homeDir, ".omo", "codegraph"),
70
- [CODEGRAPH_NO_DAEMON_ENV]: "1",
70
+ ...options.daemon === false ? { [CODEGRAPH_NO_DAEMON_ENV]: "1" } : {},
71
71
  [CODEGRAPH_NO_DOWNLOAD_ENV]: "1",
72
72
  [CODEGRAPH_TELEMETRY_ENV]: "0",
73
73
  [DO_NOT_TRACK_ENV]: "1"
@@ -399,6 +399,9 @@ var ANSI_ESCAPE_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
399
399
  var CODEGRAPH_STATUS_PROJECT_PATTERN = /^.*?\bProject:\s*(.+?)\s*$/im;
400
400
  var CODEGRAPH_STATUS_UNINITIALIZED_PATTERN = /^.*?\bNot initialized\s*$/im;
401
401
  var CODEGRAPH_INIT_HINT_PATTERN = /Run\s+["'`]codegraph init["'`]\s+(?:in that project first|to initialize)\.?/i;
402
+ var CODEGRAPH_PROJECT_NOT_INDEXED_PATTERN = /The project at (.+?) isn't indexed with codegraph\b/i;
403
+ var CODEGRAPH_NO_PROJECT_LOADED_PATTERN = /No CodeGraph project is loaded for this session\./i;
404
+ var CODEGRAPH_NO_PROJECT_SEARCHED_FROM_PATTERN = /^Searched for a \.codegraph\/ directory starting from:\s*(.+?)\s*$/im;
402
405
  function getCodegraphUninitializedProject(input) {
403
406
  const output = textFromUnknown(input.toolOutput);
404
407
  if (!isCodegraphTool(input.toolName))
@@ -439,6 +442,16 @@ function extractProjectPath(output) {
439
442
  const uninitializedProject = uninitializedMatch?.[1]?.trim();
440
443
  if (uninitializedProject && uninitializedProject.length > 0)
441
444
  return uninitializedProject;
445
+ const notIndexedMatch = normalizedOutput.match(CODEGRAPH_PROJECT_NOT_INDEXED_PATTERN);
446
+ const notIndexedProject = notIndexedMatch?.[1]?.trim();
447
+ if (notIndexedProject && notIndexedProject.length > 0)
448
+ return notIndexedProject;
449
+ if (CODEGRAPH_NO_PROJECT_LOADED_PATTERN.test(normalizedOutput)) {
450
+ const searchedFromMatch = normalizedOutput.match(CODEGRAPH_NO_PROJECT_SEARCHED_FROM_PATTERN);
451
+ const searchedFromProject = searchedFromMatch?.[1]?.trim();
452
+ if (searchedFromProject && searchedFromProject.length > 0)
453
+ return searchedFromProject;
454
+ }
442
455
  if (!looksLikeCodegraphUninitializedOutput(normalizedOutput))
443
456
  return null;
444
457
  const statusMatch = normalizedOutput.match(CODEGRAPH_STATUS_PROJECT_PATTERN);
@@ -479,16 +492,101 @@ function isRecord(value) {
479
492
  return typeof value === "object" && value !== null && !Array.isArray(value);
480
493
  }
481
494
 
495
+ // ../../utils/src/codegraph/managed-runtime.ts
496
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
497
+ import { join as join6, resolve as resolve4 } from "node:path";
498
+
499
+ // ../../utils/src/record-type-guard.ts
500
+ function isPlainRecord(value) {
501
+ return typeof value === "object" && value !== null && !Array.isArray(value);
502
+ }
503
+
504
+ // ../../utils/src/codegraph/manifest.ts
505
+ var CODEGRAPH_PINNED_VERSION = "1.4.1";
506
+ var CODEGRAPH_PROVISION_MANIFEST = {
507
+ assets: {
508
+ "darwin-arm64": {
509
+ executableName: "codegraph",
510
+ sha256: "4a679ae5a5cb9fff900dd59bb786da6a581b7f68f4cf713bdedd137e347d34dc",
511
+ url: "https://github.com/colbymchenry/codegraph/releases/download/v1.4.1/codegraph-darwin-arm64.tar.gz"
512
+ },
513
+ "darwin-x64": {
514
+ executableName: "codegraph",
515
+ sha256: "436f96943cfd926ea6d0a8454f18833d21254d5fd9b3d224317b1426132def95",
516
+ url: "https://github.com/colbymchenry/codegraph/releases/download/v1.4.1/codegraph-darwin-x64.tar.gz"
517
+ },
518
+ "linux-arm64": {
519
+ executableName: "codegraph",
520
+ sha256: "0d62c5eb2722f8d19d20f7a1bd974445e18d5294cb59be116a0c3d55ce87591f",
521
+ url: "https://github.com/colbymchenry/codegraph/releases/download/v1.4.1/codegraph-linux-arm64.tar.gz"
522
+ },
523
+ "linux-x64": {
524
+ executableName: "codegraph",
525
+ sha256: "fb585ff5018d6faaa46d282b61f4f689bc7967ed8a1b467a5c556dd7ced9b542",
526
+ url: "https://github.com/colbymchenry/codegraph/releases/download/v1.4.1/codegraph-linux-x64.tar.gz"
527
+ },
528
+ "win32-arm64": {
529
+ executableName: "codegraph.cmd",
530
+ sha256: "e2a2a28c802a79804c7df203afa50bd461309c6c180ce3f76079fdc7cddc7697",
531
+ url: "https://registry.npmjs.org/@colbymchenry/codegraph-win32-arm64/-/codegraph-win32-arm64-1.4.1.tgz"
532
+ },
533
+ "win32-x64": {
534
+ executableName: "codegraph.cmd",
535
+ sha256: "4f08700fda5f4a03ad5b2956135c5788d739a351b3433db2b5820e5d5224c30d",
536
+ url: "https://registry.npmjs.org/@colbymchenry/codegraph-win32-x64/-/codegraph-win32-x64-1.4.1.tgz"
537
+ }
538
+ },
539
+ version: CODEGRAPH_PINNED_VERSION
540
+ };
541
+
542
+ // ../../utils/src/codegraph/managed-runtime.ts
543
+ function resolvePinnedCodegraphBin(installDir, options = {}) {
544
+ if (installDir === undefined)
545
+ return null;
546
+ const fileExists = options.fileExists ?? existsSync3;
547
+ const readText = options.readText ?? ((filePath) => readFileSync3(filePath, "utf8"));
548
+ const expectedBin = join6(installDir, "bin", (options.platform ?? process.platform) === "win32" ? "codegraph.cmd" : "codegraph");
549
+ const markerPath = join6(installDir, ".provisioned", `codegraph-${CODEGRAPH_PINNED_VERSION}.json`);
550
+ if (!fileExists(expectedBin) || !fileExists(markerPath))
551
+ return null;
552
+ let markerText;
553
+ try {
554
+ markerText = readText(markerPath);
555
+ } catch {
556
+ return null;
557
+ }
558
+ const marker = parseProvisionMarker(markerText);
559
+ if (marker === null || marker.version !== CODEGRAPH_PINNED_VERSION)
560
+ return null;
561
+ return resolve4(marker.binPath) === resolve4(expectedBin) ? expectedBin : null;
562
+ }
563
+ function parseProvisionMarker(text) {
564
+ try {
565
+ const value = JSON.parse(text);
566
+ if (!isPlainRecord(value))
567
+ return null;
568
+ const binPath = value["binPath"];
569
+ const version = value["version"];
570
+ if (typeof binPath !== "string" || typeof version !== "string")
571
+ return null;
572
+ return { binPath, version };
573
+ } catch (error) {
574
+ if (error instanceof SyntaxError)
575
+ return null;
576
+ throw error;
577
+ }
578
+ }
579
+
482
580
  // ../../utils/src/codegraph/resolve.ts
483
- import { existsSync as existsSync3 } from "node:fs";
581
+ import { existsSync as existsSync4 } from "node:fs";
484
582
  import { homedir as homedir6 } from "node:os";
485
583
  import { spawnSync } from "node:child_process";
486
- import { basename as basename2, dirname as dirname2, join as join7 } from "node:path";
584
+ import { basename as basename2, dirname as dirname2, join as join8 } from "node:path";
487
585
  import { createRequire } from "node:module";
488
586
 
489
587
  // ../../utils/src/runtime/which.ts
490
588
  import { accessSync, constants } from "node:fs";
491
- import { delimiter, join as join6 } from "node:path";
589
+ import { delimiter, join as join7 } from "node:path";
492
590
  var runtime = globalThis;
493
591
  function isUnsafeCommandName(commandName) {
494
592
  if (commandName.includes("/") || commandName.includes("\\"))
@@ -543,7 +641,7 @@ function bunWhich(commandName) {
543
641
  return null;
544
642
  for (const pathEntry of pathEntries) {
545
643
  for (const candidateName of candidateNames) {
546
- const candidatePath = join6(pathEntry, candidateName);
644
+ const candidatePath = join7(pathEntry, candidateName);
547
645
  if (isExecutable(candidatePath))
548
646
  return candidatePath;
549
647
  }
@@ -660,18 +758,13 @@ function defaultNodeRuntime(env, fileExists, which, nodeVersion) {
660
758
  return null;
661
759
  }
662
760
  function defaultProvisionedBin(homeDir, fileExists) {
663
- const binaryName = process.platform === "win32" ? "codegraph.cmd" : "codegraph";
664
- const candidates = [
665
- join7(homeDir, ".omo", "codegraph", "bin", binaryName),
666
- join7(homeDir, ".omo", "codegraph", "node-servers", "node_modules", ".bin", binaryName)
667
- ];
668
- return candidates.find((candidate) => fileExists(candidate)) ?? null;
761
+ return resolvePinnedCodegraphBin(join8(homeDir, ".omo", "codegraph"), { fileExists });
669
762
  }
670
763
  function resolveBundledShim(requireResolve, fileExists) {
671
764
  try {
672
765
  const packageJson = requireResolve(`${CODEGRAPH_PACKAGE}/package.json`);
673
766
  const packageRoot = dirname2(packageJson);
674
- const candidates = [join7(packageRoot, "bin", "codegraph.js"), join7(packageRoot, "npm-shim.js")];
767
+ const candidates = [join8(packageRoot, "bin", "codegraph.js"), join8(packageRoot, "npm-shim.js")];
675
768
  return candidates.find((candidate) => fileExists(candidate)) ?? null;
676
769
  } catch (error) {
677
770
  if (error instanceof Error)
@@ -687,7 +780,7 @@ function resolveBundledShim(requireResolve, fileExists) {
687
780
  }
688
781
  function resolveCodegraphCommand(options = {}) {
689
782
  const env = options.env ?? process.env;
690
- const fileExists = options.fileExists ?? existsSync3;
783
+ const fileExists = options.fileExists ?? existsSync4;
691
784
  const configuredBin = env[CODEGRAPH_ENV_BIN]?.trim() || env[CODEGRAPH_LEGACY_ENV_BIN]?.trim();
692
785
  if (configuredBin !== undefined && configuredBin.length > 0) {
693
786
  return { argsPrefix: [], command: configuredBin, exists: fileExists(configuredBin), source: "env" };
@@ -712,15 +805,97 @@ function resolveCodegraphCommand(options = {}) {
712
805
  };
713
806
  }
714
807
 
808
+ // ../../utils/src/process-tree.ts
809
+ import { execFile, spawn } from "node:child_process";
810
+ import { StringDecoder } from "node:string_decoder";
811
+ function runProcessWithTreeTimeout(options) {
812
+ return new Promise((resolvePromise) => {
813
+ const child = spawn(options.command, [...options.args], {
814
+ cwd: options.cwd,
815
+ detached: process.platform !== "win32",
816
+ env: options.env,
817
+ stdio: ["ignore", "pipe", "pipe"],
818
+ windowsHide: true
819
+ });
820
+ let stderr = "";
821
+ let stderrBytes = 0;
822
+ let stdout = "";
823
+ let stdoutBytes = 0;
824
+ let timedOut = false;
825
+ let overflowed = false;
826
+ let settled = false;
827
+ let treeTermination;
828
+ const stderrDecoder = new StringDecoder("utf8");
829
+ const stdoutDecoder = new StringDecoder("utf8");
830
+ const capture = (target, chunk) => {
831
+ if (overflowed)
832
+ return;
833
+ const currentBytes = target === "stdout" ? stdoutBytes : stderrBytes;
834
+ if (currentBytes + chunk.length > options.maxBuffer) {
835
+ overflowed = true;
836
+ treeTermination ??= terminateProcessTree(child.pid);
837
+ return;
838
+ }
839
+ if (target === "stdout") {
840
+ stdoutBytes += chunk.length;
841
+ stdout += stdoutDecoder.write(chunk);
842
+ } else {
843
+ stderrBytes += chunk.length;
844
+ stderr += stderrDecoder.write(chunk);
845
+ }
846
+ };
847
+ child.stdout.on("data", (chunk) => capture("stdout", chunk));
848
+ child.stderr.on("data", (chunk) => capture("stderr", chunk));
849
+ const timeout = setTimeout(() => {
850
+ timedOut = true;
851
+ treeTermination ??= terminateProcessTree(child.pid);
852
+ }, options.timeoutMs);
853
+ timeout.unref();
854
+ const settle = async (exitCode, signal) => {
855
+ if (settled)
856
+ return;
857
+ settled = true;
858
+ clearTimeout(timeout);
859
+ await treeTermination;
860
+ stderr += stderrDecoder.end();
861
+ stdout += stdoutDecoder.end();
862
+ resolvePromise({ exitCode, signal, stderr, stdout, timedOut });
863
+ };
864
+ child.once("error", () => void settle(1, null));
865
+ child.once("close", (code, signal) => void settle(overflowed ? 1 : code ?? 1, signal));
866
+ });
867
+ }
868
+ function terminateProcessTree(pid) {
869
+ if (pid === undefined)
870
+ return Promise.resolve();
871
+ if (process.platform === "win32")
872
+ return taskkillProcessTree(pid);
873
+ try {
874
+ process.kill(-pid, "SIGKILL");
875
+ } catch (error) {
876
+ if (!isIgnorableKillError(error))
877
+ throw error;
878
+ }
879
+ return Promise.resolve();
880
+ }
881
+ function taskkillProcessTree(pid) {
882
+ return new Promise((resolvePromise) => {
883
+ execFile("taskkill.exe", ["/PID", String(pid), "/T", "/F"], { timeout: 5000, windowsHide: true }, () => resolvePromise());
884
+ });
885
+ }
886
+ function isIgnorableKillError(error) {
887
+ return error instanceof Error && "code" in error && (error.code === "EPERM" || error.code === "ESRCH");
888
+ }
889
+
715
890
  // shared/src/config-loader.ts
716
891
  import { homedir as homedir8 } from "node:os";
717
892
 
718
893
  // ../../utils/src/omo-config/loader.ts
719
- import { existsSync as existsSync5 } from "node:fs";
894
+ import { existsSync as existsSync6 } from "node:fs";
720
895
  import { homedir as homedir7 } from "node:os";
721
896
 
722
897
  // ../../utils/src/omo-config/body.ts
723
- import { readFileSync as readFileSync3 } from "node:fs";
898
+ import { readFileSync as readFileSync4 } from "node:fs";
724
899
 
725
900
  // ../../utils/src/deep-merge.ts
726
901
  var DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
@@ -1597,8 +1772,9 @@ function parseJsoncSafe(content) {
1597
1772
  var HARNESS_IDS = ["codex", "opencode", "omo"];
1598
1773
  var SETTING_HARNESS_SUPPORT = {
1599
1774
  "codegraph.auto_provision": HARNESS_IDS,
1775
+ "codegraph.daemon": ["codex", "opencode"],
1600
1776
  "codegraph.enabled": HARNESS_IDS,
1601
- "codegraph.excluded_roots": ["codex"],
1777
+ "codegraph.excluded_roots": ["codex", "opencode"],
1602
1778
  "codegraph.install_dir": HARNESS_IDS,
1603
1779
  "codegraph.telemetry": HARNESS_IDS,
1604
1780
  "codegraph.watch_debounce_ms": ["opencode", "omo"]
@@ -1608,6 +1784,7 @@ var SETTING_HARNESS_SUPPORT = {
1608
1784
  var BUILT_IN_DEFAULTS = {
1609
1785
  codegraph: {
1610
1786
  auto_provision: true,
1787
+ daemon: true,
1611
1788
  enabled: true,
1612
1789
  telemetry: false
1613
1790
  }
@@ -1615,6 +1792,7 @@ var BUILT_IN_DEFAULTS = {
1615
1792
  var HARNESS_BLOCK_KEYS = HARNESS_IDS.map((harness) => `[${harness}]`);
1616
1793
  var CODEGRAPH_SETTING_KEYS = [
1617
1794
  "auto_provision",
1795
+ "daemon",
1618
1796
  "enabled",
1619
1797
  "excluded_roots",
1620
1798
  "install_dir",
@@ -1688,6 +1866,10 @@ function setCodegraphSetting(config, key, value) {
1688
1866
  if (typeof value === "boolean")
1689
1867
  config.auto_provision = value;
1690
1868
  return;
1869
+ case "daemon":
1870
+ if (typeof value === "boolean")
1871
+ config.daemon = value;
1872
+ return;
1691
1873
  case "enabled":
1692
1874
  if (typeof value === "boolean")
1693
1875
  config.enabled = value;
@@ -1762,7 +1944,7 @@ function normalizeActiveHarnessBlock(value, harness, pathPrefix, warnings) {
1762
1944
  }
1763
1945
  function loadConfigFile(path, harness) {
1764
1946
  try {
1765
- const content = readFileSync3(path, "utf-8");
1947
+ const content = readFileSync4(path, "utf-8");
1766
1948
  const parsed = parseJsoncSafe(content);
1767
1949
  if (parsed.errors.length > 0) {
1768
1950
  return {
@@ -1885,22 +2067,22 @@ function buildEnvOverrides(harness, env, warnings, merge) {
1885
2067
  }
1886
2068
 
1887
2069
  // ../../utils/src/omo-config/resolve.ts
1888
- import { existsSync as existsSync4 } from "node:fs";
1889
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve4 } from "node:path";
2070
+ import { existsSync as existsSync5 } from "node:fs";
2071
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join9, relative, resolve as resolve5 } from "node:path";
1890
2072
  function containsPath(parent, child) {
1891
2073
  const pathToChild = relative(parent, child);
1892
2074
  return pathToChild === "" || !pathToChild.startsWith("..") && !isAbsolute2(pathToChild);
1893
2075
  }
1894
2076
  function findProjectConfigPathsNearestFirst(cwd, homeDir) {
1895
- const startDir = resolve4(cwd);
1896
- const stopBeforeDir = containsPath(resolve4(homeDir), startDir) ? resolve4(homeDir) : null;
2077
+ const startDir = resolve5(cwd);
2078
+ const stopBeforeDir = containsPath(resolve5(homeDir), startDir) ? resolve5(homeDir) : null;
1897
2079
  const paths = [];
1898
2080
  let currentDir = startDir;
1899
2081
  while (true) {
1900
2082
  if (stopBeforeDir !== null && currentDir === stopBeforeDir)
1901
2083
  break;
1902
- const configPath = join8(currentDir, ".omo", "config.jsonc");
1903
- if (existsSync4(configPath)) {
2084
+ const configPath = join9(currentDir, ".omo", "config.jsonc");
2085
+ if (existsSync5(configPath)) {
1904
2086
  paths.push(configPath);
1905
2087
  }
1906
2088
  const parentDir = dirname3(currentDir);
@@ -1911,7 +2093,7 @@ function findProjectConfigPathsNearestFirst(cwd, homeDir) {
1911
2093
  return paths;
1912
2094
  }
1913
2095
  function resolveOmoConfigPaths(options) {
1914
- const globalPath = join8(resolve4(options.homeDir), ".omo", "config.jsonc");
2096
+ const globalPath = join9(resolve5(options.homeDir), ".omo", "config.jsonc");
1915
2097
  const projectPathsFarthestFirst = findProjectConfigPathsNearestFirst(options.cwd, options.homeDir).reverse();
1916
2098
  return [
1917
2099
  { path: globalPath, scope: "global" },
@@ -1936,7 +2118,7 @@ function loadOmoConfig(options) {
1936
2118
  const sources = [];
1937
2119
  const warnings = [];
1938
2120
  for (const candidate of resolveOmoConfigPaths({ cwd, homeDir })) {
1939
- if (!existsSync5(candidate.path)) {
2121
+ if (!existsSync6(candidate.path)) {
1940
2122
  if (candidate.scope === "global") {
1941
2123
  sources.push(toMissingSource(candidate));
1942
2124
  }
@@ -2007,77 +2189,131 @@ function isNonFatalCodegraphGcError(error) {
2007
2189
 
2008
2190
  // components/codegraph/src/hook-sweep.ts
2009
2191
  import { fileURLToPath } from "node:url";
2010
- // ../../utils/src/codegraph/process-sweeper.ts
2011
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, statSync as statSync2, utimesSync, writeFileSync as writeFileSync2 } from "node:fs";
2192
+ // ../../utils/src/process-sweep/sweeper.ts
2193
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, statSync as statSync2, utimesSync, writeFileSync as writeFileSync2 } from "node:fs";
2012
2194
  import { homedir as homedir10 } from "node:os";
2013
- import { dirname as dirname4, join as join10 } from "node:path";
2014
-
2015
- // ../../utils/src/codegraph/process-exec.ts
2016
- import { execFile } from "node:child_process";
2195
+ import { dirname as dirname5, join as join12 } from "node:path";
2017
2196
 
2018
- // ../../utils/src/codegraph/process-match.ts
2019
- import { posix, win32 } from "node:path";
2020
- var SERVE_WRAPPER_SUFFIX = "/components/codegraph/dist/serve.js";
2021
- var UPSTREAM_PACKAGE_SEGMENT = "/@colbymchenry/codegraph/";
2022
- function parsePosixProcessTable(output) {
2023
- const processes = [];
2024
- for (const line of output.split(/\r?\n/)) {
2025
- const match = /^\s*(\d+)\s+(\d+)\s+(.+?)\s*$/.exec(line);
2026
- if (match === null)
2027
- continue;
2028
- const pid = Number(match[1]);
2029
- const ppid = Number(match[2]);
2030
- const command = match[3];
2031
- if (!isValidProcessId(pid) || !Number.isInteger(ppid) || ppid < 0 || command === undefined)
2032
- continue;
2033
- processes.push({ command, pid, ppid });
2197
+ // ../../utils/src/codegraph/daemon-lock.ts
2198
+ import { readFileSync as readFileSync5, realpathSync as realpathSync4 } from "node:fs";
2199
+ import { dirname as dirname4, join as join10, resolve as resolve6 } from "node:path";
2200
+ function parseDaemonLock(raw) {
2201
+ const trimmed = raw.trim();
2202
+ if (trimmed.length === 0)
2203
+ return null;
2204
+ try {
2205
+ const parsed = JSON.parse(trimmed);
2206
+ if (typeof parsed === "object" && parsed !== null && "pid" in parsed) {
2207
+ const pid = parsed.pid;
2208
+ if (typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0) {
2209
+ const record = parsed;
2210
+ return {
2211
+ pid,
2212
+ ...typeof record["socketPath"] === "string" ? { socketPath: record["socketPath"] } : {},
2213
+ ...typeof record["startedAt"] === "number" ? { startedAt: record["startedAt"] } : {},
2214
+ ...typeof record["version"] === "string" ? { version: record["version"] } : {}
2215
+ };
2216
+ }
2217
+ }
2218
+ return null;
2219
+ } catch (error) {
2220
+ if (!(error instanceof SyntaxError))
2221
+ throw error;
2034
2222
  }
2035
- return processes;
2223
+ const legacyPid = Number(trimmed);
2224
+ if (Number.isSafeInteger(legacyPid) && legacyPid > 0)
2225
+ return { pid: legacyPid };
2226
+ return null;
2036
2227
  }
2037
- function parseWindowsProcessTable(output) {
2038
- const parsed = parseJson(output);
2039
- const entries = Array.isArray(parsed) ? parsed : parsed === undefined ? [] : [parsed];
2040
- const processes = [];
2041
- for (const entry of entries) {
2042
- if (!isRecord3(entry))
2228
+ function daemonLockCandidates(projectRoot) {
2229
+ const dirs = new Set;
2230
+ const resolved = resolve6(projectRoot);
2231
+ collectAncestors(resolved, dirs);
2232
+ collectAncestors(realpathIfPossible2(resolved), dirs);
2233
+ return [...dirs].map((dir) => join10(dir, ".codegraph", "daemon.pid"));
2234
+ }
2235
+ function evaluateDaemonStaleness(pid, projectRoot) {
2236
+ let sawLock = false;
2237
+ for (const lockPath of daemonLockCandidates(projectRoot)) {
2238
+ const raw = readLockIfPresent(lockPath);
2239
+ if (raw === undefined)
2043
2240
  continue;
2044
- const pid = numberField(entry, "ProcessId");
2045
- const ppid = numberField(entry, "ParentProcessId");
2046
- const command = stringField(entry, "CommandLine");
2047
- if (pid === undefined || ppid === undefined || command === undefined || command.trim().length === 0)
2048
- continue;
2049
- processes.push({ command, pid, ppid });
2241
+ if (raw === null)
2242
+ return { stale: false, reason: "lock-unreadable" };
2243
+ sawLock = true;
2244
+ const lock = parseDaemonLock(raw);
2245
+ if (lock === null)
2246
+ return { stale: false, reason: "lock-unparseable" };
2247
+ if (lock.pid === pid)
2248
+ return { stale: false, reason: "lock-pid-match" };
2249
+ }
2250
+ return sawLock ? { stale: true, reason: "lock-pid-mismatch" } : { stale: true, reason: "lock-absent" };
2251
+ }
2252
+ function readLockIfPresent(lockPath) {
2253
+ try {
2254
+ return readFileSync5(lockPath, "utf8");
2255
+ } catch (error) {
2256
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
2257
+ return;
2258
+ if (error instanceof Error && "code" in error && error.code === "ENOTDIR")
2259
+ return;
2260
+ return null;
2050
2261
  }
2051
- return processes;
2052
2262
  }
2053
- function selectZombieCodegraphProcesses(processes, options) {
2054
- const platform = options.platform ?? process.platform;
2055
- const livePids = new Set(processes.map((processInfo) => processInfo.pid));
2056
- const roots = normalizeRoots(options.ownedRoots, platform);
2057
- const zombies = [];
2058
- for (const processInfo of processes) {
2059
- const match = matchOwnedCodegraphCommand(processInfo.command, roots, platform);
2060
- if (match === null)
2061
- continue;
2062
- if (!isOrphaned(processInfo, livePids))
2063
- continue;
2064
- zombies.push({ ...processInfo, matchedRoot: match.root, matchKind: match.kind });
2263
+ function collectAncestors(start, output) {
2264
+ let current = start;
2265
+ for (;; ) {
2266
+ output.add(current);
2267
+ const parent = dirname4(current);
2268
+ if (parent === current)
2269
+ return;
2270
+ current = parent;
2065
2271
  }
2066
- return zombies;
2067
2272
  }
2068
- function matchOwnedCodegraphCommand(command, roots, platform) {
2069
- const normalizedCommand = normalizeForComparison2(command, platform);
2070
- for (const root of roots) {
2071
- if (root.length === 0)
2273
+ function realpathIfPossible2(path) {
2274
+ try {
2275
+ return realpathSync4(path);
2276
+ } catch (error) {
2277
+ if (error instanceof Error)
2278
+ return path;
2279
+ throw error;
2280
+ }
2281
+ }
2282
+
2283
+ // ../../utils/src/process-sweep/command-match.ts
2284
+ import { posix, win32 } from "node:path";
2285
+ function splitCommandTokens(command) {
2286
+ const tokens = [];
2287
+ let current = "";
2288
+ let quote = null;
2289
+ let tokenStarted = false;
2290
+ for (const char of command) {
2291
+ if (quote !== null) {
2292
+ if (char === quote) {
2293
+ quote = null;
2294
+ } else {
2295
+ current += char;
2296
+ }
2297
+ continue;
2298
+ }
2299
+ if (char === '"' || char === "'") {
2300
+ quote = char;
2301
+ tokenStarted = true;
2302
+ continue;
2303
+ }
2304
+ if (/\s/.test(char)) {
2305
+ if (tokenStarted || current.length > 0) {
2306
+ tokens.push(current);
2307
+ current = "";
2308
+ tokenStarted = false;
2309
+ }
2072
2310
  continue;
2073
- const serveWrapper = `${root}${SERVE_WRAPPER_SUFFIX}`;
2074
- if (hasExecutableToken(normalizedCommand, serveWrapper))
2075
- return { kind: "serve-wrapper", root };
2076
- if (upstreamPackagePathIsUnderRoot(normalizedCommand, root)) {
2077
- return { kind: "upstream-codegraph", root };
2078
2311
  }
2312
+ current += char;
2079
2313
  }
2080
- return null;
2314
+ if (tokenStarted || current.length > 0)
2315
+ tokens.push(current);
2316
+ return tokens;
2081
2317
  }
2082
2318
  function hasExecutableToken(command, expectedPath) {
2083
2319
  let searchFrom = 0;
@@ -2093,24 +2329,19 @@ function hasExecutableToken(command, expectedPath) {
2093
2329
  }
2094
2330
  }
2095
2331
  function tokenLooksExecutable(command, tokenStart) {
2096
- const prefix = command.slice(0, tokenStart).trimEnd();
2332
+ let prefix = command.slice(0, tokenStart).trimEnd();
2097
2333
  if (prefix.length === 0)
2098
2334
  return true;
2099
- const previousTokenStart = findTokenStart(prefix, prefix.length - 1);
2100
- const previousToken = prefix.slice(previousTokenStart);
2101
- const executableName = previousToken.split("/").at(-1) ?? previousToken;
2102
- return /^node\d*(\.exe)?$/.test(executableName) || /^bun(\.exe)?$/.test(executableName);
2103
- }
2104
- function upstreamPackagePathIsUnderRoot(command, root) {
2105
- let searchFrom = 0;
2106
2335
  for (;; ) {
2107
- const packageIndex = command.indexOf(UPSTREAM_PACKAGE_SEGMENT, searchFrom);
2108
- if (packageIndex < 0)
2336
+ const previousTokenStart = findTokenStart(prefix, prefix.length - 1);
2337
+ const previousToken = prefix.slice(previousTokenStart);
2338
+ if (!previousToken.startsWith("-")) {
2339
+ const executableName = previousToken.split("/").at(-1) ?? previousToken;
2340
+ return /^node\d*(\.exe)?$/i.test(executableName) || /^bun(\.exe)?$/i.test(executableName);
2341
+ }
2342
+ prefix = prefix.slice(0, previousTokenStart).trimEnd();
2343
+ if (prefix.length === 0)
2109
2344
  return false;
2110
- const tokenStart = findTokenStart(command, packageIndex);
2111
- if (command.slice(tokenStart).startsWith(`${root}/`) && tokenLooksExecutable(command, tokenStart))
2112
- return true;
2113
- searchFrom = packageIndex + UPSTREAM_PACKAGE_SEGMENT.length;
2114
2345
  }
2115
2346
  }
2116
2347
  function findTokenStart(command, index) {
@@ -2144,6 +2375,39 @@ function normalizeForComparison2(value, platform) {
2144
2375
  const normalized = value.replaceAll("\\", "/").replace(/\/+$/, "");
2145
2376
  return platform === "win32" ? normalized.toLowerCase() : normalized;
2146
2377
  }
2378
+
2379
+ // ../../utils/src/process-sweep/process-table.ts
2380
+ function parsePosixProcessTable(output) {
2381
+ const processes = [];
2382
+ for (const line of output.split(/\r?\n/)) {
2383
+ const match = /^\s*(\d+)\s+(\d+)\s+(.+?)\s*$/.exec(line);
2384
+ if (match === null)
2385
+ continue;
2386
+ const pid = Number(match[1]);
2387
+ const ppid = Number(match[2]);
2388
+ const command = match[3];
2389
+ if (!isValidProcessId(pid) || !Number.isInteger(ppid) || ppid < 0 || command === undefined)
2390
+ continue;
2391
+ processes.push({ command, pid, ppid });
2392
+ }
2393
+ return processes;
2394
+ }
2395
+ function parseWindowsProcessTable(output) {
2396
+ const parsed = parseJson(output);
2397
+ const entries = Array.isArray(parsed) ? parsed : parsed === undefined ? [] : [parsed];
2398
+ const processes = [];
2399
+ for (const entry of entries) {
2400
+ if (!isRecord3(entry))
2401
+ continue;
2402
+ const pid = numberField(entry, "ProcessId");
2403
+ const ppid = numberField(entry, "ParentProcessId");
2404
+ const command = stringField(entry, "CommandLine");
2405
+ if (pid === undefined || ppid === undefined || command === undefined || command.trim().length === 0)
2406
+ continue;
2407
+ processes.push({ command, pid, ppid });
2408
+ }
2409
+ return processes;
2410
+ }
2147
2411
  function isOrphaned(processInfo, livePids) {
2148
2412
  return processInfo.ppid === 1 || !livePids.has(processInfo.ppid);
2149
2413
  }
@@ -2171,13 +2435,113 @@ function isRecord3(value) {
2171
2435
  return typeof value === "object" && value !== null;
2172
2436
  }
2173
2437
 
2174
- // ../../utils/src/codegraph/process-exec.ts
2175
- function enumerateCodegraphProcesses(platform = process.platform) {
2438
+ // ../../utils/src/process-sweep/codegraph-family.ts
2439
+ var SERVE_WRAPPER_SUFFIX = "/components/codegraph/dist/serve.js";
2440
+ var UPSTREAM_PACKAGE_SEGMENT = "/@colbymchenry/codegraph/";
2441
+ function selectZombieCodegraphProcesses(processes, options) {
2442
+ const platform = options.platform ?? process.platform;
2443
+ const livePids = new Set(processes.map((processInfo) => processInfo.pid));
2444
+ const roots = normalizeRoots(options.ownedRoots, platform);
2445
+ const zombies = [];
2446
+ for (const processInfo of processes) {
2447
+ const daemon = matchDaemonCommand(processInfo.command, roots, platform);
2448
+ if (daemon !== null) {
2449
+ if (!isOrphaned(processInfo, livePids))
2450
+ continue;
2451
+ zombies.push({
2452
+ ...processInfo,
2453
+ daemonProjectRoot: daemon.projectRoot,
2454
+ matchedRoot: daemon.root,
2455
+ matchKind: "upstream-daemon"
2456
+ });
2457
+ continue;
2458
+ }
2459
+ const match = matchOwnedCodegraphCommand(processInfo.command, roots, platform);
2460
+ if (match === null)
2461
+ continue;
2462
+ if (!isOrphaned(processInfo, livePids))
2463
+ continue;
2464
+ zombies.push({ ...processInfo, matchedRoot: match.root, matchKind: match.kind });
2465
+ }
2466
+ return zombies;
2467
+ }
2468
+ var STANDALONE_LAUNCHER_SUFFIXES = ["/bin/codegraph", "/bin/codegraph.exe"];
2469
+ var BUNDLE_SCRIPT_SUFFIX = "/lib/dist/bin/codegraph.js";
2470
+ function matchDaemonCommand(command, roots, platform) {
2471
+ const projectRoot = extractDaemonProjectRoot(splitCommandTokens(command));
2472
+ if (projectRoot === null)
2473
+ return null;
2474
+ const normalizedCommand = normalizeForComparison2(command, platform);
2475
+ for (const root of roots) {
2476
+ if (root.length === 0)
2477
+ continue;
2478
+ if (upstreamPackagePathIsUnderRoot(normalizedCommand, root))
2479
+ return { projectRoot, root };
2480
+ for (const suffix of STANDALONE_LAUNCHER_SUFFIXES) {
2481
+ if (hasExecutableToken(normalizedCommand, `${root}${suffix}`))
2482
+ return { projectRoot, root };
2483
+ }
2484
+ if (hasExecutableToken(normalizedCommand, `${root}${BUNDLE_SCRIPT_SUFFIX}`))
2485
+ return { projectRoot, root };
2486
+ }
2487
+ return null;
2488
+ }
2489
+ function extractDaemonProjectRoot(tokens) {
2490
+ if (!tokens.includes("serve") || !tokens.includes("--mcp"))
2491
+ return null;
2492
+ const pathIndex = tokens.indexOf("--path");
2493
+ if (pathIndex < 0)
2494
+ return null;
2495
+ const value = tokens[pathIndex + 1];
2496
+ if (value === undefined || value.length === 0 || value.startsWith("--"))
2497
+ return null;
2498
+ return value;
2499
+ }
2500
+ function matchOwnedCodegraphCommand(command, roots, platform) {
2501
+ const normalizedCommand = normalizeForComparison2(command, platform);
2502
+ for (const root of roots) {
2503
+ if (root.length === 0)
2504
+ continue;
2505
+ const serveWrapper = `${root}${SERVE_WRAPPER_SUFFIX}`;
2506
+ if (hasExecutableToken(normalizedCommand, serveWrapper))
2507
+ return { kind: "serve-wrapper", root };
2508
+ if (upstreamPackagePathIsUnderRoot(normalizedCommand, root)) {
2509
+ return { kind: "upstream-codegraph", root };
2510
+ }
2511
+ }
2512
+ return null;
2513
+ }
2514
+ function upstreamPackagePathIsUnderRoot(command, root) {
2515
+ let searchFrom = 0;
2516
+ for (;; ) {
2517
+ const packageIndex = command.indexOf(UPSTREAM_PACKAGE_SEGMENT, searchFrom);
2518
+ if (packageIndex < 0)
2519
+ return false;
2520
+ const tokenStart = findTokenStart(command, packageIndex);
2521
+ if (command.slice(tokenStart).startsWith(`${root}/`) && tokenLooksExecutable(command, tokenStart))
2522
+ return true;
2523
+ searchFrom = packageIndex + UPSTREAM_PACKAGE_SEGMENT.length;
2524
+ }
2525
+ }
2526
+
2527
+ // ../../utils/src/process-sweep/exec.ts
2528
+ import { execFile as execFile2 } from "node:child_process";
2529
+ function enumerateProcesses(platform = process.platform) {
2176
2530
  return platform === "win32" ? enumerateWindowsProcesses() : enumeratePosixProcesses();
2177
2531
  }
2178
- function createDefaultCodegraphProcessKiller(platform = process.platform) {
2532
+ function createDefaultProcessKiller(platform = process.platform) {
2179
2533
  return platform === "win32" ? createWindowsKiller() : createPosixKiller();
2180
2534
  }
2535
+ function defaultIsProcessAlive(pid) {
2536
+ try {
2537
+ process.kill(pid, 0);
2538
+ return true;
2539
+ } catch (error) {
2540
+ if (!(error instanceof Error))
2541
+ throw error;
2542
+ return processKillErrorMeansAlive(error);
2543
+ }
2544
+ }
2181
2545
  function enumeratePosixProcesses() {
2182
2546
  return execFileText("ps", ["-eo", "pid=,ppid=,command="]).then(parsePosixProcessTable);
2183
2547
  }
@@ -2191,16 +2555,7 @@ function enumerateWindowsProcesses() {
2191
2555
  }
2192
2556
  function createPosixKiller() {
2193
2557
  return {
2194
- isAlive: (pid) => {
2195
- try {
2196
- process.kill(pid, 0);
2197
- return true;
2198
- } catch (error) {
2199
- if (!(error instanceof Error))
2200
- throw error;
2201
- return processKillErrorMeansAlive(error);
2202
- }
2203
- },
2558
+ isAlive: defaultIsProcessAlive,
2204
2559
  kill: (pid) => {
2205
2560
  process.kill(pid, "SIGKILL");
2206
2561
  return Promise.resolve();
@@ -2213,23 +2568,14 @@ function createPosixKiller() {
2213
2568
  }
2214
2569
  function createWindowsKiller() {
2215
2570
  return {
2216
- isAlive: (pid) => {
2217
- try {
2218
- process.kill(pid, 0);
2219
- return true;
2220
- } catch (error) {
2221
- if (!(error instanceof Error))
2222
- throw error;
2223
- return processKillErrorMeansAlive(error);
2224
- }
2225
- },
2571
+ isAlive: defaultIsProcessAlive,
2226
2572
  kill: (pid) => execFileVoid("taskkill.exe", ["/PID", String(pid), "/T", "/F"]),
2227
2573
  terminate: (pid) => execFileVoid("taskkill.exe", ["/PID", String(pid), "/T"])
2228
2574
  };
2229
2575
  }
2230
2576
  function execFileText(command, args) {
2231
2577
  return new Promise((resolvePromise, reject) => {
2232
- execFile(command, [...args], { encoding: "utf8", maxBuffer: 16 * 1024 * 1024, windowsHide: true }, (error, stdout) => {
2578
+ execFile2(command, [...args], { encoding: "utf8", maxBuffer: 16 * 1024 * 1024, windowsHide: true }, (error, stdout) => {
2233
2579
  if (error !== null) {
2234
2580
  reject(error);
2235
2581
  return;
@@ -2252,10 +2598,10 @@ function processKillErrorMeansAlive(error) {
2252
2598
  return false;
2253
2599
  }
2254
2600
 
2255
- // ../../utils/src/codegraph/process-roots.ts
2256
- import { existsSync as existsSync6, readdirSync as readdirSync2, realpathSync as realpathSync4 } from "node:fs";
2601
+ // ../../utils/src/process-sweep/roots.ts
2602
+ import { existsSync as existsSync7, readdirSync as readdirSync2, realpathSync as realpathSync5 } from "node:fs";
2257
2603
  import { homedir as homedir9 } from "node:os";
2258
- import { join as join9, resolve as resolve5 } from "node:path";
2604
+ import { join as join11, resolve as resolve7 } from "node:path";
2259
2605
  function discoverCodegraphOwnedRoots(options = {}) {
2260
2606
  const env = options.env ?? process.env;
2261
2607
  const homeDir = options.homeDir ?? env["HOME"] ?? env["USERPROFILE"] ?? homedir9();
@@ -2265,33 +2611,33 @@ function discoverCodegraphOwnedRoots(options = {}) {
2265
2611
  addRoot(roots, options.pluginRoot);
2266
2612
  for (const root of options.extraRoots ?? [])
2267
2613
  addRoot(roots, root);
2268
- for (const root of readCodexPluginCacheRoots(options.codexHome ?? env["CODEX_HOME"] ?? join9(homeDir, ".codex"))) {
2614
+ for (const root of readCodexPluginCacheRoots(options.codexHome ?? env["CODEX_HOME"] ?? join11(homeDir, ".codex"))) {
2269
2615
  addRoot(roots, root);
2270
2616
  }
2271
2617
  return [...roots];
2272
2618
  }
2273
2619
  function readCodexPluginCacheRoots(codexHome) {
2274
- const cacheRoot = join9(codexHome, "plugins", "cache");
2275
- if (!existsSync6(cacheRoot))
2620
+ const cacheRoot = join11(codexHome, "plugins", "cache");
2621
+ if (!existsSync7(cacheRoot))
2276
2622
  return [];
2277
2623
  const roots = [];
2278
2624
  for (const publisher of safeReadDir(cacheRoot)) {
2279
2625
  if (!OMO_CODEX_PLUGIN_CACHE_PUBLISHERS.has(publisher))
2280
2626
  continue;
2281
- const omoRoot = join9(cacheRoot, publisher, "omo");
2282
- if (!existsSync6(omoRoot))
2627
+ const omoRoot = join11(cacheRoot, publisher, "omo");
2628
+ if (!existsSync7(omoRoot))
2283
2629
  continue;
2284
2630
  for (const version of safeReadDir(omoRoot))
2285
- roots.push(join9(omoRoot, version));
2631
+ roots.push(join11(omoRoot, version));
2286
2632
  }
2287
2633
  return roots;
2288
2634
  }
2289
2635
  function addRoot(roots, root) {
2290
2636
  if (root === undefined || root.trim().length === 0)
2291
2637
  return;
2292
- const resolved = resolve5(root);
2638
+ const resolved = resolve7(root);
2293
2639
  roots.add(resolved);
2294
- roots.add(realpathIfPossible2(resolved));
2640
+ roots.add(realpathIfPossible3(resolved));
2295
2641
  }
2296
2642
  function safeReadDir(path) {
2297
2643
  try {
@@ -2302,12 +2648,12 @@ function safeReadDir(path) {
2302
2648
  throw error;
2303
2649
  }
2304
2650
  }
2305
- function realpathIfPossible2(path) {
2651
+ function realpathIfPossible3(path) {
2306
2652
  try {
2307
- return realpathSync4(path);
2653
+ return realpathSync5(path);
2308
2654
  } catch (error) {
2309
2655
  if (error instanceof Error)
2310
- return resolve5(path);
2656
+ return resolve7(path);
2311
2657
  throw error;
2312
2658
  }
2313
2659
  }
@@ -2319,88 +2665,124 @@ function isNonFatalFsError(error) {
2319
2665
  }
2320
2666
  var OMO_CODEX_PLUGIN_CACHE_PUBLISHERS = new Set(["sisyphuslabs"]);
2321
2667
 
2322
- // ../../utils/src/codegraph/process-sweeper.ts
2668
+ // ../../utils/src/process-sweep/sweeper.ts
2323
2669
  var DEFAULT_GRACE_MS = 2000;
2324
2670
  var DEFAULT_THROTTLE_MS = 60 * 60 * 1000;
2325
- var SWEEP_STAMP_FILE = "zombie-sweep.stamp";
2326
- async function sweepCodegraphZombies(options = {}) {
2327
- const homeDir = options.homeDir ?? options.env?.["HOME"] ?? options.env?.["USERPROFILE"] ?? homedir10();
2328
- const stampFile = join10(codegraphDataRoot(homeDir), SWEEP_STAMP_FILE);
2671
+ async function runProcessFamilySweep(config, options) {
2329
2672
  const nowMs = options.nowMs ?? Date.now();
2330
2673
  const dryRun = options.dryRun === true;
2331
- const ownedRoots = options.ownedRoots ?? discoverCodegraphOwnedRoots(options);
2332
- if (options.force !== true && isSweepThrottled(stampFile, nowMs, options.throttleMs ?? DEFAULT_THROTTLE_MS)) {
2333
- return emptyResult("throttled", dryRun, ownedRoots, stampFile);
2674
+ if (options.force !== true && isSweepThrottled(config.stampFile, nowMs, options.throttleMs ?? DEFAULT_THROTTLE_MS)) {
2675
+ return { action: "throttled", candidates: [], dryRun, failed: [], killed: [], spared: [], stampFile: config.stampFile };
2334
2676
  }
2335
2677
  try {
2336
- const provider = options.processProvider ?? (() => enumerateCodegraphProcesses(options.platform));
2337
- const candidates = selectZombieCodegraphProcesses(await provider(), {
2338
- ownedRoots,
2339
- ...options.platform === undefined ? {} : { platform: options.platform }
2340
- });
2341
- const result = dryRun ? { failed: [], killed: [] } : await killCandidates(candidates, options.killer ?? createDefaultCodegraphProcessKiller(options.platform), options);
2678
+ const plan = await config.collect();
2679
+ const { failed, killed } = dryRun ? { failed: [], killed: [] } : await killTargets(plan.killList, options.killer ?? createDefaultProcessKiller(options.platform), options, config.familyLabel);
2342
2680
  if (!dryRun)
2343
- writeSweepStamp(stampFile, nowMs);
2344
- return { action: "swept", candidates, dryRun, failed: result.failed, killed: result.killed, ownedRoots, stampFile };
2681
+ writeSweepStamp(config.stampFile, nowMs);
2682
+ return {
2683
+ action: "swept",
2684
+ candidates: plan.candidates,
2685
+ dryRun,
2686
+ failed,
2687
+ killed,
2688
+ spared: plan.spared,
2689
+ stampFile: config.stampFile
2690
+ };
2345
2691
  } catch (error) {
2346
- options.log?.(`CodeGraph zombie sweep skipped: ${error instanceof Error ? error.message : String(error)}`);
2347
- return emptyResult("failed", dryRun, ownedRoots, stampFile);
2692
+ options.log?.(`${config.familyLabel} skipped: ${error instanceof Error ? error.message : String(error)}`);
2693
+ return { action: "failed", candidates: [], dryRun, failed: [], killed: [], spared: [], stampFile: config.stampFile };
2694
+ }
2695
+ }
2696
+ var CODEGRAPH_SWEEP_STAMP_FILE = "zombie-sweep.stamp";
2697
+ async function sweepCodegraphZombies(options = {}) {
2698
+ const homeDir = options.homeDir ?? options.env?.["HOME"] ?? options.env?.["USERPROFILE"] ?? homedir10();
2699
+ const stampFile = join12(codegraphDataRoot(homeDir), CODEGRAPH_SWEEP_STAMP_FILE);
2700
+ const ownedRoots = options.ownedRoots ?? discoverCodegraphOwnedRoots(options);
2701
+ const result = await runProcessFamilySweep({
2702
+ familyLabel: "CodeGraph zombie sweep",
2703
+ stampFile,
2704
+ collect: async () => {
2705
+ const provider = options.processProvider ?? (() => enumerateProcesses(options.platform));
2706
+ const candidates = selectZombieCodegraphProcesses(await provider(), {
2707
+ ownedRoots,
2708
+ ...options.platform === undefined ? {} : { platform: options.platform }
2709
+ });
2710
+ const { killList, spared } = partitionByDaemonStaleness(candidates, options.log);
2711
+ return { candidates, killList, spared };
2712
+ }
2713
+ }, options);
2714
+ return { ...result, ownedRoots };
2715
+ }
2716
+ function partitionByDaemonStaleness(candidates, log) {
2717
+ const killList = [];
2718
+ const spared = [];
2719
+ for (const candidate of candidates) {
2720
+ if (candidate.matchKind !== "upstream-daemon") {
2721
+ killList.push(candidate);
2722
+ continue;
2723
+ }
2724
+ const staleness = evaluateDaemonStaleness(candidate.pid, candidate.daemonProjectRoot ?? candidate.matchedRoot);
2725
+ if (staleness.stale) {
2726
+ log?.(`CodeGraph zombie sweep sweeping stale daemon pid ${candidate.pid} (${staleness.reason})`);
2727
+ killList.push(candidate);
2728
+ continue;
2729
+ }
2730
+ log?.(`CodeGraph zombie sweep spared live daemon pid ${candidate.pid} (${staleness.reason})`);
2731
+ spared.push(candidate);
2348
2732
  }
2733
+ return { killList, spared };
2349
2734
  }
2350
- async function killCandidates(candidates, killer, options) {
2735
+ async function killTargets(targets, killer, options, familyLabel) {
2351
2736
  const failed = [];
2352
2737
  const killed = [];
2353
- for (const candidate of candidates) {
2354
- const terminated = await safelyTerminate(candidate.pid, killer, failed, options.log);
2738
+ for (const target of targets) {
2739
+ const terminated = await safelyTerminate(target.pid, killer, failed, options.log, familyLabel);
2355
2740
  if (!terminated)
2356
2741
  continue;
2357
2742
  await delay(options.graceMs ?? DEFAULT_GRACE_MS);
2358
- if (!await killer.isAlive(candidate.pid)) {
2359
- killed.push(candidate);
2743
+ if (!await killer.isAlive(target.pid)) {
2744
+ killed.push(target);
2360
2745
  continue;
2361
2746
  }
2362
- if (await safelyKill(candidate.pid, killer, failed, options.log))
2363
- killed.push(candidate);
2747
+ if (await safelyKill(target.pid, killer, failed, options.log, familyLabel))
2748
+ killed.push(target);
2364
2749
  }
2365
2750
  return { failed, killed };
2366
2751
  }
2367
- async function safelyTerminate(pid, killer, failed, log) {
2752
+ async function safelyTerminate(pid, killer, failed, log, familyLabel) {
2368
2753
  try {
2369
2754
  await killer.terminate(pid);
2370
2755
  return true;
2371
2756
  } catch (error) {
2372
2757
  const message = error instanceof Error ? error.message : String(error);
2373
2758
  failed.push({ error: message, pid, stage: "terminate" });
2374
- log?.(`CodeGraph zombie sweep failed to terminate pid ${pid}: ${message}`);
2759
+ log?.(`${familyLabel} failed to terminate pid ${pid}: ${message}`);
2375
2760
  return false;
2376
2761
  }
2377
2762
  }
2378
- async function safelyKill(pid, killer, failed, log) {
2763
+ async function safelyKill(pid, killer, failed, log, familyLabel) {
2379
2764
  try {
2380
2765
  await killer.kill(pid);
2381
2766
  return true;
2382
2767
  } catch (error) {
2383
2768
  const message = error instanceof Error ? error.message : String(error);
2384
2769
  failed.push({ error: message, pid, stage: "kill" });
2385
- log?.(`CodeGraph zombie sweep failed to kill pid ${pid}: ${message}`);
2770
+ log?.(`${familyLabel} failed to kill pid ${pid}: ${message}`);
2386
2771
  return false;
2387
2772
  }
2388
2773
  }
2389
2774
  function isSweepThrottled(stampFile, nowMs, throttleMs) {
2390
- if (!existsSync7(stampFile))
2775
+ if (!existsSync8(stampFile))
2391
2776
  return false;
2392
2777
  return nowMs - statSync2(stampFile).mtimeMs < throttleMs;
2393
2778
  }
2394
2779
  function writeSweepStamp(stampFile, nowMs) {
2395
- mkdirSync2(dirname4(stampFile), { recursive: true });
2780
+ mkdirSync2(dirname5(stampFile), { recursive: true });
2396
2781
  writeFileSync2(stampFile, `${new Date(nowMs).toISOString()}
2397
2782
  `);
2398
2783
  const stampDate = new Date(nowMs);
2399
2784
  utimesSync(stampFile, stampDate, stampDate);
2400
2785
  }
2401
- function emptyResult(action, dryRun, ownedRoots, stampFile) {
2402
- return { action, candidates: [], dryRun, failed: [], killed: [], ownedRoots, stampFile };
2403
- }
2404
2786
  function delay(ms) {
2405
2787
  if (ms <= 0)
2406
2788
  return Promise.resolve();
@@ -2425,71 +2807,31 @@ function defaultPluginRoot() {
2425
2807
  }
2426
2808
 
2427
2809
  // components/codegraph/src/session-start-worker.ts
2428
- import { execFile as execFile3 } from "node:child_process";
2429
- import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync3 } from "node:fs";
2810
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync3 } from "node:fs";
2430
2811
  import { homedir as homedir12 } from "node:os";
2431
- import { extname, join as join12 } from "node:path";
2812
+ import { extname, join as join14 } from "node:path";
2432
2813
  import { cwd as processCwd, env as processEnv, execPath as processExecPath, stderr as processStderr } from "node:process";
2433
2814
 
2434
2815
  // ../../utils/src/codegraph/provision.ts
2435
2816
  import { createHash as createHash2, randomUUID } from "node:crypto";
2436
- import { execFile as execFile2 } from "node:child_process";
2817
+ import { execFile as execFile3 } from "node:child_process";
2437
2818
  import { chmod, mkdir, readdir, readFile, rename, rm, rmdir, stat, writeFile } from "node:fs/promises";
2438
- import { existsSync as existsSync8 } from "node:fs";
2819
+ import { existsSync as existsSync9 } from "node:fs";
2439
2820
  import { homedir as homedir11, hostname } from "node:os";
2440
- import { basename as basename3, join as join11 } from "node:path";
2821
+ import { basename as basename3, join as join13 } from "node:path";
2441
2822
  import { promisify } from "node:util";
2442
-
2443
- // ../../utils/src/codegraph/manifest.ts
2444
- var CODEGRAPH_PROVISION_MANIFEST = {
2445
- assets: {
2446
- "darwin-arm64": {
2447
- executableName: "codegraph",
2448
- sha256: "95bb27bf6382b69659e158e0c04d71cc394778951e1317d582be7807e7866908",
2449
- url: "https://github.com/colbymchenry/codegraph/releases/download/v1.0.1/codegraph-darwin-arm64.tar.gz"
2450
- },
2451
- "darwin-x64": {
2452
- executableName: "codegraph",
2453
- sha256: "3311cc1d1f0f0ad742709b6a43d8a9187b1ef0af0dd30e0b58008dc673e29478",
2454
- url: "https://github.com/colbymchenry/codegraph/releases/download/v1.0.1/codegraph-darwin-x64.tar.gz"
2455
- },
2456
- "linux-arm64": {
2457
- executableName: "codegraph",
2458
- sha256: "e16f612bc96c2ebccd04574cbed500c9939147c80666ad6bb024398dff7992ae",
2459
- url: "https://github.com/colbymchenry/codegraph/releases/download/v1.0.1/codegraph-linux-arm64.tar.gz"
2460
- },
2461
- "linux-x64": {
2462
- executableName: "codegraph",
2463
- sha256: "d45a068f44596a85c7ba7d0ef924eaf7103fbbf3cafbeb668127daff60a52228",
2464
- url: "https://github.com/colbymchenry/codegraph/releases/download/v1.0.1/codegraph-linux-x64.tar.gz"
2465
- },
2466
- "win32-arm64": {
2467
- executableName: "codegraph.cmd",
2468
- sha256: "8d57ced73b24d35f758f2ede2318e80e1d7241987f37a999e3d80edb6fddf961",
2469
- url: "https://registry.npmjs.org/@colbymchenry/codegraph-win32-arm64/-/codegraph-win32-arm64-1.0.1.tgz"
2470
- },
2471
- "win32-x64": {
2472
- executableName: "codegraph.cmd",
2473
- sha256: "52607fe73b05e741fd1087da2ceca9d3c8f565e36bf1a7070600bdbdf3931e32",
2474
- url: "https://registry.npmjs.org/@colbymchenry/codegraph-win32-x64/-/codegraph-win32-x64-1.0.1.tgz"
2475
- }
2476
- },
2477
- version: "1.0.1"
2478
- };
2479
-
2480
- // ../../utils/src/codegraph/provision.ts
2481
2823
  var DEFAULT_LOCK_WAIT_MS = 5000;
2482
2824
  var DEFAULT_LOCK_STALE_MS = 120000;
2483
2825
  var DEFAULT_DOWNLOAD_TIMEOUT_MS = 60000;
2484
- var execFileAsync = promisify(execFile2);
2826
+ var execFileAsync = promisify(execFile3);
2485
2827
  function platformKey() {
2486
2828
  return `${process.platform}-${process.arch}`;
2487
2829
  }
2488
2830
  function markerPath(installDir, version) {
2489
- return join11(installDir, ".provisioned", `codegraph-${version}.json`);
2831
+ return join13(installDir, ".provisioned", `codegraph-${version}.json`);
2490
2832
  }
2491
2833
  function defaultInstallDir() {
2492
- return join11(homedir11(), ".omo", "codegraph");
2834
+ return join13(homedir11(), ".omo", "codegraph");
2493
2835
  }
2494
2836
  function sha256(bytes) {
2495
2837
  return createHash2("sha256").update(bytes).digest("hex");
@@ -2509,7 +2851,7 @@ async function removeEmptyDirectory(path) {
2509
2851
  }
2510
2852
  }
2511
2853
  function sleep(ms) {
2512
- return new Promise((resolve6) => setTimeout(resolve6, ms));
2854
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
2513
2855
  }
2514
2856
  async function defaultDownloader(asset, timeoutMs = DEFAULT_DOWNLOAD_TIMEOUT_MS) {
2515
2857
  const response = await fetch(asset.url, { signal: AbortSignal.timeout(timeoutMs) });
@@ -2523,7 +2865,7 @@ function forcedBadChecksumOptions(options) {
2523
2865
  const key = options.platformKey ?? platformKey();
2524
2866
  return {
2525
2867
  downloader: async () => new TextEncoder().encode("checksum mismatch"),
2526
- installDir: options.installDir ?? join11(options.lockDir, "codegraph-force-bad-checksum"),
2868
+ installDir: options.installDir ?? join13(options.lockDir, "codegraph-force-bad-checksum"),
2527
2869
  manifest: {
2528
2870
  assets: {
2529
2871
  [key]: { executableName: process.platform === "win32" ? "codegraph.cmd" : "codegraph", sha256: "0000", url: "memory://bad" }
@@ -2534,13 +2876,13 @@ function forcedBadChecksumOptions(options) {
2534
2876
  };
2535
2877
  }
2536
2878
  async function readMarker(path) {
2537
- if (!existsSync8(path))
2879
+ if (!existsSync9(path))
2538
2880
  return null;
2539
2881
  try {
2540
2882
  const raw = JSON.parse(await readFile(path, "utf8"));
2541
2883
  if (typeof raw === "object" && raw !== null && "binPath" in raw) {
2542
2884
  const value = raw.binPath;
2543
- return typeof value === "string" && existsSync8(value) ? value : null;
2885
+ return typeof value === "string" && existsSync9(value) ? value : null;
2544
2886
  }
2545
2887
  return null;
2546
2888
  } catch (error) {
@@ -2551,7 +2893,7 @@ async function readMarker(path) {
2551
2893
  }
2552
2894
  async function acquireLock(lockPath, waitMs, staleMs) {
2553
2895
  const startedAt = Date.now();
2554
- await mkdir(join11(lockPath, ".."), { recursive: true });
2896
+ await mkdir(join13(lockPath, ".."), { recursive: true });
2555
2897
  while (Date.now() - startedAt <= waitMs) {
2556
2898
  try {
2557
2899
  await mkdir(lockPath);
@@ -2576,24 +2918,24 @@ async function installExtractedBundle(extractDir, installDir, executableName) {
2576
2918
  const roots = await readdir(extractDir);
2577
2919
  if (roots.length !== 1)
2578
2920
  throw new Error(`CodeGraph archive should contain one root directory, found ${roots.length}`);
2579
- const bundleDir = join11(extractDir, roots[0] ?? "");
2921
+ const bundleDir = join13(extractDir, roots[0] ?? "");
2580
2922
  const bundleEntries = await readdir(bundleDir);
2581
2923
  await mkdir(installDir, { recursive: true });
2582
2924
  for (const entry of bundleEntries) {
2583
- await rm(join11(installDir, entry), { force: true, recursive: true });
2584
- await rename(join11(bundleDir, entry), join11(installDir, entry));
2925
+ await rm(join13(installDir, entry), { force: true, recursive: true });
2926
+ await rename(join13(bundleDir, entry), join13(installDir, entry));
2585
2927
  }
2586
- const destination = join11(installDir, "bin", executableName);
2587
- if (!existsSync8(destination))
2928
+ const destination = join13(installDir, "bin", executableName);
2929
+ if (!existsSync9(destination))
2588
2930
  throw new Error(`CodeGraph archive did not contain bin/${executableName}`);
2589
2931
  await chmod(destination, 493);
2590
2932
  return destination;
2591
2933
  }
2592
2934
  async function installAsset(layout) {
2593
2935
  const { asset, downloader, installDir, version } = layout;
2594
- const stagingDir = join11(installDir, ".staging", randomUUID());
2595
- const archivePath = join11(stagingDir, basename3(asset.url));
2596
- const extractDir = join11(stagingDir, "extract");
2936
+ const stagingDir = join13(installDir, ".staging", randomUUID());
2937
+ const archivePath = join13(stagingDir, basename3(asset.url));
2938
+ const extractDir = join13(stagingDir, "extract");
2597
2939
  try {
2598
2940
  await mkdir(extractDir, { recursive: true });
2599
2941
  const bytes = await downloader(asset);
@@ -2607,13 +2949,13 @@ async function installAsset(layout) {
2607
2949
  await writeFile(archivePath, bytes);
2608
2950
  await extractTarGz(archivePath, extractDir);
2609
2951
  const destination = await installExtractedBundle(extractDir, installDir, asset.executableName);
2610
- await mkdir(join11(installDir, ".provisioned"), { recursive: true });
2952
+ await mkdir(join13(installDir, ".provisioned"), { recursive: true });
2611
2953
  await writeFile(markerPath(installDir, version), `${JSON.stringify({ binPath: destination, version })}
2612
2954
  `);
2613
2955
  return destination;
2614
2956
  } finally {
2615
2957
  await rm(stagingDir, { force: true, recursive: true });
2616
- await removeEmptyDirectory(join11(installDir, ".staging"));
2958
+ await removeEmptyDirectory(join13(installDir, ".staging"));
2617
2959
  }
2618
2960
  }
2619
2961
  async function ensureCodegraphProvisioned(options) {
@@ -2626,7 +2968,7 @@ async function ensureCodegraphProvisioned(options) {
2626
2968
  const existing = await readMarker(marker);
2627
2969
  if (existing !== null)
2628
2970
  return { binPath: existing, provisioned: true };
2629
- const lockPath = join11(options.lockDir, `codegraph-${hostname()}.lock`);
2971
+ const lockPath = join13(options.lockDir, `codegraph-${hostname()}.lock`);
2630
2972
  const release = await acquireLock(lockPath, options.lockWaitMs ?? DEFAULT_LOCK_WAIT_MS, options.lockStaleMs ?? DEFAULT_LOCK_STALE_MS);
2631
2973
  if (release === null)
2632
2974
  return { error: "timed out waiting for codegraph provisioning lock", provisioned: false };
@@ -2652,7 +2994,7 @@ async function ensureCodegraphProvisioned(options) {
2652
2994
 
2653
2995
  // components/codegraph/src/session-start-worker.ts
2654
2996
  var SESSION_START_CWD_ENV = "OMO_CODEGRAPH_SESSION_START_CWD";
2655
- var CODEGRAPH_VERSION = "1.0.1";
2997
+ var CODEGRAPH_VERSION = CODEGRAPH_PINNED_VERSION;
2656
2998
  var COMMAND_TIMEOUT_MS = 60000;
2657
2999
  var WINDOWS_CMD_EXTENSIONS = new Set([".bat", ".cmd"]);
2658
3000
  var WINDOWS_NODE_SCRIPT_EXTENSIONS = new Set([".cjs", ".js", ".mjs"]);
@@ -2716,15 +3058,15 @@ async function resolveOrProvisionCommand(deps, config, env, homeDir, nodeSupport
2716
3058
  return { kind: "unsupported-node" };
2717
3059
  if (config.auto_provision === false)
2718
3060
  return { error: "codegraph binary unavailable and auto_provision is disabled", kind: "unavailable", source: resolved.source };
2719
- const installDir = trustedInstallDir ?? join12(homeDir, ".omo", "codegraph");
2720
- const provisioned = await deps.ensureProvisioned({ installDir, lockDir: join12(installDir, ".locks"), version: CODEGRAPH_VERSION });
3061
+ const installDir = trustedInstallDir ?? join14(homeDir, ".omo", "codegraph");
3062
+ const provisioned = await deps.ensureProvisioned({ installDir, lockDir: join14(installDir, ".locks"), version: CODEGRAPH_VERSION });
2721
3063
  if (!provisioned.provisioned || provisioned.binPath === undefined) {
2722
3064
  return { error: provisioned.error ?? "provisioning did not produce a binary", kind: "unavailable", source: resolved.source };
2723
3065
  }
2724
3066
  return { kind: "resolved", resolution: { argsPrefix: [], command: provisioned.binPath, exists: true, source: "provisioned" } };
2725
3067
  }
2726
3068
  function codegraphEnvForConfig(config, homeDir) {
2727
- const env = buildCodegraphEnv({ homeDir });
3069
+ const env = buildCodegraphEnv({ daemon: false, homeDir });
2728
3070
  return config.trustedCodegraphInstallDir === undefined ? env : { ...env, CODEGRAPH_INSTALL_DIR: config.trustedCodegraphInstallDir };
2729
3071
  }
2730
3072
  function canUseResolvedCommand(resolved, nodeSupport) {
@@ -2764,15 +3106,14 @@ function jsonSaysInitialized(value) {
2764
3106
  }
2765
3107
  async function runCodegraphCommand(projectRoot, command, args, options) {
2766
3108
  const invocation = resolveCodegraphCommandInvocation(command, args);
2767
- return new Promise((resolvePromise) => {
2768
- execFile3(invocation.command, [...invocation.args], { cwd: projectRoot, encoding: "utf8", env: buildCodegraphChildEnv({ ambientEnv: processEnv, codegraphEnv: options.env }), maxBuffer: 1024 * 1024, timeout: options.timeoutMs, windowsHide: true }, (error, stdout, stderr) => {
2769
- if (error === null) {
2770
- resolvePromise({ exitCode: 0, stderr: toOutputText(stderr), stdout: toOutputText(stdout), timedOut: false });
2771
- return;
2772
- }
2773
- resolvePromise({ exitCode: resolveExitCode(error), stderr: toOutputText(stderr), stdout: toOutputText(stdout), timedOut: error.killed === true });
2774
- });
2775
- });
3109
+ return runProcessWithTreeTimeout({
3110
+ args: invocation.args,
3111
+ command: invocation.command,
3112
+ cwd: projectRoot,
3113
+ env: buildCodegraphChildEnv({ ambientEnv: processEnv, codegraphEnv: options.env }),
3114
+ maxBuffer: 1024 * 1024,
3115
+ timeoutMs: options.timeoutMs
3116
+ }).then(({ exitCode, stderr, stdout, timedOut }) => ({ exitCode, stderr, stdout, timedOut }));
2776
3117
  }
2777
3118
  function resolveCodegraphCommandInvocation(command, args, platform = process.platform) {
2778
3119
  if (platform !== "win32")
@@ -2785,9 +3126,9 @@ function resolveCodegraphCommandInvocation(command, args, platform = process.pla
2785
3126
  return { args: ["/d", "/s", "/c", command, ...args], command: "cmd.exe" };
2786
3127
  }
2787
3128
  function appendOutcome(homeDir, outcome) {
2788
- const logDir = join12(homeDir, ".omo", "codegraph");
3129
+ const logDir = join14(homeDir, ".omo", "codegraph");
2789
3130
  mkdirSync3(logDir, { recursive: true });
2790
- appendFileSync2(join12(logDir, "session-start.jsonl"), `${JSON.stringify({ ...outcome, timestamp: new Date().toISOString() })}
3131
+ appendFileSync2(join14(logDir, "session-start.jsonl"), `${JSON.stringify({ ...outcome, timestamp: new Date().toISOString() })}
2791
3132
  `);
2792
3133
  }
2793
3134
  function safeLogOutcome(logOutcome, outcome) {
@@ -2802,18 +3143,7 @@ function safeLogOutcome(logOutcome, outcome) {
2802
3143
  }
2803
3144
  }
2804
3145
  function provisionedBinFromInstallDir(installDir) {
2805
- if (installDir === undefined)
2806
- return null;
2807
- const candidate = join12(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
2808
- return existsSync9(candidate) ? candidate : null;
2809
- }
2810
- function resolveExitCode(error) {
2811
- if ("code" in error && typeof error.code === "number")
2812
- return error.code;
2813
- return 1;
2814
- }
2815
- function toOutputText(value) {
2816
- return Buffer.isBuffer(value) ? value.toString("utf8") : value;
3146
+ return resolvePinnedCodegraphBin(installDir);
2817
3147
  }
2818
3148
  function resolveHomeDir2(env) {
2819
3149
  return env["HOME"] ?? env["USERPROFILE"] ?? homedir12();
@@ -2846,13 +3176,6 @@ async function executeCodegraphSessionStartHook(options = {}) {
2846
3176
  const projectRoot = resolveProjectRoot(input, options.cwd ?? processCwd2());
2847
3177
  const homeDir = resolveHomeDir3(env);
2848
3178
  const config = options.config ?? getCodexOmoConfig({ cwd: projectRoot, env, homeDir });
2849
- pruneCodegraphProjectStoresBestEffort(homeDir, { debugLog: writeDebugLog });
2850
- await sweepCodegraphZombiesBestEffort({
2851
- env,
2852
- homeDir,
2853
- ...config.trustedCodegraphInstallDir === undefined ? {} : { trustedCodegraphInstallDir: config.trustedCodegraphInstallDir },
2854
- log: writeDebugLog
2855
- }, options.sweepZombies);
2856
3179
  if (config.codegraph?.enabled === false) {
2857
3180
  return { action: "skipped-disabled", exitCode: 0 };
2858
3181
  }
@@ -2864,7 +3187,15 @@ async function executeCodegraphSessionStartHook(options = {}) {
2864
3187
  if (exclusion.excluded) {
2865
3188
  return { action: "skipped-excluded", exitCode: 0 };
2866
3189
  }
3190
+ pruneCodegraphProjectStoresBestEffort(homeDir, { debugLog: writeDebugLog });
3191
+ await sweepCodegraphZombiesBestEffort({
3192
+ env,
3193
+ homeDir,
3194
+ ...config.trustedCodegraphInstallDir === undefined ? {} : { trustedCodegraphInstallDir: config.trustedCodegraphInstallDir },
3195
+ log: writeDebugLog
3196
+ }, options.sweepZombies);
2867
3197
  const isInitialized = await (options.statusProbe ?? isCodegraphProjectInitialized)({
3198
+ daemon: config.codegraph?.daemon !== false,
2868
3199
  env,
2869
3200
  homeDir,
2870
3201
  projectRoot,
@@ -2895,7 +3226,7 @@ async function isCodegraphProjectInitialized(options) {
2895
3226
  return false;
2896
3227
  const invocation = resolveCodegraphCommandInvocation(resolved.command, [...resolved.argsPrefix, "status", "--json"]);
2897
3228
  const codegraphEnv = {
2898
- ...buildCodegraphEnv({ homeDir: options.homeDir }),
3229
+ ...buildCodegraphEnv({ daemon: options.daemon, homeDir: options.homeDir }),
2899
3230
  ...options.trustedCodegraphInstallDir === undefined ? {} : { CODEGRAPH_INSTALL_DIR: options.trustedCodegraphInstallDir }
2900
3231
  };
2901
3232
  const status = await runStatusProbe(options.projectRoot, invocation.command, invocation.args, buildCodegraphChildEnv({ ambientEnv: options.env, codegraphEnv, runtimeEnv: options.env }));
@@ -2904,22 +3235,14 @@ async function isCodegraphProjectInitialized(options) {
2904
3235
  return codegraphStatusSaysInitialized(status.stdout);
2905
3236
  }
2906
3237
  function runStatusProbe(projectRoot, command, args, env) {
2907
- return new Promise((resolveProbe) => {
2908
- execFile4(command, [...args], {
2909
- cwd: projectRoot,
2910
- encoding: "utf8",
2911
- env,
2912
- maxBuffer: 1024 * 1024,
2913
- timeout: STATUS_PROBE_TIMEOUT_MS,
2914
- windowsHide: true
2915
- }, (error, stdout) => {
2916
- if (error === null) {
2917
- resolveProbe({ exitCode: 0, stdout: toOutputText2(stdout), timedOut: false });
2918
- return;
2919
- }
2920
- resolveProbe({ exitCode: resolveExitCode2(error), stdout: toOutputText2(stdout), timedOut: error.killed === true });
2921
- });
2922
- });
3238
+ return runProcessWithTreeTimeout({
3239
+ args,
3240
+ command,
3241
+ cwd: projectRoot,
3242
+ env,
3243
+ maxBuffer: 1024 * 1024,
3244
+ timeoutMs: STATUS_PROBE_TIMEOUT_MS
3245
+ }).then(({ exitCode, stdout, timedOut }) => ({ exitCode, stdout, timedOut }));
2923
3246
  }
2924
3247
  function codegraphStatusSaysInitialized(stdout) {
2925
3248
  const parsed = parseJson3(stdout);
@@ -2935,9 +3258,7 @@ function codegraphStatusSaysInitialized(stdout) {
2935
3258
  return (normalized.includes("initialized") || normalized.includes("ready")) && !normalized.includes("not initialized") && !normalized.includes("uninitialized");
2936
3259
  }
2937
3260
  function provisionedBinFromInstallDir2(installDir) {
2938
- if (installDir === undefined)
2939
- return null;
2940
- return join13(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
3261
+ return resolvePinnedCodegraphBin(installDir);
2941
3262
  }
2942
3263
  async function executeCodegraphPostToolUseHook(options = {}) {
2943
3264
  const env = options.env ?? processEnv2;
@@ -2980,20 +3301,12 @@ function writeDebugLog(message) {
2980
3301
  `);
2981
3302
  }
2982
3303
  function spawnDetachedWorker(invocation) {
2983
- const child = spawn(invocation.command, [...invocation.args], { detached: true, env: invocation.env, stdio: "ignore" });
3304
+ const child = spawn2(invocation.command, [...invocation.args], { detached: true, env: invocation.env, stdio: "ignore" });
2984
3305
  child.unref();
2985
3306
  }
2986
3307
  function resolveHomeDir3(env) {
2987
3308
  return env["HOME"] ?? env["USERPROFILE"] ?? homedir13();
2988
3309
  }
2989
- function resolveExitCode2(error) {
2990
- if ("code" in error && typeof error.code === "number")
2991
- return error.code;
2992
- return 1;
2993
- }
2994
- function toOutputText2(value) {
2995
- return Buffer.isBuffer(value) ? value.toString("utf8") : value;
2996
- }
2997
3310
  function resolveProjectRoot(input, fallback) {
2998
3311
  if (!isRecord5(input))
2999
3312
  return fallback;
@@ -3027,9 +3340,9 @@ function defaultWorkerCliPath() {
3027
3340
  }
3028
3341
 
3029
3342
  // components/codegraph/src/serve.ts
3030
- import { existsSync as existsSync10, realpathSync as realpathSync5 } from "node:fs";
3343
+ import { existsSync as existsSync10, realpathSync as realpathSync6 } from "node:fs";
3031
3344
  import { homedir as homedir14 } from "node:os";
3032
- import { basename as basename4, join as join14, resolve as resolve6 } from "node:path";
3345
+ import { basename as basename4, join as join15, resolve as resolve8 } from "node:path";
3033
3346
  import {
3034
3347
  cwd as processCwd3,
3035
3348
  env as processEnv3,
@@ -3040,10 +3353,10 @@ import {
3040
3353
  import { fileURLToPath as fileURLToPath3 } from "node:url";
3041
3354
 
3042
3355
  // components/codegraph/src/mcp-bridge.ts
3043
- import { spawn as spawn2 } from "node:child_process";
3356
+ import { spawn as spawn3 } from "node:child_process";
3044
3357
 
3045
3358
  // ../../mcp-stdio-core/src/record.ts
3046
- function isPlainRecord(value) {
3359
+ function isPlainRecord2(value) {
3047
3360
  return typeof value === "object" && value !== null && !Array.isArray(value);
3048
3361
  }
3049
3362
  // ../../mcp-stdio-core/src/responses.ts
@@ -3090,7 +3403,7 @@ ${body}` : `${body}
3090
3403
  await writeChunk(output, payload);
3091
3404
  }
3092
3405
  function writeChunk(output, chunk) {
3093
- return new Promise((resolve6, reject) => {
3406
+ return new Promise((resolve8, reject) => {
3094
3407
  let settled = false;
3095
3408
  const onError = (error) => {
3096
3409
  if (settled)
@@ -3110,7 +3423,7 @@ function writeChunk(output, chunk) {
3110
3423
  return;
3111
3424
  }
3112
3425
  output.removeListener("error", onError);
3113
- resolve6();
3426
+ resolve8();
3114
3427
  });
3115
3428
  } catch (error) {
3116
3429
  output.removeListener("error", onError);
@@ -3202,16 +3515,35 @@ function bufferFromChunk(chunk) {
3202
3515
 
3203
3516
  // ../../mcp-stdio-core/src/server.ts
3204
3517
  var DEFAULT_IDLE_TIMEOUT_MS = 10 * 60000;
3518
+ var DEFAULT_PARENT_POLL_INTERVAL_MS = 30000;
3205
3519
  var noopLog = () => {};
3520
+ function isProcessAlive(pid) {
3521
+ try {
3522
+ process.kill(pid, 0);
3523
+ return true;
3524
+ } catch (error) {
3525
+ return !hasErrorCode(error, "ESRCH");
3526
+ }
3527
+ }
3206
3528
  async function runJsonRpcStdioServer(config) {
3207
3529
  const log = config.log ?? noopLog;
3208
3530
  const idleTimeoutMs = config.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
3209
- const idleTimer = createIdleTimer(idleTimeoutMs, log, config.onIdleTimeout);
3531
+ let isClosed = false;
3532
+ const idleTimer = createIdleTimer(idleTimeoutMs, log, () => {
3533
+ isClosed = true;
3534
+ config.onIdleTimeout?.();
3535
+ });
3536
+ const watchdog = createParentWatchdog(config.parentWatchdog, (parentPid, pollIntervalMs) => {
3537
+ isClosed = true;
3538
+ log("parent_exit", { parent_pid: parentPid, poll_interval_ms: pollIntervalMs });
3539
+ config.onParentExit?.();
3540
+ config.input.destroy();
3541
+ });
3210
3542
  log("stdio_started", { cwd: process.cwd(), idle_timeout_ms: idleTimeoutMs });
3211
3543
  idleTimer.arm();
3212
3544
  try {
3213
3545
  for await (const message of readStdioJsonRpcMessages(config.input)) {
3214
- if (idleTimer.closed())
3546
+ if (isClosed)
3215
3547
  break;
3216
3548
  idleTimer.arm();
3217
3549
  if (message.kind === "parse_error") {
@@ -3222,8 +3554,12 @@ async function runJsonRpcStdioServer(config) {
3222
3554
  if (!await handleRequest(message, config, log))
3223
3555
  break;
3224
3556
  }
3557
+ } catch (error) {
3558
+ if (!(isClosed && hasErrorCode(error, "ERR_STREAM_PREMATURE_CLOSE")))
3559
+ throw error;
3225
3560
  } finally {
3226
3561
  idleTimer.clear();
3562
+ watchdog.clear();
3227
3563
  log("stdio_stopped");
3228
3564
  }
3229
3565
  }
@@ -3240,8 +3576,8 @@ async function handleParseError(message, config, log) {
3240
3576
  }
3241
3577
  async function handleRequest(message, config, log) {
3242
3578
  const parsed = message.payload;
3243
- const id = isPlainRecord(parsed) ? jsonRpcId(parsed["id"]) : null;
3244
- const method = isPlainRecord(parsed) && typeof parsed["method"] === "string" ? parsed["method"] : null;
3579
+ const id = isPlainRecord2(parsed) ? jsonRpcId(parsed["id"]) : null;
3580
+ const method = isPlainRecord2(parsed) && typeof parsed["method"] === "string" ? parsed["method"] : null;
3245
3581
  log("request", { id: id === null ? null : String(id), method });
3246
3582
  let response;
3247
3583
  try {
@@ -3279,9 +3615,33 @@ function isTerminalOutputError(error) {
3279
3615
  return false;
3280
3616
  return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
3281
3617
  }
3282
- function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3618
+ function hasErrorCode(error, code) {
3619
+ return error instanceof Error && "code" in error && error.code === code;
3620
+ }
3621
+ function createParentWatchdog(config, onDeadParent) {
3622
+ if (config === undefined)
3623
+ return { clear: () => {} };
3624
+ const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_PARENT_POLL_INTERVAL_MS;
3625
+ if (pollIntervalMs <= 0)
3626
+ return { clear: () => {} };
3627
+ const parentPid = config.parentPid ?? process.ppid;
3628
+ const probeAlive = config.probeAlive ?? isProcessAlive;
3629
+ let fired = false;
3630
+ const timer = setInterval(() => {
3631
+ if (fired || probeAlive(parentPid))
3632
+ return;
3633
+ fired = true;
3634
+ onDeadParent(parentPid, pollIntervalMs);
3635
+ }, pollIntervalMs);
3636
+ timer.unref();
3637
+ return {
3638
+ clear: () => {
3639
+ clearInterval(timer);
3640
+ }
3641
+ };
3642
+ }
3643
+ function createIdleTimer(idleTimeoutMs, log, onTimeout) {
3283
3644
  let timer = null;
3284
- let isClosed = false;
3285
3645
  return {
3286
3646
  arm: () => {
3287
3647
  if (timer !== null)
@@ -3289,9 +3649,8 @@ function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3289
3649
  if (idleTimeoutMs <= 0)
3290
3650
  return;
3291
3651
  timer = setTimeout(() => {
3292
- isClosed = true;
3293
3652
  log("idle_timeout", { idle_timeout_ms: idleTimeoutMs });
3294
- onIdleTimeout?.();
3653
+ onTimeout();
3295
3654
  }, idleTimeoutMs);
3296
3655
  timer.unref();
3297
3656
  },
@@ -3300,8 +3659,7 @@ function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3300
3659
  return;
3301
3660
  clearTimeout(timer);
3302
3661
  timer = null;
3303
- },
3304
- closed: () => isClosed
3662
+ }
3305
3663
  };
3306
3664
  }
3307
3665
  // components/codegraph/src/serve-invocation.ts
@@ -3334,9 +3692,10 @@ class CodegraphBridgeStdioError extends Error {
3334
3692
  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.";
3335
3693
  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.";
3336
3694
  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.";
3695
+ var SIGKILL_ESCALATION_MS = 2000;
3337
3696
  async function runBridgedCodegraphProcess(command, args, options) {
3338
3697
  const invocation = resolveServeProcessInvocation(command, args);
3339
- const child = spawn2(invocation.command, invocation.args, {
3698
+ const child = spawn3(invocation.command, invocation.args, {
3340
3699
  cwd: options.cwd,
3341
3700
  env: options.env,
3342
3701
  stdio: ["pipe", "pipe", "inherit"]
@@ -3359,17 +3718,24 @@ async function runBridgedCodegraphProcess(command, args, options) {
3359
3718
  resolveExit(signal === null ? 0 : 1);
3360
3719
  });
3361
3720
  });
3362
- const clientForwardingDone = forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
3363
- defaultResponseMode = mode;
3364
- });
3365
- const responseForwardingDone = forwardCodegraphToClient(childOutput, options.output, pendingResponses, () => defaultResponseMode);
3366
- const bridgeDone = Promise.all([clientForwardingDone, responseForwardingDone]);
3367
- const childAndResponsesDone = Promise.all([childExit, responseForwardingDone]).then(([exitCode]) => exitCode);
3368
3721
  const destroyChildPipes = () => {
3369
3722
  childInput.destroy();
3370
3723
  childOutput.destroy();
3371
3724
  };
3372
3725
  childExit.then(destroyChildPipes, destroyChildPipes);
3726
+ let parentWatchdogFired = false;
3727
+ const parentWatchdog = createParentWatchdog(options.parentWatchdog, () => {
3728
+ parentWatchdogFired = true;
3729
+ options.input.destroy();
3730
+ destroyChildPipes();
3731
+ terminateCodegraphChild(child);
3732
+ });
3733
+ const clientForwardingDone = forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
3734
+ defaultResponseMode = mode;
3735
+ }, () => parentWatchdogFired);
3736
+ const responseForwardingDone = forwardCodegraphToClient(childOutput, options.output, pendingResponses, () => defaultResponseMode, () => parentWatchdogFired);
3737
+ const bridgeDone = Promise.all([clientForwardingDone, responseForwardingDone]);
3738
+ const childAndResponsesDone = Promise.all([childExit, responseForwardingDone]).then(([exitCode]) => exitCode);
3373
3739
  try {
3374
3740
  return await Promise.race([childAndResponsesDone, bridgeDone.then(() => childExit)]);
3375
3741
  } catch (error) {
@@ -3380,58 +3746,85 @@ async function runBridgedCodegraphProcess(command, args, options) {
3380
3746
  return;
3381
3747
  });
3382
3748
  throw error;
3749
+ } finally {
3750
+ parentWatchdog.clear();
3383
3751
  }
3384
3752
  }
3385
- async function forwardClientToCodegraph(input, childInput, pendingResponses, setDefaultResponseMode) {
3386
- for await (const message of readStdioJsonRpcMessages(input)) {
3387
- if (message.kind === "parse_error") {
3388
- continue;
3389
- }
3390
- const responseMode = message.responseMode;
3391
- setDefaultResponseMode(responseMode);
3392
- const key = responseModeKey(message.payload);
3393
- if (key !== null) {
3394
- pendingResponses.set(key, {
3395
- method: jsonRpcMethod(message.payload),
3396
- responseMode,
3397
- toolName: jsonRpcToolName(message.payload)
3398
- });
3753
+ function terminateCodegraphChild(child) {
3754
+ if (child.exitCode !== null || child.signalCode !== null)
3755
+ return;
3756
+ child.kill("SIGTERM");
3757
+ const escalation = setTimeout(() => {
3758
+ if (child.exitCode === null && child.signalCode === null)
3759
+ child.kill("SIGKILL");
3760
+ }, SIGKILL_ESCALATION_MS);
3761
+ escalation.unref();
3762
+ }
3763
+ function isWatchdogTeardownError(error) {
3764
+ if (!(error instanceof Error) || !("code" in error))
3765
+ return false;
3766
+ return error.code === "ERR_STREAM_PREMATURE_CLOSE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END" || error.code === "EPIPE";
3767
+ }
3768
+ async function forwardClientToCodegraph(input, childInput, pendingResponses, setDefaultResponseMode, tolerateWatchdogClose) {
3769
+ try {
3770
+ for await (const message of readStdioJsonRpcMessages(input)) {
3771
+ if (message.kind === "parse_error") {
3772
+ continue;
3773
+ }
3774
+ const responseMode = message.responseMode;
3775
+ setDefaultResponseMode(responseMode);
3776
+ const key = responseModeKey(message.payload);
3777
+ if (key !== null) {
3778
+ pendingResponses.set(key, {
3779
+ method: jsonRpcMethod(message.payload),
3780
+ responseMode,
3781
+ toolName: jsonRpcToolName(message.payload)
3782
+ });
3783
+ }
3784
+ await writeLine(childInput, JSON.stringify(message.payload));
3399
3785
  }
3400
- await writeLine(childInput, JSON.stringify(message.payload));
3786
+ childInput.end();
3787
+ } catch (error) {
3788
+ if (!(tolerateWatchdogClose() && isWatchdogTeardownError(error)))
3789
+ throw error;
3401
3790
  }
3402
- childInput.end();
3403
3791
  }
3404
- async function forwardCodegraphToClient(childOutput, output, pendingResponses, defaultResponseMode) {
3405
- for await (const message of readStdioJsonRpcMessages(childOutput)) {
3406
- if (message.kind === "parse_error") {
3407
- await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
3408
- continue;
3792
+ async function forwardCodegraphToClient(childOutput, output, pendingResponses, defaultResponseMode, tolerateWatchdogClose) {
3793
+ try {
3794
+ for await (const message of readStdioJsonRpcMessages(childOutput)) {
3795
+ if (message.kind === "parse_error") {
3796
+ await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
3797
+ continue;
3798
+ }
3799
+ const key = responseModeKey(message.payload);
3800
+ const pendingResponse = key === null ? undefined : pendingResponses.get(key);
3801
+ const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
3802
+ if (key !== null)
3803
+ pendingResponses.delete(key);
3804
+ await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
3409
3805
  }
3410
- const key = responseModeKey(message.payload);
3411
- const pendingResponse = key === null ? undefined : pendingResponses.get(key);
3412
- const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
3413
- if (key !== null)
3414
- pendingResponses.delete(key);
3415
- await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
3806
+ } catch (error) {
3807
+ if (!(tolerateWatchdogClose() && isWatchdogTeardownError(error)))
3808
+ throw error;
3416
3809
  }
3417
3810
  }
3418
3811
  function responseModeKey(payload) {
3419
- if (!isPlainRecord(payload) || !("id" in payload))
3812
+ if (!isPlainRecord2(payload) || !("id" in payload))
3420
3813
  return null;
3421
3814
  const id = jsonRpcId(payload["id"]);
3422
3815
  return `${typeof id}:${String(id)}`;
3423
3816
  }
3424
3817
  function jsonRpcMethod(payload) {
3425
- if (!isPlainRecord(payload))
3818
+ if (!isPlainRecord2(payload))
3426
3819
  return null;
3427
3820
  const method = payload["method"];
3428
3821
  return typeof method === "string" ? method : null;
3429
3822
  }
3430
3823
  function jsonRpcToolName(payload) {
3431
- if (jsonRpcMethod(payload) !== "tools/call" || !isPlainRecord(payload))
3824
+ if (jsonRpcMethod(payload) !== "tools/call" || !isPlainRecord2(payload))
3432
3825
  return null;
3433
3826
  const params = payload["params"];
3434
- if (!isPlainRecord(params))
3827
+ if (!isPlainRecord2(params))
3435
3828
  return null;
3436
3829
  const name = params["name"];
3437
3830
  return typeof name === "string" ? name : null;
@@ -3445,14 +3838,14 @@ function clarifyCodegraphResponse(payload, pendingResponse) {
3445
3838
  return payload;
3446
3839
  }
3447
3840
  function clarifyCodegraphToolsList(payload) {
3448
- if (!isPlainRecord(payload))
3841
+ if (!isPlainRecord2(payload))
3449
3842
  return payload;
3450
3843
  const result = payload["result"];
3451
- if (!isPlainRecord(result) || !Array.isArray(result["tools"]))
3844
+ if (!isPlainRecord2(result) || !Array.isArray(result["tools"]))
3452
3845
  return payload;
3453
3846
  let changed = false;
3454
3847
  const tools = result["tools"].map((tool) => {
3455
- if (!isPlainRecord(tool) || tool["name"] !== "codegraph_node")
3848
+ if (!isPlainRecord2(tool) || tool["name"] !== "codegraph_node")
3456
3849
  return tool;
3457
3850
  if (!hasCodegraphNodeContractMetadata(tool))
3458
3851
  return tool;
@@ -3469,7 +3862,7 @@ function clarifyCodegraphNodeTool(tool) {
3469
3862
  description: CODEGRAPH_NODE_DESCRIPTION
3470
3863
  };
3471
3864
  const inputSchema = tool["inputSchema"];
3472
- if (isPlainRecord(inputSchema))
3865
+ if (isPlainRecord2(inputSchema))
3473
3866
  clarified["inputSchema"] = clarifyCodegraphNodeInputSchema(inputSchema);
3474
3867
  return clarified;
3475
3868
  }
@@ -3477,17 +3870,17 @@ function hasCodegraphNodeContractMetadata(tool) {
3477
3870
  if (typeof tool["description"] === "string")
3478
3871
  return true;
3479
3872
  const inputSchema = tool["inputSchema"];
3480
- if (!isPlainRecord(inputSchema))
3873
+ if (!isPlainRecord2(inputSchema))
3481
3874
  return false;
3482
3875
  const properties = inputSchema["properties"];
3483
- return isPlainRecord(properties) && isPlainRecord(properties["includeCode"]);
3876
+ return isPlainRecord2(properties) && isPlainRecord2(properties["includeCode"]);
3484
3877
  }
3485
3878
  function clarifyCodegraphNodeInputSchema(inputSchema) {
3486
3879
  const properties = inputSchema["properties"];
3487
- if (!isPlainRecord(properties))
3880
+ if (!isPlainRecord2(properties))
3488
3881
  return inputSchema;
3489
3882
  const includeCode = properties["includeCode"];
3490
- if (!isPlainRecord(includeCode))
3883
+ if (!isPlainRecord2(includeCode))
3491
3884
  return inputSchema;
3492
3885
  return {
3493
3886
  ...inputSchema,
@@ -3501,14 +3894,14 @@ function clarifyCodegraphNodeInputSchema(inputSchema) {
3501
3894
  };
3502
3895
  }
3503
3896
  function clarifyCodegraphNodeCallResult(payload) {
3504
- if (!isPlainRecord(payload))
3897
+ if (!isPlainRecord2(payload))
3505
3898
  return payload;
3506
3899
  const result = payload["result"];
3507
- if (!isPlainRecord(result) || !Array.isArray(result["content"]))
3900
+ if (!isPlainRecord2(result) || !Array.isArray(result["content"]))
3508
3901
  return payload;
3509
3902
  let changed = false;
3510
3903
  const content = result["content"].map((item) => {
3511
- if (!isPlainRecord(item) || item["type"] !== "text" || typeof item["text"] !== "string")
3904
+ if (!isPlainRecord2(item) || item["type"] !== "text" || typeof item["text"] !== "string")
3512
3905
  return item;
3513
3906
  const text = clarifyContainerOutlineGuidance(item["text"]);
3514
3907
  if (text === item["text"])
@@ -3544,11 +3937,12 @@ async function runUnavailableCodegraphMcpServer(options) {
3544
3937
  serverVersion: options.serverVersion
3545
3938
  },
3546
3939
  input: options.input,
3547
- output: options.output
3940
+ output: options.output,
3941
+ parentWatchdog: options.parentWatchdog ?? {}
3548
3942
  });
3549
3943
  }
3550
3944
  async function handleUnavailableCodegraphMcpRequest(input, options) {
3551
- if (!isPlainRecord(input)) {
3945
+ if (!isPlainRecord2(input)) {
3552
3946
  return errorResponse(null, -32600, "Invalid Request");
3553
3947
  }
3554
3948
  const id = jsonRpcId(input["id"]);
@@ -3576,7 +3970,7 @@ async function handleUnavailableCodegraphMcpRequest(input, options) {
3576
3970
  return errorResponse(id, -32601, `Method not found: ${String(method)}`);
3577
3971
  }
3578
3972
  function requestedProtocolVersion(params) {
3579
- if (!isPlainRecord(params) || typeof params["protocolVersion"] !== "string")
3973
+ if (!isPlainRecord2(params) || typeof params["protocolVersion"] !== "string")
3580
3974
  return "2024-11-05";
3581
3975
  return params["protocolVersion"];
3582
3976
  }
@@ -3588,7 +3982,7 @@ var CODEGRAPH_DISABLED_HINT = `CodeGraph MCP skipped: disabled by OMO SOT config
3588
3982
  `;
3589
3983
  var CODEGRAPH_EXCLUDED_HINT = `CodeGraph MCP skipped: project excluded by OMO CodeGraph policy.
3590
3984
  `;
3591
- var CODEGRAPH_VERSION2 = "1.0.1";
3985
+ var CODEGRAPH_VERSION2 = CODEGRAPH_PINNED_VERSION;
3592
3986
  var PROJECT_CWD_ENV_KEYS = ["OMO_CODEGRAPH_PROJECT_CWD", SESSION_START_CWD_ENV, "PWD"];
3593
3987
  async function runCodegraphServe(options = {}) {
3594
3988
  const env = options.env ?? processEnv3;
@@ -3636,7 +4030,7 @@ async function runCodegraphServe(options = {}) {
3636
4030
  return runUnavailableMcp(buildCodegraphNodeSkipHint(nodeSupport), options);
3637
4031
  }
3638
4032
  const runProcess = options.runProcess ?? runBridgedCodegraphProcess;
3639
- const codegraphEnv = codegraphEnvForConfig2(trustedInstallDir, homeDir, options.buildEnv);
4033
+ const codegraphEnv = codegraphEnvForConfig2(trustedInstallDir, homeDir, codegraphConfig.daemon !== false, options.buildEnv);
3640
4034
  const mergedEnv = buildCodegraphChildEnv({ ambientEnv: env, codegraphEnv, runtimeEnv: env });
3641
4035
  return runProcess(resolution.command, [...resolution.argsPrefix, "serve", "--mcp"], {
3642
4036
  cwd: projectCwd,
@@ -3644,7 +4038,8 @@ async function runCodegraphServe(options = {}) {
3644
4038
  input: options.stdin ?? processStdin2,
3645
4039
  output: options.stdout ?? processStdout2,
3646
4040
  stderr: options.stderr ?? processStderr3,
3647
- stdio: "pipe"
4041
+ stdio: "pipe",
4042
+ parentWatchdog: options.parentWatchdog ?? {}
3648
4043
  });
3649
4044
  }
3650
4045
  async function runUnavailableMcp(reason, options) {
@@ -3653,7 +4048,8 @@ async function runUnavailableMcp(reason, options) {
3653
4048
  input: options.stdin ?? processStdin2,
3654
4049
  output: options.stdout ?? processStdout2,
3655
4050
  reason,
3656
- serverVersion: CODEGRAPH_VERSION2
4051
+ serverVersion: CODEGRAPH_VERSION2,
4052
+ parentWatchdog: options.parentWatchdog ?? {}
3657
4053
  });
3658
4054
  return 0;
3659
4055
  }
@@ -3662,10 +4058,10 @@ async function provisionMissingCodegraph(options) {
3662
4058
  return null;
3663
4059
  if (options.config.auto_provision === false)
3664
4060
  return null;
3665
- const installDir = options.trustedInstallDir ?? join14(options.homeDir, ".omo", "codegraph");
4061
+ const installDir = options.trustedInstallDir ?? join15(options.homeDir, ".omo", "codegraph");
3666
4062
  const result = await options.ensureProvisioned({
3667
4063
  installDir,
3668
- lockDir: join14(installDir, ".locks"),
4064
+ lockDir: join15(installDir, ".locks"),
3669
4065
  version: CODEGRAPH_VERSION2
3670
4066
  });
3671
4067
  if (!result.provisioned || result.binPath === undefined)
@@ -3682,8 +4078,8 @@ function shouldSkipResolvedCommand(resolution, commandExists) {
3682
4078
  function looksLikePath2(command) {
3683
4079
  return command.includes("/") || command.includes("\\");
3684
4080
  }
3685
- function codegraphEnvForConfig2(trustedInstallDir, homeDir, buildEnv) {
3686
- const env = { ...buildEnv?.({ homeDir }) ?? buildCodegraphEnv({ homeDir }), [CODEGRAPH_NO_DAEMON_ENV]: "1" };
4081
+ function codegraphEnvForConfig2(trustedInstallDir, homeDir, daemon, buildEnv) {
4082
+ const env = buildEnv?.({ daemon, homeDir }) ?? buildCodegraphEnv({ daemon, homeDir });
3687
4083
  return trustedInstallDir === undefined ? env : { ...env, CODEGRAPH_INSTALL_DIR: trustedInstallDir };
3688
4084
  }
3689
4085
  function resolveProjectCwd(env, fallback) {
@@ -3691,17 +4087,14 @@ function resolveProjectCwd(env, fallback) {
3691
4087
  const candidate = env[key]?.trim();
3692
4088
  if (candidate === undefined || candidate.length === 0)
3693
4089
  continue;
3694
- const resolved = resolve6(candidate);
4090
+ const resolved = resolve8(candidate);
3695
4091
  if (existsSync10(resolved))
3696
4092
  return resolved;
3697
4093
  }
3698
- return resolve6(fallback);
4094
+ return resolve8(fallback);
3699
4095
  }
3700
4096
  function provisionedBinFromInstallDir3(installDir) {
3701
- if (installDir === undefined)
3702
- return null;
3703
- const candidate = join14(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
3704
- return existsSync10(candidate) ? candidate : null;
4097
+ return resolvePinnedCodegraphBin(installDir);
3705
4098
  }
3706
4099
  async function runCodegraphServeCli() {
3707
4100
  process.exitCode = await runCodegraphServe();
@@ -3720,7 +4113,7 @@ function isDirectInvocation(argvPath) {
3720
4113
  const moduleName = basename4(modulePath);
3721
4114
  if (moduleName !== "serve.js" && moduleName !== "serve.ts")
3722
4115
  return false;
3723
- return realpathSync5(resolve6(argvPath)) === realpathSync5(modulePath);
4116
+ return realpathSync6(resolve8(argvPath)) === realpathSync6(modulePath);
3724
4117
  }
3725
4118
 
3726
4119
  // components/codegraph/src/sweep-cli.ts
@@ -3767,6 +4160,7 @@ function writeSweepReport(stdout, result) {
3767
4160
  failed: result.failed,
3768
4161
  killed: result.killed.map(formatProcess),
3769
4162
  ownedRoots: result.ownedRoots,
4163
+ spared: result.spared.map(formatProcess),
3770
4164
  stampFile: result.stampFile
3771
4165
  })}
3772
4166
  `);
@@ -3830,7 +4224,7 @@ function isDirectInvocation2(argvPath) {
3830
4224
  const moduleName = basename5(modulePath);
3831
4225
  if (moduleName !== "cli.js" && moduleName !== "cli.ts")
3832
4226
  return false;
3833
- return realpathSync6(resolve7(argvPath)) === realpathSync6(modulePath);
4227
+ return realpathSync7(resolve9(argvPath)) === realpathSync7(modulePath);
3834
4228
  }
3835
4229
  export {
3836
4230
  runCodegraphCli