oh-my-codex 0.12.5 → 0.12.6

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 (386) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +17 -0
  4. package/dist/cli/__tests__/ask.test.js +26 -0
  5. package/dist/cli/__tests__/ask.test.js.map +1 -1
  6. package/dist/cli/__tests__/explore.test.js +95 -8
  7. package/dist/cli/__tests__/explore.test.js.map +1 -1
  8. package/dist/cli/__tests__/index.test.js +58 -0
  9. package/dist/cli/__tests__/index.test.js.map +1 -1
  10. package/dist/cli/__tests__/launch-fallback.test.js +169 -0
  11. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  12. package/dist/cli/__tests__/mcp-parity.test.js +31 -0
  13. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  14. package/dist/cli/__tests__/setup-agents-overwrite.test.js +66 -2
  15. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  16. package/dist/cli/__tests__/setup-refresh.test.js +11 -1
  17. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  18. package/dist/cli/__tests__/team.test.js +92 -0
  19. package/dist/cli/__tests__/team.test.js.map +1 -1
  20. package/dist/cli/__tests__/uninstall.test.js +14 -1
  21. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  22. package/dist/cli/cleanup.js +1 -1
  23. package/dist/cli/cleanup.js.map +1 -1
  24. package/dist/cli/constants.d.ts +1 -0
  25. package/dist/cli/constants.d.ts.map +1 -1
  26. package/dist/cli/constants.js +1 -0
  27. package/dist/cli/constants.js.map +1 -1
  28. package/dist/cli/explore.d.ts +1 -0
  29. package/dist/cli/explore.d.ts.map +1 -1
  30. package/dist/cli/explore.js +45 -1
  31. package/dist/cli/explore.js.map +1 -1
  32. package/dist/cli/index.d.ts +1 -1
  33. package/dist/cli/index.d.ts.map +1 -1
  34. package/dist/cli/index.js +103 -10
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/mcp-parity.d.ts +1 -1
  37. package/dist/cli/mcp-parity.d.ts.map +1 -1
  38. package/dist/cli/mcp-parity.js +24 -0
  39. package/dist/cli/mcp-parity.js.map +1 -1
  40. package/dist/cli/setup.d.ts.map +1 -1
  41. package/dist/cli/setup.js +2 -2
  42. package/dist/cli/setup.js.map +1 -1
  43. package/dist/cli/team.d.ts.map +1 -1
  44. package/dist/cli/team.js +54 -14
  45. package/dist/cli/team.js.map +1 -1
  46. package/dist/cli/uninstall.d.ts.map +1 -1
  47. package/dist/cli/uninstall.js +1 -0
  48. package/dist/cli/uninstall.js.map +1 -1
  49. package/dist/config/__tests__/generator-idempotent.test.js +1 -0
  50. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  51. package/dist/config/__tests__/mcp-registry.test.js +61 -0
  52. package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
  53. package/dist/config/__tests__/wiki-config-contract.test.d.ts +2 -0
  54. package/dist/config/__tests__/wiki-config-contract.test.d.ts.map +1 -0
  55. package/dist/config/__tests__/wiki-config-contract.test.js +19 -0
  56. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -0
  57. package/dist/config/generator.d.ts.map +1 -1
  58. package/dist/config/generator.js +8 -0
  59. package/dist/config/generator.js.map +1 -1
  60. package/dist/config/mcp-registry.d.ts +2 -0
  61. package/dist/config/mcp-registry.d.ts.map +1 -1
  62. package/dist/config/mcp-registry.js +12 -0
  63. package/dist/config/mcp-registry.js.map +1 -1
  64. package/dist/hooks/__tests__/agents-overlay.test.js +18 -0
  65. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  66. package/dist/hooks/__tests__/keyword-detector.test.js +75 -0
  67. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  68. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +140 -1
  69. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  70. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +35 -0
  71. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  72. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +86 -2
  73. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +176 -1
  75. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +193 -0
  77. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  78. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +90 -2
  79. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  80. package/dist/hooks/__tests__/session.test.js +41 -1
  81. package/dist/hooks/__tests__/session.test.js.map +1 -1
  82. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts +2 -0
  83. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts.map +1 -0
  84. package/dist/hooks/__tests__/wiki-docs-contract.test.js +34 -0
  85. package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -0
  86. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  87. package/dist/hooks/agents-overlay.js +0 -1
  88. package/dist/hooks/agents-overlay.js.map +1 -1
  89. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +32 -0
  90. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  91. package/dist/hooks/extensibility/__tests__/sdk.test.js +33 -3
  92. package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
  93. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  94. package/dist/hooks/extensibility/dispatcher.js +41 -0
  95. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  96. package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
  97. package/dist/hooks/extensibility/sdk/runtime-state.js +7 -1
  98. package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
  99. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  100. package/dist/hooks/keyword-detector.js +42 -5
  101. package/dist/hooks/keyword-detector.js.map +1 -1
  102. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  103. package/dist/hooks/keyword-registry.js +3 -0
  104. package/dist/hooks/keyword-registry.js.map +1 -1
  105. package/dist/hooks/session.d.ts.map +1 -1
  106. package/dist/hooks/session.js +26 -4
  107. package/dist/hooks/session.js.map +1 -1
  108. package/dist/hud/__tests__/state.test.js +27 -0
  109. package/dist/hud/__tests__/state.test.js.map +1 -1
  110. package/dist/hud/state.d.ts.map +1 -1
  111. package/dist/hud/state.js +3 -1
  112. package/dist/hud/state.js.map +1 -1
  113. package/dist/mcp/__tests__/bootstrap.test.js +65 -1
  114. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  115. package/dist/mcp/__tests__/state-server.test.js +31 -0
  116. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  117. package/dist/mcp/__tests__/wiki-server.test.d.ts +2 -0
  118. package/dist/mcp/__tests__/wiki-server.test.d.ts.map +1 -0
  119. package/dist/mcp/__tests__/wiki-server.test.js +30 -0
  120. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -0
  121. package/dist/mcp/bootstrap.d.ts +18 -1
  122. package/dist/mcp/bootstrap.d.ts.map +1 -1
  123. package/dist/mcp/bootstrap.js +158 -0
  124. package/dist/mcp/bootstrap.js.map +1 -1
  125. package/dist/mcp/state-server.d.ts +4 -4
  126. package/dist/mcp/state-server.d.ts.map +1 -1
  127. package/dist/mcp/state-server.js +27 -22
  128. package/dist/mcp/state-server.js.map +1 -1
  129. package/dist/mcp/wiki-server.d.ts +181 -0
  130. package/dist/mcp/wiki-server.d.ts.map +1 -0
  131. package/dist/mcp/wiki-server.js +235 -0
  132. package/dist/mcp/wiki-server.js.map +1 -0
  133. package/dist/modes/__tests__/base-autoresearch-contract.test.js +22 -1
  134. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  135. package/dist/modes/base.d.ts.map +1 -1
  136. package/dist/modes/base.js +10 -0
  137. package/dist/modes/base.js.map +1 -1
  138. package/dist/notifications/__tests__/formatter.test.js +11 -0
  139. package/dist/notifications/__tests__/formatter.test.js.map +1 -1
  140. package/dist/notifications/__tests__/idle-cooldown.test.js +32 -1
  141. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  142. package/dist/notifications/__tests__/index.test.d.ts +2 -0
  143. package/dist/notifications/__tests__/index.test.d.ts.map +1 -0
  144. package/dist/notifications/__tests__/index.test.js +113 -0
  145. package/dist/notifications/__tests__/index.test.js.map +1 -0
  146. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts +2 -0
  147. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts.map +1 -0
  148. package/dist/notifications/__tests__/lifecycle-dedupe.test.js +86 -0
  149. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -0
  150. package/dist/notifications/__tests__/reply-listener.test.js +155 -0
  151. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  152. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts +2 -0
  153. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts.map +1 -0
  154. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +93 -0
  155. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -0
  156. package/dist/notifications/__tests__/session-registry.test.js +48 -1
  157. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  158. package/dist/notifications/__tests__/session-status.test.d.ts +2 -0
  159. package/dist/notifications/__tests__/session-status.test.d.ts.map +1 -0
  160. package/dist/notifications/__tests__/session-status.test.js +159 -0
  161. package/dist/notifications/__tests__/session-status.test.js.map +1 -0
  162. package/dist/notifications/__tests__/tmux.test.js +58 -1
  163. package/dist/notifications/__tests__/tmux.test.js.map +1 -1
  164. package/dist/notifications/idle-cooldown.d.ts +11 -0
  165. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  166. package/dist/notifications/idle-cooldown.js +42 -8
  167. package/dist/notifications/idle-cooldown.js.map +1 -1
  168. package/dist/notifications/index.d.ts +1 -1
  169. package/dist/notifications/index.d.ts.map +1 -1
  170. package/dist/notifications/index.js +41 -8
  171. package/dist/notifications/index.js.map +1 -1
  172. package/dist/notifications/lifecycle-dedupe.d.ts +8 -0
  173. package/dist/notifications/lifecycle-dedupe.d.ts.map +1 -0
  174. package/dist/notifications/lifecycle-dedupe.js +112 -0
  175. package/dist/notifications/lifecycle-dedupe.js.map +1 -0
  176. package/dist/notifications/reply-listener.d.ts +2 -0
  177. package/dist/notifications/reply-listener.d.ts.map +1 -1
  178. package/dist/notifications/reply-listener.js +42 -6
  179. package/dist/notifications/reply-listener.js.map +1 -1
  180. package/dist/notifications/session-registry.d.ts.map +1 -1
  181. package/dist/notifications/session-registry.js +7 -1
  182. package/dist/notifications/session-registry.js.map +1 -1
  183. package/dist/notifications/session-status.d.ts +23 -0
  184. package/dist/notifications/session-status.d.ts.map +1 -0
  185. package/dist/notifications/session-status.js +187 -0
  186. package/dist/notifications/session-status.js.map +1 -0
  187. package/dist/notifications/tmux.d.ts +10 -0
  188. package/dist/notifications/tmux.d.ts.map +1 -1
  189. package/dist/notifications/tmux.js +59 -5
  190. package/dist/notifications/tmux.js.map +1 -1
  191. package/dist/notifications/types.d.ts +2 -0
  192. package/dist/notifications/types.d.ts.map +1 -1
  193. package/dist/openclaw/__tests__/index.test.js +84 -0
  194. package/dist/openclaw/__tests__/index.test.js.map +1 -1
  195. package/dist/openclaw/index.d.ts.map +1 -1
  196. package/dist/openclaw/index.js +7 -14
  197. package/dist/openclaw/index.js.map +1 -1
  198. package/dist/openclaw/types.d.ts +2 -2
  199. package/dist/openclaw/types.d.ts.map +1 -1
  200. package/dist/scripts/__tests__/codex-native-hook.test.js +270 -1
  201. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  202. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts +2 -0
  203. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts.map +1 -0
  204. package/dist/scripts/__tests__/hook-derived-watcher.test.js +87 -0
  205. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -0
  206. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  207. package/dist/scripts/codex-native-hook.js +134 -6
  208. package/dist/scripts/codex-native-hook.js.map +1 -1
  209. package/dist/scripts/hook-derived-watcher.js +43 -1
  210. package/dist/scripts/hook-derived-watcher.js.map +1 -1
  211. package/dist/scripts/notify-fallback-watcher.js +27 -2
  212. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  213. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  214. package/dist/scripts/notify-hook/auto-nudge.js +17 -1
  215. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  216. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  217. package/dist/scripts/notify-hook/managed-tmux.js +5 -3
  218. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  219. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  220. package/dist/scripts/notify-hook/team-dispatch.js +305 -167
  221. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  222. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  223. package/dist/scripts/notify-hook/team-leader-nudge.js +79 -3
  224. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  225. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  226. package/dist/scripts/notify-hook/tmux-injection.js +2 -1
  227. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  228. package/dist/scripts/run-provider-advisor.js +20 -2
  229. package/dist/scripts/run-provider-advisor.js.map +1 -1
  230. package/dist/scripts/smoke-packed-install.d.ts +1 -8
  231. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  232. package/dist/scripts/smoke-packed-install.js +12 -68
  233. package/dist/scripts/smoke-packed-install.js.map +1 -1
  234. package/dist/state/__tests__/operations.test.js +113 -0
  235. package/dist/state/__tests__/operations.test.js.map +1 -1
  236. package/dist/state/operations.d.ts +1 -1
  237. package/dist/state/operations.d.ts.map +1 -1
  238. package/dist/state/operations.js +88 -2
  239. package/dist/state/operations.js.map +1 -1
  240. package/dist/state/skill-active.d.ts +1 -1
  241. package/dist/state/skill-active.d.ts.map +1 -1
  242. package/dist/state/skill-active.js +1 -0
  243. package/dist/state/skill-active.js.map +1 -1
  244. package/dist/team/__tests__/api-interop.test.js +90 -0
  245. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  246. package/dist/team/__tests__/hardening-e2e.test.js +17 -0
  247. package/dist/team/__tests__/hardening-e2e.test.js.map +1 -1
  248. package/dist/team/__tests__/runtime.test.js +23 -0
  249. package/dist/team/__tests__/runtime.test.js.map +1 -1
  250. package/dist/team/__tests__/tmux-session.test.js +210 -0
  251. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  252. package/dist/team/api-interop.d.ts.map +1 -1
  253. package/dist/team/api-interop.js +10 -1
  254. package/dist/team/api-interop.js.map +1 -1
  255. package/dist/team/delivery-log.d.ts +1 -1
  256. package/dist/team/delivery-log.d.ts.map +1 -1
  257. package/dist/team/delivery-log.js.map +1 -1
  258. package/dist/team/leader-activity.d.ts +1 -0
  259. package/dist/team/leader-activity.d.ts.map +1 -1
  260. package/dist/team/leader-activity.js +4 -2
  261. package/dist/team/leader-activity.js.map +1 -1
  262. package/dist/team/progress-evidence.d.ts +2 -0
  263. package/dist/team/progress-evidence.d.ts.map +1 -0
  264. package/dist/team/progress-evidence.js +77 -0
  265. package/dist/team/progress-evidence.js.map +1 -0
  266. package/dist/team/runtime.d.ts.map +1 -1
  267. package/dist/team/runtime.js +44 -0
  268. package/dist/team/runtime.js.map +1 -1
  269. package/dist/team/tmux-session.d.ts.map +1 -1
  270. package/dist/team/tmux-session.js +57 -8
  271. package/dist/team/tmux-session.js.map +1 -1
  272. package/dist/team/worktree.d.ts +6 -1
  273. package/dist/team/worktree.d.ts.map +1 -1
  274. package/dist/team/worktree.js +4 -2
  275. package/dist/team/worktree.js.map +1 -1
  276. package/dist/utils/__tests__/agents-md.test.js +21 -1
  277. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  278. package/dist/utils/__tests__/repo-deps.test.d.ts +2 -0
  279. package/dist/utils/__tests__/repo-deps.test.d.ts.map +1 -0
  280. package/dist/utils/__tests__/repo-deps.test.js +71 -0
  281. package/dist/utils/__tests__/repo-deps.test.js.map +1 -0
  282. package/dist/utils/agents-md.d.ts +1 -0
  283. package/dist/utils/agents-md.d.ts.map +1 -1
  284. package/dist/utils/agents-md.js +7 -3
  285. package/dist/utils/agents-md.js.map +1 -1
  286. package/dist/utils/paths.d.ts +4 -0
  287. package/dist/utils/paths.d.ts.map +1 -1
  288. package/dist/utils/paths.js +20 -0
  289. package/dist/utils/paths.js.map +1 -1
  290. package/dist/utils/repo-deps.d.ts +20 -0
  291. package/dist/utils/repo-deps.d.ts.map +1 -0
  292. package/dist/utils/repo-deps.js +78 -0
  293. package/dist/utils/repo-deps.js.map +1 -0
  294. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts +2 -0
  295. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts.map +1 -0
  296. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +54 -0
  297. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -0
  298. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts +12 -0
  299. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts.map +1 -0
  300. package/dist/wiki/__tests__/cjk-tokenize.test.js +139 -0
  301. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -0
  302. package/dist/wiki/__tests__/crlf-parse.test.d.ts +2 -0
  303. package/dist/wiki/__tests__/crlf-parse.test.d.ts.map +1 -0
  304. package/dist/wiki/__tests__/crlf-parse.test.js +24 -0
  305. package/dist/wiki/__tests__/crlf-parse.test.js.map +1 -0
  306. package/dist/wiki/__tests__/escape-newline.test.d.ts +2 -0
  307. package/dist/wiki/__tests__/escape-newline.test.d.ts.map +1 -0
  308. package/dist/wiki/__tests__/escape-newline.test.js +45 -0
  309. package/dist/wiki/__tests__/escape-newline.test.js.map +1 -0
  310. package/dist/wiki/__tests__/ingest.test.d.ts +5 -0
  311. package/dist/wiki/__tests__/ingest.test.d.ts.map +1 -0
  312. package/dist/wiki/__tests__/ingest.test.js +181 -0
  313. package/dist/wiki/__tests__/ingest.test.js.map +1 -0
  314. package/dist/wiki/__tests__/lint.test.d.ts +5 -0
  315. package/dist/wiki/__tests__/lint.test.d.ts.map +1 -0
  316. package/dist/wiki/__tests__/lint.test.js +163 -0
  317. package/dist/wiki/__tests__/lint.test.js.map +1 -0
  318. package/dist/wiki/__tests__/query.test.d.ts +5 -0
  319. package/dist/wiki/__tests__/query.test.d.ts.map +1 -0
  320. package/dist/wiki/__tests__/query.test.js +141 -0
  321. package/dist/wiki/__tests__/query.test.js.map +1 -0
  322. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts +2 -0
  323. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts.map +1 -0
  324. package/dist/wiki/__tests__/reserved-file-guard.test.js +44 -0
  325. package/dist/wiki/__tests__/reserved-file-guard.test.js.map +1 -0
  326. package/dist/wiki/__tests__/session-hooks.test.d.ts +5 -0
  327. package/dist/wiki/__tests__/session-hooks.test.d.ts.map +1 -0
  328. package/dist/wiki/__tests__/session-hooks.test.js +36 -0
  329. package/dist/wiki/__tests__/session-hooks.test.js.map +1 -0
  330. package/dist/wiki/__tests__/slug-nonascii.test.d.ts +2 -0
  331. package/dist/wiki/__tests__/slug-nonascii.test.d.ts.map +1 -0
  332. package/dist/wiki/__tests__/slug-nonascii.test.js +24 -0
  333. package/dist/wiki/__tests__/slug-nonascii.test.js.map +1 -0
  334. package/dist/wiki/__tests__/storage.test.d.ts +5 -0
  335. package/dist/wiki/__tests__/storage.test.d.ts.map +1 -0
  336. package/dist/wiki/__tests__/storage.test.js +278 -0
  337. package/dist/wiki/__tests__/storage.test.js.map +1 -0
  338. package/dist/wiki/__tests__/test-helpers.d.ts +31 -0
  339. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -0
  340. package/dist/wiki/__tests__/test-helpers.js +108 -0
  341. package/dist/wiki/__tests__/test-helpers.js.map +1 -0
  342. package/dist/wiki/index.d.ts +14 -0
  343. package/dist/wiki/index.d.ts.map +1 -0
  344. package/dist/wiki/index.js +17 -0
  345. package/dist/wiki/index.js.map +1 -0
  346. package/dist/wiki/ingest.d.ts +20 -0
  347. package/dist/wiki/ingest.d.ts.map +1 -0
  348. package/dist/wiki/ingest.js +115 -0
  349. package/dist/wiki/ingest.js.map +1 -0
  350. package/dist/wiki/lifecycle.d.ts +20 -0
  351. package/dist/wiki/lifecycle.d.ts.map +1 -0
  352. package/dist/wiki/lifecycle.js +212 -0
  353. package/dist/wiki/lifecycle.js.map +1 -0
  354. package/dist/wiki/lint.d.ts +25 -0
  355. package/dist/wiki/lint.d.ts.map +1 -0
  356. package/dist/wiki/lint.js +166 -0
  357. package/dist/wiki/lint.js.map +1 -0
  358. package/dist/wiki/query.d.ts +36 -0
  359. package/dist/wiki/query.d.ts.map +1 -0
  360. package/dist/wiki/query.js +138 -0
  361. package/dist/wiki/query.js.map +1 -0
  362. package/dist/wiki/storage.d.ts +33 -0
  363. package/dist/wiki/storage.d.ts.map +1 -0
  364. package/dist/wiki/storage.js +321 -0
  365. package/dist/wiki/storage.js.map +1 -0
  366. package/dist/wiki/types.d.ts +83 -0
  367. package/dist/wiki/types.d.ts.map +1 -0
  368. package/dist/wiki/types.js +15 -0
  369. package/dist/wiki/types.js.map +1 -0
  370. package/package.json +3 -1
  371. package/skills/configure-notifications/SKILL.md +1 -0
  372. package/skills/doctor/SKILL.md +11 -0
  373. package/skills/omx-setup/SKILL.md +1 -1
  374. package/skills/wiki/SKILL.md +57 -0
  375. package/src/scripts/__tests__/codex-native-hook.test.ts +378 -0
  376. package/src/scripts/__tests__/hook-derived-watcher.test.ts +111 -0
  377. package/src/scripts/codex-native-hook.ts +187 -7
  378. package/src/scripts/hook-derived-watcher.ts +43 -1
  379. package/src/scripts/notify-fallback-watcher.ts +26 -2
  380. package/src/scripts/notify-hook/auto-nudge.ts +19 -1
  381. package/src/scripts/notify-hook/managed-tmux.ts +8 -3
  382. package/src/scripts/notify-hook/team-dispatch.ts +326 -168
  383. package/src/scripts/notify-hook/team-leader-nudge.ts +82 -3
  384. package/src/scripts/notify-hook/tmux-injection.ts +2 -1
  385. package/src/scripts/run-provider-advisor.ts +20 -2
  386. package/src/scripts/smoke-packed-install.ts +16 -83
