oh-my-codex 0.18.12 → 0.18.14

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 (317) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +38 -11
  4. package/dist/agents/__tests__/native-config.test.js +25 -0
  5. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  6. package/dist/agents/native-config.d.ts.map +1 -1
  7. package/dist/agents/native-config.js +5 -1
  8. package/dist/agents/native-config.js.map +1 -1
  9. package/dist/autopilot/__tests__/fsm.test.js +7 -0
  10. package/dist/autopilot/__tests__/fsm.test.js.map +1 -1
  11. package/dist/autopilot/__tests__/planner-routing.test.d.ts +2 -0
  12. package/dist/autopilot/__tests__/planner-routing.test.d.ts.map +1 -0
  13. package/dist/autopilot/__tests__/planner-routing.test.js +74 -0
  14. package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -0
  15. package/dist/autopilot/__tests__/ralplan-gate.test.js +936 -0
  16. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  17. package/dist/autopilot/completion-gate.js +1 -1
  18. package/dist/autopilot/completion-gate.js.map +1 -1
  19. package/dist/autopilot/fsm.d.ts +2 -2
  20. package/dist/autopilot/fsm.d.ts.map +1 -1
  21. package/dist/autopilot/fsm.js +3 -0
  22. package/dist/autopilot/fsm.js.map +1 -1
  23. package/dist/autopilot/planner-routing.d.ts +21 -0
  24. package/dist/autopilot/planner-routing.d.ts.map +1 -0
  25. package/dist/autopilot/planner-routing.js +59 -0
  26. package/dist/autopilot/planner-routing.js.map +1 -0
  27. package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
  28. package/dist/autopilot/ralplan-gate.js +62 -18
  29. package/dist/autopilot/ralplan-gate.js.map +1 -1
  30. package/dist/autoresearch/goal.d.ts.map +1 -1
  31. package/dist/autoresearch/goal.js +10 -1
  32. package/dist/autoresearch/goal.js.map +1 -1
  33. package/dist/cli/__tests__/autoresearch-goal.test.js +33 -0
  34. package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
  35. package/dist/cli/__tests__/codex-plugin-layout.test.js +30 -0
  36. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  37. package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts +2 -0
  38. package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts.map +1 -0
  39. package/dist/cli/__tests__/doctor-artifact-ownership.test.js +158 -0
  40. package/dist/cli/__tests__/doctor-artifact-ownership.test.js.map +1 -0
  41. package/dist/cli/__tests__/doctor-invalid-config.test.js +35 -0
  42. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  43. package/dist/cli/__tests__/doctor-spark-routing.test.js +13 -0
  44. package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
  45. package/dist/cli/__tests__/doctor-warning-copy.test.js +1 -0
  46. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  47. package/dist/cli/__tests__/index.test.js +59 -1
  48. package/dist/cli/__tests__/index.test.js.map +1 -1
  49. package/dist/cli/__tests__/nested-help-routing.test.js +4 -0
  50. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  51. package/dist/cli/__tests__/package-bin-contract.test.js +7 -1
  52. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  53. package/dist/cli/__tests__/performance-goal.test.js +22 -0
  54. package/dist/cli/__tests__/performance-goal.test.js.map +1 -1
  55. package/dist/cli/__tests__/resume.test.js +313 -1
  56. package/dist/cli/__tests__/resume.test.js.map +1 -1
  57. package/dist/cli/__tests__/session-scoped-runtime.test.js +162 -0
  58. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  59. package/dist/cli/__tests__/session-search-help.test.js +3 -2
  60. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  61. package/dist/cli/__tests__/session-search.test.js +100 -2
  62. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  63. package/dist/cli/__tests__/setup-agents-overwrite.test.js +35 -1
  64. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  65. package/dist/cli/__tests__/setup-install-mode.test.js +39 -5
  66. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  67. package/dist/cli/__tests__/setup-prompts-overwrite.test.js +74 -0
  68. package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
  69. package/dist/cli/__tests__/setup-scope.test.js +45 -0
  70. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  71. package/dist/cli/__tests__/team.test.js +17 -3
  72. package/dist/cli/__tests__/team.test.js.map +1 -1
  73. package/dist/cli/__tests__/ultragoal.test.js +37 -0
  74. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  75. package/dist/cli/__tests__/update.test.js +6 -2
  76. package/dist/cli/__tests__/update.test.js.map +1 -1
  77. package/dist/cli/autoresearch-goal.d.ts.map +1 -1
  78. package/dist/cli/autoresearch-goal.js +2 -1
  79. package/dist/cli/autoresearch-goal.js.map +1 -1
  80. package/dist/cli/doctor.d.ts +22 -0
  81. package/dist/cli/doctor.d.ts.map +1 -1
  82. package/dist/cli/doctor.js +272 -5
  83. package/dist/cli/doctor.js.map +1 -1
  84. package/dist/cli/index.d.ts +10 -2
  85. package/dist/cli/index.d.ts.map +1 -1
  86. package/dist/cli/index.js +274 -11
  87. package/dist/cli/index.js.map +1 -1
  88. package/dist/cli/performance-goal.d.ts.map +1 -1
  89. package/dist/cli/performance-goal.js +4 -2
  90. package/dist/cli/performance-goal.js.map +1 -1
  91. package/dist/cli/plugin-marketplace.d.ts +1 -0
  92. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  93. package/dist/cli/plugin-marketplace.js +67 -5
  94. package/dist/cli/plugin-marketplace.js.map +1 -1
  95. package/dist/cli/project-runtime-codex-homes.d.ts +8 -0
  96. package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -0
  97. package/dist/cli/project-runtime-codex-homes.js +134 -0
  98. package/dist/cli/project-runtime-codex-homes.js.map +1 -0
  99. package/dist/cli/session-search.d.ts.map +1 -1
  100. package/dist/cli/session-search.js +8 -1
  101. package/dist/cli/session-search.js.map +1 -1
  102. package/dist/cli/setup.d.ts +1 -0
  103. package/dist/cli/setup.d.ts.map +1 -1
  104. package/dist/cli/setup.js +183 -10
  105. package/dist/cli/setup.js.map +1 -1
  106. package/dist/cli/ultragoal.d.ts.map +1 -1
  107. package/dist/cli/ultragoal.js +5 -1
  108. package/dist/cli/ultragoal.js.map +1 -1
  109. package/dist/cli/update.d.ts.map +1 -1
  110. package/dist/cli/update.js +5 -0
  111. package/dist/cli/update.js.map +1 -1
  112. package/dist/config/__tests__/codex-hooks.test.js +38 -24
  113. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  114. package/dist/config/__tests__/models.test.js +21 -1
  115. package/dist/config/__tests__/models.test.js.map +1 -1
  116. package/dist/config/codex-hooks.d.ts +6 -0
  117. package/dist/config/codex-hooks.d.ts.map +1 -1
  118. package/dist/config/codex-hooks.js +34 -49
  119. package/dist/config/codex-hooks.js.map +1 -1
  120. package/dist/config/models.d.ts +5 -0
  121. package/dist/config/models.d.ts.map +1 -1
  122. package/dist/config/models.js +23 -0
  123. package/dist/config/models.js.map +1 -1
  124. package/dist/goal-workflows/codex-goal-snapshot.d.ts +2 -0
  125. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  126. package/dist/goal-workflows/codex-goal-snapshot.js +10 -0
  127. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  128. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +7 -0
  129. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  130. package/dist/hooks/__tests__/keyword-detector.test.js +46 -0
  131. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  132. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +100 -0
  133. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  134. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +75 -0
  135. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  136. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +225 -0
  137. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  138. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +75 -0
  139. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  140. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +200 -0
  141. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  142. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +187 -12
  143. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  144. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +225 -0
  145. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  146. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
  147. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  148. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +75 -0
  149. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  150. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  151. package/dist/hooks/keyword-detector.js +4 -0
  152. package/dist/hooks/keyword-detector.js.map +1 -1
  153. package/dist/hud/__tests__/reconcile.test.js +137 -2
  154. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  155. package/dist/hud/__tests__/state.test.js +107 -0
  156. package/dist/hud/__tests__/state.test.js.map +1 -1
  157. package/dist/hud/__tests__/tmux.test.js +17 -0
  158. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  159. package/dist/hud/reconcile.d.ts.map +1 -1
  160. package/dist/hud/reconcile.js +21 -7
  161. package/dist/hud/reconcile.js.map +1 -1
  162. package/dist/hud/render.d.ts.map +1 -1
  163. package/dist/hud/render.js +9 -0
  164. package/dist/hud/render.js.map +1 -1
  165. package/dist/hud/state.d.ts.map +1 -1
  166. package/dist/hud/state.js +43 -5
  167. package/dist/hud/state.js.map +1 -1
  168. package/dist/hud/types.d.ts +6 -0
  169. package/dist/hud/types.d.ts.map +1 -1
  170. package/dist/hud/types.js.map +1 -1
  171. package/dist/modes/__tests__/base-autopilot-gates.test.js +48 -0
  172. package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
  173. package/dist/modes/base.d.ts.map +1 -1
  174. package/dist/modes/base.js +1 -0
  175. package/dist/modes/base.js.map +1 -1
  176. package/dist/performance-goal/artifacts.d.ts.map +1 -1
  177. package/dist/performance-goal/artifacts.js +10 -1
  178. package/dist/performance-goal/artifacts.js.map +1 -1
  179. package/dist/pipeline/__tests__/orchestrator.test.js +125 -0
  180. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  181. package/dist/pipeline/__tests__/stages.test.js +109 -0
  182. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  183. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  184. package/dist/pipeline/orchestrator.js +7 -0
  185. package/dist/pipeline/orchestrator.js.map +1 -1
  186. package/dist/ralplan/__tests__/consensus-gate.test.js +552 -1
  187. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  188. package/dist/ralplan/consensus-gate.d.ts +25 -0
  189. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  190. package/dist/ralplan/consensus-gate.js +289 -75
  191. package/dist/ralplan/consensus-gate.js.map +1 -1
  192. package/dist/scripts/__tests__/codex-native-hook.test.js +1149 -47
  193. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  194. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  195. package/dist/scripts/codex-native-hook.js +542 -33
  196. package/dist/scripts/codex-native-hook.js.map +1 -1
  197. package/dist/scripts/codex-native-pre-post.d.ts +1 -0
  198. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  199. package/dist/scripts/codex-native-pre-post.js +130 -0
  200. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  201. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  202. package/dist/scripts/notify-hook/team-tmux-guard.js +87 -8
  203. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  204. package/dist/scripts/notify-hook/tmux-injection.js +1 -1
  205. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  206. package/dist/scripts/run-compiled-ci.d.ts +2 -0
  207. package/dist/scripts/run-compiled-ci.d.ts.map +1 -0
  208. package/dist/scripts/run-compiled-ci.js +77 -0
  209. package/dist/scripts/run-compiled-ci.js.map +1 -0
  210. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  211. package/dist/scripts/sync-plugin-mirror.js +1 -0
  212. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  213. package/dist/session-history/__tests__/search.test.js +166 -0
  214. package/dist/session-history/__tests__/search.test.js.map +1 -1
  215. package/dist/session-history/search.d.ts +7 -0
  216. package/dist/session-history/search.d.ts.map +1 -1
  217. package/dist/session-history/search.js +99 -25
  218. package/dist/session-history/search.js.map +1 -1
  219. package/dist/sidecar/__tests__/collector.test.js +60 -0
  220. package/dist/sidecar/__tests__/collector.test.js.map +1 -1
  221. package/dist/sidecar/collector.d.ts.map +1 -1
  222. package/dist/sidecar/collector.js +3 -6
  223. package/dist/sidecar/collector.js.map +1 -1
  224. package/dist/state/__tests__/operations.test.js +107 -3
  225. package/dist/state/__tests__/operations.test.js.map +1 -1
  226. package/dist/state/operations.d.ts.map +1 -1
  227. package/dist/state/operations.js +57 -2
  228. package/dist/state/operations.js.map +1 -1
  229. package/dist/team/__tests__/delivery-e2e-smoke.test.js +22 -1
  230. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  231. package/dist/team/__tests__/model-contract.test.js +57 -1
  232. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  233. package/dist/team/__tests__/runtime.test.js +1468 -110
  234. package/dist/team/__tests__/runtime.test.js.map +1 -1
  235. package/dist/team/__tests__/scaling.test.js +63 -0
  236. package/dist/team/__tests__/scaling.test.js.map +1 -1
  237. package/dist/team/__tests__/state.test.js +24 -1
  238. package/dist/team/__tests__/state.test.js.map +1 -1
  239. package/dist/team/__tests__/tmux-session.test.js +512 -0
  240. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  241. package/dist/team/__tests__/worker-bootstrap.test.js +42 -9
  242. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  243. package/dist/team/model-contract.d.ts +16 -0
  244. package/dist/team/model-contract.d.ts.map +1 -1
  245. package/dist/team/model-contract.js +41 -1
  246. package/dist/team/model-contract.js.map +1 -1
  247. package/dist/team/runtime.d.ts.map +1 -1
  248. package/dist/team/runtime.js +116 -30
  249. package/dist/team/runtime.js.map +1 -1
  250. package/dist/team/scaling.d.ts.map +1 -1
  251. package/dist/team/scaling.js +9 -1
  252. package/dist/team/scaling.js.map +1 -1
  253. package/dist/team/state/types.d.ts +2 -0
  254. package/dist/team/state/types.d.ts.map +1 -1
  255. package/dist/team/state/types.js.map +1 -1
  256. package/dist/team/state.d.ts +3 -0
  257. package/dist/team/state.d.ts.map +1 -1
  258. package/dist/team/state.js +20 -0
  259. package/dist/team/state.js.map +1 -1
  260. package/dist/team/tmux-session.d.ts +38 -3
  261. package/dist/team/tmux-session.d.ts.map +1 -1
  262. package/dist/team/tmux-session.js +275 -36
  263. package/dist/team/tmux-session.js.map +1 -1
  264. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  265. package/dist/team/worker-bootstrap.js +26 -1
  266. package/dist/team/worker-bootstrap.js.map +1 -1
  267. package/dist/ultragoal/__tests__/artifacts.test.js +238 -0
  268. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  269. package/dist/ultragoal/__tests__/docs-contract.test.js +3 -0
  270. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  271. package/dist/ultragoal/artifacts.d.ts +15 -0
  272. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  273. package/dist/ultragoal/artifacts.js +167 -4
  274. package/dist/ultragoal/artifacts.js.map +1 -1
  275. package/dist/utils/__tests__/agents-md.test.js +39 -1
  276. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  277. package/dist/utils/__tests__/agents-model-table.test.js +28 -0
  278. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  279. package/dist/utils/agents-md.d.ts +4 -0
  280. package/dist/utils/agents-md.d.ts.map +1 -1
  281. package/dist/utils/agents-md.js +25 -0
  282. package/dist/utils/agents-md.js.map +1 -1
  283. package/dist/utils/agents-model-table.d.ts +7 -3
  284. package/dist/utils/agents-model-table.d.ts.map +1 -1
  285. package/dist/utils/agents-model-table.js +16 -8
  286. package/dist/utils/agents-model-table.js.map +1 -1
  287. package/dist/verification/__tests__/ci-rust-gates.test.js +8 -2
  288. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  289. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +71 -3
  290. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  291. package/package.json +2 -2
  292. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  293. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +59 -8
  294. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +9 -5
  295. package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +3 -0
  296. package/plugins/oh-my-codex/skills/performance-goal/SKILL.md +3 -0
  297. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
  298. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +22 -4
  299. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +2 -2
  300. package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +61 -0
  301. package/skills/autopilot/SKILL.md +9 -5
  302. package/skills/autoresearch-goal/SKILL.md +3 -0
  303. package/skills/ecomode/SKILL.md +1 -1
  304. package/skills/ecomode/references/agent-tiers.md +61 -0
  305. package/skills/performance-goal/SKILL.md +3 -0
  306. package/skills/ralplan/SKILL.md +1 -1
  307. package/skills/ultragoal/SKILL.md +22 -4
  308. package/skills/ultrawork/SKILL.md +2 -2
  309. package/skills/ultrawork/references/agent-tiers.md +61 -0
  310. package/src/scripts/__tests__/codex-native-hook.test.ts +1417 -119
  311. package/src/scripts/codex-native-hook.ts +597 -40
  312. package/src/scripts/codex-native-pre-post.ts +137 -0
  313. package/src/scripts/notify-hook/team-tmux-guard.ts +92 -8
  314. package/src/scripts/notify-hook/tmux-injection.ts +1 -1
  315. package/src/scripts/run-compiled-ci.ts +85 -0
  316. package/src/scripts/sync-plugin-mirror.ts +1 -0
  317. package/templates/AGENTS.md +6 -0
