oh-my-opencode 4.18.1 → 4.19.0

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 (217) hide show
  1. package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +39 -16
  2. package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +111 -28
  3. package/README.ja.md +2 -2
  4. package/README.ko.md +2 -2
  5. package/README.md +4 -4
  6. package/README.ru.md +2 -2
  7. package/README.zh-cn.md +2 -2
  8. package/bin/AGENTS.md +33 -0
  9. package/dist/agents/atlas/agent.d.ts +6 -5
  10. package/dist/agents/sisyphus/index.d.ts +5 -0
  11. package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
  12. package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
  13. package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
  14. package/dist/agents/sisyphus-junior/index.d.ts +2 -0
  15. package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
  16. package/dist/agents/types.d.ts +2 -2
  17. package/dist/cli/index.js +690 -139
  18. package/dist/cli-node/index.js +690 -139
  19. package/dist/config/index.d.ts +1 -1
  20. package/dist/config/schema/browser-automation.d.ts +1 -0
  21. package/dist/config/schema/commands.d.ts +1 -3
  22. package/dist/config/schema/default-mode.d.ts +1 -1
  23. package/dist/config/schema/goal.d.ts +7 -0
  24. package/dist/config/schema/hooks.d.ts +1 -1
  25. package/dist/config/schema/oh-my-opencode-config.d.ts +6 -10
  26. package/dist/config/schema/ralph-loop.d.ts +1 -1
  27. package/dist/config/schema.d.ts +1 -1
  28. package/dist/create-hooks.d.ts +1 -1
  29. package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
  30. package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
  31. package/dist/features/builtin-commands/types.d.ts +1 -1
  32. package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
  33. package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
  34. package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
  35. package/dist/hooks/comment-checker/hook.d.ts +8 -1
  36. package/dist/hooks/goal/command-arguments.d.ts +13 -0
  37. package/dist/hooks/goal/controller.d.ts +26 -0
  38. package/dist/hooks/goal/index.d.ts +23 -0
  39. package/dist/hooks/goal/prompt.d.ts +3 -0
  40. package/dist/hooks/goal/store.d.ts +8 -0
  41. package/dist/hooks/goal/tools.d.ts +10 -0
  42. package/dist/hooks/goal/types.d.ts +93 -0
  43. package/dist/hooks/goal/validation.d.ts +4 -0
  44. package/dist/hooks/index.d.ts +1 -0
  45. package/dist/hooks/keyword-detector/hook.d.ts +1 -2
  46. package/dist/hooks/ralph-loop/types.d.ts +1 -1
  47. package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
  48. package/dist/index.js +3706 -4329
  49. package/dist/oh-my-opencode.schema.json +23 -19
  50. package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
  51. package/dist/plugin/chat-message/types.d.ts +23 -10
  52. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
  53. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
  54. package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
  55. package/dist/plugin/messages-transform.d.ts +1 -0
  56. package/dist/plugin/stop-continuation.d.ts +2 -2
  57. package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
  58. package/dist/shared/system-directive.d.ts +0 -1
  59. package/dist/skills/ulw-plan/SKILL.md +11 -7
  60. package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
  61. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  62. package/dist/skills/ulw-research/SKILL.md +3 -3
  63. package/dist/tools/delegate-task/types.d.ts +0 -1
  64. package/dist/tui.js +115 -28
  65. package/package.json +14 -14
  66. package/packages/git-bash-mcp/dist/cli.js +81 -19
  67. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +4 -4
  68. package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +118 -1
  69. package/packages/lsp-core/src/lsp/directory-diagnostics.ts +1 -1
  70. package/packages/lsp-daemon/dist/cli.js +262 -63
  71. package/packages/lsp-daemon/dist/client.js +194 -50
  72. package/packages/lsp-daemon/dist/daemon-client.d.ts +2 -2
  73. package/packages/lsp-daemon/dist/daemon-client.js +26 -2
  74. package/packages/lsp-daemon/dist/ensure-daemon.d.ts +5 -4
  75. package/packages/lsp-daemon/dist/ensure-daemon.js +79 -18
  76. package/packages/lsp-daemon/dist/index.js +262 -63
  77. package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
  78. package/packages/lsp-daemon/dist/proxy.js +79 -23
  79. package/packages/lsp-tools-mcp/dist/cli.js +82 -20
  80. package/packages/lsp-tools-mcp/dist/mcp.js +82 -20
  81. package/packages/lsp-tools-mcp/dist/tools.js +1 -1
  82. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  83. package/packages/omo-codex/plugin/.mcp.json +2 -1
  84. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +352 -17
  85. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  86. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  87. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
  88. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +195 -56
  89. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
  90. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  91. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
  92. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
  93. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
  94. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
  95. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
  96. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  97. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  98. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  99. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  100. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  101. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  102. package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
  103. package/packages/omo-codex/plugin/components/lsp/AGENTS.md +1 -0
  104. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  105. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +113 -31
  106. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  107. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  108. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +1 -0
  109. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
  110. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
  111. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  112. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  113. package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
  114. package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
  115. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
  116. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +148 -61
  117. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  118. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  119. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +4 -72
  120. package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
  121. package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
  122. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +213 -24
  123. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
  124. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +25 -4
  125. package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
  126. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  127. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  128. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  129. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  130. package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +10 -10
  131. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
  132. package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
  133. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  134. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  135. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +50 -33
  136. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +11 -7
  137. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
  138. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  139. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
  140. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
  141. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
  142. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  143. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  144. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
  145. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
  146. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
  147. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  148. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  149. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  150. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  151. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  152. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  153. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  154. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  155. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  156. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  157. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  158. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  159. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  160. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  161. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  162. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  163. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  164. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  165. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  166. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  167. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  168. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  169. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  170. package/packages/omo-codex/plugin/package-lock.json +13 -13
  171. package/packages/omo-codex/plugin/package.json +1 -1
  172. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
  173. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
  174. package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
  175. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +13 -20
  176. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
  177. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -2
  178. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
  179. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
  180. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +11 -7
  181. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
  182. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  183. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +2 -2
  184. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
  185. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
  186. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
  187. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
  188. package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
  189. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
  190. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
  191. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +38 -22
  192. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
  193. package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
  194. package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
  195. package/packages/omo-codex/scripts/install-dist/install-local.mjs +353 -18
  196. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
  197. package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -7
  198. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
  199. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  200. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
  201. package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
  202. package/dist/skills/ultraresearch/SKILL.md +0 -10
  203. package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
  204. package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
  205. package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
  206. package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
  207. package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
  208. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
  209. package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
  210. package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
  211. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
  212. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
  213. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
  214. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
  215. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
  216. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
  217. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/agents/openai.yaml +0 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  // components/codegraph/src/cli.ts