@@ -43,6 +43,7 @@ import {
43
43
  import type { HookEventEnvelope } from "../hooks/extensibility/types.js";
44
44
  import { dispatchHookEvent } from "../hooks/extensibility/dispatcher.js";
45
45
  import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
46
+ import { onSessionStart as buildWikiSessionStartContext } from "../wiki/lifecycle.js";
46
47
 
47
48
  type CodexHookEventName =
48
49
  | "SessionStart"
@@ -70,6 +71,14 @@ const TERMINAL_MODE_PHASES = new Set(["complete", "failed", "cancelled"]);
70
71
  const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
71
72
  const TEAM_TERMINAL_TASK_STATUSES = new Set(["completed", "failed"]);
72
73
  const NATIVE_STOP_STATE_FILE = "native-stop-state.json";
74
+ const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
75
+ /^\s*(?:launch|release|ship)-?ready\s*:\s*(?:yes|no)\b[^\n\r]*/im,
76
+ /^\s*ready to release\s*:\s*(?:yes|no)\b[^\n\r]*/im,
77
+ /^\s*(?:final\s+)?recommendation\s*:\s*(?:yes|no|ship|hold|release|do not release|proceed|do not proceed)\b[^\n\r]*/im,
78
+ /^\s*decision\s*:\s*(?:yes|no|ship|hold|release|do not release|proceed|do not proceed)\b[^\n\r]*/im,
79
+ ] as const;
80
+ const RELEASE_READINESS_FINALIZE_SYSTEM_MESSAGE =
81
+ "OMX release-readiness detected a stable final recommendation with no active worker tasks; emit one concise final decision summary and finalize.";
73
82
 
