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
@@ -2613,8 +2613,13 @@ class LspClient extends LspClientConnection {
2613
2613
  if (!pushFallbackOnly)
2614
2614
  continue;
2615
2615
  const remainingMs = deadlineAt - Date.now();
2616
- if (remainingMs <= 0)
2616
+ if (remainingMs <= 0) {
2617
+ if (!this.isDiagnosticPullSupported() && snapshot.publishGeneration === 0) {
2618
+ const cached = this.documents.getPullCache(snapshot);
2619
+ return { items: cached === null ? [] : [...cached.diagnostics] };
2620
+ }
2617
2621
  return this.freshnessTimeout(absPath);
2622
+ }
2618
2623
  const waitMs = push.status === "wait" ? Math.min(push.waitMs, remainingMs) : remainingMs;
2619
2624
  await waitForDiagnosticsActivity(this.documents.waitForDiagnosticsActivity(snapshot, waitMs), signal);
2620
2625
  }
@@ -3184,6 +3189,81 @@ function isPlainRecord(value) {
3184
3189
  return typeof value === "object" && value !== null && !Array.isArray(value);
3185
3190
  }
3186
3191
 
3192
+ // src/daemon-request-error.ts
3193
+ class DaemonRequestError extends Error {
3194
+ requestWritten;
3195
+ constructor(message, requestWritten) {
3196
+ super(message);
3197
+ this.name = "DaemonRequestError";
3198
+ this.requestWritten = requestWritten;
3199
+ }
3200
+ }
3201
+
3202
+ class DaemonAuthenticationRejectedError extends DaemonRequestError {
3203
+ constructor() {
3204
+ super("daemon authentication failed before dispatch", true);
3205
+ this.name = "DaemonAuthenticationRejectedError";
3206
+ }
3207
+ }
3208
+
3209
+ class DaemonRequestCancelledError extends DaemonRequestError {
3210
+ constructor(requestWritten) {
3211
+ super("daemon request cancelled", requestWritten);
3212
+ this.name = "DaemonRequestCancelledError";
3213
+ }
3214
+ }
3215
+
3216
+ class DaemonRequestTimedOutError extends DaemonRequestError {
3217
+ timeoutMs;
3218
+ constructor(requestWritten, timeoutMs) {
3219
+ super("daemon request timed out", requestWritten);
3220
+ this.name = "DaemonRequestTimedOutError";
3221
+ this.timeoutMs = timeoutMs;
3222
+ }
3223
+ }
3224
+
3225
+ // src/daemon-failure-result.ts
3226
+ function daemonFailureResult(paths, error) {
3227
+ if (error instanceof DaemonRequestCancelledError)
3228
+ return daemonCancelledResult(paths);
3229
+ if (error instanceof DaemonRequestTimedOutError)
3230
+ return daemonTimedOutResult(paths, error.timeoutMs);
3231
+ return daemonUnreachableResult(paths, error);
3232
+ }
3233
+ function daemonCancelledResult(paths) {
3234
+ const text = [
3235
+ "LSP daemon request cancelled: the caller aborted this request (for example, the turn was interrupted).",
3236
+ "The daemon stays available; no LSP work was applied. Retry when you are ready.",
3237
+ `Socket: ${paths.socket}`
3238
+ ].join(`
3239
+ `);
3240
+ return { content: [{ type: "text", text }], isError: true };
3241
+ }
3242
+ function daemonTimedOutResult(paths, timeoutMs) {
3243
+ const text = [
3244
+ `LSP daemon request timed out after ${timeoutMs}ms: the daemon did not respond in time.`,
3245
+ "The daemon stays available but may be busy. Retry when you are ready.",
3246
+ `Socket: ${paths.socket}`,
3247
+ `Logs: ${paths.log}`
3248
+ ].join(`
3249
+ `);
3250
+ return { content: [{ type: "text", text }], isError: true };
3251
+ }
3252
+ function daemonUnreachableResult(paths, error) {
3253
+ const text = [
3254
+ `LSP daemon unreachable: ${errorText(error)}.`,
3255
+ "The MCP server is a thin proxy and never runs language servers in-process.",
3256
+ `Socket: ${paths.socket}`,
3257
+ `Logs: ${paths.log}`,
3258
+ "The daemon is auto-started on demand and will be retried on the next request."
3259
+ ].join(`
3260
+ `);
3261
+ return { content: [{ type: "text", text }], isError: true };
3262
+ }
3263
+ function errorText(error) {
3264
+ return error instanceof Error ? error.message : String(error);
3265
+ }
3266
+
3187
3267
  // src/ensure-daemon.ts