4
4
  import { realpathSync as realpathSync6 } from "node:fs";
5
- import { basename as basename5, resolve as resolve6 } from "node:path";
5
+ import { basename as basename5, resolve as resolve7 } from "node:path";
6
6
  import { stderr as processStderr4 } from "node:process";
7
7
  import { fileURLToPath as fileURLToPath4 } from "node:url";
8
8
 
@@ -98,8 +98,9 @@ function buildCodegraphChildEnv(options = {}) {
98
98
  import { homedir as homedir5 } from "node:os";
99
99
 
100
100
  // ../../utils/src/codegraph/workspace.ts
101
+ import { execFileSync } from "node:child_process";
101
102
  import { appendFileSync, existsSync as existsSync2, lstatSync as lstatSync2, mkdirSync, realpathSync as realpathSync3, readFileSync as readFileSync2, statSync, symlinkSync } from "node:fs";
102
- import { join as join5 } from "node:path";
103
+ import { dirname, join as join5, resolve as resolve3 } from "node:path";
103
104
 
104
105
  // ../../utils/src/codegraph/paths.ts
105
106
  import { createHash } from "node:crypto";
@@ -305,12 +306,52 @@ function prepareCodegraphWorkspace(workspace, options = {}) {
305
306
  }
306
307
  }
307
308
  function ensureCodegraphGitignored(workspace) {
308
- const gitDir = join5(workspace, ".git");
309
- if (!existsSync2(gitDir))
309
+ const gitMarkerPath = join5(workspace, ".git");
310
+ if (!existsSync2(gitMarkerPath))
310
311
  return false;
311
- const excludePath = join5(gitDir, "info", "exclude");
312
312
  try {
313
- mkdirSync(join5(gitDir, "info"), { recursive: true });
313
+ const isWorktree = execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
314
+ cwd: workspace,
315
+ encoding: "utf8",
316
+ shell: false,
317
+ stdio: ["ignore", "pipe", "ignore"],
318
+ timeout: 5000,
319
+ windowsHide: true
320
+ }).trim();
321
+ if (isWorktree !== "true")
322
+ return false;
323
+ const gitTopLevel = execFileSync("git", ["rev-parse", "--show-toplevel"], {
324
+ cwd: workspace,
325
+ encoding: "utf8",
326
+ shell: false,
327
+ stdio: ["ignore", "pipe", "ignore"],
328
+ timeout: 5000,
329
+ windowsHide: true
330
+ }).trim();
331
+ if (gitTopLevel.length === 0 || realpathSync3.native(gitTopLevel) !== realpathSync3.native(workspace))
332
+ return false;
333
+ const gitDir = execFileSync("git", ["rev-parse", "--absolute-git-dir"], {
334
+ cwd: workspace,
335
+ encoding: "utf8",
336
+ shell: false,
337
+ stdio: ["ignore", "pipe", "ignore"],
338
+ timeout: 5000,
339
+ windowsHide: true
340
+ }).trim();
341
+ if (gitDir.length === 0 || !gitMarkerOwnsGitDir(gitMarkerPath, workspace, gitDir))
342
+ return false;
343
+ const gitExcludePath = execFileSync("git", ["rev-parse", "--git-path", "info/exclude"], {
344
+ cwd: workspace,
345
+ encoding: "utf8",
346
+ shell: false,
347
+ stdio: ["ignore", "pipe", "ignore"],
348
+ timeout: 5000,
349
+ windowsHide: true
350
+ }).trim();
351
+ if (gitExcludePath.length === 0)
352
+ return false;
353
+ const excludePath = resolve3(workspace, gitExcludePath);
354
+ mkdirSync(dirname(excludePath), { recursive: true });
314
355
  const existing = existsSync2(excludePath) ? readFileSync2(excludePath, "utf8") : "";