74
83
  function safeString(value: unknown): string {
75
84
  return typeof value === "string" ? value : "";
@@ -141,6 +150,21 @@ function readPromptText(payload: CodexHookPayload): string {
141
150
  return "";
142
151
  }
143
152
 
153
+ function sanitizePayloadForHookContext(
154
+ payload: CodexHookPayload,
155
+ hookEventName: CodexHookEventName,
156
+ ): CodexHookPayload {
157
+ if (hookEventName !== "UserPromptSubmit") return payload;
158
+
159
+ const sanitized = { ...payload };
160
+ delete sanitized.prompt;
161
+ delete sanitized.input;
162
+ delete sanitized.user_prompt;
163
+ delete sanitized.userPrompt;
164
+ delete sanitized.text;
165
+ return sanitized;
166
+ }
167
+
144
168
  function buildBaseContext(
145
169
  cwd: string,
146
170
  payload: CodexHookPayload,
@@ -151,10 +175,7 @@ function buildBaseContext(
151
175
  project_path: cwd,
152
176
  transcript_path: safeString(payload.transcript_path ?? payload.transcriptPath) || null,
153
177
  source: safeString(payload.source),
154
- payload,
155
- ...(hookEventName === "UserPromptSubmit"
156
- ? { prompt: readPromptText(payload) }
157
- : {}),
178
+ payload: sanitizePayloadForHookContext(payload, hookEventName),
158
179
  };
159
180
  }
160
181
 
@@ -426,6 +447,11 @@ async function buildSessionStartContext(
426
447
  }
427
448
  }