3188
3268
  import { spawn as spawn2 } from "node:child_process";
3189
3269
  import { closeSync as closeSync2, mkdirSync as mkdirSync2, openSync as openSync2 } from "node:fs";
@@ -3911,16 +3991,35 @@ function bufferFromChunk(chunk) {
3911
3991
 
3912
3992
  // ../mcp-stdio-core/src/server.ts
3913
3993
  var DEFAULT_IDLE_TIMEOUT_MS = 10 * 60000;
3994
+ var DEFAULT_PARENT_POLL_INTERVAL_MS = 30000;
3914
3995
  var noopLog = () => {};
3996
+ function isProcessAlive(pid) {
3997
+ try {
3998
+ process.kill(pid, 0);
3999
+ return true;
4000
+ } catch (error) {
4001
+ return !hasErrorCode(error, "ESRCH");
4002
+ }
4003
+ }
3915
4004
  async function runJsonRpcStdioServer(config) {
3916
4005
  const log = config.log ?? noopLog;
3917
4006
  const idleTimeoutMs = config.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
3918
- const idleTimer = createIdleTimer(idleTimeoutMs, log, config.onIdleTimeout);
4007
+ let isClosed = false;
4008
+ const idleTimer = createIdleTimer(idleTimeoutMs, log, () => {
4009
+ isClosed = true;
4010
+ config.onIdleTimeout?.();
4011
+ });
4012
+ const watchdog = createParentWatchdog(config.parentWatchdog, (parentPid, pollIntervalMs) => {
4013
+ isClosed = true;
4014
+ log("parent_exit", { parent_pid: parentPid, poll_interval_ms: pollIntervalMs });
4015
+ config.onParentExit?.();
4016
+ config.input.destroy();
4017
+ });
3919
4018
  log("stdio_started", { cwd: process.cwd(), idle_timeout_ms: idleTimeoutMs });
3920
4019
  idleTimer.arm();
3921
4020
  try {
3922
4021
  for await (const message of readStdioJsonRpcMessages(config.input)) {
3923
- if (idleTimer.closed())
4022
+ if (isClosed)
3924
4023
  break;
3925
4024
  idleTimer.arm();
3926
4025
  if (message.kind === "parse_error") {
@@ -3931,8 +4030,12 @@ async function runJsonRpcStdioServer(config) {
3931
4030
  if (!await handleRequest(message, config, log))
3932
4031
  break;
3933
4032
  }
4033
+ } catch (error) {
4034
+ if (!(isClosed && hasErrorCode(error, "ERR_STREAM_PREMATURE_CLOSE")))
4035
+ throw error;
3934
4036
  } finally {
3935
4037
  idleTimer.clear();
4038
+ watchdog.clear();
3936
4039
  log("stdio_stopped");
3937
4040
  }
3938
4041
  }
@@ -3988,9 +4091,33 @@ function isTerminalOutputError(error) {
3988
4091
  return false;
3989
4092
  return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
3990
4093
  }
3991
- function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
4094
+ function hasErrorCode(error, code) {
4095
+ return error instanceof Error && "code" in error && error.code === code;
4096
+ }
4097
+ function createParentWatchdog(config, onDeadParent) {
4098
+ if (config === undefined)
4099
+ return { clear: () => {} };
4100
+ const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_PARENT_POLL_INTERVAL_MS;
4101
+ if (pollIntervalMs <= 0)
4102
+ return { clear: () => {} };
4103
+ const parentPid = config.parentPid ?? process.ppid;
4104
+ const probeAlive = config.probeAlive ?? isProcessAlive;
4105
+ let fired = false;
4106
+ const timer = setInterval(() => {
4107
+ if (fired || probeAlive(parentPid))
4108
+ return;
4109
+ fired = true;
4110
+ onDeadParent(parentPid, pollIntervalMs);
4111
+ }, pollIntervalMs);
4112
+ timer.unref();
4113
+ return {
4114
+ clear: () => {
4115
+ clearInterval(timer);
4116
+ }
4117
+ };
4118
+ }
4119
+ function createIdleTimer(idleTimeoutMs, log, onTimeout) {
3992
4120
  let timer = null;
3993
- let isClosed = false;
3994
4121
  return {
3995
4122
  arm: () => {
3996
4123
  if (timer !== null)
@@ -3998,9 +4125,8 @@ function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3998
4125
  if (idleTimeoutMs <= 0)
3999
4126
  return;
4000
4127
  timer = setTimeout(() => {
4001
- isClosed = true;
4002
4128
  log("idle_timeout", { idle_timeout_ms: idleTimeoutMs });
4003
- onIdleTimeout?.();
4129
+ onTimeout();
4004
4130
  }, idleTimeoutMs);
4005
4131
  timer.unref();
4006
4132
  },
@@ -4009,8 +4135,7 @@ function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
4009
4135
  return;
4010
4136
  clearTimeout(timer);
4011
4137
  timer = null;
4012
- },
4013
- closed: () => isClosed
4138
+ }
4014
4139
  };
4015
4140
  }
4016
4141
  // ../lsp-core/src/lsp/client-wrapper.ts
@@ -5714,29 +5839,6 @@ function parseContext(value) {
5714
5839
  // src/daemon-client.ts
5715
5840
  var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
5716
5841
  var nextProxyRequestId = 1;
5717
-
5718
- class DaemonRequestError extends Error {
5719
- requestWritten;
5720
- constructor(message, requestWritten) {
5721
- super(message);
5722
- this.name = "DaemonRequestError";
5723
- this.requestWritten = requestWritten;
5724
- }
5725
- }
5726
-
5727
- class DaemonAuthenticationRejectedError extends DaemonRequestError {
5728
- constructor() {
5729
- super("daemon authentication failed before dispatch", true);
5730
- this.name = "DaemonAuthenticationRejectedError";
5731
- }
5732
- }
5733
-
5734
- class DaemonRequestCancelledError extends DaemonRequestError {
5735
- constructor(requestWritten) {
5736
- super("daemon request cancelled", requestWritten);
5737
- this.name = "DaemonRequestCancelledError";
5738
- }
5739
- }
5740
5842
  async function callToolViaDaemon(name, args, options) {
5741
5843
  const context = requireContext(options.context);
5742
5844
  const paths = options.paths ?? daemonPaths();
@@ -5765,7 +5867,7 @@ async function callToolViaDaemon(name, args, options) {
5765
5867
  break;
5766
5868
  }
5767
5869
  }
5768
- return daemonUnreachableResult(paths, lastError);
5870
+ return daemonFailureResult(paths, lastError);
5769
5871
  }
5770
5872
  function callDiagnosticsViaDaemon(filePath, options) {
5771
5873
  return callToolViaDaemon("diagnostics", { filePath, severity: "error" }, options);
@@ -5810,17 +5912,6 @@ function ensureDaemonAvailable(paths, ensure, signal) {
5810
5912
  Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() => resolve9()), (error) => finish(() => reject(signal.aborted ? new DaemonRequestCancelledError(false) : error)));
5811
5913
  });