315
356
  if (existing.split(/\r?\n/).includes(".codegraph"))
316
357
  return true;
@@ -319,12 +360,38 @@ function ensureCodegraphGitignored(workspace) {
319
360
  `}.codegraph
320
361
  `);
321
362
  return true;
322
- } catch (error) {
323
- if (error instanceof Error)
324
- return false;
325
- throw error;
363
+ } catch {
364
+ return false;
326
365
  }
327
366
  }
367
+ function gitMarkerOwnsGitDir(gitMarkerPath, workspace, gitDir) {
368
+ const markerStat = lstatSync2(gitMarkerPath);
369
+ if (markerStat.isSymbolicLink())
370
+ return false;
371
+ const resolvedGitDir = realpathSync3.native(gitDir);
372
+ if (markerStat.isDirectory())
373
+ return realpathSync3.native(gitMarkerPath) === resolvedGitDir;
374
+ if (!markerStat.isFile())
375
+ return false;
376
+ const markerMatch = /^gitdir:\s*(.+)\s*$/i.exec(readFileSync2(gitMarkerPath, "utf8").trim());
377
+ if (markerMatch?.[1] === undefined)
378
+ return false;
379
+ if (realpathSync3.native(resolve3(workspace, markerMatch[1])) !== resolvedGitDir)
380
+ return false;
381
+ const backlinkPath = join5(resolvedGitDir, "gitdir");
382
+ if (existsSync2(backlinkPath)) {
383
+ const backlink = readFileSync2(backlinkPath, "utf8").trim();
384
+ return backlink.length > 0 && realpathSync3.native(resolve3(resolvedGitDir, backlink)) === realpathSync3.native(gitMarkerPath);
385
+ }
386
+ const coreWorktree = execFileSync("git", ["config", "--file", join5(resolvedGitDir, "config"), "--get", "core.worktree"], {
387
+ encoding: "utf8",
388
+ shell: false,
389
+ stdio: ["ignore", "pipe", "ignore"],
390
+ timeout: 5000,
391
+ windowsHide: true
392
+ }).trim();
393
+ return coreWorktree.length > 0 && realpathSync3.native(resolve3(resolvedGitDir, coreWorktree)) === realpathSync3.native(workspace);
394
+ }
328
395
 
329
396
  // ../../utils/src/codegraph/guidance.ts
