oh-my-opencode 4.18.0 → 4.18.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 (226) hide show
  1. package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +3677 -0
  2. package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +3154 -0
  3. package/.agents/skills/work-with-pr/SKILL.md +16 -37
  4. package/.agents/skills/work-with-pr-workspace/evals/evals.json +3 -3
  5. package/.opencode/skills/work-with-pr/SKILL.md +16 -37
  6. package/.opencode/skills/work-with-pr-workspace/evals/evals.json +3 -3
  7. package/bin/AGENTS.md +33 -0
  8. package/dist/cli/index.js +500 -158
  9. package/dist/cli-node/index.js +500 -158
  10. package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
  11. package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
  12. package/dist/hooks/comment-checker/hook.d.ts +8 -1
  13. package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
  14. package/dist/index.js +956 -702
  15. package/dist/plugin/messages-transform.d.ts +1 -0
  16. package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
  17. package/dist/tui.js +43 -4
  18. package/package.json +16 -16
  19. package/packages/git-bash-mcp/dist/cli.js +81 -19
  20. package/packages/lsp-core/package.json +4 -0
  21. package/packages/lsp-core/src/index.ts +1 -0
  22. package/packages/lsp-core/src/lsp/cleanup-errors.test.ts +18 -0
  23. package/packages/lsp-core/src/lsp/cleanup-errors.ts +12 -3
  24. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +261 -0
  25. package/packages/lsp-core/src/lsp/client-wrapper.test.ts +63 -0
  26. package/packages/lsp-core/src/lsp/client-wrapper.ts +35 -5
  27. package/packages/lsp-core/src/lsp/client.ts +262 -80
  28. package/packages/lsp-core/src/lsp/config-loader.ts +5 -17
  29. package/packages/lsp-core/src/lsp/connection.ts +12 -6
  30. package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +221 -0
  31. package/packages/lsp-core/src/lsp/directory-diagnostics.ts +61 -28
  32. package/packages/lsp-core/src/lsp/errors.ts +11 -0
  33. package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +283 -0
  34. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-contract-probe.ts +196 -0
  35. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +215 -0
  36. package/packages/lsp-core/src/lsp/formatters.ts +3 -0
  37. package/packages/lsp-core/src/lsp/json-rpc-connection-cancellation.test.ts +97 -0
  38. package/packages/lsp-core/src/lsp/json-rpc-connection.ts +73 -5
  39. package/packages/lsp-core/src/lsp/server-install-state.ts +3 -6
  40. package/packages/lsp-core/src/lsp/transport-protocol.ts +52 -0
  41. package/packages/lsp-core/src/lsp/transport.ts +96 -70
  42. package/packages/lsp-core/src/lsp/workspace-apply-edit-failure.ts +19 -0
  43. package/packages/lsp-core/src/lsp/workspace-apply-edit-lease.integration.test.ts +214 -0
  44. package/packages/lsp-core/src/lsp/workspace-apply-edit-sync.integration.test.ts +113 -0
  45. package/packages/lsp-core/src/lsp/workspace-apply-edit-test-support.ts +163 -0
  46. package/packages/lsp-core/src/lsp/workspace-apply-edit.integration.test.ts +163 -0
  47. package/packages/lsp-core/src/lsp/workspace-document-state.test.ts +67 -0
  48. package/packages/lsp-core/src/lsp/workspace-document-state.ts +368 -0
  49. package/packages/lsp-core/src/lsp/workspace-edit-adversarial.test.ts +113 -0
  50. package/packages/lsp-core/src/lsp/workspace-edit-commit.test.ts +140 -0
  51. package/packages/lsp-core/src/lsp/workspace-edit-commit.ts +220 -0
  52. package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.test.ts +56 -0
  53. package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.ts +30 -0
  54. package/packages/lsp-core/src/lsp/workspace-edit-fingerprint.ts +44 -0
  55. package/packages/lsp-core/src/lsp/workspace-edit-options.test.ts +147 -0
  56. package/packages/lsp-core/src/lsp/workspace-edit-parse-helpers.ts +59 -0
  57. package/packages/lsp-core/src/lsp/workspace-edit-parser.ts +130 -0
  58. package/packages/lsp-core/src/lsp/workspace-edit-path.ts +98 -0
  59. package/packages/lsp-core/src/lsp/workspace-edit-plan-types.ts +60 -0
  60. package/packages/lsp-core/src/lsp/workspace-edit-plan.ts +73 -0
  61. package/packages/lsp-core/src/lsp/workspace-edit-prevalidation.test.ts +174 -0
  62. package/packages/lsp-core/src/lsp/workspace-edit-resource-parser.ts +89 -0
  63. package/packages/lsp-core/src/lsp/workspace-edit-simulation.ts +183 -0
  64. package/packages/lsp-core/src/lsp/workspace-edit-snapshot.ts +53 -0
  65. package/packages/lsp-core/src/lsp/workspace-edit-text.ts +125 -0
  66. package/packages/lsp-core/src/lsp/workspace-edit-types.ts +121 -0
  67. package/packages/lsp-core/src/lsp/workspace-edit.characterization.test.ts +95 -0
  68. package/packages/lsp-core/src/lsp/workspace-edit.ts +49 -200
  69. package/packages/lsp-core/src/lsp/workspace-mutation-controller.ts +182 -0
  70. package/packages/lsp-core/src/mcp.ts +18 -7
  71. package/packages/lsp-core/src/missing-dependency-result.test.ts +105 -0
  72. package/packages/lsp-core/src/missing-dependency-result.ts +57 -0
  73. package/packages/lsp-core/src/post-edit/index.ts +1 -0
  74. package/packages/lsp-core/src/post-edit/orchestration.test.ts +157 -0
  75. package/packages/lsp-core/src/post-edit/orchestration.ts +178 -0
  76. package/packages/lsp-core/src/request-context.test.ts +171 -0
  77. package/packages/lsp-core/src/request-context.ts +222 -9
  78. package/packages/lsp-core/src/tool-surface.test.ts +4 -1
  79. package/packages/lsp-core/src/tools/diagnostics.ts +32 -13
  80. package/packages/lsp-core/src/tools/navigation.ts +12 -12
  81. package/packages/lsp-core/src/tools/rename.ts +10 -15
  82. package/packages/lsp-core/src/tools/symbols.ts +11 -11
  83. package/packages/lsp-core/src/tools/types.ts +2 -1
  84. package/packages/lsp-daemon/dist/cli.js +3330 -764
  85. package/packages/lsp-daemon/dist/client.d.ts +105 -0
  86. package/packages/lsp-daemon/dist/client.js +5995 -0
  87. package/packages/lsp-daemon/dist/daemon-client.d.ts +12 -7
  88. package/packages/lsp-daemon/dist/daemon-client.js +139 -32
  89. package/packages/lsp-daemon/dist/daemon-server.d.ts +1 -0
  90. package/packages/lsp-daemon/dist/daemon-server.js +40 -15
  91. package/packages/lsp-daemon/dist/ensure-daemon.d.ts +10 -8
  92. package/packages/lsp-daemon/dist/ensure-daemon.js +135 -51
  93. package/packages/lsp-daemon/dist/index.d.ts +2 -2
  94. package/packages/lsp-daemon/dist/index.js +3093 -786
  95. package/packages/lsp-daemon/dist/ipc-protocol.d.ts +46 -0
  96. package/packages/lsp-daemon/dist/ipc-protocol.js +187 -0
  97. package/packages/lsp-daemon/dist/lock.js +14 -4
  98. package/packages/lsp-daemon/dist/ownership.d.ts +49 -0
  99. package/packages/lsp-daemon/dist/ownership.js +168 -0
  100. package/packages/lsp-daemon/dist/paths.d.ts +33 -9
  101. package/packages/lsp-daemon/dist/paths.js +72 -33
  102. package/packages/lsp-daemon/dist/proxy.d.ts +5 -0
  103. package/packages/lsp-daemon/dist/proxy.js +123 -16
  104. package/packages/lsp-daemon/dist/request-routing.d.ts +7 -2
  105. package/packages/lsp-daemon/dist/request-routing.js +71 -22
  106. package/packages/lsp-daemon/dist/run-daemon.js +9 -2
  107. package/packages/lsp-daemon/dist/runtime-contract.d.ts +21 -0
  108. package/packages/lsp-daemon/dist/runtime-contract.js +58 -0
  109. package/packages/lsp-daemon/dist/socket-jsonrpc.js +6 -1
  110. package/packages/lsp-daemon/package.json +12 -3
  111. package/packages/lsp-tools-mcp/dist/cli.js +2189 -454
  112. package/packages/lsp-tools-mcp/dist/lsp/manager.js +1741 -148
  113. package/packages/lsp-tools-mcp/dist/mcp.js +2206 -471
  114. package/packages/lsp-tools-mcp/dist/request-context.js +176 -6
  115. package/packages/lsp-tools-mcp/dist/tools.js +2119 -447
  116. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  117. package/packages/omo-codex/plugin/.mcp.json +2 -1
  118. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  119. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  120. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +100 -28
  121. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
  122. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  123. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
  124. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
  125. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
  126. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
  127. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  128. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  129. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  130. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  131. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  132. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  133. package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
  134. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +55 -0
  135. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +3033 -936
  136. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook-cli.js +0 -4
  137. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.d.ts +5 -2
  138. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +41 -62
  139. package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.d.ts +1 -1
  140. package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.js +24 -15
  141. package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.d.ts +3 -7
  142. package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.js +23 -49
  143. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  144. package/packages/omo-codex/plugin/components/lsp/package.json +3 -2
  145. package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-daemon.mjs +31 -1
  146. package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-daemon.test.mjs +76 -0
  147. package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.mjs +201 -0
  148. package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.test.mjs +55 -0
  149. package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +0 -4
  150. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +49 -71
  151. package/packages/omo-codex/plugin/components/lsp/src/daemon-cli-path.ts +26 -15
  152. package/packages/omo-codex/plugin/components/lsp/src/lsp-session-state.ts +26 -64
  153. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-unavailable.test.ts +16 -17
  154. package/packages/omo-codex/plugin/components/lsp/test/codex-hook.test.ts +30 -4
  155. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +20 -5
  156. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
  157. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
  158. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  159. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  160. package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
  161. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
  162. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +2 -2
  163. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  164. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  165. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +1 -1
  166. package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
  167. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +15 -1
  168. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +20 -0
  169. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  170. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  171. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  172. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  173. package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
  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 +50 -33
  177. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
  178. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
  179. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
  180. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  181. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  182. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
  183. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +6 -5
  184. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
  185. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
  186. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  187. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  188. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  189. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  190. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  191. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  192. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  193. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  194. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  195. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  196. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  197. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  198. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  199. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  200. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  201. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  202. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  203. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  204. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  205. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  206. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  207. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  208. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  209. package/packages/omo-codex/plugin/package-lock.json +26 -14
  210. package/packages/omo-codex/plugin/package.json +1 -1
  211. package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +2 -3
  212. package/packages/omo-codex/plugin/scripts/build-components.mjs +13 -1
  213. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +9 -1
  214. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
  215. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +2 -1
  216. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
  217. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
  218. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +6 -5
  219. package/packages/omo-codex/plugin/test/aggregate-build.test.mjs +8 -0
  220. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +128 -15
  221. package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +10 -0
  222. package/packages/omo-codex/plugin/test/lsp-prebuild-layouts.test.mjs +2 -0
  223. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
  224. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
  225. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +34 -3
  226. package/packages/omo-codex/scripts/install-dist/install-local.mjs +328 -63