428
449
 
450
+ const wikiContext = buildWikiSessionStartContext({ cwd });
451
+ if (wikiContext.additionalContext) {
452
+ sections.push(wikiContext.additionalContext);
453
+ }
454
+
429
455
  const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
430
456
  if (subagentSummary && subagentSummary.activeSubagentThreadIds.length > 0) {
431
457
  sections.push(`[Subagents]\n- active subagent threads: ${subagentSummary.activeSubagentThreadIds.length}`);
@@ -676,6 +702,12 @@ async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Rec
676
702
  const teamState = await readTeamModeStateForStop(cwd, sessionId);
677
703
  if (teamState?.active !== true) return null;
678
704
  const teamName = safeString(teamState.team_name).trim();
705
+ if (teamName) {
706
+ const canonicalTeamDir = join(resolveCanonicalTeamStateRoot(cwd), "team", teamName);
707
+ if (!existsSync(canonicalTeamDir)) {
708
+ return null;
709
+ }
710
+ }
679
711
  const coarsePhase = teamState.current_phase;
680
712
  const canonicalPhase = teamName ? (await readTeamPhase(teamName, cwd))?.current_phase ?? coarsePhase : coarsePhase;
681
713
  if (!isNonTerminalPhase(canonicalPhase)) return null;
@@ -696,6 +728,54 @@ function buildTeamStopOutputForPhase(teamName: string, phase: string): Record<st
696
728
  };