330
397
  var CODEGRAPH_UNINITIALIZED_PATTERN = /CodeGraph not initialized in ([\s\S]*?)\.\s*Run ['`]codegraph init['`] in that project first\./i;
@@ -416,7 +483,7 @@ function isRecord(value) {
416
483
  import { existsSync as existsSync3 } from "node:fs";
417
484
  import { homedir as homedir6 } from "node:os";
418
485
  import { spawnSync } from "node:child_process";
419
- import { basename as basename2, dirname, join as join7 } from "node:path";
486
+ import { basename as basename2, dirname as dirname2, join as join7 } from "node:path";
420
487
  import { createRequire } from "node:module";
421
488
 
422
489
  // ../../utils/src/runtime/which.ts
@@ -603,7 +670,7 @@ function defaultProvisionedBin(homeDir, fileExists) {
603
670
  function resolveBundledShim(requireResolve, fileExists) {
604
671
  try {
605
672
  const packageJson = requireResolve(`${CODEGRAPH_PACKAGE}/package.json`);
606
- const packageRoot = dirname(packageJson);
673
+ const packageRoot = dirname2(packageJson);
607
674
  const candidates = [join7(packageRoot, "bin", "codegraph.js"), join7(packageRoot, "npm-shim.js")];
608
675
  return candidates.find((candidate) => fileExists(candidate)) ?? null;
609
676
  } catch (error) {
@@ -1819,14 +1886,14 @@ function buildEnvOverrides(harness, env, warnings, merge) {
1819
1886
 
1820
1887
  // ../../utils/src/omo-config/resolve.ts
1821
1888
  import { existsSync as existsSync4 } from "node:fs";
1822
- import { dirname as dirname2, isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve3 } from "node:path";
1889
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve4 } from "node:path";
1823
1890
  function containsPath(parent, child) {
1824
1891
  const pathToChild = relative(parent, child);
1825
1892
  return pathToChild === "" || !pathToChild.startsWith("..") && !isAbsolute2(pathToChild);
1826
1893
  }
1827
1894
  function findProjectConfigPathsNearestFirst(cwd, homeDir) {
1828
- const startDir = resolve3(cwd);
1829
- const stopBeforeDir = containsPath(resolve3(homeDir), startDir) ? resolve3(homeDir) : null;
1895
+ const startDir = resolve4(cwd);
1896
+ const stopBeforeDir = containsPath(resolve4(homeDir), startDir) ? resolve4(homeDir) : null;
1830
1897
  const paths = [];
1831
1898
  let currentDir = startDir;
1832
1899
  while (true) {
@@ -1836,7 +1903,7 @@ function findProjectConfigPathsNearestFirst(cwd, homeDir) {
1836
1903
  if (existsSync4(configPath)) {
1837
1904
  paths.push(configPath);
1838
1905
  }
1839
- const parentDir = dirname2(currentDir);
1906
+ const parentDir = dirname3(currentDir);
1840
1907
  if (parentDir === currentDir)
1841
1908
  break;
1842
1909
  currentDir = parentDir;
@@ -1844,7 +1911,7 @@ function findProjectConfigPathsNearestFirst(cwd, homeDir) {
1844
1911
  return paths;
1845
1912
  }
1846
1913
  function resolveOmoConfigPaths(options) {
1847
- const globalPath = join8(resolve3(options.homeDir), ".omo", "config.jsonc");
1914
+ const globalPath = join8(resolve4(options.homeDir), ".omo", "config.jsonc");
1848
1915
  const projectPathsFarthestFirst = findProjectConfigPathsNearestFirst(options.cwd, options.homeDir).reverse();
1849
1916
  return [
1850
1917
  { path: globalPath, scope: "global" },
@@ -1943,7 +2010,7 @@ import { fileURLToPath } from "node:url";
1943
2010
  // ../../utils/src/codegraph/process-sweeper.ts
1944
2011
  import { existsSync as existsSync7, mkdirSync as mkdirSync2, statSync as statSync2, utimesSync, writeFileSync as writeFileSync2 } from "node:fs";
1945
2012
  import { homedir as homedir10 } from "node:os";
1946
- import { dirname as dirname3, join as join10 } from "node:path";
2013
+ import { dirname as dirname4, join as join10 } from "node:path";
1947
2014
 
1948
2015
  // ../../utils/src/codegraph/process-exec.ts
1949
2016
  import { execFile } from "node:child_process";
@@ -2188,7 +2255,7 @@ function processKillErrorMeansAlive(error) {
2188
2255
  // ../../utils/src/codegraph/process-roots.ts
2189
2256
  import { existsSync as existsSync6, readdirSync as readdirSync2, realpathSync as realpathSync4 } from "node:fs";
2190
2257
  import { homedir as homedir9 } from "node:os";
2191
- import { join as join9, resolve as resolve4 } from "node:path";
2258
+ import { join as join9, resolve as resolve5 } from "node:path";
2192
2259
  function discoverCodegraphOwnedRoots(options = {}) {
2193
2260
  const env = options.env ?? process.env;
2194
2261
  const homeDir = options.homeDir ?? env["HOME"] ?? env["USERPROFILE"] ?? homedir9();
@@ -2222,7 +2289,7 @@ function readCodexPluginCacheRoots(codexHome) {
2222
2289
  function addRoot(roots, root) {
2223
2290
  if (root === undefined || root.trim().length === 0)
2224
2291
  return;
2225
- const resolved = resolve4(root);
2292
+ const resolved = resolve5(root);
2226
2293
  roots.add(resolved);
2227
2294
  roots.add(realpathIfPossible2(resolved));
2228
2295
  }
@@ -2240,7 +2307,7 @@ function realpathIfPossible2(path) {
2240
2307
  return realpathSync4(path);
2241
2308
  } catch (error) {
2242
2309
  if (error instanceof Error)
2243
- return resolve4(path);
2310
+ return resolve5(path);
2244
2311
  throw error;
2245
2312
  }
2246
2313
  }
@@ -2325,7 +2392,7 @@ function isSweepThrottled(stampFile, nowMs, throttleMs) {
2325
2392
  return nowMs - statSync2(stampFile).mtimeMs < throttleMs;
2326
2393
  }
2327
2394
  function writeSweepStamp(stampFile, nowMs) {
2328
- mkdirSync2(dirname3(stampFile), { recursive: true });
2395
+ mkdirSync2(dirname4(stampFile), { recursive: true });
2329
2396
  writeFileSync2(stampFile, `${new Date(nowMs).toISOString()}
2330
2397
  `);
2331
2398
  const stampDate = new Date(nowMs);
@@ -2442,7 +2509,7 @@ async function removeEmptyDirectory(path) {
2442
2509
  }
2443
2510
  }
2444
2511
  function sleep(ms) {
2445
- return new Promise((resolve5) => setTimeout(resolve5, ms));
2512
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
2446
2513
  }
2447
2514
  async function defaultDownloader(asset, timeoutMs = DEFAULT_DOWNLOAD_TIMEOUT_MS) {
2448
2515
  const response = await fetch(asset.url, { signal: AbortSignal.timeout(timeoutMs) });
@@ -2962,7 +3029,7 @@ function defaultWorkerCliPath() {
2962
3029
  // components/codegraph/src/serve.ts
2963
3030
  import { existsSync as existsSync10, realpathSync as realpathSync5 } from "node:fs";
2964
3031
  import { homedir as homedir14 } from "node:os";
2965
- import { basename as basename4, join as join14, resolve as resolve5 } from "node:path";
3032
+ import { basename as basename4, join as join14, resolve as resolve6 } from "node:path";
2966
3033
  import {
2967
3034
  cwd as processCwd3,
2968
3035
  env as processEnv3,
@@ -2989,6 +3056,9 @@ function errorResponse(id, code, message, data) {
2989
3056
  function jsonRpcId(value) {
2990
3057
  return typeof value === "string" || typeof value === "number" || value === null ? value : null;
2991
3058
  }
3059
+ function messageFromError(error) {
3060
+ return error instanceof Error ? error.message : String(error);
3061
+ }
2992
3062
  // ../../mcp-stdio-core/src/transport.ts
2993
3063
  var HEADER_SEPARATOR = Buffer.from(`\r
2994
3064
  \r
@@ -3011,16 +3081,45 @@ async function* readStdioJsonRpcMessages(input) {
3011
3081
  yield parseJsonPayload(trailing, "line");
3012
3082
  }
3013
3083
  }