@@ -325,6 +325,11 @@ function tokenizeShellCommandWithBoundaries(commandText: string): ShellToken[] |
325
325
  let quote: "'" | "\"" | null = null;
326
326
  let escaping = false;
327
327
  let nextTokenStartsCommand = false;
328
+ // Command substitution executes even inside double quotes, so we track an
329
+ // active `"$(…)"` (paren depth) or `` "`…`" `` (backtick) substitution to
330
+ // restore double-quote mode once it closes.
331
+ let dquoteSubstParenDepth = 0;
332
+ let backtickFromDquote = false;
328
333
 
329
334
  const pushCurrent = () => {
330
335
  if (!current) return;
@@ -357,6 +362,25 @@ function tokenizeShellCommandWithBoundaries(commandText: string): ShellToken[] |
357
362
  if (isDoubleQuotedShellEscapeTarget(trimmed[index + 1])) escaping = true;
358
363
  else current += char;
359
364
  }
365
+ // Command substitution runs inside double quotes, so `"$(cmd …)"` and
366
+ // `` "`cmd …`" `` are real invocations, not literal mentions. Treat the
367
+ // opener as a command-position boundary and parse the substitution body
368
+ // unquoted so the command-head walk resumes, then restore double-quote
369
+ // mode when it closes. Parameter expansion (`${VAR}`), `$HOME`, and an
370
+ // escaped `\$(` stay literal text (no false positive on `grep "(x"`).
371
+ else if (char === "$" && trimmed[index + 1] === "(") {
372
+ pushCurrent();
373
+ nextTokenStartsCommand = true;
374
+ index += 1;
375
+ dquoteSubstParenDepth = 1;
376
+ quote = null;
377
+ }
378
+ else if (char === "`") {
379
+ pushCurrent();
380
+ nextTokenStartsCommand = true;
381
+ backtickFromDquote = true;
382
+ quote = null;
383
+ }
360
384
  else current += char;