697
729
  }
698
730
 
731
+ function extractStableFinalRecommendationSummary(message: string): string {
732
+ for (const pattern of STABLE_FINAL_RECOMMENDATION_PATTERNS) {
733
+ const match = pattern.exec(message);
734
+ if (!match) continue;
735
+ const summary = match[0]?.trim().replace(/\s+/g, " ");
736
+ if (!summary) continue;
737
+ return /[.!?]$/.test(summary) ? summary : `${summary}.`;
738
+ }
739
+ return "";
740
+ }
741
+
742
+ function buildStableFinalRecommendationStopSignature(
743
+ payload: CodexHookPayload,
744
+ teamName: string,
745
+ summary: string,
746
+ ): string {
747
+ const sessionId = readPayloadSessionId(payload) || "no-session";
748
+ const threadId = readPayloadThreadId(payload) || "no-thread";
749
+ const normalizedSummary = normalizeAutoNudgeSignatureText(summary) || summary.toLowerCase();
750
+ return ["release-readiness-finalize", sessionId, threadId, teamName, normalizedSummary].join("|");
751
+ }
752
+
753
+ function hasReleaseReadinessMode(payload: CodexHookPayload): boolean {
754
+ const mode = safeString(payload.mode).trim().toLowerCase();
755
+ return mode === "release-readiness";
756
+ }
757
+
758
+ async function hasReleaseReadinessStopMarker(
759
+ cwd: string,
760
+ sessionId: string,
761
+ teamName: string,
762
+ ): Promise<boolean> {
763
+ if (!sessionId) return false;
764
+
765
+ const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId);
766
+ if (markerState?.active !== true || markerState.stable_final_recommendation_emitted !== true) {
767
+ return false;
768
+ }
769
+
770
+ const markerTeamName = safeString(markerState.team_name).trim();
771
+ if (markerTeamName && markerTeamName !== teamName) return false;
772
+
773
+ const markerSessionId = safeString(markerState.session_id).trim();
774
+ if (markerSessionId && markerSessionId !== sessionId) return false;
775
+
776
+ return true;
777
+ }
778
+
699
779
  function readPayloadSessionId(payload: CodexHookPayload): string {
700
780
  return safeString(payload.session_id ?? payload.sessionId).trim();
701
781
  }
