oh-my-codex 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/dist/autoresearch/__tests__/skill-validation.test.d.ts +2 -0
  4. package/dist/autoresearch/__tests__/skill-validation.test.d.ts.map +1 -0
  5. package/dist/autoresearch/__tests__/skill-validation.test.js +91 -0
  6. package/dist/autoresearch/__tests__/skill-validation.test.js.map +1 -0
  7. package/dist/autoresearch/skill-validation.d.ts +13 -0
  8. package/dist/autoresearch/skill-validation.d.ts.map +1 -0
  9. package/dist/autoresearch/skill-validation.js +165 -0
  10. package/dist/autoresearch/skill-validation.js.map +1 -0
  11. package/dist/catalog/__tests__/schema.test.js +6 -0
  12. package/dist/catalog/__tests__/schema.test.js.map +1 -1
  13. package/dist/cli/__tests__/autoresearch-guided.test.js +236 -273
  14. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  15. package/dist/cli/__tests__/autoresearch.test.js +64 -653
  16. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  17. package/dist/cli/__tests__/index.test.js +7 -0
  18. package/dist/cli/__tests__/index.test.js.map +1 -1
  19. package/dist/cli/__tests__/nested-help-routing.test.js +2 -1
  20. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  21. package/dist/cli/__tests__/question.test.d.ts +2 -0
  22. package/dist/cli/__tests__/question.test.d.ts.map +1 -0
  23. package/dist/cli/__tests__/question.test.js +113 -0
  24. package/dist/cli/__tests__/question.test.js.map +1 -0
  25. package/dist/cli/__tests__/session-search-help.test.js +1 -1
  26. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  27. package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -0
  28. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  29. package/dist/cli/autoresearch-guided.d.ts +24 -7
  30. package/dist/cli/autoresearch-guided.d.ts.map +1 -1
  31. package/dist/cli/autoresearch-guided.js +189 -130
  32. package/dist/cli/autoresearch-guided.js.map +1 -1
  33. package/dist/cli/autoresearch.d.ts +3 -2
  34. package/dist/cli/autoresearch.d.ts.map +1 -1
  35. package/dist/cli/autoresearch.js +29 -305
  36. package/dist/cli/autoresearch.js.map +1 -1
  37. package/dist/cli/doctor.d.ts.map +1 -1
  38. package/dist/cli/doctor.js +43 -0
  39. package/dist/cli/doctor.js.map +1 -1
  40. package/dist/cli/index.d.ts +1 -1
  41. package/dist/cli/index.d.ts.map +1 -1
  42. package/dist/cli/index.js +8 -1
  43. package/dist/cli/index.js.map +1 -1
  44. package/dist/cli/question.d.ts +3 -0
  45. package/dist/cli/question.d.ts.map +1 -0
  46. package/dist/cli/question.js +182 -0
  47. package/dist/cli/question.js.map +1 -0
  48. package/dist/hooks/__tests__/analyze-routing-contract.test.js +22 -13
  49. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  50. package/dist/hooks/__tests__/anti-slop-workflow.test.js +3 -3
  51. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  52. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +2 -2
  53. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -1
  54. package/dist/hooks/__tests__/deep-interview-contract.test.js +22 -5
  55. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  56. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +2 -2
  57. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
  58. package/dist/hooks/__tests__/keyword-detector.test.js +308 -17
  59. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  60. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +570 -2
  61. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  62. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +717 -16
  63. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  64. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +25 -0
  65. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  66. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +894 -1
  67. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  68. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +34 -0
  69. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  70. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +132 -0
  71. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  72. package/dist/hooks/__tests__/prompt-guidance-contract.test.js +22 -4
  73. package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
  74. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +4 -2
  75. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  76. package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts +1 -0
  77. package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts.map +1 -1
  78. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js +4 -1
  79. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  80. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +28 -0
  81. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  82. package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js +5 -4
  83. package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js.map +1 -1
  84. package/dist/hooks/__tests__/prompt-team-routing.test.js +2 -2
  85. package/dist/hooks/__tests__/prompt-team-routing.test.js.map +1 -1
  86. package/dist/hooks/__tests__/triage-config.test.d.ts +2 -0
  87. package/dist/hooks/__tests__/triage-config.test.d.ts.map +1 -0
  88. package/dist/hooks/__tests__/triage-config.test.js +211 -0
  89. package/dist/hooks/__tests__/triage-config.test.js.map +1 -0
  90. package/dist/hooks/__tests__/triage-heuristic.test.d.ts +2 -0
  91. package/dist/hooks/__tests__/triage-heuristic.test.d.ts.map +1 -0
  92. package/dist/hooks/__tests__/triage-heuristic.test.js +230 -0
  93. package/dist/hooks/__tests__/triage-heuristic.test.js.map +1 -0
  94. package/dist/hooks/__tests__/triage-state.test.d.ts +2 -0
  95. package/dist/hooks/__tests__/triage-state.test.d.ts.map +1 -0
  96. package/dist/hooks/__tests__/triage-state.test.js +426 -0
  97. package/dist/hooks/__tests__/triage-state.test.js.map +1 -0
  98. package/dist/hooks/keyword-detector.d.ts +26 -7
  99. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  100. package/dist/hooks/keyword-detector.js +97 -26
  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 +16 -9
  104. package/dist/hooks/keyword-registry.js.map +1 -1
  105. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  106. package/dist/hooks/prompt-guidance-contract.js +28 -1
  107. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  108. package/dist/hooks/triage-config.d.ts +33 -0
  109. package/dist/hooks/triage-config.d.ts.map +1 -0
  110. package/dist/hooks/triage-config.js +87 -0
  111. package/dist/hooks/triage-config.js.map +1 -0
  112. package/dist/hooks/triage-heuristic.d.ts +20 -0
  113. package/dist/hooks/triage-heuristic.d.ts.map +1 -0
  114. package/dist/hooks/triage-heuristic.js +210 -0
  115. package/dist/hooks/triage-heuristic.js.map +1 -0
  116. package/dist/hooks/triage-state.d.ts +63 -0
  117. package/dist/hooks/triage-state.d.ts.map +1 -0
  118. package/dist/hooks/triage-state.js +138 -0
  119. package/dist/hooks/triage-state.js.map +1 -0
  120. package/dist/hud/__tests__/reconcile.test.js +20 -0
  121. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  122. package/dist/hud/reconcile.d.ts +1 -0
  123. package/dist/hud/reconcile.d.ts.map +1 -1
  124. package/dist/hud/reconcile.js +2 -1
  125. package/dist/hud/reconcile.js.map +1 -1
  126. package/dist/mcp/__tests__/state-server.test.js +1 -0
  127. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  128. package/dist/mcp/state-server.d.ts +8 -0
  129. package/dist/mcp/state-server.d.ts.map +1 -1
  130. package/dist/mcp/state-server.js +4 -0
  131. package/dist/mcp/state-server.js.map +1 -1
  132. package/dist/modes/__tests__/base-ralph-contract.test.js +15 -0
  133. package/dist/modes/__tests__/base-ralph-contract.test.js.map +1 -1
  134. package/dist/modes/base.d.ts +1 -0
  135. package/dist/modes/base.d.ts.map +1 -1
  136. package/dist/modes/base.js +22 -6
  137. package/dist/modes/base.js.map +1 -1
  138. package/dist/notifications/__tests__/index.test.js +78 -0
  139. package/dist/notifications/__tests__/index.test.js.map +1 -1
  140. package/dist/notifications/index.d.ts.map +1 -1
  141. package/dist/notifications/index.js +39 -22
  142. package/dist/notifications/index.js.map +1 -1
  143. package/dist/openclaw/index.d.ts +5 -3
  144. package/dist/openclaw/index.d.ts.map +1 -1
  145. package/dist/openclaw/index.js +5 -3
  146. package/dist/openclaw/index.js.map +1 -1
  147. package/dist/question/__tests__/client.test.d.ts +2 -0
  148. package/dist/question/__tests__/client.test.d.ts.map +1 -0
  149. package/dist/question/__tests__/client.test.js +70 -0
  150. package/dist/question/__tests__/client.test.js.map +1 -0
  151. package/dist/question/__tests__/deep-interview.test.d.ts +2 -0
  152. package/dist/question/__tests__/deep-interview.test.d.ts.map +1 -0
  153. package/dist/question/__tests__/deep-interview.test.js +108 -0
  154. package/dist/question/__tests__/deep-interview.test.js.map +1 -0
  155. package/dist/question/__tests__/policy.test.d.ts +2 -0
  156. package/dist/question/__tests__/policy.test.d.ts.map +1 -0
  157. package/dist/question/__tests__/policy.test.js +107 -0
  158. package/dist/question/__tests__/policy.test.js.map +1 -0
  159. package/dist/question/__tests__/renderer.test.d.ts +2 -0
  160. package/dist/question/__tests__/renderer.test.d.ts.map +1 -0
  161. package/dist/question/__tests__/renderer.test.js +88 -0
  162. package/dist/question/__tests__/renderer.test.js.map +1 -0
  163. package/dist/question/__tests__/state.test.d.ts +2 -0
  164. package/dist/question/__tests__/state.test.d.ts.map +1 -0
  165. package/dist/question/__tests__/state.test.js +55 -0
  166. package/dist/question/__tests__/state.test.js.map +1 -0
  167. package/dist/question/__tests__/types.test.d.ts +2 -0
  168. package/dist/question/__tests__/types.test.d.ts.map +1 -0
  169. package/dist/question/__tests__/types.test.js +44 -0
  170. package/dist/question/__tests__/types.test.js.map +1 -0
  171. package/dist/question/__tests__/ui.test.d.ts +2 -0
  172. package/dist/question/__tests__/ui.test.d.ts.map +1 -0
  173. package/dist/question/__tests__/ui.test.js +169 -0
  174. package/dist/question/__tests__/ui.test.js.map +1 -0
  175. package/dist/question/client.d.ts +54 -0
  176. package/dist/question/client.d.ts.map +1 -0
  177. package/dist/question/client.js +77 -0
  178. package/dist/question/client.js.map +1 -0
  179. package/dist/question/deep-interview.d.ts +27 -0
  180. package/dist/question/deep-interview.d.ts.map +1 -0
  181. package/dist/question/deep-interview.js +101 -0
  182. package/dist/question/deep-interview.js.map +1 -0
  183. package/dist/question/policy.d.ts +18 -0
  184. package/dist/question/policy.d.ts.map +1 -0
  185. package/dist/question/policy.js +77 -0
  186. package/dist/question/policy.js.map +1 -0
  187. package/dist/question/renderer.d.ts +18 -0
  188. package/dist/question/renderer.d.ts.map +1 -0
  189. package/dist/question/renderer.js +128 -0
  190. package/dist/question/renderer.js.map +1 -0
  191. package/dist/question/state.d.ts +19 -0
  192. package/dist/question/state.d.ts.map +1 -0
  193. package/dist/question/state.js +108 -0
  194. package/dist/question/state.js.map +1 -0
  195. package/dist/question/types.d.ts +66 -0
  196. package/dist/question/types.d.ts.map +1 -0
  197. package/dist/question/types.js +82 -0
  198. package/dist/question/types.js.map +1 -0
  199. package/dist/question/ui.d.ts +38 -0
  200. package/dist/question/ui.d.ts.map +1 -0
  201. package/dist/question/ui.js +321 -0
  202. package/dist/question/ui.js.map +1 -0
  203. package/dist/ralph/contract.d.ts +1 -1
  204. package/dist/ralph/contract.d.ts.map +1 -1
  205. package/dist/ralph/contract.js +4 -1
  206. package/dist/ralph/contract.js.map +1 -1
  207. package/dist/ralplan/runtime.js +1 -1
  208. package/dist/ralplan/runtime.js.map +1 -1
  209. package/dist/runtime/__tests__/run-loop.test.d.ts +2 -0
  210. package/dist/runtime/__tests__/run-loop.test.d.ts.map +1 -0
  211. package/dist/runtime/__tests__/run-loop.test.js +35 -0
  212. package/dist/runtime/__tests__/run-loop.test.js.map +1 -0
  213. package/dist/runtime/__tests__/run-outcome.test.d.ts +2 -0
  214. package/dist/runtime/__tests__/run-outcome.test.d.ts.map +1 -0
  215. package/dist/runtime/__tests__/run-outcome.test.js +64 -0
  216. package/dist/runtime/__tests__/run-outcome.test.js.map +1 -0
  217. package/dist/runtime/run-loop.d.ts +41 -0
  218. package/dist/runtime/run-loop.d.ts.map +1 -0
  219. package/dist/runtime/run-loop.js +46 -0
  220. package/dist/runtime/run-loop.js.map +1 -0
  221. package/dist/runtime/run-outcome.d.ts +28 -0
  222. package/dist/runtime/run-outcome.d.ts.map +1 -0
  223. package/dist/runtime/run-outcome.js +136 -0
  224. package/dist/runtime/run-outcome.js.map +1 -0
  225. package/dist/runtime/run-state.d.ts +36 -0
  226. package/dist/runtime/run-state.d.ts.map +1 -0
  227. package/dist/runtime/run-state.js +110 -0
  228. package/dist/runtime/run-state.js.map +1 -0
  229. package/dist/scripts/__tests__/codex-native-hook.test.js +1128 -85
  230. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  231. package/dist/scripts/codex-native-hook.d.ts +2 -0
  232. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  233. package/dist/scripts/codex-native-hook.js +199 -11
  234. package/dist/scripts/codex-native-hook.js.map +1 -1
  235. package/dist/scripts/notify-fallback-watcher.js +81 -2
  236. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  237. package/dist/scripts/notify-hook/auto-nudge.d.ts +27 -0
  238. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  239. package/dist/scripts/notify-hook/auto-nudge.js +83 -20
  240. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  241. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  242. package/dist/scripts/notify-hook/managed-tmux.js +64 -38
  243. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  244. package/dist/scripts/notify-hook/ralph-session-resume.js +1 -1
  245. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  246. package/dist/scripts/notify-hook.js +15 -5
  247. package/dist/scripts/notify-hook.js.map +1 -1
  248. package/dist/scripts/sync-prompt-guidance-fragments.js +5 -0
  249. package/dist/scripts/sync-prompt-guidance-fragments.js.map +1 -1
  250. package/dist/state/__tests__/operations-ralph-phase.test.js +21 -0
  251. package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
  252. package/dist/state/__tests__/workflow-transition.test.js +11 -0
  253. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  254. package/dist/state/operations.d.ts.map +1 -1
  255. package/dist/state/operations.js +15 -0
  256. package/dist/state/operations.js.map +1 -1
  257. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  258. package/dist/state/workflow-transition-reconcile.js +14 -1
  259. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  260. package/dist/state/workflow-transition.d.ts.map +1 -1
  261. package/dist/state/workflow-transition.js +3 -1
  262. package/dist/state/workflow-transition.js.map +1 -1
  263. package/dist/team/__tests__/followup-planner.test.js +15 -0
  264. package/dist/team/__tests__/followup-planner.test.js.map +1 -1
  265. package/dist/team/__tests__/role-router.test.js +41 -0
  266. package/dist/team/__tests__/role-router.test.js.map +1 -1
  267. package/dist/team/followup-planner.d.ts.map +1 -1
  268. package/dist/team/followup-planner.js +31 -9
  269. package/dist/team/followup-planner.js.map +1 -1
  270. package/dist/team/role-router.d.ts.map +1 -1
  271. package/dist/team/role-router.js +73 -0
  272. package/dist/team/role-router.js.map +1 -1
  273. package/package.json +3 -2
  274. package/prompts/dependency-expert.md +3 -0
  275. package/prompts/executor.md +5 -0
  276. package/prompts/explore.md +2 -0
  277. package/prompts/planner.md +5 -0
  278. package/prompts/product-analyst.md +8 -8
  279. package/prompts/researcher.md +78 -30
  280. package/prompts/verifier.md +4 -0
  281. package/skills/autoresearch/SKILL.md +68 -0
  282. package/skills/deep-interview/SKILL.md +10 -9
  283. package/skills/help/SKILL.md +3 -1
  284. package/skills/ralplan/SKILL.md +1 -0
  285. package/skills/team/SKILL.md +1 -0
  286. package/skills/ultrawork/SKILL.md +1 -0
  287. package/src/scripts/__tests__/codex-native-hook.test.ts +1495 -188
  288. package/src/scripts/codex-native-hook.ts +235 -19
  289. package/src/scripts/notify-fallback-watcher.ts +92 -2
  290. package/src/scripts/notify-hook/auto-nudge.ts +89 -20
  291. package/src/scripts/notify-hook/managed-tmux.ts +70 -31
  292. package/src/scripts/notify-hook/ralph-session-resume.ts +1 -1
  293. package/src/scripts/notify-hook.ts +23 -5
  294. package/src/scripts/sync-prompt-guidance-fragments.ts +4 -0
  295. package/templates/AGENTS.md +48 -37
  296. package/templates/catalog-manifest.json +7 -0
