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,33 +1,29 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { execFileSync } from 'node:child_process';
4
- import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
5
- import { join } from 'node:path';
6
- import { tmpdir } from 'node:os';
7
- import { parseSandboxContract } from '../../autoresearch/contracts.js';
8
- import { isLaunchReadyEvaluatorCommand, resolveAutoresearchDeepInterviewResult, writeAutoresearchDeepInterviewArtifacts, writeAutoresearchDraftArtifact, } from '../autoresearch-intake.js';
9
- import { buildAutoresearchDeepInterviewPrompt, initAutoresearchMission, parseInitArgs, checkTmuxAvailable, runAutoresearchNoviceBridge, spawnAutoresearchTmux, } from '../autoresearch-guided.js';
10
- import { OMX_ENTRY_PATH_ENV, OMX_STARTUP_CWD_ENV } from '../../utils/paths.js';
11
- async function initRepo() {
12
- const cwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-guided-test-'));
13
- execFileSync('git', ['init'], { cwd, stdio: 'ignore' });
14
- execFileSync('git', ['config', 'user.email', 'test@example.com'], { cwd, stdio: 'ignore' });
15
- execFileSync('git', ['config', 'user.name', 'Test User'], { cwd, stdio: 'ignore' });
16
- const { writeFile } = await import('node:fs/promises');
17
- await writeFile(join(cwd, 'README.md'), 'hello\n', 'utf-8');
18
- execFileSync('git', ['add', 'README.md'], { cwd, stdio: 'ignore' });
19
- execFileSync('git', ['commit', '-m', 'init'], { cwd, stdio: 'ignore' });
20
- return cwd;
1
+ import assert from "node:assert/strict";
2
+ import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { describe, it } from "node:test";
6
+ import { OmxQuestionError } from "../../question/client.js";
7
+ import { buildAutoresearchDeepInterviewPrompt, parseInitArgs, runAutoresearchNoviceBridge, } from "../autoresearch-guided.js";
8
+ import { isLaunchReadyEvaluatorCommand, resolveAutoresearchDeepInterviewResult, writeAutoresearchDeepInterviewArtifacts, writeAutoresearchDraftArtifact, } from "../autoresearch-intake.js";
9
+ async function initWorkspace() {
10
+ return mkdtemp(join(tmpdir(), "omx-autoresearch-guided-test-"));
21
11
  }
22
12
  function withMockedTty(fn) {
23
- const descriptor = Object.getOwnPropertyDescriptor(process.stdin, 'isTTY');
24
- Object.defineProperty(process.stdin, 'isTTY', { configurable: true, value: true });
13
+ const descriptor = Object.getOwnPropertyDescriptor(process.stdin, "isTTY");
14
+ Object.defineProperty(process.stdin, "isTTY", {
15
+ configurable: true,
16
+ value: true,
17
+ });
25
18
  return fn().finally(() => {
26
19
  if (descriptor) {
27
- Object.defineProperty(process.stdin, 'isTTY', descriptor);
20
+ Object.defineProperty(process.stdin, "isTTY", descriptor);
28
21
  }
29
22
  else {
30
- Object.defineProperty(process.stdin, 'isTTY', { configurable: true, value: false });
23
+ Object.defineProperty(process.stdin, "isTTY", {
24
+ configurable: true,
25
+ value: false,
26
+ });
31
27
  }
32
28
  });
33
29
  }
@@ -35,222 +31,122 @@ function makeFakeIo(answers) {
35
31
  const queue = [...answers];
36
32
  return {
37
33
  async question() {
38
- return queue.shift() ?? '';
34
+ return queue.shift() ?? "";
39
35
  },
40
36
  close() { },
41
37
  };
42
38
  }
43
- function escapeRegExp(value) {
44
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
45
- }
46
- describe('initAutoresearchMission', () => {
47
- it('creates mission.md with correct content', async () => {
48
- const repo = await initRepo();
49
- try {
50
- const result = await initAutoresearchMission({
51
- topic: 'Improve test coverage for the auth module',
52
- evaluatorCommand: 'node scripts/eval.js',
53
- keepPolicy: 'score_improvement',
54
- slug: 'auth-coverage',
55
- repoRoot: repo,
56
- });
57
- assert.equal(result.slug, 'auth-coverage');
58
- assert.equal(result.missionDir, join(repo, 'missions', 'auth-coverage'));
59
- const missionContent = await readFile(join(result.missionDir, 'mission.md'), 'utf-8');
60
- assert.match(missionContent, /# Mission/);
61
- assert.match(missionContent, /Improve test coverage for the auth module/);
62
- }
63
- finally {
64
- await rm(repo, { recursive: true, force: true });
65
- }
66
- });
67
- it('creates sandbox.md with valid YAML frontmatter', async () => {
68
- const repo = await initRepo();
69
- try {
70
- const result = await initAutoresearchMission({
71
- topic: 'Optimize database queries',
72
- evaluatorCommand: 'node scripts/eval-perf.js',
73
- keepPolicy: 'pass_only',
74
- slug: 'db-perf',
75
- repoRoot: repo,
76
- });
77
- const sandboxContent = await readFile(join(result.missionDir, 'sandbox.md'), 'utf-8');
78
- assert.match(sandboxContent, /^---\n/);
79
- assert.match(sandboxContent, /evaluator:/);
80
- assert.match(sandboxContent, /command: node scripts\/eval-perf\.js/);
81
- assert.match(sandboxContent, /format: json/);
82
- assert.match(sandboxContent, /keep_policy: pass_only/);
83
- }
84
- finally {
85
- await rm(repo, { recursive: true, force: true });
86
- }
87
- });
88
- it('generated sandbox.md passes parseSandboxContract validation', async () => {
89
- const repo = await initRepo();
90
- try {
91
- const result = await initAutoresearchMission({
92
- topic: 'Fix flaky tests',
93
- evaluatorCommand: 'bash run-tests.sh',
94
- keepPolicy: 'score_improvement',
95
- slug: 'flaky-tests',
96
- repoRoot: repo,
97
- });
98
- const sandboxContent = await readFile(join(result.missionDir, 'sandbox.md'), 'utf-8');
99
- const parsed = parseSandboxContract(sandboxContent);
100
- assert.equal(parsed.evaluator.command, 'bash run-tests.sh');
101
- assert.equal(parsed.evaluator.format, 'json');
102
- assert.equal(parsed.evaluator.keep_policy, 'score_improvement');
103
- }
104
- finally {
105
- await rm(repo, { recursive: true, force: true });
106
- }
107
- });
108
- it('throws if mission directory already exists', async () => {
109
- const repo = await initRepo();
110
- try {
111
- const missionDir = join(repo, 'missions', 'existing');
112
- await mkdir(missionDir, { recursive: true });
113
- await assert.rejects(() => initAutoresearchMission({
114
- topic: 'duplicate',
115
- evaluatorCommand: 'echo ok',
116
- keepPolicy: 'pass_only',
117
- slug: 'existing',
118
- repoRoot: repo,
119
- }), /already exists/);
120
- }
121
- finally {
122
- await rm(repo, { recursive: true, force: true });
39
+ function makeFakeStructuredQuestionAsker(answers, questions = []) {
40
+ const queue = [...answers];
41
+ return async (input) => {
42
+ questions.push({
43
+ question: input.question,
44
+ options: input.options.map((option) => option.value),
45
+ allowOther: input.allow_other,
46
+ });
47
+ const next = queue.shift() ?? "";
48
+ const matchingOption = input.options.find((option) => option.value === next);
49
+ if (matchingOption) {
50
+ return {
51
+ ok: true,
52
+ question_id: `q-${questions.length}`,
53
+ prompt: {
54
+ header: input.header,
55
+ question: input.question,
56
+ options: input.options,
57
+ allow_other: input.allow_other,
58
+ other_label: input.other_label ?? "Other",
59
+ multi_select: input.multi_select ?? false,
60
+ source: input.source,
61
+ },
62
+ answer: {
63
+ kind: "option",
64
+ value: matchingOption.value,
65
+ selected_labels: [matchingOption.label],
66
+ selected_values: [matchingOption.value],
67
+ },
68
+ };
123
69
  }
124
- });
125
- });
126
- describe('parseInitArgs', () => {
127
- it('parses all flags with space-separated values', () => {
70
+ return {
71
+ ok: true,
72
+ question_id: `q-${questions.length}`,
73
+ prompt: {
74
+ header: input.header,
75
+ question: input.question,
76
+ options: input.options,
77
+ allow_other: input.allow_other,
78
+ other_label: input.other_label ?? "Other",
79
+ multi_select: input.multi_select ?? false,
80
+ source: input.source,
81
+ },
82
+ answer: {
83
+ kind: "other",
84
+ value: next,
85
+ selected_labels: [input.other_label ?? "Other"],
86
+ selected_values: [next],
87
+ other_text: next,
88
+ },
89
+ };
90
+ };
91
+ }
92
+ describe("parseInitArgs", () => {
93
+ it("parses all flags with space-separated values", () => {
128
94
  const result = parseInitArgs([
129
- '--topic', 'my topic',
130
- '--evaluator', 'node eval.js',
131
- '--keep-policy', 'pass_only',
132
- '--slug', 'my-slug',
95
+ "--topic",
96
+ "my topic",
97
+ "--evaluator",
98
+ "node eval.js",
99
+ "--keep-policy",
100
+ "pass_only",
101
+ "--slug",
102
+ "my-slug",
133
103
  ]);
134
- assert.equal(result.topic, 'my topic');
135
- assert.equal(result.evaluatorCommand, 'node eval.js');
136
- assert.equal(result.keepPolicy, 'pass_only');
137
- assert.equal(result.slug, 'my-slug');
104
+ assert.equal(result.topic, "my topic");
105
+ assert.equal(result.evaluatorCommand, "node eval.js");
106
+ assert.equal(result.keepPolicy, "pass_only");
107
+ assert.equal(result.slug, "my-slug");
138
108
  });
139
- it('parses all flags with = syntax', () => {
109
+ it("parses all flags with = syntax", () => {
140
110
  const result = parseInitArgs([
141
- '--topic=my topic',
142
- '--evaluator=node eval.js',
143
- '--keep-policy=score_improvement',
144
- '--slug=my-slug',
111
+ "--topic=my topic",
112
+ "--evaluator=node eval.js",
113
+ "--keep-policy=score_improvement",
114
+ "--slug=my-slug",
145
115
  ]);
146
- assert.equal(result.topic, 'my topic');
147
- assert.equal(result.evaluatorCommand, 'node eval.js');
148
- assert.equal(result.keepPolicy, 'score_improvement');
149
- assert.equal(result.slug, 'my-slug');
116
+ assert.equal(result.topic, "my topic");
117
+ assert.equal(result.evaluatorCommand, "node eval.js");
118
+ assert.equal(result.keepPolicy, "score_improvement");
119
+ assert.equal(result.slug, "my-slug");
150
120
  });
151
- it('returns partial result when some flags are missing', () => {
152
- const result = parseInitArgs(['--topic', 'my topic']);
153
- assert.equal(result.topic, 'my topic');
121
+ it("returns partial result when some flags are missing", () => {
122
+ const result = parseInitArgs(["--topic", "my topic"]);
123
+ assert.equal(result.topic, "my topic");
154
124
  assert.equal(result.evaluatorCommand, undefined);
155
125
  assert.equal(result.keepPolicy, undefined);
156
126
  assert.equal(result.slug, undefined);
157
127
  });
158
- it('throws on invalid keep-policy', () => {
159
- assert.throws(() => parseInitArgs(['--keep-policy', 'invalid']), /must be one of/);
128
+ it("throws on invalid keep-policy", () => {
129
+ assert.throws(() => parseInitArgs(["--keep-policy", "invalid"]), /must be one of/);
160
130
  });
161
- it('throws on unknown flags', () => {
162
- assert.throws(() => parseInitArgs(['--unknown-flag', 'value']), /Unknown init flag: --unknown-flag/);
131
+ it("throws on unknown flags", () => {
132
+ assert.throws(() => parseInitArgs(["--unknown-flag", "value"]), /Unknown init flag: --unknown-flag/);
163
133
  });
164
- it('sanitizes slug via slugifyMissionName', () => {
165
- const result = parseInitArgs(['--slug', '../../etc/cron.d/omx']);
134
+ it("sanitizes slug via slugifyMissionName", () => {
135
+ const result = parseInitArgs(["--slug", "../../etc/cron.d/omx"]);
166
136
  assert.ok(result.slug);
167
137
  assert.doesNotMatch(result.slug, /\.\./);
168
138
  assert.doesNotMatch(result.slug, /\//);
169
139
  });
170
140
  });
171
- describe('checkTmuxAvailable', () => {
172
- it('returns a boolean', () => {
173
- const result = checkTmuxAvailable();
174
- assert.equal(typeof result, 'boolean');
175
- });
176
- it('launches background tmux sessions with an absolute omx entry path even from a relative launcher', async () => {
177
- const repo = await initRepo();
178
- const fakeBin = await mkdtemp(join(tmpdir(), 'omx-autoresearch-guided-bin-'));
179
- const startupCwd = await mkdtemp(join(tmpdir(), 'omx-autoresearch-guided-start-'));
180
- const missionDir = join(repo, 'missions', 'demo');
181
- const tmuxLog = join(repo, 'tmux.log');
182
- const previousPath = process.env.PATH;
183
- const previousEntryPath = process.env[OMX_ENTRY_PATH_ENV];
184
- const previousStartupCwd = process.env[OMX_STARTUP_CWD_ENV];
185
- try {
186
- await mkdir(missionDir, { recursive: true });
187
- const launcherDir = join(startupCwd, 'dist', 'cli');
188
- const launcherPath = join(launcherDir, 'omx.js');
189
- await mkdir(launcherDir, { recursive: true });
190
- await writeFile(launcherPath, '#!/usr/bin/env node\n', 'utf-8');
191
- const fakeTmuxPath = join(fakeBin, 'tmux');
192
- await writeFile(fakeTmuxPath, `#!/bin/sh
193
- printf '%s\n' "$*" >>"${tmuxLog}"
194
- case "$1" in
195
- -V)
196
- exit 0
197
- ;;
198
- has-session)
199
- exit 1
200
- ;;
201
- new-session)
202
- exit 0
203
- ;;
204
- *)
205
- exit 0
206
- ;;
207
- esac
208
- `, 'utf-8');
209
- execFileSync('chmod', ['+x', fakeTmuxPath], { stdio: 'ignore' });
210
- process.env.PATH = `${fakeBin}:${previousPath || ''}`;
211
- delete process.env[OMX_ENTRY_PATH_ENV];
212
- process.env[OMX_STARTUP_CWD_ENV] = startupCwd;
213
- const previousArgv = process.argv;
214
- process.argv = [previousArgv[0] || 'node', 'dist/cli/omx.js'];
215
- try {
216
- spawnAutoresearchTmux(missionDir, 'demo');
217
- }
218
- finally {
219
- process.argv = previousArgv;
220
- }
221
- const tmuxOutput = await readFile(tmuxLog, 'utf-8');
222
- assert.match(tmuxOutput, /new-session -d -s omx-autoresearch-demo/);
223
- assert.match(tmuxOutput, new RegExp(escapeRegExp(launcherPath)));
224
- assert.doesNotMatch(tmuxOutput, /dist\/cli\/omx\.js autoresearch/);
225
- }
226
- finally {
227
- if (typeof previousPath === 'string')
228
- process.env.PATH = previousPath;
229
- else
230
- delete process.env.PATH;
231
- if (typeof previousEntryPath === 'string')
232
- process.env[OMX_ENTRY_PATH_ENV] = previousEntryPath;
233
- else
234
- delete process.env[OMX_ENTRY_PATH_ENV];
235
- if (typeof previousStartupCwd === 'string')
236
- process.env[OMX_STARTUP_CWD_ENV] = previousStartupCwd;
237
- else
238
- delete process.env[OMX_STARTUP_CWD_ENV];
239
- await rm(repo, { recursive: true, force: true });
240
- await rm(fakeBin, { recursive: true, force: true });
241
- await rm(startupCwd, { recursive: true, force: true });
242
- }
243
- });
244
- });
245
- describe('autoresearch intake draft artifacts', () => {
246
- it('writes a canonical deep-interview autoresearch draft artifact from vague input', async () => {
247
- const repo = await initRepo();
141
+ describe("autoresearch intake draft artifacts", () => {
142
+ it("writes a canonical deep-interview autoresearch draft artifact from vague input", async () => {
143
+ const repo = await initWorkspace();
248
144
  try {
249
145
  const artifact = await writeAutoresearchDraftArtifact({
250
146
  repoRoot: repo,
251
- topic: 'Improve onboarding for first-time contributors',
252
- keepPolicy: 'score_improvement',
253
- seedInputs: { topic: 'Improve onboarding for first-time contributors' },
147
+ topic: "Improve onboarding for first-time contributors",
148
+ keepPolicy: "score_improvement",
149
+ seedInputs: { topic: "Improve onboarding for first-time contributors" },
254
150
  });
255
151
  assert.match(artifact.path, /\.omx\/specs\/deep-interview-autoresearch-improve-onboarding-for-first-time-contributors\.md$/);
256
152
  assert.equal(artifact.launchReady, false);
@@ -265,48 +161,51 @@ describe('autoresearch intake draft artifacts', () => {
265
161
  await rm(repo, { recursive: true, force: true });
266
162
  }
267
163
  });
268
- it('rejects placeholder evaluator commands and accepts concrete commands', () => {
269
- assert.equal(isLaunchReadyEvaluatorCommand('TODO replace me'), false);
270
- assert.equal(isLaunchReadyEvaluatorCommand('node scripts/eval.js'), true);
271
- assert.equal(isLaunchReadyEvaluatorCommand('bash scripts/eval.sh'), true);
164
+ it("rejects placeholder evaluator commands and accepts concrete commands", () => {
165
+ assert.equal(isLaunchReadyEvaluatorCommand("TODO replace me"), false);
166
+ assert.equal(isLaunchReadyEvaluatorCommand("node scripts/eval.js"), true);
167
+ assert.equal(isLaunchReadyEvaluatorCommand("bash scripts/eval.sh"), true);
272
168
  });
273
- it('writes launch-consumable mission/sandbox/result artifacts and resolves them back', async () => {
274
- const repo = await initRepo();
169
+ it("writes launch-consumable mission/sandbox/result artifacts and resolves them back", async () => {
170
+ const repo = await initWorkspace();
275
171
  try {
276
172
  const artifacts = await writeAutoresearchDeepInterviewArtifacts({
277
173
  repoRoot: repo,
278
- topic: 'Measure onboarding friction',
279
- evaluatorCommand: 'node scripts/eval.js',
280
- keepPolicy: 'pass_only',
281
- slug: 'onboarding-friction',
282
- seedInputs: { topic: 'Measure onboarding friction' },
174
+ topic: "Measure onboarding friction",
175
+ evaluatorCommand: "node scripts/eval.js",
176
+ keepPolicy: "pass_only",
177
+ slug: "onboarding-friction",
178
+ seedInputs: { topic: "Measure onboarding friction" },
283
179
  });
284
180
  assert.match(artifacts.draftArtifactPath, /deep-interview-autoresearch-onboarding-friction\.md$/);
285
181
  assert.match(artifacts.missionArtifactPath, /autoresearch-onboarding-friction\/mission\.md$/);
286
182
  assert.match(artifacts.sandboxArtifactPath, /autoresearch-onboarding-friction\/sandbox\.md$/);
287
183
  assert.match(artifacts.resultPath, /autoresearch-onboarding-friction\/result\.json$/);
288
- const resolved = await resolveAutoresearchDeepInterviewResult(repo, { slug: 'onboarding-friction' });
184
+ const resolved = await resolveAutoresearchDeepInterviewResult(repo, {
185
+ slug: "onboarding-friction",
186
+ });
289
187
  assert.ok(resolved);
290
- assert.equal(resolved?.compileTarget.slug, 'onboarding-friction');
291
- assert.equal(resolved?.compileTarget.keepPolicy, 'pass_only');
188
+ assert.equal(resolved?.compileTarget.slug, "onboarding-friction");
189
+ assert.equal(resolved?.compileTarget.keepPolicy, "pass_only");
292
190
  assert.equal(resolved?.launchReady, true);
293
- assert.match(resolved?.missionContent || '', /Measure onboarding friction/);
294
- assert.match(resolved?.sandboxContent || '', /command: node scripts\/eval\.js/);
191
+ assert.match(resolved?.missionContent || "", /Measure onboarding friction/);
192
+ assert.match(resolved?.sandboxContent || "", /command: node scripts\/eval\.js/);
295
193
  }
296
194
  finally {
297
195
  await rm(repo, { recursive: true, force: true });
298
196
  }
299
197
  });
300
198
  });
301
- describe('buildAutoresearchDeepInterviewPrompt', () => {
302
- it('activates deep-interview --autoresearch and includes seed inputs', () => {
199
+ describe("buildAutoresearchDeepInterviewPrompt", () => {
200
+ it("activates deep-interview --autoresearch and includes seed inputs", () => {
303
201
  const prompt = buildAutoresearchDeepInterviewPrompt({
304
- topic: 'Investigate flaky tests',
305
- evaluatorCommand: 'node scripts/eval.js',
306
- keepPolicy: 'score_improvement',
307
- slug: 'flaky-tests',
202
+ topic: "Investigate flaky tests",
203
+ evaluatorCommand: "node scripts/eval.js",
204
+ keepPolicy: "score_improvement",
205
+ slug: "flaky-tests",
308
206
  });
309
207
  assert.match(prompt, /\$deep-interview --autoresearch/);
208
+ assert.match(prompt, /Do not launch tmux or run `omx autoresearch` yourself/);
310
209
  assert.match(prompt, /deep-interview-autoresearch-\{slug\}\.md/);
311
210
  assert.match(prompt, /autoresearch-\{slug\}\/mission\.md/);
312
211
  assert.match(prompt, /- topic: Investigate flaky tests/);
@@ -315,29 +214,99 @@ describe('buildAutoresearchDeepInterviewPrompt', () => {
315
214
  assert.match(prompt, /- slug: flaky-tests/);
316
215
  });
317
216
  });
318
- describe('runAutoresearchNoviceBridge', () => {
319
- it('loops through refine further before launching and writes draft + mission files', async () => {
320
- const repo = await initRepo();
217
+ describe("runAutoresearchNoviceBridge", () => {
218
+ it("falls back to plain terminal prompts when omx question is unavailable", async () => {
219
+ const repo = await initWorkspace();
321
220
  try {
322
221
  const result = await withMockedTty(() => runAutoresearchNoviceBridge(repo, {}, makeFakeIo([
323
- 'Improve evaluator UX',
324
- 'Make success measurable',
325
- 'TODO replace with evaluator command',
326
- 'score_improvement',
327
- 'ux-eval',
328
- 'refine further',
329
- 'Improve evaluator UX',
330
- 'Passing evaluator output',
331
- 'node scripts/eval.js',
332
- 'pass_only',
333
- 'ux-eval',
334
- 'launch',
222
+ "Improve evaluator UX",
223
+ "Passing evaluator output",
224
+ "node scripts/eval.js",
225
+ "pass_only",
226
+ "ux-eval",
227
+ "launch",
228
+ ]), async () => {
229
+ throw new Error("omx question requires tmux for OMX-owned question UI rendering in this session.");
230
+ }));
231
+ assert.equal(result.slug, "ux-eval");
232
+ assert.equal(result.resultPath, join(repo, ".omx", "specs", "autoresearch-ux-eval", "result.json"));
233
+ }
234
+ finally {
235
+ await rm(repo, { recursive: true, force: true });
236
+ }
237
+ });
238
+ it("does not fall back to plain prompts when question policy denies structured questions", async () => {
239
+ const repo = await initWorkspace();
240
+ try {
241
+ await mkdir(join(repo, ".omx", "state", "sessions", "sess-autoresearch"), {
242
+ recursive: true,
243
+ });
244
+ await writeFile(join(repo, ".omx", "state", "session.json"), JSON.stringify({ session_id: "sess-autoresearch" }));
245
+ await writeFile(join(repo, ".omx", "state", "sessions", "sess-autoresearch", "autoresearch-state.json"), JSON.stringify({ mode: "autoresearch", active: true }));
246
+ await assert.rejects(() => withMockedTty(() => runAutoresearchNoviceBridge(repo, {}, makeFakeIo([
247
+ "should not be used",
248
+ "should not be used",
249
+ ]), async () => {
250
+ throw new OmxQuestionError("active_execution_mode_blocked", "omx question is unavailable while auto-executing workflows are active: autoresearch.");
251
+ })), (error) => {
252
+ assert.ok(error instanceof OmxQuestionError);
253
+ assert.equal(error.code, "active_execution_mode_blocked");
254
+ return true;
255
+ });
256
+ }
257
+ finally {
258
+ await rm(repo, { recursive: true, force: true });
259
+ }
260
+ });
261
+ it("uses structured omx-question prompts and resumes from returned stdout answers", async () => {
262
+ const repo = await initWorkspace();
263
+ const askedQuestions = [];
264
+ try {
265
+ const result = await withMockedTty(() => runAutoresearchNoviceBridge(repo, {}, makeFakeIo([]), makeFakeStructuredQuestionAsker([
266
+ "Improve evaluator UX",
267
+ "Passing evaluator output",
268
+ "node scripts/eval.js",
269
+ "pass_only",
270
+ "ux-eval",
271
+ "launch",
272
+ ], askedQuestions)));
273
+ assert.equal(result.slug, "ux-eval");
274
+ assert.equal(result.resultPath, join(repo, ".omx", "specs", "autoresearch-ux-eval", "result.json"));
275
+ assert.equal(askedQuestions.length, 6);
276
+ assert.equal(askedQuestions[0]?.question, "Research topic/goal");
277
+ assert.deepEqual(askedQuestions[0]?.options, []);
278
+ assert.equal(askedQuestions[0]?.allowOther, true);
279
+ assert.match(askedQuestions[5]?.question || "", /Next step/);
280
+ assert.deepEqual(askedQuestions[5]?.options, ["launch", "refine"]);
281
+ assert.equal(askedQuestions[5]?.allowOther, false);
282
+ }
283
+ finally {
284
+ await rm(repo, { recursive: true, force: true });
285
+ }
286
+ });
287
+ it("loops through refine further before launching and writes canonical spec artifacts", async () => {
288
+ const repo = await initWorkspace();
289
+ try {
290
+ const result = await withMockedTty(() => runAutoresearchNoviceBridge(repo, {}, makeFakeIo([
291
+ "Improve evaluator UX",
292
+ "Make success measurable",
293
+ "TODO replace with evaluator command",
294
+ "score_improvement",
295
+ "ux-eval",
296
+ "refine further",
297
+ "Improve evaluator UX",
298
+ "Passing evaluator output",
299
+ "node scripts/eval.js",
300
+ "pass_only",
301
+ "ux-eval",
302
+ "launch",
335
303
  ])));
336
- const draftContent = await readFile(join(repo, '.omx', 'specs', 'deep-interview-autoresearch-ux-eval.md'), 'utf-8');
337
- const resultContent = await readFile(join(repo, '.omx', 'specs', 'autoresearch-ux-eval', 'result.json'), 'utf-8');
338
- const missionContent = await readFile(join(result.missionDir, 'mission.md'), 'utf-8');
339
- const sandboxContent = await readFile(join(result.missionDir, 'sandbox.md'), 'utf-8');
340
- assert.equal(result.slug, 'ux-eval');
304
+ const draftContent = await readFile(join(repo, ".omx", "specs", "deep-interview-autoresearch-ux-eval.md"), "utf-8");
305
+ const resultContent = await readFile(result.resultPath, "utf-8");
306
+ const missionContent = await readFile(result.missionArtifactPath, "utf-8");
307
+ const sandboxContent = await readFile(result.sandboxArtifactPath, "utf-8");
308
+ assert.equal(result.artifactDir, join(repo, ".omx", "specs", "autoresearch-ux-eval"));
309
+ assert.equal(result.slug, "ux-eval");
341
310
  assert.match(draftContent, /Launch-ready: yes/);
342
311
  assert.match(resultContent, /"launchReady": true/);
343
312
  assert.match(missionContent, /Improve evaluator UX/);
@@ -348,24 +317,18 @@ describe('runAutoresearchNoviceBridge', () => {
348
317
  await rm(repo, { recursive: true, force: true });
349
318
  }
350
319
  });
351
- it('uses seeded novice inputs while still requiring confirmation-driven launch', async () => {
352
- const repo = await initRepo();
320
+ it("uses seeded novice inputs while still requiring confirmation-driven launch", async () => {
321
+ const repo = await initWorkspace();
353
322
  try {
354
323
  const result = await withMockedTty(() => runAutoresearchNoviceBridge(repo, {
355
- topic: 'Seeded topic',
356
- evaluatorCommand: 'node scripts/eval.js',
357
- keepPolicy: 'score_improvement',
358
- slug: 'seeded-topic',
359
- }, makeFakeIo([
360
- '',
361
- '',
362
- '',
363
- '',
364
- '',
365
- 'launch',
366
- ])));
367
- const draftContent = await readFile(join(repo, '.omx', 'specs', 'deep-interview-autoresearch-seeded-topic.md'), 'utf-8');
368
- assert.equal(result.slug, 'seeded-topic');
324
+ topic: "Seeded topic",
325
+ evaluatorCommand: "node scripts/eval.js",
326
+ keepPolicy: "score_improvement",
327
+ slug: "seeded-topic",
328
+ }, makeFakeIo(["", "", "", "", "", "launch"])));
329
+ const draftContent = await readFile(join(repo, ".omx", "specs", "deep-interview-autoresearch-seeded-topic.md"), "utf-8");
330
+ assert.equal(result.resultPath, join(repo, ".omx", "specs", "autoresearch-seeded-topic", "result.json"));
331
+ assert.equal(result.slug, "seeded-topic");
369
332
  assert.match(draftContent, /- topic: Seeded topic/);
370
333
  assert.match(draftContent, /- evaluator: node scripts\/eval\.js/);
371
334
  assert.match(draftContent, /Launch-ready: yes/);