@@ -935,6 +1015,66 @@ async function findCanonicalActiveTeamForSession(
935
1015
  return null;
936
1016
  }
937
1017
 
1018
+ async function resolveActiveTeamNameForStop(
1019
+ cwd: string,
1020
+ sessionId: string,
1021
+ ): Promise<string> {
1022
+ const directState = await readTeamModeStateForStop(cwd, sessionId);
1023
+ const directTeamName = safeString(directState?.team_name).trim();
1024
+ if (directState?.active === true && directTeamName) return directTeamName;
1025
+
1026
+ const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId);
1027
+ return canonicalTeam?.teamName ?? "";
1028
+ }
1029
+
1030
+ async function maybeBuildReleaseReadinessFinalizeStopOutput(
1031
+ payload: CodexHookPayload,
1032
+ cwd: string,
1033
+ stateDir: string,
1034
+ sessionId: string,
1035
+ ): Promise<{ matched: boolean; output: Record<string, unknown> | null }> {
1036
+ if (!sessionId) return { matched: false, output: null };
1037
+
1038
+ const teamName = await resolveActiveTeamNameForStop(cwd, sessionId);
1039
+ if (!teamName) return { matched: false, output: null };
1040
+
1041
+ const explicitReleaseReadinessContext =
1042
+ hasReleaseReadinessMode(payload)
1043
+ || await hasReleaseReadinessStopMarker(cwd, sessionId, teamName);
1044
+ if (!explicitReleaseReadinessContext) {
1045
+ return { matched: false, output: null };
1046
+ }
1047
+
1048
+ const summary = extractStableFinalRecommendationSummary(
1049
+ safeString(payload.last_assistant_message ?? payload.lastAssistantMessage),
1050
+ );
1051
+ if (!summary) return { matched: false, output: null };
1052
+
1053
+ const leaderAttention = await readTeamLeaderAttention(teamName, cwd);
1054
+ if (
1055
+ !leaderAttention
1056
+ || leaderAttention.leader_decision_state !== "done_waiting_on_leader"
1057
+ || leaderAttention.work_remaining !== false
1058
+ ) {
1059
+ return { matched: false, output: null };
1060
+ }
1061
+
1062
+ const signature = buildStableFinalRecommendationStopSignature(payload, teamName, summary);
1063
+ const output = await maybeReturnRepeatableStopOutput(
1064
+ payload,
1065
+ stateDir,
1066
+ signature,
1067
+ {
1068
+ decision: "block",
1069
+ reason:
1070
+ `Stable final recommendation already reached with no active worker tasks. Emit exactly one concise final decision summary aligned to "${summary}" with no filler or residual acknowledgements (for example "yes"), then stop.`,
1071
+ stopReason: "release_readiness_auto_finalize",
1072
+ systemMessage: RELEASE_READINESS_FINALIZE_SYSTEM_MESSAGE,
1073
+ },
1074
+ );
1075
+ return { matched: true, output };
1076
+ }
1077
+
938
1078
  async function buildSkillStopOutput(
939
1079
  cwd: string,
940
1080
  sessionId: string,
@@ -1088,6 +1228,14 @@ async function buildStopHookOutput(
1088
1228
  const ultraqaOutput = await buildModeBasedStopOutput("ultraqa", cwd, canonicalSessionId);
1089
1229
  if (!stopHookActive && ultraqaOutput) return ultraqaOutput;
1090
1230
 
1231
+ const releaseReadinessFinalizeResult = await maybeBuildReleaseReadinessFinalizeStopOutput(
1232
+ payload,
1233
+ cwd,
1234
+ stateDir,
1235
+ canonicalSessionId,
1236
+ );
1237
+ if (releaseReadinessFinalizeResult.matched) return releaseReadinessFinalizeResult.output;
1238
+
1091
1239
  const teamOutput = await buildTeamStopOutput(cwd, canonicalSessionId);
1092
1240
  if (teamOutput) {
1093
1241
  const teamSignature = buildRepeatableStopSignature(payload, "team-stop", safeString(teamOutput.stopReason));
@@ -1257,17 +1405,49 @@ export async function dispatchCodexNativeHook(
1257
1405
  };
1258
1406
  }
1259
1407
 
1260
- async function readStdinJson(): Promise<CodexHookPayload> {
1408
+ interface NativeHookCliReadResult {
1409
+ payload: CodexHookPayload;
1410
+ parseError: Error | null;
1411
+ }
1412
+
1413
+ async function readStdinJson(): Promise<NativeHookCliReadResult> {
1261
1414
  const chunks: Buffer[] = [];
1262
1415
  for await (const chunk of process.stdin) {
1263
1416
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
1264
1417
  }
1265
1418
  const raw = Buffer.concat(chunks).toString("utf-8").trim();
1266
- return raw ? safeObject(JSON.parse(raw)) : {};
1419
+ if (!raw) {
1420
+ return { payload: {}, parseError: null };
1421
+ }
1422
+
1423
+ try {
1424
+ return {
1425
+ payload: safeObject(JSON.parse(raw)),
1426
+ parseError: null,
1427
+ };
1428
+ } catch (error) {
1429
+ return {
1430
+ payload: {},
1431
+ parseError: error instanceof Error ? error : new Error(String(error)),
1432
+ };
1433
+ }
1267
1434
  }
1268
1435
 
1269
1436
  export async function runCodexNativeHookCli(): Promise<void> {
1270
- const payload = await readStdinJson();
1437
+ const { payload, parseError } = await readStdinJson();
1438
+ if (parseError) {
1439
+ process.stdout.write(`${JSON.stringify({
1440
+ decision: "block",
1441
+ reason: "OMX native hook received malformed JSON input. Preserve runtime state and inspect the emitting hook payload before retrying.",
1442
+ hookSpecificOutput: {
1443
+ hookEventName: "Unknown",
1444
+ additionalContext:
1445
+ `stdin JSON parsing failed inside codex-native-hook: ${parseError.message}. Emit valid JSON from the native hook caller before retrying.`,
1446
+ },
1447
+ })}\n`);
1448
+ return;
1449
+ }
1450
+
1271
1451
  const result = await dispatchCodexNativeHook(payload);
1272
1452
  if (result.outputJson) {
1273
1453
  process.stdout.write(`${JSON.stringify(result.outputJson)}\n`);
@@ -62,6 +62,48 @@ function safeString(value: unknown): string {
62
62
  return typeof value === 'string' ? value : '';
63
63
  }
64
64
 
65
+ function collectMessageTextFragments(value: unknown, fragments: string[]): void {
66
+ if (typeof value === 'string') {
67
+ if (value.trim() !== '') fragments.push(value);
68
+ return;
69
+ }
70
+
71
+ if (Array.isArray(value)) {
72
+ for (const item of value) collectMessageTextFragments(item, fragments);
73
+ return;
74
+ }
75
+
76
+ if (!value || typeof value !== 'object') return;
77
+
78
+ const record = value as Record<string, unknown>;
79
+ let usedPreferredField = false;
80
+ for (const key of ['text', 'message', 'content']) {
81
+ if (!(key in record)) continue;
82
+ usedPreferredField = true;
83
+ collectMessageTextFragments(record[key], fragments);
84
+ }
85
+ if (usedPreferredField) return;
86
+
87
+ for (const child of Object.values(record)) {
88
+ collectMessageTextFragments(child, fragments);
89
+ }
90
+ }
91
+
92
+ function extractMessageText(payload: Record<string, unknown>): string {
93
+ for (const candidate of [payload.text, payload.message, payload.content]) {
94
+ if (typeof candidate === 'string') {
95
+ if (candidate.trim() !== '') return candidate;
96
+ continue;
97
+ }
98
+
99
+ const fragments: string[] = [];
100
+ collectMessageTextFragments(candidate, fragments);
101
+ const text = fragments.join('\n').trim();
102
+ if (text) return text;
103
+ }
104
+ return '';
105
+ }
106
+
65
107
  function derivedLog(entry: Record<string, unknown>): Promise<void> {
66
108
  return appendFile(logPath, `${JSON.stringify({ timestamp: new Date().toISOString(), ...entry })}\n`).catch(() => {});
67
109
  }
@@ -185,7 +227,7 @@ function inferDerivedEvent(parsed: Record<string, unknown> | null, meta: FileMet
185
227
  }
186
228
 
187
229
  if (payloadType === 'assistant_message') {
188
- const message = safeString(payload.text || payload.message || payload.content);
230
+ const message = extractMessageText(payload);
189
231
  const looksLikeQuestion = /\?|\b(can you|could you|please provide|need input|what should)/i.test(message);
190
232
  if (looksLikeQuestion) {
191
233
  return {
@@ -33,6 +33,7 @@ import {
33
33
  readSubagentSessionSummary,
34
34
  } from '../subagents/tracker.js';
35
35
  import { listNotifyCanonicalActiveTeams } from './notify-hook/active-team.js';
36
+ import { sameFilePath } from '../utils/paths.js';
36
37
 
37
38
  function argValue(name: string, fallback = ''): string {
38
39
  const idx = process.argv.indexOf(name);
@@ -128,6 +129,7 @@ const RALPH_CONTINUE_TEXT = 'Ralph loop active continue';
128
129
  const RALPH_CONTINUE_CADENCE_MS = 60_000;
129
130
  const RALPH_STEER_LOCK_STALE_MS = 30_000;
130
131
  const RALPH_TERMINAL_PHASES = new Set(['complete', 'failed', 'cancelled']);
132
+ const RALPH_STARTING_PHASE_TIMEOUT_MS = RALPH_CONTINUE_CADENCE_MS * 2;
131
133
  const QUIET_ONCE_EVENT_TYPES = new Set(['watcher_start', 'watcher_once_complete']);
132
134
 
133
135
  interface WatcherFileMeta {
@@ -457,10 +459,19 @@ function hasRalphTerminalState(raw: Record<string, unknown> | null | undefined):
457
459
  if (raw.active !== true) return true;
458
460
  const phase = safeString(raw.current_phase).trim().toLowerCase();
459
461
  if (phase && RALPH_TERMINAL_PHASES.has(phase)) return true;
462
+ if (isStaleRalphStartingPhase(raw)) return true;
460
463
  if (safeString(raw.completed_at).trim()) return true;
461
464
  return false;
462
465
  }
463
466
 
467
+ function isStaleRalphStartingPhase(raw: Record<string, unknown>): boolean {
468
+ const phase = safeString(raw.current_phase).trim().toLowerCase();
469
+ if (phase !== 'starting') return false;
470
+ const reference = parseIsoMillis(safeString(raw.last_turn_at)) ?? parseIsoMillis(safeString(raw.started_at));
471
+ if (reference === null) return false;
472
+ return Date.now() - reference > RALPH_STARTING_PHASE_TIMEOUT_MS;
473
+ }
474
+
464
475
  async function loadPersistedWatcherState(): Promise<void> {
465
476
  const persisted = await readFile(statePath, 'utf-8')
466
477
  .then((content) => JSON.parse(content) as Record<string, unknown>)
@@ -537,6 +548,14 @@ async function resolveActiveModeState(mode: string): Promise<ActiveModeResult> {
537
548
  .then((content) => JSON.parse(content) as Record<string, unknown>)
538
549
  .catch(() => null);
539
550
  if (!parsed || typeof parsed !== 'object') continue;
551
+ if (mode === 'ralph' && dir !== stateDir && isStaleRalphStartingPhase(parsed)) {
552
+ return {
553
+ active: false,
554
+ reason: 'starting_stale',
555
+ path,
556
+ state: parsed,
557
+ };
558
+ }
540
559
  if (hasRalphTerminalState(parsed)) {
541
560
  return {
542
561
  active: false,
@@ -909,7 +928,7 @@ async function resolveAuthorityPrimaryWatcherHealth(now = Date.now()): Promise<A
909
928
 
910
929
  const existingRecord = await readPidFileRecord(pidFilePath).catch(() => null);
911
930
  if (!existingRecord) return createAuthorityBackoffState('pid_missing');
912
- if (existingRecord.cwd && resolve(existingRecord.cwd) !== cwd) return createAuthorityBackoffState('cwd_mismatch');
931
+ if (existingRecord.cwd && !sameFilePath(existingRecord.cwd, cwd)) return createAuthorityBackoffState('cwd_mismatch');
913
932
  if (!isPidAlive(existingRecord.pid)) {
914
933
  return createAuthorityBackoffState('pid_stale', {
915
934
  primary_pid: existingRecord.pid,
@@ -1004,7 +1023,12 @@ async function runRalphContinueSteerTick(): Promise<void> {
1004
1023
  singleton_lock_path: ralphSteerLockPath,
1005
1024
  };
1006
1025
 
1007
- if (!activeRalph.active) return;
1026
+ if (!activeRalph.active) {
1027
+ if (activeRalph.reason === 'starting_stale') {
1028
+ lastRalphContinueSteer.last_reason = 'starting_stale';
1029
+ }
1030
+ return;
1031
+ }
1008
1032
 
1009
1033
  if (parseIsoMillis(lastRalphContinueSteer.last_sent_at) === null && parseIsoMillis(lastRalphContinueSteer.cooldown_anchor_at) === null) {
1010
1034
  lastRalphContinueSteer.cooldown_anchor_at = startupIso;
@@ -347,6 +347,24 @@ function looksLikePermissionSeekingContinuation(normalizedText) {
347
347
  return matchesNormalizedPatterns(normalizedText, normalizePatternList(PERMISSION_SEEKING_STALL_PATTERNS));
348
348
  }
349
349
 
350
+ /**
351
+ * Pattern to identify test-runner output lines (status symbols, PASS/FAIL prefixes).
352
+ * These lines may incidentally contain stall-pattern words inside test names and
353
+ * should be excluded before running stall detection on pane captures.
354
+ */
355
+ const CAPTURE_TEST_LINE_RE = /^\s*(?:[✓✗✕×●✔✘▶◆○]|(?:PASS|FAIL|SKIP|ERROR)\s)/u;
356
+
357
+ /**
358
+ * Strip lines that look like test-runner output so stall patterns inside test
359
+ * names (e.g. "✓ should continue with the next step") do not trigger a nudge.
360
+ */
361
+ function filterCapturedTestLines(text) {
362
+ return safeString(text)
363
+ .split('\n')
364
+ .filter((line) => !CAPTURE_TEST_LINE_RE.test(line))
365
+ .join('\n');
366
+ }
367
+
350
368
  function summarizePaneCaptureForLog(captured, maxLines = 6) {
351
369
  const lines = safeString(captured)
352
370
  .replace(/\r\n?/g, '\n')
@@ -516,7 +534,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
516
534
 
517
535
  if (!detected && paneId) {
518
536
  captured = await capturePane(paneId);
519
- detected = detectStallPattern(captured, config.patterns, skillState?.phase);
537
+ detected = detectStallPattern(filterCapturedTestLines(captured), config.patterns, skillState?.phase);
520
538
  source = 'capture-pane';
521
539
  }
522
540
 
@@ -1,9 +1,10 @@
1
1
  import { execFileSync } from 'child_process';
2
2
  import { readFileSync } from 'fs';
3
- import { basename, dirname, resolve as resolvePath } from 'path';
3
+ import { basename, dirname } from 'path';
4
4
  import { readSessionState, isSessionStale } from '../../hooks/session.js';
5
5
  import { runProcess } from './process-runner.js';
6
6
  import { safeString } from './utils.js';
7
+ import { sameFilePath } from '../../utils/paths.js';
7
8
 
8
9
  function sanitizeTmuxToken(value: string): string {
9
10
  const cleaned = safeString(value)
@@ -139,7 +140,7 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
139
140
  if (!sessionState) {
140
141
  return { managed: false, reason: 'missing_session_state', invocationSessionId, sessionState: null, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
141
142
  }
142
- if (resolvePath(safeString(sessionState.cwd || cwd)) !== resolvePath(cwd)) {
143
+ if (!sameFilePath(safeString(sessionState.cwd || cwd), cwd)) {
143
144
  return { managed: false, reason: 'cwd_mismatch', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
144
145
  }
145
146
  const canonicalSessionId = safeString(sessionState.session_id).trim();
@@ -152,7 +153,11 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
152
153
  return { managed: false, reason: 'stale_session', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
153
154
  }
154
155
 
155
- const expectedTmuxSessionName = buildExpectedManagedTmuxSessionName(cwd, canonicalSessionId || invocationSessionId);
156
+ const authoritativeSessionCwd = safeString(sessionState.cwd || cwd).trim() || cwd;
157
+ const expectedTmuxSessionName = buildExpectedManagedTmuxSessionName(
158
+ authoritativeSessionCwd,
159
+ canonicalSessionId || invocationSessionId,
160
+ );
156
161
  const currentTmuxSessionName = readCurrentTmuxSessionName();
157
162
  if (currentTmuxSessionName && currentTmuxSessionName === expectedTmuxSessionName) {
158
163
  return {