@@ -1,29 +1,17 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import { execFileSync, spawnSync } from 'node:child_process';
4
- import { existsSync, readdirSync, realpathSync } from 'node:fs';
5
- import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
4
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
5
+ import { existsSync, realpathSync } from 'node:fs';
6
6
  import { dirname, join } from 'node:path';
7
7
  import { tmpdir } from 'node:os';
8
8
  import { fileURLToPath } from 'node:url';
9
- import { normalizeAutoresearchCodexArgs, parseAutoresearchArgs } from '../autoresearch.js';
10
- function withMockedTty(fn) {
11
- const descriptor = Object.getOwnPropertyDescriptor(process.stdin, 'isTTY');
12
- Object.defineProperty(process.stdin, 'isTTY', { configurable: true, value: true });
13
- return fn().finally(() => {
14
- if (descriptor) {
15
- Object.defineProperty(process.stdin, 'isTTY', descriptor);
16
- }
17
- else {
18
- Object.defineProperty(process.stdin, 'isTTY', { configurable: true, value: false });
19
- }
20
- });
21
- }
9
+ import { AUTORESEARCH_DEPRECATION_MESSAGE, autoresearchCommand, normalizeAutoresearchCodexArgs, parseAutoresearchArgs, } from '../autoresearch.js';
22
10
  function runOmx(cwd, argv, envOverrides = {}) {
23
11
  const testDir = dirname(fileURLToPath(import.meta.url));
24
12
  const repoRoot = join(testDir, '..', '..', '..');
25
13
  const omxBin = join(repoRoot, 'dist', 'cli', 'omx.js');
26
- const r = spawnSync(process.execPath, [omxBin, ...argv], {
14
+ const result = spawnSync(process.execPath, [omxBin, ...argv], {
27
15
  cwd,
28
16
  encoding: 'utf-8',
29
17
  env: {
@@ -34,7 +22,7 @@ function runOmx(cwd, argv, envOverrides = {}) {
34
22
  ...envOverrides,
35
23
  },
36
24
  });
37
- return { status: r.status, stdout: r.stdout || '', stderr: r.stderr || '', error: r.error?.message };
25
+ return { status: result.status, stdout: result.stdout || '', stderr: result.stderr || '', error: result.error?.message };
38
26
  }
39
27
  async function initRepo() {
40
28
  const raw = await mkdtemp(join(tmpdir(), 'omx-autoresearch-test-'));
@@ -47,11 +35,6 @@ async function initRepo() {
47
35
  execFileSync('git', ['commit', '-m', 'init'], { cwd, stdio: 'ignore' });
48
36
  return cwd;
49
37
  }
50
- async function installFakePs(fakeBin) {
51
- const fakePsPath = join(fakeBin, 'ps');
52
- await writeFile(fakePsPath, '#!/bin/sh\nexit 0\n', 'utf-8');
53
- execFileSync('chmod', ['+x', fakePsPath], { stdio: 'ignore' });
54
- }
55
38
  describe('normalizeAutoresearchCodexArgs', () => {
56
39
  it('adds sandbox bypass by default for autoresearch workers', () => {
57
40
  assert.deepEqual(normalizeAutoresearchCodexArgs(['--model', 'gpt-5']), ['--model', 'gpt-5', '--dangerously-bypass-approvals-and-sandbox']);
@@ -63,57 +46,7 @@ describe('normalizeAutoresearchCodexArgs', () => {
63
46
  assert.deepEqual(normalizeAutoresearchCodexArgs(['--madmax']), ['--dangerously-bypass-approvals-and-sandbox']);
64
47
  });
65
48
  });
66
- describe('omx autoresearch', () => {
67
- it('documents autoresearch in top-level help', async () => {
68
- const cwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-help-'));
69
- try {
70
- const result = runOmx(cwd, ['--help']);
71
- assert.equal(result.status, 0, result.stderr || result.stdout);
72
- assert.match(result.stdout, /omx autoresearch\s+Launch thin-supervisor autoresearch with keep\/discard\/reset parity/i);
73
- }
74
- finally {
75
- await rm(cwd, { recursive: true, force: true });
76
- }
77
- });
78
- it('routes autoresearch --help to command-local help', async () => {
79
- const cwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-local-help-'));
80
- try {
81
- const result = runOmx(cwd, ['autoresearch', '--help']);
82
- assert.equal(result.status, 0, result.stderr || result.stdout);
83
- assert.match(result.stdout, /Usage:[\s\S]*omx autoresearch run <mission-dir>/i);
84
- assert.match(result.stdout, /omx autoresearch init/i);
85
- assert.match(result.stdout, /--topic\/\.\.\./i);
86
- assert.match(result.stdout, /deep-interview/i);
87
- assert.match(result.stdout, /human entrypoint/i);
88
- assert.doesNotMatch(result.stdout, /oh-my-codex \(omx\) - Multi-agent orchestration for Codex CLI/i);
89
- }
90
- finally {
91
- await rm(cwd, { recursive: true, force: true });
92
- }
93
- });
94
- it('documents --resume in command-local help', async () => {
95
- const cwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-resume-help-'));
96
- try {
97
- const result = runOmx(cwd, ['autoresearch', '--help']);
98
- assert.equal(result.status, 0, result.stderr || result.stdout);
99
- assert.match(result.stdout, /--resume <run-id>/i);
100
- assert.match(result.stdout, /run-tagged/i);
101
- }
102
- finally {
103
- await rm(cwd, { recursive: true, force: true });
104
- }
105
- });
106
- it('fails fast when mission dir is missing', async () => {
107
- const cwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-missing-arg-'));
108
- try {
109
- const result = runOmx(cwd, ['autoresearch']);
110
- assert.notEqual(result.status, 0, result.stderr || result.stdout);
111
- assert.match(`${result.stderr}\n${result.stdout}`, /mission-dir|Usage:\s*omx autoresearch <mission-dir>/i);
112
- }
113
- finally {
114
- await rm(cwd, { recursive: true, force: true });
115
- }
116
- });
49
+ describe('parseAutoresearchArgs', () => {
117
50
  it('treats top-level topic/evaluator flags as seeded deep-interview input', () => {
118
51
  const parsed = parseAutoresearchArgs(['--topic', 'Improve docs', '--evaluator', 'node eval.js', '--slug', 'docs-run']);
119
52
  assert.equal(parsed.guided, true);
@@ -129,7 +62,7 @@ describe('omx autoresearch', () => {
129
62
  assert.equal(flagged.guided, true);
130
63
  assert.deepEqual(flagged.initArgs, ['--topic', 'Ship feature']);
131
64
  });
132
- it('parses explicit run subcommand without breaking bare mission-dir execution', () => {
65
+ it('parses explicit run subcommand without breaking bare mission-dir parsing', () => {
133
66
  const runParsed = parseAutoresearchArgs(['run', 'missions/demo', '--model', 'gpt-5']);
134
67
  assert.equal(runParsed.runSubcommand, true);
135
68
  assert.equal(runParsed.missionDir, 'missions/demo');
@@ -139,606 +72,84 @@ describe('omx autoresearch', () => {
139
72
  assert.equal(bareParsed.missionDir, 'missions/demo');
140
73
  assert.deepEqual(bareParsed.codexArgs, ['--model', 'gpt-5']);
141
74
  });
142
- it('resolves guided deep-interview artifacts by seeded slug even when file mtimes predate launch timestamp', async () => {
143
- const repo = await initRepo();
144
- const fakeBin = await mkdtemp(join(tmpdir(), 'omx-autoresearch-deep-interview-mtime-bin-'));
145
- try {
146
- const fakeCodexPath = join(fakeBin, 'codex');
147
- await writeFile(fakeCodexPath, `#!/bin/sh
148
- if [ "$1" = "exec" ]; then
149
- candidate_file=$(find "$OMX_TEST_REPO_ROOT/.omx/logs/autoresearch" -name candidate.json | head -n 1)
150
- head_commit=$(git rev-parse HEAD)
151
- cat >"$candidate_file" <<'EOF'
152
- {
153
- "status": "abort",
154
- "candidate_commit": null,
155
- "base_commit": "HEAD_PLACEHOLDER",
156
- "description": "stop after guided handoff",
157
- "notes": ["fake codex exec"],
158
- "created_at": "2026-03-18T00:00:00.000Z"
159
- }
160
- EOF
161
- perl -0pi -e "s/HEAD_PLACEHOLDER/$head_commit/g" "$candidate_file"
162
- exit 0
163
- fi
164
- mkdir -p "$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch"
165
- cat >"$OMX_TEST_REPO_ROOT/.omx/specs/deep-interview-autoresearch-test-launch.md" <<'EOF'
166
- # Deep Interview Autoresearch Draft — test-launch
167
-
168
- ## Mission Draft
169
- Investigate flaky onboarding behavior
170
-
171
- ## Evaluator Draft
172
- node scripts/eval.js
173
-
174
- ## Keep Policy
175
- score_improvement
176
-
177
- ## Session Slug
178
- test-launch
179
-
180
- ## Seed Inputs
181
- - topic: (none)
182
- - evaluator: (none)
183
- - keep_policy: (none)
184
- - slug: (none)
185
-
186
- ## Launch Readiness
187
- Launch-ready: yes
188
- - Evaluator command is concrete and can be compiled into sandbox.md
189
-
190
- ## Confirmation Bridge
191
- - refine further
192
- - launch
193
- EOF
194
- cat >"$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch/mission.md" <<'EOF'
195
- # Mission
196
-
197
- Investigate flaky onboarding behavior
198
- EOF
199
- cat >"$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch/sandbox.md" <<'EOF'
200
- ---
201
- evaluator:
202
- command: node scripts/eval.js
203
- format: json
204
- keep_policy: score_improvement
205
- ---
206
- EOF
207
- cat >"$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch/result.json" <<'EOF'
208
- {
209
- "kind": "omx.autoresearch.deep-interview/v1",
210
- "compileTarget": {
211
- "topic": "Investigate flaky onboarding behavior",
212
- "evaluatorCommand": "node scripts/eval.js",
213
- "keepPolicy": "score_improvement",
214
- "slug": "test-launch",
215
- "repoRoot": "${repo}"
216
- },
217
- "draftArtifactPath": "${repo}/.omx/specs/deep-interview-autoresearch-test-launch.md",
218
- "missionArtifactPath": "${repo}/.omx/specs/autoresearch-test-launch/mission.md",
219
- "sandboxArtifactPath": "${repo}/.omx/specs/autoresearch-test-launch/sandbox.md",
220
- "launchReady": true,
221
- "blockedReasons": []
222
- }
223
- EOF
224
- touch -t 202603180000 "$OMX_TEST_REPO_ROOT/.omx/specs/deep-interview-autoresearch-test-launch.md"
225
- touch -t 202603180000 "$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch/mission.md"
226
- touch -t 202603180000 "$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch/sandbox.md"
227
- touch -t 202603180000 "$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch/result.json"
228
- `, 'utf-8');
229
- execFileSync('chmod', ['+x', fakeCodexPath], { stdio: 'ignore' });
230
- await installFakePs(fakeBin);
231
- const result = runOmx(repo, ['autoresearch', '--topic', 'Investigate flaky onboarding behavior', '--evaluator', 'node scripts/eval.js', '--slug', 'test-launch'], {
232
- PATH: `${fakeBin}:${process.env.PATH || ''}`,
233
- OMX_TEST_REPO_ROOT: repo,
234
- });
235
- assert.equal(result.status, 0, result.stderr || result.stdout);
236
- const missionContent = await readFile(join(repo, 'missions', 'test-launch', 'mission.md'), 'utf-8');
237
- const sandboxContent = await readFile(join(repo, 'missions', 'test-launch', 'sandbox.md'), 'utf-8');
238
- assert.match(missionContent, /Investigate flaky onboarding behavior/);
239
- assert.match(sandboxContent, /command: node scripts\/eval\.js/);
240
- }
241
- finally {
242
- await rm(repo, { recursive: true, force: true });
243
- await rm(fakeBin, { recursive: true, force: true });
244
- }
245
- });
246
- it('launches interactive deep-interview intake, materializes mission files, and then prefers split-pane handoff', async () => {
247
- const repo = await initRepo();
248
- const fakeBin = await mkdtemp(join(tmpdir(), 'omx-autoresearch-deep-interview-bin-'));
75
+ });
76
+ describe('omx autoresearch hard deprecation', () => {
77
+ it('documents autoresearch as deprecated in top-level help', async () => {
78
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-help-'));
249
79
  try {
250
- const codexLog = join(repo, 'codex-launch.log');
251
- const tmuxLog = join(repo, 'guided-tmux.log');
252
- const fakeCodexPath = join(fakeBin, 'codex');
253
- await writeFile(fakeCodexPath, `#!/bin/sh
254
- printf '%s\n' "$*" >>"${codexLog}"
255
- if [ "$1" = "exec" ]; then
256
- candidate_file=$(find "$OMX_TEST_REPO_ROOT/.omx/logs/autoresearch" -name candidate.json | head -n 1)
257
- head_commit=$(git rev-parse HEAD)
258
- cat >"$candidate_file" <<'EOF'
259
- {
260
- "status": "abort",
261
- "candidate_commit": null,
262
- "base_commit": "HEAD_PLACEHOLDER",
263
- "description": "stop after guided handoff",
264
- "notes": ["fake codex exec"],
265
- "created_at": "2026-03-18T00:00:00.000Z"
266
- }
267
- EOF
268
- perl -0pi -e "s/HEAD_PLACEHOLDER/$head_commit/g" "$candidate_file"
269
- exit 0
270
- fi
271
- mkdir -p "$OMX_TEST_REPO_ROOT/.omx/specs/deep-int"
272
- mkdir -p "$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch"
273
- cat >"$OMX_TEST_REPO_ROOT/.omx/specs/deep-interview-autoresearch-test-launch.md" <<'EOF'
274
- # Deep Interview Autoresearch Draft — test-launch
275
-
276
- ## Mission Draft
277
- Investigate flaky onboarding behavior
278
-
279
- ## Evaluator Draft
280
- node scripts/eval.js
281
-
282
- ## Keep Policy
283
- score_improvement
284
-
285
- ## Session Slug
286
- test-launch
287
-
288
- ## Seed Inputs
289
- - topic: (none)
290
- - evaluator: (none)
291
- - keep_policy: (none)
292
- - slug: (none)
293
-
294
- ## Launch Readiness
295
- Launch-ready: yes
296
- - Evaluator command is concrete and can be compiled into sandbox.md
297
-
298
- ## Confirmation Bridge
299
- - refine further
300
- - launch
301
- EOF
302
- cat >"$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch/mission.md" <<'EOF'
303
- # Mission
304
-
305
- Investigate flaky onboarding behavior
306
- EOF
307
- cat >"$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch/sandbox.md" <<'EOF'
308
- ---
309
- evaluator:
310
- command: node scripts/eval.js
311
- format: json
312
- keep_policy: score_improvement
313
- ---
314
- EOF
315
- cat >"$OMX_TEST_REPO_ROOT/.omx/specs/autoresearch-test-launch/result.json" <<'EOF'
316
- {
317
- "kind": "omx.autoresearch.deep-interview/v1",
318
- "compileTarget": {
319
- "topic": "Investigate flaky onboarding behavior",
320
- "evaluatorCommand": "node scripts/eval.js",
321
- "keepPolicy": "score_improvement",
322
- "slug": "test-launch",
323
- "repoRoot": "${repo}"
324
- },
325
- "draftArtifactPath": "${repo}/.omx/specs/deep-interview-autoresearch-test-launch.md",
326
- "missionArtifactPath": "${repo}/.omx/specs/autoresearch-test-launch/mission.md",
327
- "sandboxArtifactPath": "${repo}/.omx/specs/autoresearch-test-launch/sandbox.md",
328
- "launchReady": true,
329
- "blockedReasons": []
330
- }
331
- EOF
332
- `, 'utf-8');
333
- execFileSync('chmod', ['+x', fakeCodexPath], { stdio: 'ignore' });
334
- await installFakePs(fakeBin);
335
- const fakeTmuxPath = join(fakeBin, 'tmux');
336
- await writeFile(fakeTmuxPath, `#!/bin/sh
337
- printf '%s\n' "$*" >>"${tmuxLog}"
338
- case "$1" in
339
- -V)
340
- printf 'tmux 3.4\n'
341
- exit 0
342
- ;;
343
- display-message)
344
- case "$*" in
345
- *"#{pane_id}"*) printf '%%42\n' ;;
346
- *"#{pane_current_path}"*) printf '%s\n' "$OMX_TEST_REPO_ROOT" ;;
347
- *"#S"*) printf 'devsession\n' ;;
348
- *) printf 'devsession\n' ;;
349
- esac
350
- exit 0
351
- ;;
352
- list-panes)
353
- exit 0
354
- ;;
355
- split-window)
356
- last=""
357
- for arg in "$@"; do
358
- last="$arg"
359
- done
360
- printf '%%2\n'
361
- if printf '%s' "$last" | grep -q 'autoresearch '; then
362
- /bin/sh -lc "$last"
363
- fi
364
- exit 0
365
- ;;
366
- attach-session|set-option|set-hook|kill-session|kill-pane)
367
- exit 0
368
- ;;
369
- *)
370
- exit 0
371
- ;;
372
- esac
373
- `, 'utf-8');
374
- execFileSync('chmod', ['+x', fakeTmuxPath], { stdio: 'ignore' });
375
- const result = runOmx(repo, ['autoresearch', '--topic', 'Investigate flaky onboarding behavior', '--evaluator', 'node scripts/eval.js', '--slug', 'test-launch'], {
376
- PATH: `${fakeBin}:${process.env.PATH || ''}`,
377
- OMX_TEST_REPO_ROOT: repo,
378
- TMUX: '/tmp/fake-tmux,12345,0',
379
- TMUX_PANE: '%42',
380
- });
80
+ const result = runOmx(cwd, ['--help']);
381
81
  assert.equal(result.status, 0, result.stderr || result.stdout);
382
- const codexArgs = await readFile(codexLog, 'utf-8');
383
- const tmuxOutput = await readFile(tmuxLog, 'utf-8');
384
- assert.match(codexArgs, /\$deep-interview --autoresearch/);
385
- assert.match(tmuxOutput, /split-window -h -t %42 -d -P -F #\{pane_id\} -c/);
386
- const missionContent = await readFile(join(repo, 'missions', 'test-launch', 'mission.md'), 'utf-8');
387
- const sandboxContent = await readFile(join(repo, 'missions', 'test-launch', 'sandbox.md'), 'utf-8');
388
- assert.match(missionContent, /Investigate flaky onboarding behavior/);
389
- assert.match(sandboxContent, /command: node scripts\/eval\.js/);
82
+ assert.match(result.stdout, /omx autoresearch\s+\[DEPRECATED\] Use \$autoresearch; direct CLI launch removed/i);
390
83
  }
391
84
  finally {
392
- await rm(repo, { recursive: true, force: true });
393
- await rm(fakeBin, { recursive: true, force: true });
85
+ await rm(cwd, { recursive: true, force: true });
394
86
  }
395
87
  });
396
- it('uses split-window launch for explicit run inside tmux while preserving the interview pane', async () => {
397
- const repo = await initRepo();
398
- const fakeBin = await mkdtemp(join(tmpdir(), 'omx-autoresearch-run-split-bin-'));
88
+ it('routes autoresearch --help to local deprecation help', async () => {
89
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-local-help-'));
399
90
  try {
400
- const missionDir = join(repo, 'missions', 'demo');
401
- const tmuxLog = join(repo, 'tmux.log');
402
- await mkdir(missionDir, { recursive: true });
403
- await mkdir(join(repo, 'scripts'), { recursive: true });
404
- await writeFile(join(missionDir, 'mission.md'), '# Mission\nSplit pane launch.\n', 'utf-8');
405
- await writeFile(join(missionDir, 'sandbox.md'), '---\nevaluator:\n command: node scripts/eval.js\n format: json\n keep_policy: pass_only\n---\nStay inside the mission boundary.\n', 'utf-8');
406
- await writeFile(join(repo, 'scripts', 'eval.js'), "process.stdout.write(JSON.stringify({ pass: true }));\n", 'utf-8');
407
- execFileSync('git', ['add', '.'], { cwd: repo, stdio: 'ignore' });
408
- execFileSync('git', ['commit', '-m', 'add autoresearch mission'], { cwd: repo, stdio: 'ignore' });
409
- const fakeCodexPath = join(fakeBin, 'codex');
410
- await writeFile(fakeCodexPath, `#!/bin/sh
411
- candidate_file=$(find "$OMX_TEST_REPO_ROOT/.omx/logs/autoresearch" -name candidate.json | head -n 1)
412
- head_commit=$(git rev-parse HEAD)
413
- cat >"$candidate_file" <<'EOF'
414
- {
415
- "status": "abort",
416
- "candidate_commit": null,
417
- "base_commit": "HEAD_PLACEHOLDER",
418
- "description": "stop after split launch",
419
- "notes": ["fake codex exec"],
420
- "created_at": "2026-03-18T00:00:00.000Z"
421
- }
422
- EOF
423
- perl -0pi -e "s/HEAD_PLACEHOLDER/$head_commit/g" "$candidate_file"
424
- `, 'utf-8');
425
- execFileSync('chmod', ['+x', fakeCodexPath], { stdio: 'ignore' });
426
- await installFakePs(fakeBin);
427
- const fakeTmuxPath = join(fakeBin, 'tmux');
428
- await writeFile(fakeTmuxPath, `#!/bin/sh
429
- printf '%s\n' "$*" >>"${tmuxLog}"
430
- case "$1" in
431
- -V)
432
- printf 'tmux 3.4\n'
433
- exit 0
434
- ;;
435
- display-message)
436
- case "$*" in
437
- *"#{pane_id}"*) printf '%%9\n' ;;
438
- *"#{pane_current_path}"*) printf '${repo}\n' ;;
439
- *"#S"*) printf 'devsess\n' ;;
440
- *) printf '0\n' ;;
441
- esac
442
- exit 0
443
- ;;
444
- list-panes)
445
- printf '%%9\tzsh\tomx autoresearch\n'
446
- exit 0
447
- ;;
448
- split-window)
449
- last=""
450
- for arg in "$@"; do
451
- last="$arg"
452
- done
453
- if printf '%s' "$last" | grep -q 'hud --watch'; then
454
- printf '%%3\n'
455
- exit 0
456
- fi
457
- printf '%%2\n'
458
- /bin/sh -lc "$last"
459
- exit 0
460
- ;;
461
- set-option|select-pane)
462
- exit 0
463
- ;;
464
- *)
465
- exit 0
466
- ;;
467
- esac
468
- `, 'utf-8');
469
- execFileSync('chmod', ['+x', fakeTmuxPath], { stdio: 'ignore' });
470
- const result = runOmx(repo, ['autoresearch', 'run', missionDir, '--model', 'gpt-5'], {
471
- PATH: `${fakeBin}:${process.env.PATH || ''}`,
472
- OMX_TEST_REPO_ROOT: repo,
473
- TMUX: '/tmp/fake-tmux,12345,0',
474
- TMUX_PANE: '%9',
475
- });
91
+ const result = runOmx(cwd, ['autoresearch', '--help']);
476
92
  assert.equal(result.status, 0, result.stderr || result.stdout);
477
- const tmuxOutput = await readFile(tmuxLog, 'utf-8');
478
- assert.match(tmuxOutput, /split-window -h -t %9 -d -P -F #\{pane_id\} -c/);
479
- assert.match(tmuxOutput, /'autoresearch' '\/tmp\/[^']+\/missions\/demo' '--model' 'gpt-5'/);
480
- assert.doesNotMatch(tmuxOutput, /kill-pane -t %9/);
93
+ assert.match(result.stdout, /hard-deprecated legacy command surface/i);
94
+ assert.match(result.stdout, /\$deep-interview --autoresearch/i);
95
+ assert.match(result.stdout, /\$autoresearch/i);
96
+ assert.match(result.stdout, /prompt-architect-artifact/i);
97
+ assert.doesNotMatch(result.stdout, /oh-my-codex \(omx\) - Multi-agent orchestration for Codex CLI/i);
481
98
  }
482
99
  finally {
483
- await rm(repo, { recursive: true, force: true });
484
- await rm(fakeBin, { recursive: true, force: true });
100
+ await rm(cwd, { recursive: true, force: true });
485
101
  }
486
102
  });
487
- it('falls back to foreground execution when tmux split-window fails', async () => {
488
- const repo = await initRepo();
489
- const fakeBin = await mkdtemp(join(tmpdir(), 'omx-autoresearch-run-fallback-bin-'));
103
+ for (const argv of [
104
+ ['autoresearch'],
105
+ ['autoresearch', 'init'],
106
+ ['autoresearch', 'run', 'missions/demo'],
107
+ ['autoresearch', 'missions/demo'],
108
+ ['autoresearch', '--resume', 'run-123'],
109
+ ['autoresearch', '--topic', 'Flaky onboarding'],
110
+ ]) {
111
+ it(`fails legacy invocation: omx ${argv.join(' ')}`, async () => {
112
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-fail-'));
113
+ try {
114
+ const result = runOmx(cwd, argv);
115
+ assert.notEqual(result.status, 0);
116
+ const output = `${result.stdout}\n${result.stderr}`;
117
+ assert.match(output, /hard-deprecated/i);
118
+ assert.match(output, /\$autoresearch/i);
119
+ assert.match(output, /Direct CLI launch, resume, run, bare mission-dir aliases, and tmux split-pane launch are no longer supported/i);
120
+ }
121
+ finally {
122
+ await rm(cwd, { recursive: true, force: true });
123
+ }
124
+ });
125
+ }
126
+ it('never invokes codex or tmux on the deprecated path', async () => {
127
+ const cwd = await initRepo();
128
+ const fakeBin = await mkdtemp(join(tmpdir(), 'omx-autoresearch-noexec-bin-'));
129
+ const codexLog = join(cwd, 'codex.log');
130
+ const tmuxLog = join(cwd, 'tmux.log');
490
131
  try {
491
- const missionDir = join(repo, 'missions', 'demo');
492
- await mkdir(missionDir, { recursive: true });
493
- await mkdir(join(repo, 'scripts'), { recursive: true });
494
- await writeFile(join(missionDir, 'mission.md'), '# Mission\nFallback launch.\n', 'utf-8');
495
- await writeFile(join(missionDir, 'sandbox.md'), '---\nevaluator:\n command: node scripts/eval.js\n format: json\n keep_policy: pass_only\n---\nStay inside the mission boundary.\n', 'utf-8');
496
- await writeFile(join(repo, 'scripts', 'eval.js'), "process.stdout.write(JSON.stringify({ pass: true }));\n", 'utf-8');
497
- execFileSync('git', ['add', '.'], { cwd: repo, stdio: 'ignore' });
498
- execFileSync('git', ['commit', '-m', 'add autoresearch mission'], { cwd: repo, stdio: 'ignore' });
499
- const fakeCodexPath = join(fakeBin, 'codex');
500
- await writeFile(fakeCodexPath, `#!/bin/sh
501
- candidate_file=$(find "$OMX_TEST_REPO_ROOT/.omx/logs/autoresearch" -name candidate.json | head -n 1)
502
- head_commit=$(git rev-parse HEAD)
503
- cat >"$candidate_file" <<'EOF'
504
- {
505
- "status": "abort",
506
- "candidate_commit": null,
507
- "base_commit": "HEAD_PLACEHOLDER",
508
- "description": "stop after foreground fallback",
509
- "notes": ["fake codex exec"],
510
- "created_at": "2026-03-18T00:00:00.000Z"
511
- }
512
- EOF
513
- perl -0pi -e "s/HEAD_PLACEHOLDER/$head_commit/g" "$candidate_file"
514
- `, 'utf-8');
515
- execFileSync('chmod', ['+x', fakeCodexPath], { stdio: 'ignore' });
516
- await installFakePs(fakeBin);
517
- const fakeTmuxPath = join(fakeBin, 'tmux');
518
- await writeFile(fakeTmuxPath, `#!/bin/sh
519
- case "$1" in
520
- -V)
521
- printf 'tmux 3.4\n'
522
- exit 0
523
- ;;
524
- display-message)
525
- case "$*" in
526
- *"#{pane_id}"*) printf '%%9\n' ;;
527
- *"#{pane_current_path}"*) printf '${repo}\n' ;;
528
- *"#S"*) printf 'devsess\n' ;;
529
- *) printf '0\n' ;;
530
- esac
531
- exit 0
532
- ;;
533
- list-panes)
534
- printf '%%9\tzsh\tomx autoresearch\n'
535
- exit 0
536
- ;;
537
- split-window)
538
- exit 1
539
- ;;
540
- set-option|select-pane)
541
- exit 0
542
- ;;
543
- *)
544
- exit 0
545
- ;;
546
- esac
547
- `, 'utf-8');
548
- execFileSync('chmod', ['+x', fakeTmuxPath], { stdio: 'ignore' });
549
- const result = runOmx(repo, ['autoresearch', 'run', missionDir], {
132
+ await writeFile(join(fakeBin, 'codex'), `#!/bin/sh\necho codex >> ${JSON.stringify(codexLog)}\nexit 99\n`, 'utf-8');
133
+ await writeFile(join(fakeBin, 'tmux'), `#!/bin/sh\necho tmux >> ${JSON.stringify(tmuxLog)}\nexit 99\n`, 'utf-8');
134
+ execFileSync('chmod', ['+x', join(fakeBin, 'codex')], { stdio: 'ignore' });
135
+ execFileSync('chmod', ['+x', join(fakeBin, 'tmux')], { stdio: 'ignore' });
136
+ const result = runOmx(cwd, ['autoresearch', 'run', 'missions/demo'], {
550
137
  PATH: `${fakeBin}:${process.env.PATH || ''}`,
551
- OMX_TEST_REPO_ROOT: repo,
552
- TMUX: '/tmp/fake-tmux,12345,0',
553
- TMUX_PANE: '%9',
554
138
  });
555
- assert.equal(result.status, 0, result.stderr || result.stdout);
556
- const logsRoot = join(repo, '.omx', 'logs', 'autoresearch');
557
- const [runId] = readdirSync(logsRoot, { withFileTypes: true })
558
- .filter(d => d.isDirectory())
559
- .map(d => d.name);
560
- assert.ok(runId);
561
- }
562
- finally {
563
- await rm(repo, { recursive: true, force: true });
564
- await rm(fakeBin, { recursive: true, force: true });
565
- }
566
- });
567
- it('rejects mission directories outside a git repo', async () => {
568
- const cwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-outside-git-'));
569
- try {
570
- await writeFile(join(cwd, 'mission.md'), '# Mission\n', 'utf-8');
571
- await writeFile(join(cwd, 'sandbox.md'), '---\nevaluator:\n command: node eval.js\n---\n', 'utf-8');
572
- const result = runOmx(cwd, ['autoresearch', cwd]);
573
- assert.notEqual(result.status, 0, result.stderr || result.stdout);
574
- assert.match(`${result.stderr}\n${result.stdout}`, /git repo|git repository|inside a git repo/i);
139
+ assert.notEqual(result.status, 0);
140
+ assert.equal(existsSync(codexLog), false);
141
+ assert.equal(existsSync(tmuxLog), false);
575
142
  }
576
143
  finally {
577
144
  await rm(cwd, { recursive: true, force: true });
578
- }
579
- });
580
- it('rejects missing mission.md inside an in-repo mission dir', async () => {
581
- const repo = await initRepo();
582
- try {
583
- const missionDir = join(repo, 'missions', 'demo');
584
- await mkdir(missionDir, { recursive: true });
585
- await writeFile(join(missionDir, 'sandbox.md'), '---\nevaluator:\n command: node eval.js\n---\n', 'utf-8');
586
- const result = runOmx(repo, ['autoresearch', missionDir]);
587
- assert.notEqual(result.status, 0, result.stderr || result.stdout);
588
- assert.match(`${result.stderr}\n${result.stdout}`, /mission\.md/i);
589
- }
590
- finally {
591
- await rm(repo, { recursive: true, force: true });
592
- }
593
- });
594
- it('rejects missing sandbox.md inside an in-repo mission dir', async () => {
595
- const repo = await initRepo();
596
- try {
597
- const missionDir = join(repo, 'missions', 'demo');
598
- await mkdir(missionDir, { recursive: true });
599
- await writeFile(join(missionDir, 'mission.md'), '# Mission\n', 'utf-8');
600
- const result = runOmx(repo, ['autoresearch', missionDir]);
601
- assert.notEqual(result.status, 0, result.stderr || result.stdout);
602
- assert.match(`${result.stderr}\n${result.stdout}`, /sandbox\.md/i);
603
- }
604
- finally {
605
- await rm(repo, { recursive: true, force: true });
606
- }
607
- });
608
- it('rejects sandbox.md without evaluator frontmatter', async () => {
609
- const repo = await initRepo();
610
- try {
611
- const missionDir = join(repo, 'missions', 'demo');
612
- await mkdir(missionDir, { recursive: true });
613
- await writeFile(join(missionDir, 'mission.md'), '# Mission\n', 'utf-8');
614
- await writeFile(join(missionDir, 'sandbox.md'), 'No frontmatter here.\n', 'utf-8');
615
- const result = runOmx(repo, ['autoresearch', missionDir]);
616
- assert.notEqual(result.status, 0, result.stderr || result.stdout);
617
- assert.match(`${result.stderr}\n${result.stdout}`, /frontmatter|evaluator/i);
618
- }
619
- finally {
620
- await rm(repo, { recursive: true, force: true });
621
- }
622
- });
623
- it('rejects autoresearch launch when root ralph mode is already active', async () => {
624
- const repo = await initRepo();
625
- try {
626
- const missionDir = join(repo, 'missions', 'demo');
627
- await mkdir(missionDir, { recursive: true });
628
- await writeFile(join(missionDir, 'mission.md'), '# Mission\n', 'utf-8');
629
- await writeFile(join(missionDir, 'sandbox.md'), '---\nevaluator:\n command: node eval.js\n format: json\n---\nStay inside the mission boundary.\n', 'utf-8');
630
- await mkdir(join(repo, '.omx', 'state'), { recursive: true });
631
- await writeFile(join(repo, '.omx', 'state', 'ralph-state.json'), JSON.stringify({
632
- active: true,
633
- mode: 'ralph',
634
- iteration: 0,
635
- max_iterations: 50,
636
- current_phase: 'executing',
637
- task_description: 'existing root ralph lane',
638
- started_at: '2026-03-14T00:00:00.000Z',
639
- }, null, 2), 'utf-8');
640
- const result = runOmx(repo, ['autoresearch', missionDir]);
641
- assert.notEqual(result.status, 0, result.stderr || result.stdout);
642
- assert.match(`${result.stderr}\n${result.stdout}`, /Cannot start autoresearch: ralph is already active/i);
643
- const worktreesRoot = join(repo, '.omx', 'worktrees');
644
- assert.equal(existsSync(worktreesRoot), false, 'expected launch to abort before creating autoresearch worktree');
645
- }
646
- finally {
647
- await rm(repo, { recursive: true, force: true });
648
- }
649
- });
650
- it('launches codex exec for autoresearch turns without shelling out to cat', async () => {
651
- const repo = await initRepo();
652
- const fakeBin = await mkdtemp(join(tmpdir(), 'omx-autoresearch-fake-bin-'));
653
- try {
654
- const missionDir = join(repo, 'missions', 'demo');
655
- await mkdir(missionDir, { recursive: true });
656
- await mkdir(join(repo, 'scripts'), { recursive: true });
657
- await writeFile(join(missionDir, 'mission.md'), '# Mission\nWrite a noop candidate artifact.\n', 'utf-8');
658
- await writeFile(join(missionDir, 'sandbox.md'), '---\nevaluator:\n command: node scripts/eval.js\n format: json\n keep_policy: pass_only\n---\nStay inside the mission boundary.\n', 'utf-8');
659
- await writeFile(join(repo, 'scripts', 'eval.js'), "process.stdout.write(JSON.stringify({ pass: true }));\n", 'utf-8');
660
- execFileSync('git', ['add', '.'], { cwd: repo, stdio: 'ignore' });
661
- execFileSync('git', ['commit', '-m', 'add autoresearch mission'], { cwd: repo, stdio: 'ignore' });
662
- const fakeCatPath = join(fakeBin, 'cat');
663
- await writeFile(fakeCatPath, `#!/bin/sh
664
- printf 'unexpected cat invocation\\n' >&2
665
- exit 97
666
- `, 'utf-8');
667
- execFileSync('chmod', ['+x', fakeCatPath], { stdio: 'ignore' });
668
- const fakeCodexPath = join(fakeBin, 'codex');
669
- await writeFile(fakeCodexPath, `#!/bin/sh
670
- printf 'fake-codex:%s\\n' "$*" >&2
671
- while IFS= read -r _; do
672
- :
673
- done
674
- candidate_file=$(find "$OMX_TEST_REPO_ROOT/.omx/logs/autoresearch" -name candidate.json | head -n 1)
675
- head_commit=$(git rev-parse HEAD)
676
- printf '{\\n "status": "abort",\\n "candidate_commit": null,\\n "base_commit": "%s",\\n "description": "stop after first exec",\\n "notes": ["fake codex exec"],\\n "created_at": "2026-03-15T00:00:00.000Z"\\n}\\n' "$head_commit" >"$candidate_file"
677
- `, 'utf-8');
678
- execFileSync('chmod', ['+x', fakeCodexPath], { stdio: 'ignore' });
679
- await installFakePs(fakeBin);
680
- const result = runOmx(repo, ['autoresearch', missionDir, '--dangerously-bypass-approvals-and-sandbox'], { PATH: `${fakeBin}:${process.env.PATH || ''}`, OMX_TEST_REPO_ROOT: repo });
681
- assert.equal(result.status, 0, result.stderr || result.stdout);
682
- assert.match(result.stderr, /fake-codex:exec --dangerously-bypass-approvals-and-sandbox -/);
683
- }
684
- finally {
685
- await rm(repo, { recursive: true, force: true });
686
145
  await rm(fakeBin, { recursive: true, force: true });
687
146
  }
688
147
  });
689
- it('stops after repeated noop turns', async () => {
690
- const repo = await initRepo();
691
- const fakeBin = await mkdtemp(join(tmpdir(), 'omx-autoresearch-noop-bin-'));
692
- try {
693
- const missionDir = join(repo, 'missions', 'demo');
694
- await mkdir(missionDir, { recursive: true });
695
- await mkdir(join(repo, 'scripts'), { recursive: true });
696
- await writeFile(join(missionDir, 'mission.md'), '# Mission\nKeep returning noop.\n', 'utf-8');
697
- await writeFile(join(missionDir, 'sandbox.md'), '---\nevaluator:\n command: node scripts/eval.js\n format: json\n keep_policy: pass_only\n---\nStay inside the mission boundary.\n', 'utf-8');
698
- await writeFile(join(repo, 'scripts', 'eval.js'), "process.stdout.write(JSON.stringify({ pass: true }));\n", 'utf-8');
699
- execFileSync('git', ['add', '.'], { cwd: repo, stdio: 'ignore' });
700
- execFileSync('git', ['commit', '-m', 'add autoresearch noop mission'], { cwd: repo, stdio: 'ignore' });
701
- const fakeCodexPath = join(fakeBin, 'codex');
702
- await writeFile(fakeCodexPath, `#!/bin/sh
703
- cat >/dev/null
704
- candidate_file=$(find "$OMX_TEST_REPO_ROOT/.omx/logs/autoresearch" -name candidate.json | head -n 1)
705
- head_commit=$(git rev-parse HEAD)
706
- cat >"$candidate_file" <<EOF
707
- {
708
- "status": "noop",
709
- "candidate_commit": null,
710
- "base_commit": "$head_commit",
711
- "description": "noop from fake codex exec",
712
- "notes": ["fake noop"],
713
- "created_at": "2026-03-15T00:00:00.000Z"
714
- }
715
- EOF
716
- `, 'utf-8');
717
- execFileSync('chmod', ['+x', fakeCodexPath], { stdio: 'ignore' });
718
- await installFakePs(fakeBin);
719
- const result = runOmx(repo, ['autoresearch', missionDir, '--dangerously-bypass-approvals-and-sandbox'], { PATH: `${fakeBin}:${process.env.PATH || ''}`, OMX_TEST_REPO_ROOT: repo });
720
- assert.equal(result.status, 0, result.stderr || result.stdout);
721
- const state = JSON.parse(await readFile(join(repo, '.omx', 'state', 'autoresearch-state.json'), 'utf-8'));
722
- assert.equal(state.active, false);
723
- const logsRoot = join(repo, '.omx', 'logs', 'autoresearch');
724
- const [runId] = readdirSync(logsRoot, { withFileTypes: true })
725
- .filter(d => d.isDirectory())
726
- .map(d => d.name);
727
- assert.ok(runId);
728
- const manifest = JSON.parse(await readFile(join(logsRoot, runId, 'manifest.json'), 'utf-8'));
729
- assert.equal(manifest.status, 'stopped');
730
- assert.equal(manifest.stop_reason, 'repeated noop limit reached (3)');
731
- assert.match(manifest.completed_at || '', /^\d{4}-\d{2}-\d{2}T/);
732
- const ledger = JSON.parse(await readFile(join(logsRoot, runId, 'iteration-ledger.json'), 'utf-8'));
733
- assert.deepEqual(ledger.entries.map((entry) => entry.decision), ['baseline', 'noop', 'noop', 'noop']);
734
- const resumeResult = runOmx(repo, ['autoresearch', '--resume', runId]);
735
- assert.notEqual(resumeResult.status, 0, resumeResult.stderr || resumeResult.stdout);
736
- assert.match(`${resumeResult.stderr}\n${resumeResult.stdout}`, /autoresearch_resume_terminal_run/i);
737
- }
738
- finally {
739
- await rm(repo, { recursive: true, force: true });
740
- await rm(fakeBin, { recursive: true, force: true });
741
- }
148
+ it('throws the same deprecation guidance from the command entrypoint', async () => {
149
+ await assert.rejects(async () => autoresearchCommand(['run', 'missions/demo']), (error) => {
150
+ assert.match(String(error), new RegExp(AUTORESEARCH_DEPRECATION_MESSAGE.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
151
+ return true;
152
+ });
742
153
  });
743
154
  });
744
155
  //# sourceMappingURL=autoresearch.test.js.map