3014
- function writeStdioJsonRpcResponse(output, response, responseMode) {
3084
+ async function writeStdioJsonRpcResponse(output, response, responseMode) {
3015
3085
  const body = JSON.stringify(response);
3016
- if (responseMode === "framed") {
3017
- output.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r
3086
+ const payload = responseMode === "framed" ? `Content-Length: ${Buffer.byteLength(body, "utf8")}\r
3018
3087
  \r
3019
- ${body}`);
3020
- return;
3021
- }
3022
- output.write(`${body}
3023
- `);
3088
+ ${body}` : `${body}
3089
+ `;
3090
+ await writeChunk(output, payload);
3091
+ }
3092
+ function writeChunk(output, chunk) {
3093
+ return new Promise((resolve6, reject) => {
3094
+ let settled = false;
3095
+ const onError = (error) => {
3096
+ if (settled)
3097
+ return;
3098
+ settled = true;
3099
+ reject(error);
3100
+ };
3101
+ output.once("error", onError);
3102
+ try {
3103
+ output.write(chunk, (error) => {
3104
+ if (settled)
3105
+ return;
3106
+ settled = true;
3107
+ if (error) {
3108
+ queueMicrotask(() => output.removeListener("error", onError));
3109
+ reject(error);
3110
+ return;
3111
+ }
3112
+ output.removeListener("error", onError);
3113
+ resolve6();
3114
+ });
3115
+ } catch (error) {
3116
+ output.removeListener("error", onError);
3117
+ if (settled)
3118
+ return;
3119
+ settled = true;
3120
+ reject(error);
3121
+ }
3122
+ });
3024
3123
  }
3025
3124
  function readNextMessage(buffer) {
3026
3125
  if (buffer.length === 0)
@@ -3116,40 +3215,70 @@ async function runJsonRpcStdioServer(config) {
3116
3215
  break;
3117
3216
  idleTimer.arm();
3118
3217
  if (message.kind === "parse_error") {
3119
- handleParseError(message, config, log);
3218
+ if (!await handleParseError(message, config, log))
3219
+ break;
3120
3220
  continue;
3121
3221
  }
3122
- await handleRequest(message, config, log);
3222
+ if (!await handleRequest(message, config, log))
3223
+ break;
3123
3224
  }
3124
3225
  } finally {
3125
3226
  idleTimer.clear();
3126
3227
  log("stdio_stopped");
3127
3228
  }
3128
3229
  }
3129
- function handleParseError(message, config, log) {
3230
+ async function handleParseError(message, config, log) {
3130
3231
  log("parse_error", { message: message.message });
3131
3232
  const response = config.parseErrorResponse?.(message.message) ?? errorResponse(null, -32700, "Parse error", message.message);
3132
- if (response !== undefined) {
3133
- writeStdioJsonRpcResponse(config.output, response, message.responseMode);
3134
- }
3233
+ if (response === undefined)
3234
+ return true;
3235
+ return writeResponse(response, {
3236
+ output: config.output,
3237
+ responseMode: message.responseMode,
3238
+ log
3239
+ });
3135
3240
  }
3136
3241
  async function handleRequest(message, config, log) {
3137
3242
  const parsed = message.payload;
3138
3243
  const id = isPlainRecord(parsed) ? jsonRpcId(parsed["id"]) : null;
3139
3244
  const method = isPlainRecord(parsed) && typeof parsed["method"] === "string" ? parsed["method"] : null;
3140
3245
  log("request", { id: id === null ? null : String(id), method });
3246
+ let response;
3141
3247
  try {
3142
- const response = await config.handler(parsed, config.handlerOptions);
3143
- if (response === undefined)
3144
- return;
3145
- writeStdioJsonRpcResponse(config.output, response, message.responseMode);
3146
- log("response", { id: String(response.id), method, is_error: response.error !== undefined });
3248
+ response = await config.handler(parsed, config.handlerOptions);
3147
3249
  } catch (error) {
3148
3250
  if (config.onHandlerError === undefined)
3149
3251
  throw error;
3150
3252
  config.onHandlerError(error);
3253
+ return true;
3254
+ }
3255
+ if (response === undefined)
3256
+ return true;
3257
+ if (!await writeResponse(response, {
3258
+ output: config.output,
3259
+ responseMode: message.responseMode,
3260
+ log
3261
+ }))
3262
+ return false;
3263
+ log("response", { id: String(response.id), method, is_error: response.error !== undefined });
3264
+ return true;
3265
+ }
3266
+ async function writeResponse(response, context) {
3267
+ try {
3268
+ await writeStdioJsonRpcResponse(context.output, response, context.responseMode);
3269
+ return true;
3270
+ } catch (error) {
3271
+ if (!isTerminalOutputError(error))
3272
+ throw error;
3273
+ context.log("output_error", { message: messageFromError(error) });
3274
+ return false;
3151
3275
  }
3152
3276
  }
3277
+ function isTerminalOutputError(error) {
3278
+ if (!(error instanceof Error) || !("code" in error))
3279
+ return false;
3280
+ return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
3281
+ }
3153
3282
  function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
3154
3283
  let timer = null;
3155
3284
  let isClosed = false;
@@ -3230,18 +3359,28 @@ async function runBridgedCodegraphProcess(command, args, options) {
3230
3359
  resolveExit(signal === null ? 0 : 1);
3231
3360
  });
3232
3361
  });
3233
- const bridgeDone = Promise.all([
3234
- forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
3235
- defaultResponseMode = mode;
3236
- }),
3237
- forwardCodegraphToClient(childOutput, options.output, pendingResponses, () => defaultResponseMode)
3238
- ]);
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);
3239
3368
  const destroyChildPipes = () => {
3240
3369
  childInput.destroy();
3241
3370
  childOutput.destroy();
3242
3371
  };
3243
3372
  childExit.then(destroyChildPipes, destroyChildPipes);
3244
- return Promise.race([childExit, bridgeDone.then(() => childExit)]);
3373
+ try {
3374
+ return await Promise.race([childAndResponsesDone, bridgeDone.then(() => childExit)]);
3375
+ } catch (error) {
3376
+ destroyChildPipes();
3377
+ if (child.exitCode === null && child.signalCode === null)
3378
+ child.kill("SIGKILL");
3379
+ await childExit.catch(() => {
3380
+ return;
3381
+ });
3382
+ throw error;
3383
+ }
3245
3384
  }
3246
3385
  async function forwardClientToCodegraph(input, childInput, pendingResponses, setDefaultResponseMode) {
3247
3386
  for await (const message of readStdioJsonRpcMessages(input)) {
@@ -3265,7 +3404,7 @@ async function forwardClientToCodegraph(input, childInput, pendingResponses, set
3265
3404
  async function forwardCodegraphToClient(childOutput, output, pendingResponses, defaultResponseMode) {
3266
3405
  for await (const message of readStdioJsonRpcMessages(childOutput)) {
3267
3406
  if (message.kind === "parse_error") {
3268
- writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
3407
+ await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
3269
3408
  continue;
3270
3409
  }
3271
3410
  const key = responseModeKey(message.payload);
@@ -3273,7 +3412,7 @@ async function forwardCodegraphToClient(childOutput, output, pendingResponses, d
3273
3412
  const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
3274
3413
  if (key !== null)
3275
3414
  pendingResponses.delete(key);
3276
- writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
3415
+ await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
3277
3416
  }
3278
3417
  }
3279
3418
  function responseModeKey(payload) {
@@ -3552,11 +3691,11 @@ function resolveProjectCwd(env, fallback) {
3552
3691
  const candidate = env[key]?.trim();
3553
3692
  if (candidate === undefined || candidate.length === 0)
3554
3693
  continue;
3555
- const resolved = resolve5(candidate);
3694
+ const resolved = resolve6(candidate);
3556
3695
  if (existsSync10(resolved))
3557
3696
  return resolved;
3558
3697
  }
3559
- return resolve5(fallback);
3698
+ return resolve6(fallback);
3560
3699
  }
3561
3700
  function provisionedBinFromInstallDir3(installDir) {
3562
3701
  if (installDir === undefined)
@@ -3581,7 +3720,7 @@ function isDirectInvocation(argvPath) {
3581
3720
  const moduleName = basename4(modulePath);
3582
3721
  if (moduleName !== "serve.js" && moduleName !== "serve.ts")
3583
3722
  return false;
3584
- return realpathSync5(resolve5(argvPath)) === realpathSync5(modulePath);
3723
+ return realpathSync5(resolve6(argvPath)) === realpathSync5(modulePath);
3585
3724
  }
3586
3725
 
3587
3726
  // components/codegraph/src/sweep-cli.ts
@@ -3691,7 +3830,7 @@ function isDirectInvocation2(argvPath) {
3691
3830
  const moduleName = basename5(modulePath);
3692
3831
  if (moduleName !== "cli.js" && moduleName !== "cli.ts")
3693
3832
  return false;
3694
- return realpathSync6(resolve6(argvPath)) === realpathSync6(modulePath);
3833
+ return realpathSync6(resolve7(argvPath)) === realpathSync6(modulePath);
3695
3834
  }
3696
3835
  export {
3697
3836
  runCodegraphCli
@@ -1897,6 +1897,9 @@ function errorResponse(id, code, message, data) {
1897
1897
  function jsonRpcId(value) {
1898
1898
  return typeof value === "string" || typeof value === "number" || value === null ? value : null;
1899
1899
  }
1900
+ function messageFromError(error) {
1901
+ return error instanceof Error ? error.message : String(error);
1902
+ }
1900
1903
  // ../../../../mcp-stdio-core/src/transport.ts
1901
1904
  var HEADER_SEPARATOR = Buffer.from(`\r
1902
1905
  \r
@@ -1919,16 +1922,45 @@ async function* readStdioJsonRpcMessages(input) {
1919
1922
  yield parseJsonPayload(trailing, "line");
1920
1923
  }
1921
1924
  }