@@ -3,15 +3,10 @@ import { tmpdir } from "node:os";
3
3
  import path from "node:path";
4
4
 
5
5
  import { afterEach, describe, expect, it } from "vitest";
6
+ import type { PostEditDiagnosticsOutcome } from "@oh-my-opencode/lsp-core/post-edit";
6
7
 
7
8
  import { runLspPostCompactHook, runLspPostToolUseHook } from "../src/codex-hook.js";
8
9
 
9
- const MARKSMAN_INITIALIZE_TIMEOUT = [
10
- "LSP request timeout (method: initialize)",
11
- 'recent stderr: [01:16:41 INF] <LSP Entry> Starting Marksman LSP server: {"arch":"Arm64"}',
12
- '[01:16:41 INF] <Folder> Loading folder documents: {"uri":"file:///repo"}',
13
- ].join("\n");
14
-
15
10
  const DAEMON_UNREACHABLE = [
16
11
  "LSP daemon unreachable: daemon did not become reachable.",
17
12
  "The MCP server is a thin proxy and never runs language servers in-process.",
@@ -37,7 +32,7 @@ describe("codex PostToolUse unavailable LSP suppression", () => {
37
32
  // when
38
33
  const firstOutput = await runLspPostToolUseHook(input, async () => {
39
34
  calls += 1;
40
- return MARKSMAN_INITIALIZE_TIMEOUT;
35
+ return markdownNotConfigured();
41
36
  });
42
37
  const secondOutput = await runLspPostToolUseHook(input, async () => {
43
38
  calls += 1;
@@ -60,7 +55,7 @@ describe("codex PostToolUse unavailable LSP suppression", () => {
60
55
  await withPluginData(pluginData, async () => {
61
56
  await runLspPostToolUseHook(input, async () => {
62
57
  calls += 1;
63
- return MARKSMAN_INITIALIZE_TIMEOUT;
58
+ return markdownNotConfigured();
64
59
  });
65
60
  await runLspPostToolUseHook(input, async () => {
66
61
  calls += 1;
@@ -80,7 +75,7 @@ describe("codex PostToolUse unavailable LSP suppression", () => {
80
75
  const compactOutput = await runLspPostCompactHook(compactInput);
81
76
  const afterCompactOutput = await runLspPostToolUseHook(input, async () => {
82
77
  calls += 1;
83
- return MARKSMAN_INITIALIZE_TIMEOUT;
78
+ return markdownNotConfigured();
84
79
  });
85
80
  await runLspPostToolUseHook(input, async () => {
86
81
  calls += 1;
@@ -103,7 +98,7 @@ describe("codex PostToolUse unavailable LSP suppression", () => {
103
98
  await withPluginData(pluginData, async () => {
104
99
  await runLspPostToolUseHook(input, async () => {
105
100
  calls += 1;
106
- return MARKSMAN_INITIALIZE_TIMEOUT;
101
+ return markdownNotConfigured();
107
102
  });
108
103
  await runLspPostCompactHook({ session_id: "session-compact-clean" });
109
104
 
@@ -154,11 +149,11 @@ describe("codex PostToolUse unavailable LSP suppression", () => {
154
149
  const input = postToolUseInput("session-daemon-down-probe", ".omo/ulw-loop/evidence/note.md");
155
150
  let calls = 0;
156
151
 
157
- await withPluginData(pluginData, async () => {
158
- await runLspPostToolUseHook(input, async () => {
159
- calls += 1;
160
- return MARKSMAN_INITIALIZE_TIMEOUT;
161
- });
152
+ await withPluginData(pluginData, async () => {
153
+ await runLspPostToolUseHook(input, async () => {
154
+ calls += 1;
155
+ return markdownNotConfigured();
156
+ });
162
157
  await runLspPostToolUseHook(input, async () => {
163
158
  calls += 1;
164
159
  return "error[markdown] (1000) at 1:1: cached call should have been skipped.";
@@ -172,7 +167,7 @@ describe("codex PostToolUse unavailable LSP suppression", () => {
172
167
  });
173
168
  const retriedProbeOutput = await runLspPostToolUseHook(input, async () => {
174
169
  calls += 1;
175
- return MARKSMAN_INITIALIZE_TIMEOUT;
170
+ return markdownNotConfigured();
176
171
  });
177
172
  await runLspPostToolUseHook(input, async () => {
178
173
  calls += 1;
@@ -193,7 +188,7 @@ describe("codex PostToolUse unavailable LSP suppression", () => {
193
188
  const typescriptInput = postToolUseInput("session-real-diagnostics", "src/broken.ts");
194
189
 
195
190
  await withPluginData(pluginData, async () => {
196
- await runLspPostToolUseHook(markdownInput, async () => MARKSMAN_INITIALIZE_TIMEOUT);
191
+ await runLspPostToolUseHook(markdownInput, async () => markdownNotConfigured());
197
192
 
198
193
  // when
199
194
  const output = await runLspPostToolUseHook(
@@ -248,6 +243,10 @@ function tempPluginData(): string {
248
243
  return dir;
249
244
  }
250
245
 
246
+ function markdownNotConfigured(): PostEditDiagnosticsOutcome {
247
+ return { kind: "not_configured", extension: ".md" };
248
+ }
249
+
251
250
  interface PostToolUseHookOutput {
252
251
  readonly decision: "block";
253
252
  readonly reason: string;
@@ -1,10 +1,11 @@
1
- import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import path from "node:path";
4
4
 
5
5
  import { afterEach, describe, expect, it } from "vitest";
6
+ import type { PostEditDiagnosticsOutcome } from "@oh-my-opencode/lsp-core/post-edit";
6
7
 
7
- import { extractMutatedFilePaths, runLspPostToolUseHook } from "../src/codex-hook.js";
8
+ import { codexLspRequestContext, extractMutatedFilePaths, runLspPostToolUseHook } from "../src/codex-hook.js";
8
9
 
9
10
  const tempDirs: string[] = [];
10
11
 
@@ -15,6 +16,21 @@ afterEach(() => {
15
16
  });
16
17
 
17
18
  describe("codex PostToolUse hook", () => {
19
+ it("#given CODEX_HOME #when building the daemon request context #then Codex config paths are exact and capability is enabled", () => {
20
+ const cwd = tempDir("codex-lsp-cwd-");
21
+ const codexHome = tempDir("codex-lsp-home-");
22
+
23
+ const context = codexLspRequestContext({ CODEX_HOME: codexHome }, cwd);
24
+
25
+ expect(context).toEqual({
26
+ cwd: realpathSync(cwd),
27
+ projectConfigPaths: [path.join(realpathSync(cwd), ".codex", "lsp-client.json")],
28
+ userConfigPath: path.join(codexHome, "lsp-client.json"),
29
+ installDecisionsPath: path.join(codexHome, "lsp-install-decisions.json"),
30
+ capabilities: { installDecisionTool: true },
31
+ });
32
+ });
33
+
18
34
  it("extracts files from Codex apply_patch command payloads", () => {
19
35
  const paths = extractMutatedFilePaths({
20
36
  tool_name: "apply_patch",
@@ -174,7 +190,7 @@ describe("codex PostToolUse hook", () => {
174
190
  return "error[typescript] (2322) at 1:7: Type 'number' is not assignable to type 'string'.";
175
191
  }
176
192
  if (filePath === "README.md") {
177
- return "No LSP server configured for extension: .md";
193
+ return markdownNotConfigured();
178
194
  }
179
195
  return "No diagnostics found";
180
196
  },
@@ -286,7 +302,7 @@ describe("codex PostToolUse hook", () => {
286
302
  },
287
303
  tool_response: "Success. Updated files.",
288
304
  },
289
- async () => "No LSP server configured for extension: .md",
305
+ async () => markdownNotConfigured(),
290
306
  );
291
307
 
292
308
  expect(output).toBe("");
@@ -356,3 +372,13 @@ function isPostToolUseHookOutput(value: unknown): value is PostToolUseHookOutput
356
372
  function isRecord(value: unknown): value is Record<string, unknown> {
357
373
  return typeof value === "object" && value !== null && !Array.isArray(value);
358
374
  }
375
+
376
+ function tempDir(prefix: string): string {
377
+ const dir = mkdtempSync(path.join(tmpdir(), prefix));
378
+ tempDirs.push(dir);
379
+ return dir;
380
+ }
381
+
382
+ function markdownNotConfigured(): PostEditDiagnosticsOutcome {
383
+ return { kind: "not_configured", extension: ".md" };
384
+ }
@@ -31,11 +31,12 @@ describe("plugin package metadata", () => {
31
31
  expect(packageJson.type).toBe("module");
32
32
  expect(packageJson.packageManager).toBe("npm@11.12.1");
33
33
  expect(packageJson.dependencies).toEqual({
34
+ "@oh-my-opencode/lsp-core": "file:../../../../lsp-core",
34
35
  "@code-yeongyu/lsp-daemon": "file:../../../../lsp-daemon",
35
36
  });
36
37
  expect(packageJson.bin["omo-lsp"]).toBe("./dist/cli.js");
37
38
  expect(packageJson.bin["codex-lsp"]).toBeUndefined();
38
- expect(scripts["build"]).toBe("node scripts/clean-dist.mjs && tsc -p tsconfig.build.json");
39
+ expect(scripts["build"]).toBe("node scripts/build-runtime.mjs");
39
40
  expect(scripts["pretest"]).toBe("npm run build --silent");
40
41
  expect(cliSource.startsWith("#!/usr/bin/env node")).toBe(true);
41
42
  expect(cliSource).toContain("Usage: omo-lsp [mcp | hook post-tool-use | hook post-compact]");
@@ -43,17 +44,31 @@ describe("plugin package metadata", () => {
43
44
  expect(postCompactCommand).toBe(`node "${pluginRoot}/dist/cli.js" hook post-compact`);
44
45
  expect(lspServer?.command).toBe("node");
45
46
  expect(lspServer?.args).toEqual(["../../../../lsp-daemon/dist/cli.js", "mcp"]);
47
+ expect(lspServer?.startup_timeout_sec).toBe(10);
46
48
  expect(cliSource).not.toContain("./lazy-lsp-mcp.js");
47
49
  expect(cliSource).toContain("resolveLspDaemonCliPath");
48
- expect(daemonCliPathSource).toContain("@code-yeongyu/lsp-daemon/dist/cli.js");
50
+ expect(daemonCliPathSource).toContain("@code-yeongyu/lsp-daemon/cli");
49
51
  expect(daemonCliPathSource).toContain("../../lsp-daemon/dist/cli.js");
50
- expect(daemonCliPathSource).toContain("CODEX_LSP_DAEMON_VERSION");
52
+ expect(daemonCliPathSource).toContain("OMO_LSP_DAEMON_VERSION");
51
53
  expect(cliSource).not.toContain("../../../../../lsp-daemon/dist/cli.js");
52
54
  expect(codexHookSource).toContain("ensureLspDaemonCliEnv");
53
- expect(codexHookCliSource).toContain("@code-yeongyu/lsp-daemon");
54
- expect(codexHookSource).toContain("@code-yeongyu/lsp-daemon");
55
+ expect(codexHookCliSource).not.toContain("@code-yeongyu/lsp-daemon");
56
+ expect(codexHookSource).toContain("@code-yeongyu/lsp-daemon/client");
57
+ expect(codexHookSource).toContain("@oh-my-opencode/lsp-core/post-edit");
58
+ expect(codexHookSource).toContain("CODEX_HOME");
55
59
  expect(codexHookCliSource).not.toContain("../../../../../lsp-daemon");
56
60
  expect(codexHookSource).not.toContain("../../../../../lsp-daemon");
57
61
  expect(sourceFiles.filter((name) => name.startsWith("lazy-mcp") || name === "lazy-lsp-mcp.ts")).toEqual([]);
58
62
  });
63
+
64
+ it("#given built component CLI #when runtime imports are inspected #then it is self-contained except Node builtins", () => {
65
+ const cliSource = readTextFile("dist/cli.js");
66
+ const imports = [
67
+ ...cliSource.matchAll(/\bimport\s+(?:[^'";]+?\s+from\s+)?["']([^"']+)["']/g),
68
+ ...cliSource.matchAll(/\bimport\s*\(\s*["']([^"']+)["']\s*\)/g),
69
+ ].flatMap((match) => (match[1] === undefined ? [] : [match[1]]));
70
+ expect(imports.filter((specifier) => !specifier.startsWith("node:"))).toEqual([]);
71
+ expect(cliSource).not.toContain("@code-yeongyu/lsp-daemon/client");
72
+ expect(cliSource).not.toContain("@oh-my-opencode/lsp-core");
73
+ });
59
74
  });
@@ -74,7 +74,7 @@ Diagnostics catch type errors, not logic bugs; tests cover only what their autho
74
74
 
75
75
  # Global Review and Debugging Gate
76
76
 
77
- Run `review-work` plus a `debugging` runtime audit only before a PR handoff or when the user asks for a review; lane pass/fail semantics live in those skills. For everything else, the gate above is the whole gate: once you have personally observed the artifact working, report your evidence. Redact secrets, tokens, and PII from ledgers, PR bodies, and handoffs.
77
+ Run `review-work` plus a `debugging` runtime audit only before a PR handoff or when the user asks for a review; lane pass/fail semantics live in those skills. Each passing review lane and debugging audit binds to the exact full commit SHA it reviewed. Immediately append a durable task-evidence/ledger record with its name, full SHA, verdict, and report artifact/source. Before reuse after continuation or compaction, re-read the record and require the exact lane/SHA pair; memory or an unstamped report is not coverage. Every missing pair at the current SHA still runs, and new commits require fresh applicable coverage. For everything else, the gate above is the whole gate: once you have personally observed the artifact working, report your evidence. Redact secrets, tokens, and PII from ledgers, PR bodies, and handoffs.
78
78
 
79
79
  # Failure Recovery
80
80
 
@@ -19,13 +19,15 @@ Status requests are not stop signals: give the update, keep working. Honor every
19
19
 
20
20
  # Discovery
21
21
 
22
- Never speculate about code you have not read: verify with tools and re-read on every hand-off. Start broad once - and WIDE: tool calls run as JavaScript through `exec`, so write programs, not single calls. Batch EVERY independent read, search, and doc lookup into ONE `exec` script via `Promise.all` over the `tools` object before the first edit; filter and reduce results in-script so only what you need returns to context. NEVER await independent calls one at a time - sequence only when one result feeds the next. Retrieve again only when the core question is open, a needed fact is missing, or a second-order question (callers, error paths, ownership) changes the design. Stop when you can act. Prefer the root fix over the symptom fix.
22
+ Never speculate about code you have not read: verify with tools and re-read on every hand-off. **USE CODE MODE AGGRESSIVELY FOR BOUNDED DISCOVERY.** When multiple independent tool calls produce results that can be materially filtered, joined, deduplicated, or reduced, make ONE `exec` / eval JavaScript program that calls eligible tools concurrently with `Promise.all` and returns only decision-relevant evidence. For shell-native repo work without programmatic tool access, use ONE Python script with `concurrent.futures`, `subprocess`, and utility functions to batch commands and reduce output. **USE LSP FOR SYMBOLS** - definitions, references, rename impact, workspace symbols, and diagnostics - instead of reconstructing semantics with text search. Use direct calls when one result chooses the next action, outputs are already small, semantic judgment is required between calls, approval or side effects are involved, or native artifacts must be preserved. Retrieve again only for a missing required fact or a second-order question that changes the design. Stop when you can act; prefer the root fix over the symptom.
23
23
 
24
24
  # Operating Loop
25
25
 
26
26
  Explore -> Plan (`update_plan`, per Task Tracking) -> Implement -> Verify -> Manually QA.
27
27
 
28
- Implement surgically, matching codebase style (naming, indentation, imports, error handling) even when you would write it differently. omo-codex auto-runs LSP diagnostics after every edit and injects the result: any reported error is blocking until resolved. Verify with targeted tests and builds for changed behavior; if validation cannot run, say why and name the next best check.
28
+ Implement surgically, matching codebase style (naming, indentation, imports, error handling) even when you would write it differently. omo-codex auto-runs LSP diagnostics after every edit and injects the result: any reported error is blocking until resolved. Verify with targeted tests and builds for changed behavior; if validation cannot run, say why and name the next best check. Re-run a validation command only when its inputs changed since its last green run; one full pass right before the final message replaces repeated identical reruns.
29
+
30
+ Waiting is not free: a status poll replays the whole accumulated context through the model. Run a long command (install, build, suite, container, CI watch) to completion in ONE `exec` call with a timeout sized to the expected wait - or send output to a log file read once on a completion signal - never re-poll the same surface with empty reads or sub-minute waits. If two consecutive checks show no state change, double the wait or switch to a completion signal.
29
31
 
30
32
  # Subagents
31
33
 
@@ -52,7 +54,7 @@ Diagnostics catch type errors, not logic bugs; tests cover only what their autho
52
54
 
53
55
  "This should work" from reading source does not pass. A defect found in usage is yours to fix this turn.
54
56
 
55
- Run `review-work` plus a `debugging` runtime audit only before a PR handoff or when the user asks for a review; lane pass/fail semantics live in those skills. For everything else, the gate above is the whole gate: once you have personally observed the artifact working, report your evidence. Redact secrets, tokens, and PII from ledgers, PR bodies, and handoffs.
57
+ Run `review-work` plus a `debugging` runtime audit only before a PR handoff or when the user asks for a review; lane pass/fail semantics live in those skills. Each passing review lane and debugging audit binds to the exact full commit SHA it reviewed. Immediately append a durable task-evidence/ledger record with its name, full SHA, verdict, and report artifact/source. Before reuse after continuation or compaction, re-read the record and require the exact lane/SHA pair; memory or an unstamped report is not coverage. Every missing pair at the current SHA still runs, and new commits require fresh applicable coverage. For everything else, the gate above is the whole gate: once you have personally observed the artifact working, report your evidence. Redact secrets, tokens, and PII from ledgers, PR bodies, and handoffs.
56
58
 
57
59
  # Failure Recovery
58
60
 
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 4.18.0) Loading Project Rules"
10
+ "statusMessage": "(OmO 4.18.2) Loading Project Rules"
11
11
  }
12
12
  ]
13
13
  }
@@ -19,7 +19,7 @@
19
19
  "type": "command",
20
20
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
21
21
  "timeout": 10,
22
- "statusMessage": "(OmO 4.18.0) Loading Project Rules"
22
+ "statusMessage": "(OmO 4.18.2) Loading Project Rules"
23
23
  }
24
24
  ]
25
25
  }
@@ -32,7 +32,7 @@
32
32
  "type": "command",
33
33
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
34
34
  "timeout": 10,
35
- "statusMessage": "(OmO 4.18.0) Matching Project Rules"
35
+ "statusMessage": "(OmO 4.18.2) Matching Project Rules"
36
36
  }
37
37
  ]
38
38
  }
@@ -45,7 +45,7 @@
45
45
  "type": "command",
46
46
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
47
47
  "timeout": 10,
48
- "statusMessage": "(OmO 4.18.0) Resetting Project Rule Cache"
48
+ "statusMessage": "(OmO 4.18.2) Resetting Project Rule Cache"
49
49
  }
50
50
  ]
51
51
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-rules",
3
- "version": "4.18.0",
3
+ "version": "4.18.2",
4
4
  "description": "Codex plugin that injects project rule files into model context through lifecycle hooks.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -2,13 +2,13 @@
2
2
 
3
3
  Codex Stop-hook continuation injector for the omo-codex `start-work` skill.
4
4
 
5
- It reads `.omo/boulder.json` in the hook payload `cwd`, resolves the active work, inspects the active plan for incomplete top-level checkboxes, and emits Codex Stop-hook JSON when the plan still has work:
5
+ It reads `.omo/boulder.json` in the hook payload `cwd`, resolves the active work, inspects the active plan's top-level checklist, and emits Codex Stop-hook JSON while the plan still has unchecked tasks or its final review/debugging gate remains pending:
6
6
 
7
7
  ```json
8
8
  {"decision":"block","reason":"<directive>"}
9
9
  ```
10
10
 
11
- The `reason` is loaded from `directive.md` on every invocation and filled with current plan state. The hook returns no output when `stop_hook_active` is `true`, when no active Boulder work exists, when the work is completed, when the active work is not tied to `codex:<session_id>`, or when all top-level plan checkboxes are complete.
11
+ The `reason` is loaded from `directive.md` on every invocation and filled with current plan state. The hook returns no output when `stop_hook_active` is `true`, when no active Boulder work exists, when the work is completed, when the active work is not tied to `codex:<session_id>`, or when the plan has no readable top-level checklist. An active plan whose checklist is fully checked still blocks Stop until the final gate runs and Boulder is marked completed.
12
12
 
13
13
  This pairs with the `start-work` skill at `plugin/skills/start-work/SKILL.md`. That skill writes `.omo/boulder.json` with Codex session ids prefixed as `codex:` so the hook can continue only its own active Codex session.
14
14
 
@@ -16,7 +16,7 @@ You are mid-flight on a Prometheus work plan; this turn is an automatic continua
16
16
  # What to do this turn
17
17
 
18
18
  1. Read `{{PLAN_PATH}}` AND `{{LEDGER_PATH}}` first — they are the only sources of truth for what remains and what evidence exists; do not trust your memory of prior turns.
19
- 2. Pick the FIRST unchecked top-level checkbox in `## TODOs` or `## Final Verification Wave`. Ignore nested checkboxes under Acceptance Criteria / Evidence / Definition of Done.
19
+ 2. When the remaining count is `0`, skip checkbox execution and perform the Final gate now. Otherwise, pick the FIRST unchecked top-level checkbox in `## TODOs` or `## Final Verification Wave`. Ignore nested checkboxes under Acceptance Criteria / Evidence / Definition of Done.
20
20
  3. Follow the `start-work` skill in full. The skill is already loaded from your earlier turn — re-read its file at `packages/omo-codex/plugin/skills/start-work/SKILL.md` if you have lost context.
21
21
  4. Apply the checkbox's tier from its ledger entry, or classify it now per the start-work skill: LIGHT (default — a narrow change inside existing layers) needs one real-surface proof of the deliverable, with auxiliary surfaces first-class for CLI- or data-shaped work, and only trigger-mapped adversarial classes; HEAVY (new module/abstraction, auth/security, external integration, schema/migration, concurrency, cross-domain refactor, care signals) takes the full per-criterion regime. When unsure, take HEAVY; never downgrade.
22
22
  5. Decompose the checkbox into atomic sub-tasks. Dispatch them in PARALLEL via `multi_agent_v1.spawn_agent` calls in this same response unless a sub-task has a NAMED blocking dependency (input from another sub-task or shared file). Use `fork_context: false` unless full history is truly required. Put role and specialty instructions inside `message`. Flat `spawn_agent` requiring `task_name` instead (`multi_agent_v2`)? Add `"task_name"`, use `fork_turns: "none"`, `wait_agent` takes only `timeout_ms`, finished agents end on their own.
@@ -40,7 +40,7 @@ You are mid-flight on a Prometheus work plan; this turn is an automatic continua
40
40
 
41
41
  # Final gate
42
42
 
43
- Before completion, run `review-work` and a `debugging` runtime audit; inconclusive lanes fail. Do not create a PR, PR handoff, branch handoff, merge, or final completion answer until this gate passes. For PR/branch work, stay in the task-owned worktree: create/update the PR, wait for CI/review/Cubic gates, merge by default unless explicitly opted out, then clean up. Redact secrets, tokens, credentials, auth headers, cookies, env dumps, logs, and PII.
43
+ Before completion, satisfy all five `review-work` lanes and a `debugging` runtime audit; inconclusive lanes or an inconclusive audit fail. Each pass binds to the exact full commit SHA it reviewed. Immediately append a durable task-evidence record to `{{LEDGER_PATH}}` for every passing lane and the debugging audit with its name, full SHA, verdict, and report artifact/source. Before same-SHA reuse after any continuation or compaction, re-read the ledger record and verify the exact lane/SHA pair; memory, chat history, or an unstamped report is not coverage. New commits require fresh applicable coverage. Do not create a PR, PR handoff, branch handoff, merge, or final completion answer until this gate passes. For PR/branch work, stay in the task-owned worktree: create/update the PR, wait for CI/review/Cubic gates, merge by default unless explicitly opted out, then clean up. After the gate and lifecycle pass, mark the Boulder work completed before the final answer. Redact secrets, tokens, credentials, auth headers, cookies, env dumps, logs, and PII.
44
44
 
45
45
  # Stop conditions for THIS turn
46
46
 
@@ -54,6 +54,6 @@ Before completion, run `review-work` and a `debugging` runtime audit; inconclusi
54
54
  - Surface only state changes: sub-agent dispatched, scenario PASS/FAIL with artifact path, checkbox marked, evidence appended.
55
55
  - Do NOT print "Should I continue?", restate the plan, or recap prior turns — the Stop hook continues you; the ledger and plan are the durable record.
56
56
 
57
- Begin now. Pick the next checkbox, dispatch the parallel sub-agents, verify, mark, continue.
57
+ Begin now. If no checkbox remains, run the Final gate; otherwise pick the next checkbox, dispatch the parallel sub-agents, verify, mark, continue.
58
58
 
59
59
  </start-work-continuation>
@@ -21,7 +21,7 @@ function readContinuationState(cwd, sessionId) {
21
21
  return null;
22
22
  const planPath = resolveBoulderPlanPathForWork(cwd, work);
23
23
  const checklist = getPlanChecklist(planPath);
24
- if (checklist.remaining === 0)
24
+ if (checklist.total === 0)
25
25
  return null;
26
26
  return {
27
27
  planName: work.planName,
@@ -239,7 +239,7 @@ function renderDirective(state, sessionId) {
239
239
  BOULDER_PATH: state.boulderPath,
240
240
  REMAINING_COUNT: String(state.checklist.remaining),
241
241
  TOTAL_COUNT: String(state.checklist.total),
242
- NEXT_TASK_LABEL: state.checklist.nextTaskLabel ?? "",
242
+ NEXT_TASK_LABEL: state.checklist.nextTaskLabel ?? "none (final gate pending)",
243
243
  WORKTREE_BLOCK: worktreeBlock,
244
244
  LEDGER_PATH: state.ledgerPath,
245
245
  SESSION_ID: sessionId
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook stop",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 4.18.0) Checking Start-Work Continuation"
10
+ "statusMessage": "(OmO 4.18.2) Checking Start-Work Continuation"
11
11
  }
12
12
  ]
13
13
  }
@@ -19,7 +19,7 @@
19
19
  "type": "command",
20
20
  "command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook subagent-stop",
21
21
  "timeout": 10,
22
- "statusMessage": "(OmO 4.18.0) Checking Start-Work Continuation"
22
+ "statusMessage": "(OmO 4.18.2) Checking Start-Work Continuation"
23
23
  }
24
24
  ]
25
25
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-start-work-continuation",
3
- "version": "4.18.0",
3
+ "version": "4.18.2",
4
4
  "description": "Codex Stop hook continuation injector for omo-codex start-work plans.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -50,7 +50,7 @@ export function readContinuationState(cwd: string, sessionId: string): Continuat
50
50
 
51
51
  const planPath = resolveBoulderPlanPathForWork(cwd, work);
52
52
  const checklist = getPlanChecklist(planPath);
53
- if (checklist.remaining === 0) return null;
53
+ if (checklist.total === 0) return null;
54
54
 
55
55
  return {
56
56
  planName: work.planName,
@@ -27,7 +27,7 @@ function renderDirective(state: ContinuationState, sessionId: string): string {
27
27
  BOULDER_PATH: state.boulderPath,
28
28
  REMAINING_COUNT: String(state.checklist.remaining),
29
29
  TOTAL_COUNT: String(state.checklist.total),
30
- NEXT_TASK_LABEL: state.checklist.nextTaskLabel ?? "",
30
+ NEXT_TASK_LABEL: state.checklist.nextTaskLabel ?? "none (final gate pending)",
31
31
  WORKTREE_BLOCK: worktreeBlock,
32
32
  LEDGER_PATH: state.ledgerPath,
33
33
  SESSION_ID: sessionId,
@@ -123,7 +123,7 @@ describe("start-work boulder state reader", () => {
123
123
  expect(state?.checklist).toEqual({ completed: 0, remaining: 1, total: 1, nextTaskLabel: "First" });
124
124
  });
125
125
 
126
- it("#given no remaining top-level checklist items #when state is read #then continuation is absent", () => {
126
+ it("#given active codex work with no remaining checklist items #when state is read #then final gate continuation remains present", () => {
127
127
  // given
128
128
  const workspace = createWorkspace({
129
129
  boulderJson: createBoulderJson({ status: "active", sessionIds: ["codex:sess_abc"] }),
@@ -133,6 +133,20 @@ describe("start-work boulder state reader", () => {
133
133
  // when
134
134
  const state = readContinuationState(workspace, "sess_abc");
135
135
 
136
+ // then
137
+ expect(state?.checklist).toEqual({ completed: 1, remaining: 0, total: 1, nextTaskLabel: null });
138
+ });
139
+
140
+ it("#given active codex work with no readable checklist #when state is read #then continuation remains absent", () => {
141
+ // given
142
+ const workspace = createWorkspace({
143
+ boulderJson: createBoulderJson({ status: "active", sessionIds: ["codex:sess_abc"] }),
144
+ planMarkdown: "# Plan\n\nNo checklist yet.\n",
145
+ });
146
+
147
+ // when
148
+ const state = readContinuationState(workspace, "sess_abc");
149
+
136
150
  // then
137
151
  expect(state).toBeNull();
138
152
  });
@@ -69,6 +69,26 @@ describe("start-work Stop hook", () => {
69
69
  expect(parsed.reason).toContain("- Your session id in boulder.json: `codex:sess_abc`");
70
70
  });
71
71
 
72
+ it("#given active codex work with zero remaining tasks #when hook runs #then blocks for the final gate", () => {
73
+ // given
74
+ const workspace = createWorkspace({
75
+ boulderJson: createBoulderJson({ sessionIds: ["codex:sess_abc"], status: "active" }),
76
+ planMarkdown: ["# Plan", "", "## TODOs", "- [x] Done"].join("\n"),
77
+ });
78
+ const fs = createMemoryFs();
79
+
80
+ // when
81
+ const output = runStopHook(createStopInput(workspace), fs);
82
+
83
+ // then
84
+ const parsed = parseBlockOutput(output);
85
+ expect(parsed.decision).toBe("block");
86
+ expect(parsed.reason).toContain("- Remaining top-level checkboxes: `0` of `1`");
87
+ expect(parsed.reason).toContain("- Next incomplete task: `none (final gate pending)`");
88
+ expect(parsed.reason).toContain("When the remaining count is `0`, skip checkbox execution");
89
+ expect(parsed.reason).toContain("re-read the ledger record and verify the exact lane/SHA pair");
90
+ });
91
+
72
92
  it("#given context-window pressure in transcript #when hook runs #then it does not inject continuation text", () => {
73
93
  // given
74
94
  const transcriptPath = "/repo/transcript.jsonl";
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
10
10
  "timeout": 10,
11
- "statusMessage": "(OmO 4.18.0) Checking Thread Title Hygiene"
11
+ "statusMessage": "(OmO 4.18.2) Checking Thread Title Hygiene"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-teammode",
3
- "version": "4.18.0",
3
+ "version": "4.18.2",
4
4
  "description": "Codex team-mode hook component that keeps background thread titles descriptive after create_thread.",
5
5
  "type": "module",
6
6
  "private": true,
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 4.18.0) Recording Session Telemetry"
10
+ "statusMessage": "(OmO 4.18.2) Recording Session Telemetry"
11
11
  }
12
12
  ]
13
13
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-telemetry",
3
- "version": "4.18.0",
3
+ "version": "4.18.2",
4
4
  "description": "Codex plugin component that emits omo-codex anonymous daily-active telemetry on SessionStart.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",