361
385
  continue;
362
386
  }
@@ -368,6 +392,48 @@ function tokenizeShellCommandWithBoundaries(commandText: string): ShellToken[] |
368
392
  continue;
369
393
  }
370
394
 
395
+ // Grouping / command-substitution openers act as command-position
396
+ // boundaries so the command-head walk resumes after them, mirroring the
397
+ // legacy `[\n;&|(]` boundary set. This catches a real command immediately
398
+ // after `(`, `((`, `$(` (command substitution), or a backtick — e.g.
399
+ // `(apply_patch …)`, `true | (apply_patch …)`, `x=$(apply_patch …)`. We
400
+ // are outside quotes here, so a literal like `grep "(apply_patch"` is left
401
+ // intact and not split.
402
+ // Closing backtick of a command substitution opened inside double quotes
403
+ // ends the substitution and restores the surrounding double-quoted literal.
404
+ if (char === "`" && backtickFromDquote) {
405
+ pushCurrent();
406
+ backtickFromDquote = false;
407
+ quote = "\"";
408
+ continue;
409
+ }
410
+
411
+ if (char === "(" || char === ")" || char === "`") {
412
+ pushCurrent();
413
+ nextTokenStartsCommand = true;
414
+ // Track paren depth of a `"$(…)"` substitution opened inside double
415
+ // quotes so the matching `)` restores double-quote mode (nested `$(`
416
+ // and `(` inside it are balanced before we return to the literal).
417
+ if (dquoteSubstParenDepth > 0) {
418
+ if (char === "(") {
419
+ dquoteSubstParenDepth += 1;
420
+ } else if (char === ")") {
421
+ dquoteSubstParenDepth -= 1;
422
+ if (dquoteSubstParenDepth === 0) quote = "\"";
423
+ }
424
+ }
425
+ continue;
426
+ }
427
+
428
+ // `{` is a group opener only as its own word (`{ apply_patch …; }`):
429
+ // require an empty pending token (preceded by start/whitespace/boundary)
430
+ // and a following whitespace/newline so brace expansion (`{a,b}`) and
431
+ // parameter expansion (`${VAR}`) are left intact.
432
+ if (char === "{" && current === "" && /\s/.test(trimmed[index + 1] ?? " ")) {
433
+ nextTokenStartsCommand = true;
434
+ continue;
435
+ }
436
+
371
437
  if (/\s/.test(char)) {
372
438
  pushCurrent();
373
439
  continue;
@@ -488,6 +554,77 @@ function findGitCommandTokenIndex(tokens: ShellToken[]): number {
488
554
  return -1;
489
555
  }
490
556
 
557
+ const APPLY_PATCH_COMMAND_WRAPPER_TOKENS = new Set(["sudo", "command", "exec"]);
558
+
559
+ // Detects a real `apply_patch` invocation at a shell command position using the
560
+ // same tokenized command-head walk the git commit guard uses
561
+ // (`findGitCommandTokenIndex`): after every statement boundary skip leading
562
+ // inline `NAME=VALUE` assignments, the `env` executable with its full option
563
+ // grammar (`-i`/`--ignore-environment`, `-u`/`--unset`/`--unset=`, `-C`/`-S`,
564
+ // `--`, interleaved assignments), and `sudo`/`command`/`exec` wrappers — in any
565
+ // order — then match when the resolved command token's basename is
566
+ // `apply_patch`. This blocks path-qualified (`/usr/bin/apply_patch`,
567
+ // `./apply_patch`), env-flag (`env -i apply_patch`, `env -u FOO apply_patch`),
568
+ // and reordered (`FOO=bar env apply_patch`, `exec env FOO=bar apply_patch`)
569
+ // forms, while read-only diagnostics that merely mention the literal token
570
+ // (e.g. `grep -n "apply_patch" file`) are not misread as write intent. Heredoc
571
+ // bodies are stripped first so patch payloads cannot break tokenization.
572
+ export function commandInvokesApplyPatch(command: string): boolean {
573
+ const tokens = tokenizeShellCommandWithBoundaries(removeHereDocBodies(command));
574
+ if (!tokens) return false;
575
+
576
+ for (let commandStart = 0; commandStart < tokens.length; commandStart = nextCommandStart(tokens, commandStart)) {
577
+ let index = commandStart;
578
+ const commandEnd = nextCommandStart(tokens, commandStart);
579
+
580
+ let advanced = true;
581
+ while (advanced && index < commandEnd) {
582
+ advanced = false;
583
+
584
+ while (index < commandEnd && isInlineShellEnvAssignment(tokens[index]?.value ?? "")) {
585
+ index += 1;
586
+ advanced = true;
587
+ }
588
+
589
+ while (index < commandEnd && isEnvExecutableToken(tokens[index]?.value ?? "")) {
590
+ index += 1;
591
+ advanced = true;
592
+ while (index < commandEnd) {
593
+ const token = tokens[index]?.value ?? "";
594
+ if (token === "--") {
595
+ index += 1;
596
+ break;
597
+ }
598
+ if (isInlineShellEnvAssignment(token)) {
599
+ index += 1;
600
+ continue;
601
+ }
602
+ if (token === "-i" || token === "--ignore-environment" || token.startsWith("--unset=")) {
603
+ index += 1;
604
+ continue;
605
+ }
606
+ if (token.startsWith("-")) {
607
+ index += envOptionConsumesNextValue(token) ? 2 : 1;
608
+ continue;
609
+ }
610
+ break;
611
+ }
612
+ }
613
+
614
+ while (index < commandEnd && APPLY_PATCH_COMMAND_WRAPPER_TOKENS.has((tokens[index]?.value ?? "").toLowerCase())) {
615
+ index += 1;
616
+ advanced = true;
617
+ }
618
+ }
619
+
620
+ if (index < commandEnd && shellCommandBasename(tokens[index]?.value ?? "") === "apply_patch") return true;
621
+ if (commandEnd <= commandStart) break;
622
+ commandStart = commandEnd - 1;
623
+ }
624
+
625
+ return false;
626
+ }
627
+
491
628
  function tokenValues(tokens: ShellToken[]): string[] {
492
629
  return tokens.map((token) => token.value);
493
630
  }
@@ -11,6 +11,28 @@ import {
11
11
  } from '../tmux-hook-engine.js';
12
12
 
13
13
  export const PANE_READINESS_UNVERIFIED_REASON = 'pane_readiness_unverified';
14
+ let nextTmuxBufferId = 0;
15
+
16
+ function buildSafePasteArgv(target: string, prompt: string): {
17
+ bufferName: string;
18
+ setBufferArgv: string[];
19
+ showBufferArgv: string[];
20
+ clearComposerArgv: string[];
21
+ pasteBufferArgv: string[];
22
+ deleteBufferArgv: string[];
23
+ } {
24
+ nextTmuxBufferId += 1;
25
+ const bufferName = `omx-pane-input-${process.pid}-${Date.now()}-${nextTmuxBufferId}`;
26
+ return {
27
+ bufferName,
28
+ setBufferArgv: ['set-buffer', '-b', bufferName, '--', prompt],
29
+ showBufferArgv: ['show-buffer', '-b', bufferName],
30
+ clearComposerArgv: ['send-keys', '-t', target, 'C-u'],
31
+ pasteBufferArgv: ['paste-buffer', '-t', target, '-b', bufferName, '-p', '-d'],
32
+ deleteBufferArgv: ['delete-buffer', '-b', bufferName],
33
+ };
34
+ }
35
+
14
36
 
15
37
  export function mapPaneInjectionReadinessReason(reason: any): any {
16
38
  return reason === 'pane_running_shell' ? 'agent_not_running' : reason;
@@ -145,24 +167,82 @@ export async function sendPaneInput({
145
167
  ? Math.max(0, Math.floor(submitKeyPresses))
146
168
  : 2;
147
169
  const literalPrompt = safeString(prompt);
148
- const argv = normalizedSubmitKeyPresses === 0
149
- ? {
150
- typeArgv: ['send-keys', '-t', target, '-l', literalPrompt],
151
- submitArgv: [] as string[][],
152
- }
170
+ const submitArgv = normalizedSubmitKeyPresses === 0
171
+ ? [] as string[][]
153
172
  : buildSendKeysArgv({
154
173
  paneTarget: target,
155
174
  prompt: literalPrompt,
156
175
  dryRun: false,
157
176
  submitKeyPresses: normalizedSubmitKeyPresses,
158
- });
159
- if (!argv) {
177
+ })?.submitArgv;
178
+ if (!submitArgv) {
160
179
  return { ok: false, sent: false, reason: 'send_failed', paneTarget: target };
161
180
  }
181
+ const pasteArgv = buildSafePasteArgv(target, literalPrompt);
182
+ const argv = {
183
+ typeArgv: pasteArgv.pasteBufferArgv,
184
+ submitArgv,
185
+ bufferName: pasteArgv.bufferName,
186
+ setBufferArgv: pasteArgv.setBufferArgv,
187
+ showBufferArgv: pasteArgv.showBufferArgv,
188
+ clearComposerArgv: pasteArgv.clearComposerArgv,
189
+ pasteBufferArgv: pasteArgv.pasteBufferArgv,
190
+ deleteBufferArgv: pasteArgv.deleteBufferArgv,
191
+ };
162
192
 
193
+ let bufferSet = false;
163
194
  try {
164
195
  if (typePrompt) {
165
- await runProcess('tmux', argv.typeArgv, 3000);
196
+ try {
197
+ await runProcess('tmux', pasteArgv.setBufferArgv, 3000);
198
+ bufferSet = true;
199
+ } catch (error) {
200
+ return {
201
+ ok: false,
202
+ sent: false,
203
+ reason: 'buffer_set_failed',
204
+ paneTarget: target,
205
+ argv,
206
+ error: error instanceof Error ? error.message : safeString(error),
207
+ };
208
+ }
209
+ let verifiedBuffer;
210
+ try {
211
+ verifiedBuffer = await runProcess('tmux', pasteArgv.showBufferArgv, 3000);
212
+ } catch (error) {
213
+ return {
214
+ ok: false,
215
+ sent: false,
216
+ reason: 'buffer_show_failed',
217
+ paneTarget: target,
218
+ argv,
219
+ error: error instanceof Error ? error.message : safeString(error),
220
+ };
221
+ }
222
+ if (verifiedBuffer.stdout !== literalPrompt) {
223
+ return {
224
+ ok: false,
225
+ sent: false,
226
+ reason: 'buffer_verify_failed',
227
+ paneTarget: target,
228
+ argv,
229
+ expectedBytes: literalPrompt.length,
230
+ actualBytes: verifiedBuffer.stdout.length,
231
+ };
232
+ }
233
+ try {
234
+ await runProcess('tmux', pasteArgv.clearComposerArgv, 3000);
235
+ await runProcess('tmux', pasteArgv.pasteBufferArgv, 3000);
236
+ } catch (error) {
237
+ return {
238
+ ok: false,
239
+ sent: false,
240
+ reason: 'buffer_paste_failed',
241
+ paneTarget: target,
242
+ argv,
243
+ error: error instanceof Error ? error.message : safeString(error),
244
+ };
245
+ }
166
246
  }
167
247
  if (queueFirstSubmit && argv.submitArgv.length > 0) {
168
248
  await runProcess('tmux', ['send-keys', '-t', target, 'Tab'], 3000);
@@ -186,6 +266,10 @@ export async function sendPaneInput({
186
266
  argv,
187
267
  error: error instanceof Error ? error.message : safeString(error),
188
268
  };
269
+ } finally {
270
+ if (bufferSet) {
271
+ await runProcess('tmux', pasteArgv.deleteBufferArgv, 3000).catch(() => {});
272
+ }
189
273
  }
190
274
  }
191
275
 
@@ -71,7 +71,7 @@ async function finalizeResolvedPane(paneId: string, reason: string, expectedCwd:
71
71
 
72
72
  async function resolveCanonicalPaneFromPaneTarget(paneTarget: any, expectedCwd: any): Promise<any> {
73
73
  const paneResult = await runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#{pane_id}']);
74
- const paneId = safeString(paneResult.stdout).trim();
74
+ const paneId = safeString(paneResult.stdout).trim() || (safeString(paneTarget).trim().match(/^%\d+$/) ? safeString(paneTarget).trim() : '');
75
75
  if (!paneId) return { paneTarget: null, reason: 'target_not_found' };
76
76
 
77
77
  let startCommand = '';
@@ -0,0 +1,85 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ const SOURCE_CHECKOUT_SENTINELS = [
6
+ "src/catalog/manifest.json",
7
+ "docs/troubleshooting.md",
8
+ ".github/workflows/ci.yml",
9
+ ] as const;
10
+
11
+ const INSTALLED_PACKAGE_TEST_FILES = [
12
+ "dist/scripts/__tests__/smoke-packed-install.test.js",
13
+ "dist/cli/__tests__/nested-help-routing.test.js",
14
+ "dist/cli/__tests__/mcp-parity.test.js",
15
+ ] as const;
16
+
17
+ const INSTALLED_PACKAGE_CLI_SMOKE_COMMANDS = [
18
+ ["--help"],
19
+ ["version"],
20
+ ["api", "--help"],
21
+ ["sparkshell", "--help"],
22
+ ["notepad", "--help"],
23
+ ["project-memory", "--help"],
24
+ ["trace", "--help"],
25
+ ["code-intel", "--help"],
26
+ ] as const;
27
+
28
+ function npmBin(): string {
29
+ return process.platform === "win32" ? "npm.cmd" : "npm";
30
+ }
31
+
32
+ function run(command: string, args: readonly string[]): void {
33
+ const result = spawnSync(command, [...args], {
34
+ cwd: process.cwd(),
35
+ env: {
36
+ ...process.env,
37
+ OMX_AUTO_UPDATE: "0",
38
+ OMX_NOTIFY_FALLBACK: "0",
39
+ OMX_HOOK_DERIVED_SIGNALS: "0",
40
+ },
41
+ stdio: "inherit",
42
+ });
43
+ if (result.error) {
44
+ throw result.error;
45
+ }
46
+ if (result.status !== 0) {
47
+ throw new Error(`Command failed: ${command} ${args.join(" ")}`);
48
+ }
49
+ }
50
+
51
+ function isSourceCheckout(): boolean {
52
+ return SOURCE_CHECKOUT_SENTINELS.every((sentinel) =>
53
+ existsSync(join(process.cwd(), sentinel)),
54
+ );
55
+ }
56
+
57
+ function runSourceCheckoutGate(): void {
58
+ run(npmBin(), ["run", "verify:native-agents"]);
59
+ run(npmBin(), ["run", "verify:plugin-bundle"]);
60
+ run(npmBin(), ["run", "test:node"]);
61
+ run(process.execPath, ["dist/scripts/generate-catalog-docs.js", "--check"]);
62
+ }
63
+
64
+ function runInstalledPackageGate(): void {
65
+ run(npmBin(), ["run", "verify:native-agents"]);
66
+ run(npmBin(), ["run", "verify:plugin-bundle"]);
67
+ run(process.execPath, [
68
+ "dist/scripts/run-test-files.js",
69
+ ...INSTALLED_PACKAGE_TEST_FILES,
70
+ ]);
71
+ for (const argv of INSTALLED_PACKAGE_CLI_SMOKE_COMMANDS) {
72
+ run(process.execPath, ["dist/cli/omx.js", ...argv]);
73
+ }
74
+ }
75
+
76
+ try {
77
+ if (isSourceCheckout()) {
78
+ runSourceCheckoutGate();
79
+ } else {
80
+ runInstalledPackageGate();
81
+ }
82
+ } catch (error) {
83
+ console.error(error instanceof Error ? error.message : String(error));
84
+ process.exit(1);
85
+ }
@@ -59,6 +59,7 @@ const SETUP_OWNED_PLUGIN_MANIFEST_FIELDS = [
59
59
  ] as const;
60
60
  const OMX_PLUGIN_HOOK_COMMAND =
61
61
  'node "${PLUGIN_ROOT}/hooks/codex-native-hook.mjs"';
62
+ // The launcher itself must not shell-wrap node.exe on Windows; see GH #2858.
62
63
  const OMX_PLUGIN_HOOK_LAUNCHER_CONTRACT_MARKER =
63
64
  "omx-plugin-hook-launcher:v1";
64
65
  // Plugin-scoped Codex hooks intentionally mirror the setup-managed lifecycle
@@ -157,6 +157,12 @@ When to use what:
157
157
  - Use normal Codex repository inspection tools/subagents for repository lookup and implementation context.
158
158
  - Use `omx sparkshell --tmux-pane` only as an explicit opt-in operator aid for shell-native tmux evidence or bounded verification; it does not replace raw evidence capture.
159
159
 
160
+ Supervisor tmux handoff safety:
161
+ - Never paste from tmux's implicit/current buffer. Load handoff text into a fresh named buffer with `tmux set-buffer -b <name> -- "$message"` or a temp-file-backed `tmux load-buffer -b <name> <file>`; never use `tmux load-buffer -- <message>`.
162
+ - Verify the named buffer with `tmux show-buffer -b <name>` before any paste. A failed load or mismatched buffer is a blocker; do not run `paste-buffer` or submit keys after it.
163
+ - Clear the pane composer with `tmux send-keys -t <pane> C-u` immediately before paste, then use bracketed paste (`tmux paste-buffer -t <pane> -b <name> -p -d`) and submit intentionally.
164
+ - Recapture the pane after paste/Enter and verify the intended turn was accepted rather than leaving stale draft text visible.
165
+
160
166
  Leader vs worker: leaders choose mode, delegate bounded work, integrate, and own verification; workers execute their slice and escalate blockers, scope expansion, shared-file conflicts, or mode mismatch upward. Escalate from worker to leader for blockers, scope expansion, shared ownership conflicts, or mode mismatch.
161
167
 
162
168
  Stop / escalate: stop when the task is verified complete, the user says stop/cancel, or no meaningful recovery path remains. Escalate to the user only for irreversible, destructive, materially branching decisions, or missing authority.