1922
- function writeStdioJsonRpcResponse(output, response, responseMode) {
1925
+ async function writeStdioJsonRpcResponse(output, response, responseMode) {
1923
1926
  const body = JSON.stringify(response);
1924
- if (responseMode === "framed") {
1925
- output.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r
1927
+ const payload = responseMode === "framed" ? `Content-Length: ${Buffer.byteLength(body, "utf8")}\r
1926
1928
  \r
1927
- ${body}`);
1928
- return;
1929
- }
1930
- output.write(`${body}
1931
- `);
1929
+ ${body}` : `${body}
1930
+ `;
1931
+ await writeChunk(output, payload);
1932
+ }
1933
+ function writeChunk(output, chunk) {
1934
+ return new Promise((resolve3, reject) => {
1935
+ let settled = false;
1936
+ const onError = (error) => {
1937
+ if (settled)
1938
+ return;
1939
+ settled = true;
1940
+ reject(error);
1941
+ };
1942
+ output.once("error", onError);
1943
+ try {
1944
+ output.write(chunk, (error) => {
1945
+ if (settled)
1946
+ return;
1947
+ settled = true;
1948
+ if (error) {
1949
+ queueMicrotask(() => output.removeListener("error", onError));
1950
+ reject(error);
1951
+ return;
1952
+ }
1953
+ output.removeListener("error", onError);
1954
+ resolve3();
1955
+ });
1956
+ } catch (error) {
1957
+ output.removeListener("error", onError);
1958
+ if (settled)
1959
+ return;
1960
+ settled = true;
1961
+ reject(error);
1962
+ }
1963
+ });
1932
1964
  }
1933
1965
  function readNextMessage(buffer) {
1934
1966
  if (buffer.length === 0)
@@ -2024,40 +2056,70 @@ async function runJsonRpcStdioServer(config) {
2024
2056
  break;
2025
2057
  idleTimer.arm();
2026
2058
  if (message.kind === "parse_error") {
2027
- handleParseError(message, config, log);
2059
+ if (!await handleParseError(message, config, log))
2060
+ break;
2028
2061
  continue;
2029
2062
  }
2030
- await handleRequest(message, config, log);
2063
+ if (!await handleRequest(message, config, log))
2064
+ break;
2031
2065
  }
2032
2066
  } finally {
2033
2067
  idleTimer.clear();
2034
2068
  log("stdio_stopped");
2035
2069
  }
2036
2070
  }
2037
- function handleParseError(message, config, log) {
2071
+ async function handleParseError(message, config, log) {
2038
2072
  log("parse_error", { message: message.message });
2039
2073
  const response = config.parseErrorResponse?.(message.message) ?? errorResponse(null, -32700, "Parse error", message.message);
2040
- if (response !== undefined) {
2041
- writeStdioJsonRpcResponse(config.output, response, message.responseMode);
2042
- }
2074
+ if (response === undefined)
2075
+ return true;
2076
+ return writeResponse(response, {
2077
+ output: config.output,
2078
+ responseMode: message.responseMode,
2079
+ log
2080
+ });
2043
2081
  }
2044
2082
  async function handleRequest(message, config, log) {
2045
2083
  const parsed = message.payload;
2046
2084
  const id = isPlainRecord(parsed) ? jsonRpcId(parsed["id"]) : null;
2047
2085
  const method = isPlainRecord(parsed) && typeof parsed["method"] === "string" ? parsed["method"] : null;
2048
2086
  log("request", { id: id === null ? null : String(id), method });
2087
+ let response;
2049
2088
  try {
2050
- const response = await config.handler(parsed, config.handlerOptions);
2051
- if (response === undefined)
2052
- return;
2053
- writeStdioJsonRpcResponse(config.output, response, message.responseMode);
2054
- log("response", { id: String(response.id), method, is_error: response.error !== undefined });
2089
+ response = await config.handler(parsed, config.handlerOptions);
2055
2090
  } catch (error) {
2056
2091
  if (config.onHandlerError === undefined)
2057
2092
  throw error;
2058
2093
  config.onHandlerError(error);
2094
+ return true;
2095
+ }
2096
+ if (response === undefined)
2097
+ return true;
2098
+ if (!await writeResponse(response, {
2099
+ output: config.output,
2100
+ responseMode: message.responseMode,
2101
+ log
2102
+ }))
2103
+ return false;
2104
+ log("response", { id: String(response.id), method, is_error: response.error !== undefined });
2105
+ return true;
2106
+ }
2107
+ async function writeResponse(response, context) {
2108
+ try {
2109
+ await writeStdioJsonRpcResponse(context.output, response, context.responseMode);
2110
+ return true;
2111
+ } catch (error) {
2112
+ if (!isTerminalOutputError(error))
2113
+ throw error;
2114
+ context.log("output_error", { message: messageFromError(error) });
2115
+ return false;
2059
2116
  }
2060
2117
  }
2118
+ function isTerminalOutputError(error) {
2119
+ if (!(error instanceof Error) || !("code" in error))
2120
+ return false;
2121
+ return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
2122
+ }
2061
2123
  function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
2062
2124
  let timer = null;
2063
2125
  let isClosed = false;
@@ -2138,18 +2200,28 @@ async function runBridgedCodegraphProcess(command, args, options) {
2138
2200
  resolveExit(signal === null ? 0 : 1);
2139
2201
  });
2140
2202
  });
2141
- const bridgeDone = Promise.all([
2142
- forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
2143
- defaultResponseMode = mode;
2144
- }),
2145
- forwardCodegraphToClient(childOutput, options.output, pendingResponses, () => defaultResponseMode)
2146
- ]);
2203
+ const clientForwardingDone = forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
2204
+ defaultResponseMode = mode;
2205
+ });
2206
+ const responseForwardingDone = forwardCodegraphToClient(childOutput, options.output, pendingResponses, () => defaultResponseMode);
2207
+ const bridgeDone = Promise.all([clientForwardingDone, responseForwardingDone]);
2208
+ const childAndResponsesDone = Promise.all([childExit, responseForwardingDone]).then(([exitCode]) => exitCode);
2147
2209
  const destroyChildPipes = () => {
2148
2210
  childInput.destroy();
2149
2211
  childOutput.destroy();
2150
2212
  };
2151
2213
  childExit.then(destroyChildPipes, destroyChildPipes);
2152
- return Promise.race([childExit, bridgeDone.then(() => childExit)]);
2214
+ try {
2215
+ return await Promise.race([childAndResponsesDone, bridgeDone.then(() => childExit)]);
2216
+ } catch (error) {
2217
+ destroyChildPipes();
2218
+ if (child.exitCode === null && child.signalCode === null)
2219
+ child.kill("SIGKILL");
2220
+ await childExit.catch(() => {
2221
+ return;
2222
+ });
2223
+ throw error;
2224
+ }
2153
2225
  }
2154
2226
  async function forwardClientToCodegraph(input, childInput, pendingResponses, setDefaultResponseMode) {
2155
2227
  for await (const message of readStdioJsonRpcMessages(input)) {
@@ -2173,7 +2245,7 @@ async function forwardClientToCodegraph(input, childInput, pendingResponses, set
2173
2245
  async function forwardCodegraphToClient(childOutput, output, pendingResponses, defaultResponseMode) {
2174
2246
  for await (const message of readStdioJsonRpcMessages(childOutput)) {
2175
2247
  if (message.kind === "parse_error") {
2176
- writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
2248
+ await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
2177
2249
  continue;
2178
2250
  }
2179
2251
  const key = responseModeKey(message.payload);
@@ -2181,7 +2253,7 @@ async function forwardCodegraphToClient(childOutput, output, pendingResponses, d
2181
2253
  const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
2182
2254
  if (key !== null)
2183
2255
  pendingResponses.delete(key);
2184
- writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
2256
+ await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
2185
2257
  }
2186
2258
  }
2187
2259
  function responseModeKey(payload) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-codegraph",
3
- "version": "4.18.1",
3
+ "version": "4.19.0",
4
4
  "description": "Codex plugin MCP wrapper for CodeGraph.",
5
5
  "type": "module",
6
6
  "private": true,