5812
5914
  }
5813
- function daemonUnreachableResult(paths, error) {
5814
- const text2 = [
5815
- `LSP daemon unreachable: ${errorText(error)}.`,
5816
- "The MCP server is a thin proxy and never runs language servers in-process.",
5817
- `Socket: ${paths.socket}`,
5818
- `Logs: ${paths.log}`,
5819
- "The daemon is auto-started on demand and will be retried on the next request."
5820
- ].join(`
5821
- `);
5822
- return { content: [{ type: "text", text: text2 }], isError: true };
5823
- }
5824
5915
  function sendToolCall(paths, token, name, args, options) {
5825
5916
  return new Promise((resolve9, reject) => {
5826
5917
  const socket = connect(paths.socket);
@@ -5860,7 +5951,7 @@ function sendToolCall(paths, token, name, args, options) {
5860
5951
  };
5861
5952
  const timer = setTimeout(() => {
5862
5953
  sendCancel();
5863
- finish(() => reject(new DaemonRequestError("daemon request timed out", requestWritten)));
5954
+ finish(() => reject(new DaemonRequestTimedOutError(requestWritten, options.timeoutMs)));
5864
5955
  }, options.timeoutMs);
5865
5956
  timer.unref();
5866
5957
  if (options.signal?.aborted) {
@@ -5919,9 +6010,6 @@ function allocateProxyRequestId() {
5919
6010
  function isRetryableTool(name) {
5920
6011
  return name !== "rename" && name !== "lsp_rename";
5921
6012
  }
5922
- function errorText(error) {
5923
- return error instanceof Error ? error.message : String(error);
5924
- }
5925
6013
  // src/proxy.ts
5926
6014
  import { existsSync as existsSync11, realpathSync as realpathSync4 } from "node:fs";
5927
6015
  import { basename as basename3, delimiter as delimiter4, dirname as dirname9, isAbsolute as isAbsolute4 } from "node:path";
@@ -5979,6 +6067,7 @@ async function runMcpStdioProxy(options = {}) {
5979
6067
  input,
5980
6068
  output,
5981
6069
  idleTimeoutMs: 0,
6070
+ parentWatchdog: options.parentWatchdog ?? {},
5982
6071
  handler: (request, requestOptions) => {
5983
6072
  clearStartupWatchdog();
5984
6073
  return runWithRequestContext(context, () => handleProxyRequest(request, requestOptions));
@@ -1,4 +1,5 @@
1
1
  import type { Readable, Writable } from "node:stream";
2
+ import { type ParentWatchdogConfig } from "@oh-my-opencode/mcp-stdio-core";
2
3
  import { type CallToolOptions, type DaemonToolContext } from "./daemon-client.js";
3
4
  import { type DaemonPaths } from "./paths.js";
4
5
  export interface ProxyOptions {
@@ -12,5 +13,6 @@ export interface ProxyOptions {
12
13
  homeDir?: string;
13
14
  ensure?: CallToolOptions["ensure"];
14
15
  startupTimeoutMs?: number;
16
+ parentWatchdog?: ParentWatchdogConfig;
15
17
  }
16
18
  export declare function runMcpStdioProxy(options?: ProxyOptions): Promise<void>;
@@ -59,6 +59,7 @@ export async function runMcpStdioProxy(options = {}) {
59
59
  input,
60
60
  output,
61
61
  idleTimeoutMs: 0,
62
+ parentWatchdog: options.parentWatchdog ?? {},
62
63
  handler: (request, requestOptions) => {
63
64
  clearStartupWatchdog();
64
65
  return runWithRequestContext(context, () => handleProxyRequest(request, requestOptions));
@@ -0,0 +1,26 @@
1
+ import { type DaemonOwner } from "./ownership.js";
2
+ import { type DaemonPaths } from "./paths.js";
3
+ export interface DaemonCliAttestationDeps {
4
+ readonly readProcFile?: (path: string) => Promise<Buffer>;
5
+ readonly executeForStdout?: (file: string, args: readonly string[]) => Promise<string | null>;
6
+ }
7
+ export interface ReapStaleDaemonVersionsDeps {
8
+ readonly platform?: NodeJS.Platform;
9
+ readonly isAlive?: (pid: number) => boolean;
10
+ readonly attest?: (pid: number, platform: NodeJS.Platform) => Promise<boolean>;
11
+ readonly sendSignal?: (pid: number, signal: NodeJS.Signals) => boolean;
12
+ readonly waitForExit?: (pid: number, timeoutMs: number) => Promise<boolean>;
13
+ readonly removeDir?: (path: string) => Promise<void>;
14
+ readonly readOwner?: (paths: DaemonPaths) => DaemonOwner | null;
15
+ readonly log?: (message: string) => void;
16
+ readonly termGraceMs?: number;
17
+ readonly killGraceMs?: number;
18
+ }
19
+ export type VersionReapStatus = "terminated" | "removed" | "deferred" | "spared";
20
+ export interface VersionReapResult {
21
+ readonly version: string;
22
+ readonly status: VersionReapStatus;
23
+ readonly reason: string;
24
+ }
25
+ export declare function attestDaemonCliProcess(pid: number, platform: NodeJS.Platform, deps?: DaemonCliAttestationDeps): Promise<boolean>;
26
+ export declare function reapStaleDaemonVersions(ownPaths: DaemonPaths, deps?: ReapStaleDaemonVersionsDeps): Promise<readonly VersionReapResult[]>;
@@ -0,0 +1,182 @@
1
+ import { execFile } from "node:child_process";
2
+ import { readdir, readFile, rm } from "node:fs/promises";
3
+ import { basename, dirname, join } from "node:path";
4
+ import { isProcessAlive, readLockPid } from "./lock.js";
5
+ import { readDaemonOwner } from "./ownership.js";
6
+ import { daemonPaths, OMO_LSP_DAEMON_DIR } from "./paths.js";
7
+ const TERM_GRACE_MS = 5_000;
8
+ const KILL_GRACE_MS = 1_000;
9
+ const VERSION_ENTRY_PATTERN = /^v([A-Za-z0-9][A-Za-z0-9._+-]{0,127})$/;
10
+ export async function attestDaemonCliProcess(pid, platform, deps = {}) {
11
+ if (platform === "win32")
12
+ return false;
13
+ if (platform === "linux") {
14
+ const readProcFile = deps.readProcFile ?? defaultReadProcFile;
15
+ const cmdline = await readProcFile(`/proc/${pid}/cmdline`).catch(() => null);
16
+ if (cmdline === null)
17
+ return false;
18
+ return isNodeCliDaemonArgv(splitCmdline(cmdline));
19
+ }
20
+ const executeForStdout = deps.executeForStdout ?? defaultExecuteForStdout;
21
+ const command = await executeForStdout("/bin/ps", ["-p", String(pid), "-o", "command="]);
22
+ if (command === null)
23
+ return false;
24
+ return isNodeCliDaemonCommand(command.trim());
25
+ }
26
+ export async function reapStaleDaemonVersions(ownPaths, deps = {}) {
27
+ const baseDir = dirname(ownPaths.dir);
28
+ const platform = deps.platform ?? process.platform;
29
+ const isAlive = deps.isAlive ?? isProcessAlive;
30
+ const attest = deps.attest ?? ((pid) => attestDaemonCliProcess(pid, platform));
31
+ const sendSignal = deps.sendSignal ?? defaultSendSignal;
32
+ const waitForExit = deps.waitForExit ?? defaultWaitForExit;
33
+ const removeDir = deps.removeDir ?? ((path) => rm(path, { recursive: true, force: true }));
34
+ const readOwner = deps.readOwner ?? readDaemonOwner;
35
+ const log = deps.log ?? defaultLog;
36
+ const termGraceMs = deps.termGraceMs ?? TERM_GRACE_MS;
37
+ const killGraceMs = deps.killGraceMs ?? KILL_GRACE_MS;
38
+ const entries = await readdir(baseDir, { withFileTypes: true }).catch(() => []);
39
+ const results = [];
40
+ for (const entry of [...entries].sort((left, right) => left.name.localeCompare(right.name))) {
41
+ if (entry.name === `v${ownPaths.version}`)
42
+ continue;
43
+ const version = parseVersionEntry(entry.name);
44
+ if (version === null || !entry.isDirectory())
45
+ continue;
46
+ const versionDir = join(baseDir, entry.name);
47
+ const siblingPaths = daemonPaths({ [OMO_LSP_DAEMON_DIR]: baseDir }, { cliPath: ownPaths.cliPath, version });
48
+ results.push(await reapOneVersion({
49
+ version,
50
+ versionDir,
51
+ siblingPaths,
52
+ platform,
53
+ isAlive,
54
+ attest,
55
+ sendSignal,
56
+ waitForExit,
57
+ removeDir,
58
+ readOwner,
59
+ log,
60
+ termGraceMs,
61
+ killGraceMs,
62
+ }));
63
+ }
64
+ return results;
65
+ }
66
+ async function reapOneVersion(context) {
67
+ const { version, versionDir } = context;
68
+ const owner = context.readOwner(context.siblingPaths);
69
+ if (!owner) {
70
+ const lockPid = readLockPid(join(versionDir, "daemon.lock"));
71
+ if (lockPid !== null && context.isAlive(lockPid)) {
72
+ context.log(`reap: sparing v${version}: owner metadata missing but daemon.lock is held by live pid ${lockPid}`);
73
+ return { version, status: "spared", reason: `owner metadata missing but lock held by live pid ${lockPid}` };
74
+ }
75
+ await context.removeDir(versionDir);
76
+ return { version, status: "removed", reason: "removed stale version dir without readable owner metadata" };
77
+ }
78
+ if (!context.isAlive(owner.pid)) {
79
+ await context.removeDir(versionDir);
80
+ return { version, status: "removed", reason: `removed stale version dir for dead owner pid ${owner.pid}` };
81
+ }
82
+ if (context.platform === "win32") {
83
+ context.log(`reap: deferring v${version}: Windows cannot prove pid ownership safely (named-pipe policy)`);
84
+ return {
85
+ version,
86
+ status: "deferred",
87
+ reason: "Windows cannot prove pid ownership safely; named-pipe reap deferred",
88
+ };
89
+ }
90
+ if (!(await context.attest(owner.pid, context.platform))) {
91
+ context.log(`reap: sparing v${version}: pid ${owner.pid} is alive but cmdline attestation failed (possible recycled pid)`);
92
+ return {
93
+ version,
94
+ status: "spared",
95
+ reason: `pid ${owner.pid} attestation failed; possible recycled pid`,
96
+ };
97
+ }
98
+ return await terminateAttestedOwner(context, owner.pid);
99
+ }
100
+ async function terminateAttestedOwner(context, pid) {
101
+ const { version, versionDir } = context;
102
+ if (!context.sendSignal(pid, "SIGTERM")) {
103
+ await context.removeDir(versionDir);
104
+ return {
105
+ version,
106
+ status: "removed",
107
+ reason: `owner pid ${pid} exited before SIGTERM; removed stale version dir`,
108
+ };
109
+ }
110
+ if (await context.waitForExit(pid, context.termGraceMs)) {
111
+ await context.removeDir(versionDir);
112
+ return { version, status: "terminated", reason: `terminated attested older daemon pid ${pid} with SIGTERM` };
113
+ }
114
+ context.log(`reap: v${version} pid ${pid} survived SIGTERM; escalating to SIGKILL`);
115
+ if (!context.sendSignal(pid, "SIGKILL") || !(await context.waitForExit(pid, context.killGraceMs))) {
116
+ context.log(`reap: deferring v${version}: pid ${pid} survived SIGKILL`);
117
+ return { version, status: "deferred", reason: `attested daemon pid ${pid} survived SIGKILL; dir kept` };
118
+ }
119
+ await context.removeDir(versionDir);
120
+ return {
121
+ version,
122
+ status: "terminated",
123
+ reason: `terminated attested older daemon pid ${pid} after SIGKILL escalation`,
124
+ };
125
+ }
126
+ function parseVersionEntry(entryName) {
127
+ const match = VERSION_ENTRY_PATTERN.exec(entryName);
128
+ return match?.[1] ?? null;
129
+ }
130
+ function splitCmdline(buffer) {
131
+ return buffer
132
+ .toString("utf8")
133
+ .split("\u0000")
134
+ .filter((value) => value.length > 0);
135
+ }
136
+ function isNodeCliDaemonArgv(argv) {
137
+ if (argv.length < 2 || !argv.includes("daemon"))
138
+ return false;
139
+ const executable = basename(argv[0] ?? "");
140
+ if (!/^node(?:\.exe)?$/i.test(executable))
141
+ return false;
142
+ return argv.some((value) => value === "cli.js" || value.endsWith("/cli.js") || value.endsWith("\\cli.js"));
143
+ }
144
+ function isNodeCliDaemonCommand(command) {
145
+ return /\bnode(?:\.exe)?\b/i.test(command) && /\bcli\.js\b/.test(command) && /\bdaemon\b/.test(command);
146
+ }
147
+ function defaultReadProcFile(path) {
148
+ return readFile(path);
149
+ }
150
+ function defaultExecuteForStdout(file, args) {
151
+ return new Promise((resolve) => {
152
+ execFile(file, [...args], { encoding: "utf8", maxBuffer: 1024 * 1024, timeout: 1_000 }, (error, stdout) => {
153
+ if (error !== null) {
154
+ resolve(null);
155
+ return;
156
+ }
157
+ resolve(stdout);
158
+ });
159
+ });
160
+ }
161
+ function defaultSendSignal(pid, signal) {
162
+ try {
163
+ process.kill(pid, signal);
164
+ return true;
165
+ }
166
+ catch {
167
+ return false;
168
+ }
169
+ }
170
+ async function defaultWaitForExit(pid, timeoutMs) {
171
+ const deadline = Date.now() + timeoutMs;
172
+ for (;;) {
173
+ if (!isProcessAlive(pid))
174
+ return true;
175
+ if (Date.now() >= deadline)
176
+ return false;
177
+ await new Promise((resolve) => setTimeout(resolve, 100));
178
+ }
179
+ }
180
+ function defaultLog(message) {
181
+ process.stderr.write(`[lsp-daemon] ${message}\n`);
182
+ }
@@ -2615,8 +2615,13 @@ class LspClient extends LspClientConnection {
2615
2615
  if (!pushFallbackOnly)
2616
2616
  continue;
2617
2617
  const remainingMs = deadlineAt - Date.now();
2618
- if (remainingMs <= 0)
2618
+ if (remainingMs <= 0) {
2619
+ if (!this.isDiagnosticPullSupported() && snapshot.publishGeneration === 0) {
2620
+ const cached = this.documents.getPullCache(snapshot);
2621
+ return { items: cached === null ? [] : [...cached.diagnostics] };
2622
+ }
2619
2623
  return this.freshnessTimeout(absPath);
2624
+ }
2620
2625
  const waitMs = push.status === "wait" ? Math.min(push.waitMs, remainingMs) : remainingMs;
2621
2626
  await waitForDiagnosticsActivity(this.documents.waitForDiagnosticsActivity(snapshot, waitMs), signal);
2622
2627
  }
@@ -3167,16 +3172,35 @@ function bufferFromChunk(chunk) {
3167
3172
 
3168
3173
  // ../mcp-stdio-core/src/server.ts
3169
3174
  var DEFAULT_IDLE_TIMEOUT_MS = 10 * 60000;
3175
+ var DEFAULT_PARENT_POLL_INTERVAL_MS = 30000;
3170
3176
  var noopLog = () => {};
3177
+ function isProcessAlive(pid) {
3178
+ try {
3179
+ process.kill(pid, 0);
3180
+ return true;
3181
+ } catch (error) {
3182
+ return !hasErrorCode(error, "ESRCH");
3183
+ }
3184
+ }
3171
3185
  async function runJsonRpcStdioServer(config) {
3172
3186
  const log = config.log ?? noopLog;
3173
3187
  const idleTimeoutMs = config.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
3174
- const idleTimer = createIdleTimer(idleTimeoutMs, log, config.onIdleTimeout);
3188
+ let isClosed = false;
3189
+ const idleTimer = createIdleTimer(idleTimeoutMs, log, () => {
3190
+ isClosed = true;
3191
+ config.onIdleTimeout?.();
3192
+ });
3193
+ const watchdog = createParentWatchdog(config.parentWatchdog, (parentPid, pollIntervalMs) => {
3194
+ isClosed = true;
3195
+ log("parent_exit", { parent_pid: parentPid, poll_interval_ms: pollIntervalMs });
3196
+ config.onParentExit?.();
3197
+ config.input.destroy();
3198
+ });
3175
3199
  log("stdio_started", { cwd: process.cwd(), idle_timeout_ms: idleTimeoutMs });
3176
3200
  idleTimer.arm();
3177
3201
  try {
3178
3202
  for await (const message of readStdioJsonRpcMessages(config.input)) {
3179
- if (idleTimer.closed())
3203
+ if (isClosed)
3180
3204
  break;
3181
3205
  idleTimer.arm();
3182
3206
  if (message.kind === "parse_error") {
@@ -3187,8 +3211,12 @@ async function runJsonRpcStdioServer(config) {
3187
3211
  if (!await handleRequest(message, config, log))
3188
3212
  break;
3189
3213
  }
3214
+ } catch (error) {
3215
+ if (!(isClosed && hasErrorCode(error, "ERR_STREAM_PREMATURE_CLOSE")))
3216
+ throw error;
3190
3217
  } finally {
3191
3218
  idleTimer.clear();
3219
+ watchdog.clear();
3192
3220
  log("stdio_stopped");
3193
3221
  }
3194
3222
  }
@@ -3244,9 +3272,33 @@ function isTerminalOutputError(error) {
3244
3272
  return false;
3245
3273
  return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
3246
3274
  }
3247
- function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3275
+ function hasErrorCode(error, code) {
3276
+ return error instanceof Error && "code" in error && error.code === code;
3277
+ }
3278
+ function createParentWatchdog(config, onDeadParent) {
3279
+ if (config === undefined)
3280
+ return { clear: () => {} };
3281
+ const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_PARENT_POLL_INTERVAL_MS;
3282
+ if (pollIntervalMs <= 0)
3283
+ return { clear: () => {} };
3284
+ const parentPid = config.parentPid ?? process.ppid;
3285
+ const probeAlive = config.probeAlive ?? isProcessAlive;
3286
+ let fired = false;
3287
+ const timer = setInterval(() => {
3288
+ if (fired || probeAlive(parentPid))
3289
+ return;
3290
+ fired = true;
3291
+ onDeadParent(parentPid, pollIntervalMs);
3292
+ }, pollIntervalMs);
3293
+ timer.unref();
3294
+ return {
3295
+ clear: () => {
3296
+ clearInterval(timer);
3297
+ }
3298
+ };
3299
+ }
3300
+ function createIdleTimer(idleTimeoutMs, log, onTimeout) {
3248
3301
  let timer = null;
3249
- let isClosed = false;
3250
3302
  return {
3251
3303
  arm: () => {
3252
3304
  if (timer !== null)
@@ -3254,9 +3306,8 @@ function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3254
3306
  if (idleTimeoutMs <= 0)
3255
3307
  return;
3256
3308
  timer = setTimeout(() => {
3257
- isClosed = true;
3258
3309
  log("idle_timeout", { idle_timeout_ms: idleTimeoutMs });
3259
- onIdleTimeout?.();
3310
+ onTimeout();
3260
3311
  }, idleTimeoutMs);
3261
3312
  timer.unref();
3262
3313
  },
@@ -3265,8 +3316,7 @@ function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3265
3316
  return;
3266
3317
  clearTimeout(timer);
3267
3318
  timer = null;
3268
- },
3269
- closed: () => isClosed
3319
+ }
3270
3320
  };
3271
3321
  }
3272
3322
  // ../lsp-core/src/lsp/client-wrapper.ts
@@ -5027,12 +5077,13 @@ async function handleLspMcpRequest(input, options = {}) {
5027
5077
  }
5028
5078
  return errorResponse(id, -32601, `Method not found: ${String(method)}`);
5029
5079
  }
5030
- async function runMcpStdioServer(input = process.stdin, output = process.stdout) {
5080
+ async function runMcpStdioServer(input = process.stdin, output = process.stdout, options = {}) {
5031
5081
  const requestContext = createStandaloneMcpRequestContext();
5032
5082
  await runJsonRpcStdioServer({
5033
5083
  input,
5034
5084
  output,
5035
5085
  idleTimeoutMs: 0,
5086
+ parentWatchdog: options.parentWatchdog ?? {},
5036
5087
  handler: (request) => runWithRequestContext(requestContext, () => handleLspMcpRequest(request)),
5037
5088
  handlerOptions: undefined
5038
5089
  });
@@ -2613,8 +2613,13 @@ class LspClient extends LspClientConnection {
2613
2613
  if (!pushFallbackOnly)
2614
2614
  continue;
2615
2615
  const remainingMs = deadlineAt - Date.now();
2616
- if (remainingMs <= 0)
2616
+ if (remainingMs <= 0) {
2617
+ if (!this.isDiagnosticPullSupported() && snapshot.publishGeneration === 0) {
2618
+ const cached = this.documents.getPullCache(snapshot);
2619
+ return { items: cached === null ? [] : [...cached.diagnostics] };
2620
+ }
2617
2621
  return this.freshnessTimeout(absPath);
2622
+ }
2618
2623
  const waitMs = push.status === "wait" ? Math.min(push.waitMs, remainingMs) : remainingMs;
2619
2624
  await waitForDiagnosticsActivity(this.documents.waitForDiagnosticsActivity(snapshot, waitMs), signal);
2620
2625
  }