oh-my-opencode 4.19.0 → 4.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/.agents/command/omomomo.md +1 -1
  2. package/.agents/skills/omomomo/SKILL.md +1 -1
  3. package/.opencode/command/omomomo.md +1 -1
  4. package/README.ja.md +6 -6
  5. package/README.ko.md +6 -6
  6. package/README.md +6 -6
  7. package/README.ru.md +6 -6
  8. package/README.zh-cn.md +6 -6
  9. package/dist/agents/atlas/agent.d.ts +5 -3
  10. package/dist/agents/explore.d.ts +4 -2
  11. package/dist/agents/gpt-prompt-identity.d.ts +3 -0
  12. package/dist/agents/hephaestus/agent.d.ts +4 -2
  13. package/dist/agents/librarian.d.ts +4 -2
  14. package/dist/agents/metis.d.ts +4 -2
  15. package/dist/agents/momus.d.ts +3 -2
  16. package/dist/agents/multimodal-looker.d.ts +4 -2
  17. package/dist/agents/oracle.d.ts +4 -2
  18. package/dist/agents/sisyphus/claude-opus-5.d.ts +33 -0
  19. package/dist/agents/sisyphus/gpt-5-5.d.ts +1 -1
  20. package/dist/agents/sisyphus/gpt-task-system-guide.d.ts +1 -0
  21. package/dist/agents/sisyphus/index.d.ts +2 -0
  22. package/dist/agents/sisyphus-agent-factory.d.ts +5 -2
  23. package/dist/agents/sisyphus-junior/agent.d.ts +4 -1
  24. package/dist/agents/sisyphus-junior/gpt-5-5.d.ts +2 -2
  25. package/dist/agents/types.d.ts +2 -2
  26. package/dist/cli/index.js +23577 -19269
  27. package/dist/cli-node/index.js +23577 -19269
  28. package/dist/config/schema/agent-names.d.ts +26 -26
  29. package/dist/config/schema/agent-overrides.d.ts +400 -400
  30. package/dist/config/schema/browser-automation.d.ts +2 -2
  31. package/dist/config/schema/categories.d.ts +36 -36
  32. package/dist/config/schema/codegraph.d.ts +2 -0
  33. package/dist/config/schema/commands.d.ts +2 -2
  34. package/dist/config/schema/dynamic-context-pruning.d.ts +1 -1
  35. package/dist/config/schema/experimental.d.ts +1 -1
  36. package/dist/config/schema/fallback-models.d.ts +25 -25
  37. package/dist/config/schema/hooks.d.ts +43 -43
  38. package/dist/config/schema/internal/permission.d.ts +9 -9
  39. package/dist/config/schema/keyword-detector.d.ts +6 -6
  40. package/dist/config/schema/oh-my-opencode-config.d.ts +404 -402
  41. package/dist/config/schema/tmux.d.ts +6 -6
  42. package/dist/create-hooks.d.ts +35 -35
  43. package/dist/features/background-agent/spawner/tmux-callback-invoker.d.ts +11 -0
  44. package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
  45. package/dist/features/claude-tasks/types.d.ts +4 -4
  46. package/dist/features/team-mode/team-runtime/shutdown-helpers.d.ts +1 -1
  47. package/dist/features/team-mode/team-runtime/shutdown-test-fixtures.d.ts +4 -4
  48. package/dist/features/team-mode/tools/lifecycle-test-fixture.d.ts +42 -42
  49. package/dist/features/tmux-subagent/environment-eligibility.d.ts +2 -0
  50. package/dist/features/tmux-subagent/index.d.ts +1 -0
  51. package/dist/features/tmux-subagent/tracked-session-state.d.ts +1 -0
  52. package/dist/features/tui-sidebar/snapshot-schema.d.ts +6 -6
  53. package/dist/help/schema/acp.d.ts +3 -3
  54. package/dist/help/schema/doctor.d.ts +4 -4
  55. package/dist/help/schema/status.d.ts +3 -3
  56. package/dist/hooks/codegraph-bootstrap/hook.d.ts +3 -2
  57. package/dist/hooks/goal/types.d.ts +5 -5
  58. package/dist/hooks/non-interactive-env/constants.d.ts +4 -1
  59. package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +1 -1
  60. package/dist/hooks/start-work/notepad-scaffold.d.ts +10 -0
  61. package/dist/hooks/start-work/parse-user-request.d.ts +2 -0
  62. package/dist/hooks/start-work/worktree-block.d.ts +5 -0
  63. package/dist/index.js +27370 -21301
  64. package/dist/mcp/types.d.ts +3 -3
  65. package/dist/oh-my-opencode.schema.json +11 -0
  66. package/dist/plugin/chat-message/loop-commands.d.ts +1 -0
  67. package/dist/plugin/command-execute-before.d.ts +3 -3
  68. package/dist/plugin/hooks/create-core-hooks.d.ts +25 -25
  69. package/dist/shared/omo-process-sweep.d.ts +18 -0
  70. package/dist/shared/tmux/tmux-utils/environment.d.ts +1 -1
  71. package/dist/shared/tmux/tmux-utils.d.ts +1 -1
  72. package/dist/shared/typescript-native-source-parser.d.ts +7 -0
  73. package/dist/skills/coding-agent-sessions/SKILL.md +5 -2
  74. package/dist/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  75. package/dist/skills/coding-agent-sessions/references/senpi.md +15 -5
  76. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
  77. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
  78. package/dist/skills/debugging/SKILL.md +2 -1
  79. package/dist/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
  80. package/dist/skills/programming/SKILL.md +11 -1
  81. package/dist/skills/programming/references/logging.md +6 -1
  82. package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
  83. package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.ts +75 -11
  84. package/dist/skills/start-work/SKILL.md +12 -2
  85. package/dist/skills/ulw-plan/SKILL.md +21 -2
  86. package/dist/skills/ulw-plan/references/full-workflow.md +12 -1
  87. package/dist/skills/ulw-plan/references/intent-clear.md +1 -1
  88. package/dist/testing/create-plugin-module.d.ts +1 -0
  89. package/dist/tools/task/types.d.ts +10 -10
  90. package/dist/tui.js +23442 -19182
  91. package/package.json +14 -14
  92. package/packages/git-bash-mcp/dist/cli.js +54 -8
  93. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +62 -2
  94. package/packages/lsp-core/src/lsp/client.ts +14 -1
  95. package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +2 -2
  96. package/packages/lsp-core/src/mcp.ts +9 -0
  97. package/packages/lsp-daemon/dist/cli.js +319 -51
  98. package/packages/lsp-daemon/dist/client.js +136 -48
  99. package/packages/lsp-daemon/dist/daemon-client.d.ts +0 -10
  100. package/packages/lsp-daemon/dist/daemon-client.js +4 -34
  101. package/packages/lsp-daemon/dist/daemon-failure-result.d.ts +3 -0
  102. package/packages/lsp-daemon/dist/daemon-failure-result.js +38 -0
  103. package/packages/lsp-daemon/dist/daemon-request-error.d.ts +14 -0
  104. package/packages/lsp-daemon/dist/daemon-request-error.js +26 -0
  105. package/packages/lsp-daemon/dist/daemon-server.js +5 -1
  106. package/packages/lsp-daemon/dist/index.js +137 -48
  107. package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
  108. package/packages/lsp-daemon/dist/proxy.js +1 -0
  109. package/packages/lsp-daemon/dist/version-reap.d.ts +26 -0
  110. package/packages/lsp-daemon/dist/version-reap.js +182 -0
  111. package/packages/lsp-tools-mcp/dist/cli.js +61 -10
  112. package/packages/lsp-tools-mcp/dist/lsp/manager.js +6 -1
  113. package/packages/lsp-tools-mcp/dist/mcp.js +61 -10
  114. package/packages/lsp-tools-mcp/dist/tools.js +6 -1
  115. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  116. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +72 -12
  117. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  118. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  119. package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +22 -2
  120. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +779 -385
  121. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +302 -173
  122. package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
  123. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +3 -1
  124. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +23 -42
  125. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +98 -32
  126. package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +5 -0
  127. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +17 -9
  128. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +18 -25
  129. package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +1 -0
  130. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +5 -0
  131. package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +176 -0
  132. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +6 -1
  133. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +31 -2
  134. package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +1 -1
  135. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +6 -0
  136. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +65 -1
  137. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -7
  138. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +2 -1
  139. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +57 -3
  140. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +42 -0
  141. package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +122 -10
  142. package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +2 -2
  143. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +101 -64
  144. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  145. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  146. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  147. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  148. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  149. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  150. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  151. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +80 -40
  152. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  153. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  154. package/packages/omo-codex/plugin/components/rules/dist/cli.js +11 -0
  155. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  156. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  157. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +11 -0
  158. package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +60 -0
  159. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +1 -1
  160. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  161. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  162. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  163. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  164. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  165. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  166. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +2 -2
  167. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +2 -2
  168. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
  169. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +1 -1
  170. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +2 -2
  171. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
  172. package/packages/omo-codex/plugin/components/ultrawork/directive.md +12 -7
  173. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +1 -1
  174. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  175. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  176. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +12 -7
  177. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +21 -2
  178. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +12 -1
  179. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +1 -1
  180. package/packages/omo-codex/plugin/components/ultrawork/src/codex-hook.ts +1 -1
  181. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook-trigger-policy.test.ts +42 -0
  182. package/packages/omo-codex/plugin/components/ulw-loop/README.md +6 -4
  183. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +12 -7
  184. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.d.ts +21 -0
  185. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +64 -0
  186. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +15 -2
  187. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.js +1 -1
  188. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +2 -1
  189. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
  190. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +3 -3
  191. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.d.ts +3 -0
  192. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +85 -0
  193. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +0 -1
  194. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -33
  195. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +998 -593
  196. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +1 -0
  197. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.d.ts +1 -1
  198. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.js +2 -0
  199. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +6 -0
  200. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.d.ts +1 -0
  201. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.js +3 -0
  202. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +1 -0
  203. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +4 -0
  204. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +1 -0
  205. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +10 -2
  206. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +1 -1
  207. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.d.ts +15 -0
  208. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +84 -0
  209. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +5 -0
  210. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +81 -0
  211. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +7 -59
  212. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +12 -4
  213. package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.d.ts +9 -0
  214. package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.js +122 -0
  215. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  216. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  217. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +2 -1
  218. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +8 -6
  219. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +84 -0
  220. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +13 -2
  221. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-arg-parser.ts +1 -1
  222. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +1 -1
  223. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +3 -3
  224. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +49 -0
  225. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +13 -45
  226. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +1 -0
  227. package/packages/omo-codex/plugin/components/ulw-loop/src/constants.ts +2 -0
  228. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +7 -0
  229. package/packages/omo-codex/plugin/components/ulw-loop/src/goal-status.ts +4 -0
  230. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +4 -1
  231. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +20 -2
  232. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +1 -1
  233. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +125 -0
  234. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +59 -0
  235. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +6 -58
  236. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +12 -4
  237. package/packages/omo-codex/plugin/components/ulw-loop/src/validation-batch.ts +122 -0
  238. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +76 -0
  239. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-checkpoint-continuation.test.ts +111 -0
  240. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-steering-batch.test.ts +67 -0
  241. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-validation-batch.test.ts +57 -0
  242. package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +1 -0
  243. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +152 -0
  244. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +29 -0
  245. package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch-checkpoint.test.ts +147 -0
  246. package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch.test.ts +73 -0
  247. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  248. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  249. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  250. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  251. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  252. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  253. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  254. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  255. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  256. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  257. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  258. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +2 -2
  259. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  260. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +2 -2
  261. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  262. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +2 -2
  263. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  264. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  265. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  266. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  267. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  268. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  269. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  270. package/packages/omo-codex/plugin/package-lock.json +41 -41
  271. package/packages/omo-codex/plugin/package.json +1 -1
  272. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +44 -0
  273. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +5 -2
  274. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  275. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/senpi.md +15 -5
  276. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
  277. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
  278. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +2 -1
  279. package/packages/omo-codex/plugin/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
  280. package/packages/omo-codex/plugin/skills/programming/SKILL.md +11 -1
  281. package/packages/omo-codex/plugin/skills/programming/references/logging.md +6 -1
  282. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +75 -11
  283. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +12 -2
  284. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +12 -7
  285. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +2 -1
  286. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +8 -6
  287. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +21 -2
  288. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +12 -1
  289. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +1 -1
  290. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +9 -9
  291. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +26 -0
  292. package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +41 -0
  293. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +22 -0
  294. package/packages/omo-codex/scripts/install-dist/install-local.mjs +49 -5
  295. package/packages/omo-codex/scripts/install-marketplace-cache.test.mjs +4 -1
  296. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +5 -2
  297. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  298. package/packages/shared-skills/skills/coding-agent-sessions/references/senpi.md +15 -5
  299. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
  300. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
  301. package/packages/shared-skills/skills/debugging/SKILL.md +2 -1
  302. package/packages/shared-skills/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
  303. package/packages/shared-skills/skills/programming/SKILL.md +11 -1
  304. package/packages/shared-skills/skills/programming/references/logging.md +6 -1
  305. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
  306. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +75 -11
  307. package/packages/shared-skills/skills/start-work/SKILL.md +12 -2
  308. package/packages/shared-skills/skills/ulw-plan/SKILL.md +21 -2
  309. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +12 -1
  310. package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-ulw-loop",
3
- "version": "4.19.0",
3
+ "version": "4.19.2",
4
4
  "description": "Codex plugin: durable repo-native multi-goal orchestration with embedded success criteria and observable evidence audit.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -20,6 +20,7 @@ This skill is intentionally compact. The full workflow lives in `references/full
20
20
  ## Non-Negotiables
21
21
 
22
22
  - Use the ulw-loop CLI state under `.omo/ulw-loop`; do not hand-edit goal state.
23
+ - Register goals up front (`omo ulw-loop create-goals`, then `create_goal` from the printed handoff) and mirror every atomic step into the live `update_plan` checklist: one ultra-granular step per action, exactly one in_progress, transitions marked the instant they happen.
23
24
  - After any compaction or context loss, re-read brief + goals + ledger FIRST plus `omo ulw-loop status --json`, then resume; never re-plan from scratch.
24
25
  - If `omo ulw-loop create-goals` says the existing aggregate is already complete, start unrelated new work with a fresh `--session-id <new-id>` instead of steering or forcing the completed default state. Use `--force` only to intentionally overwrite completed evidence.
25
26
  - Every success criterion needs observable evidence from a real surface: a channel (terminal/TUI via the xterm.js web terminal, HTTP, browser, computer-use) or, for CLI- or data-shaped criteria, an auxiliary surface (CLI stdout, DB diff, parsed config dump).
@@ -31,7 +32,7 @@ This skill is intentionally compact. The full workflow lives in `references/full
31
32
  - Track spawned agent names locally. Use `wait_agent` for mailbox signals, not proof of completion. A timeout only means no new mailbox update arrived. Treat a running child as alive.
32
33
  - While children run, surface the active subagent count, agent names, and latest `WORKING:` phase.
33
34
  - Fallback only when the child is completed without the deliverable, ack-only after `followup_task`, explicitly `BLOCKED:`, or no longer running. Then record inconclusive and respawn a smaller `fork_turns: "none"` task with the missing deliverable.
34
- - Use `git-master` for git-tracked edits: inspect recent and touched-path commit history, then commit each verified work unit atomically in the repository's observed language, scope, and message style with only that unit's files staged.
35
+ - Use `git-master` for git-tracked edits: inspect recent and touched-path commit history, then commit each verified work unit atomically in the repository's observed language, scope, and message style with only that unit's files staged. Never carry verified units into a later omnibus commit.
35
36
 
36
37
  ## Codex Tool Mapping
37
38
 
@@ -113,9 +113,9 @@ If `ULW_LOOP_CLI` is empty, open the durable notepad first, record the missing C
113
113
 
114
114
  Run one form:
115
115
  ```sh
116
- omo ulw-loop create-goals --brief "<brief>" --json
117
- omo ulw-loop create-goals --brief-file <path> --json
118
- cat <brief> | omo ulw-loop create-goals --from-stdin --json
116
+ omo ulw-loop create-goals --brief "<brief>" [--validation-batch-json <json-or-path>] --json
117
+ omo ulw-loop create-goals --brief-file <path> [--validation-batch-json <json-or-path>] --json
118
+ cat <brief> | omo ulw-loop create-goals --from-stdin [--validation-batch-json <json-or-path>] --json
119
119
  ```
120
120
  If the existing aggregate is already complete, do not steer or force the
121
121
  completed default state for unrelated new work. Start a fresh run with
@@ -145,7 +145,7 @@ Read pending goals, criteria IDs, current ledger head, blockers, and aggregate C
145
145
  Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures at 3.
146
146
 
147
147
  ### Acquire Next Goal
148
- 1. Run `omo ulw-loop complete-goals --json` and read the handoff, including criteria.
148
+ 1. Run `omo ulw-loop complete-goals --json` and read the handoff, including criteria. After the first goal starts, a successful complete checkpoint normally prints the next goal instruction directly; use `complete-goals` as the manual fallback/resume path.
149
149
  2. Call `get_goal` and inspect active Codex state.
150
150
  3. Apply this table exactly:
151
151
 
@@ -177,7 +177,7 @@ Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures a
177
177
  ### Goal Completion
178
178
  1. Non-final aggregate goal: confirm every `essential` criterion is `pass`; non-essential criteria may remain pending. Final aggregate goal: confirm every criterion across the whole plan is `pass`.
179
179
  2. Call `get_goal` for a fresh snapshot.
180
- 3. Run `omo ulw-loop checkpoint --goal-id <id> --status complete --evidence "<criteria evidence summary>" --codex-goal-json <snapshot> --json`.
180
+ 3. Run `omo ulw-loop checkpoint --goal-id <id> --status complete --evidence "<criteria evidence summary>" --codex-goal-json <snapshot> --json`; on success it auto-starts and prints the next eligible goal unless `--no-advance` is passed.
181
181
  4. If blocked or failed, checkpoint with `--status blocked` or `--status failed` and include diagnosis evidence.
182
182
  5. If this is the final goal, run the final quality gate first and pass `--quality-gate-json`.
183
183
 
@@ -219,7 +219,9 @@ Use steering only for structured evidence-backed mutation. Reject natural-langua
219
219
  | annotate_ledger | Audit-only note | `--evidence`, `--rationale` |
220
220
  | mark_blocked_superseded | Old story replaced by new evidence | `--goal-id`, `--replacements?`, `--evidence`, `--rationale` |
221
221
 
222
- Command form: `omo ulw-loop steer --kind <kind> [<kind-specific-fields>] --evidence "<...>" --rationale "<...>" --json`.
222
+ Command form: `omo ulw-loop steer --kind <kind> [<kind-specific-fields>] --evidence "<...>" --rationale "<...>" --json`. For multiple evidence-backed plan-shape changes discovered together, pass `--proposals-json <json-or-path>` with an array of proposals; the batch applies atomically or rejects without partial plan mutation.
223
+
224
+ Validation batches are optional aggregate-mode review boundaries declared at create time with `--validation-batch-json`. A batch-final member requires all other members resolved, all member criteria pass, and a member-spanning quality gate; split/supersede steering keeps batch membership updated.
223
225
  Structured prompt directives accepted: `OMO_ULW_LOOP_STEER: { ... }`, `omo.ulw-loop.steer: {...}`, `omo ulw-loop steer: {...}`.
224
226
 
225
227
  ## Constraints
@@ -0,0 +1,84 @@
1
+ import { type CheckpointUlwLoopArgs, type CheckpointUlwLoopResult, checkpointUlwLoop } from "./checkpoint.js";
2
+ import { hasFlag, parseCodexGoalJson, readValue } from "./cli-arg-parser.js";
3
+ import { blockedDecisionHandoff, printJson } from "./cli-output.js";
4
+ import { buildCodexGoalInstruction, type UlwLoopGoalInstruction } from "./codex-goal-instruction.js";
5
+ import type { UlwLoopScope } from "./paths.js";
6
+ import { startNextUlwLoop, summarizeUlwLoopPlan } from "./plan-crud.js";
7
+ import type { UlwLoopItem, UlwLoopPlan } from "./types.js";
8
+ import { UlwLoopError } from "./types.js";
9
+
10
+ type ContinuationNext =
11
+ | { readonly resumed: boolean; readonly goal: UlwLoopItem; readonly instruction: UlwLoopGoalInstruction }
12
+ | { readonly done: true; readonly blocked: boolean; readonly handoff: string };
13
+
14
+ export type CheckpointAndContinueResult = CheckpointUlwLoopResult & { readonly next?: ContinuationNext };
15
+
16
+ export async function checkpointAndContinue(
17
+ repoRoot: string,
18
+ args: CheckpointUlwLoopArgs & { readonly advance: boolean },
19
+ scope?: UlwLoopScope,
20
+ ): Promise<CheckpointAndContinueResult> {
21
+ const result = await checkpointUlwLoop(repoRoot, args, scope);
22
+ if (args.status !== "complete" || result.aggregateCompletion !== undefined || !args.advance) return result;
23
+ const next = await startNextUlwLoop(repoRoot, {}, scope);
24
+ if ("done" in next) return { ...result, plan: next.plan, next: doneNext(next.plan) };
25
+ const instruction = buildCodexGoalInstruction({ plan: next.plan, goal: next.goal });
26
+ return { ...result, plan: next.plan, next: { resumed: next.resumed, goal: next.goal, instruction } };
27
+ }
28
+
29
+ export async function checkpoint(
30
+ repoRoot: string,
31
+ argv: readonly string[],
32
+ json: boolean,
33
+ scope?: UlwLoopScope,
34
+ ): Promise<number> {
35
+ const goalId = required(argv, "--goal-id");
36
+ const statusValue = checkpointStatus(required(argv, "--status"));
37
+ const evidence = required(argv, "--evidence");
38
+ const codexGoalJson = await parseCodexGoalJson(
39
+ statusValue === "complete" ? required(argv, "--codex-goal-json") : readValue(argv, "--codex-goal-json"),
40
+ );
41
+ if (statusValue === "complete" && codexGoalJson === undefined) {
42
+ throw new UlwLoopError("Missing --codex-goal-json.", "ULW_LOOP_CODEX_GOAL_JSON_REQUIRED");
43
+ }
44
+ const qualityGateJson = readValue(argv, "--quality-gate-json");
45
+ const args: CheckpointUlwLoopArgs & { readonly advance: boolean } = {
46
+ goalId,
47
+ status: statusValue,
48
+ evidence,
49
+ advance: !hasFlag(argv, "--no-advance"),
50
+ ...(codexGoalJson === undefined ? {} : { codexGoalJson }),
51
+ ...(qualityGateJson === undefined ? {} : { qualityGateJson }),
52
+ };
53
+ const result = await checkpointAndContinue(repoRoot, args, scope);
54
+ if (json) printJson({ ok: true, ...result, summary: summarizeUlwLoopPlan(result.plan) });
55
+ else printCheckpointText(result);
56
+ return 0;
57
+ }
58
+
59
+ function printCheckpointText(result: CheckpointAndContinueResult): void {
60
+ process.stdout.write(`ulw-loop checkpoint: ${result.goal.id} -> ${result.goal.status}\n`);
61
+ if (result.next === undefined) return;
62
+ if ("instruction" in result.next) process.stdout.write(`${result.next.instruction.text}\n`);
63
+ else process.stdout.write(`${result.next.handoff || "ulw-loop: all goals complete"}\n`);
64
+ }
65
+
66
+ function doneNext(plan: UlwLoopPlan): ContinuationNext {
67
+ const handoff = blockedDecisionHandoff(plan);
68
+ return { done: true, blocked: handoff.length > 0, handoff };
69
+ }
70
+
71
+ function required(argv: readonly string[], flag: string): string {
72
+ const value = readValue(argv, flag)?.trim();
73
+ if (value) return value;
74
+ throw new UlwLoopError(`Missing ${flag}.`, "ULW_LOOP_ARGUMENT_MISSING", { details: { flag } });
75
+ }
76
+
77
+ function checkpointStatus(value: string): CheckpointUlwLoopArgs["status"] {
78
+ if (value === "complete" || value === "failed" || value === "blocked") return value;
79
+ throw new UlwLoopError(
80
+ "Missing or invalid --status; expected complete, failed, or blocked.",
81
+ "ULW_LOOP_STATUS_INVALID",
82
+ { details: { status: value } },
83
+ );
84
+ }
@@ -1,4 +1,4 @@
1
- import { ulwLoopAttemptEvidenceDir } from "./paths.js";
1
+ // biome-ignore-all format: checkpoint stays under the pure LOC ceiling.
2
2
  import { existsSync, statSync } from "node:fs";
3
3
  import { readFile } from "node:fs/promises";
4
4
  import { resolve } from "node:path";
@@ -20,6 +20,7 @@ import {
20
20
  isFinalRunCompletionCandidate,
21
21
  } from "./goal-status.js";
22
22
  import type { UlwLoopScope } from "./paths.js";
23
+ import { ulwLoopAttemptEvidenceDir } from "./paths.js";
23
24
  import { appendLedger, readUlwLoopPlan, withUlwLoopMutationLock, writePlan } from "./plan-io.js";
24
25
  import {
25
26
  classifyExternalAuthorizationBlocker,
@@ -35,6 +36,7 @@ import type {
35
36
  UlwLoopQualityGate,
36
37
  } from "./types.js";
37
38
  import { iso, UlwLoopError } from "./types.js";
39
+ import { batchClosedBy, requireAllValidationBatchesClosed, requireBatchFinalReady, requireBatchGate } from "./validation-batch.js";
38
40
 
39
41
  export interface CheckpointUlwLoopArgs {
40
42
  readonly goalId: string;
@@ -169,9 +171,11 @@ export async function checkpointUlwLoop(
169
171
  if (args.status === "complete") {
170
172
  const aggregate = codexGoalMode(plan) === "aggregate";
171
173
  const final = isFinalRunCompletionCandidate(plan, goal);
174
+ const closesBatch = batchClosedBy(plan, goal.id) !== undefined;
172
175
  if (final) {
173
176
  requireAllCriteriaPass(goal);
174
177
  requireAllPlanCriteriaPass(plan);
178
+ requireAllValidationBatchesClosed(plan, goal.id);
175
179
  } else if (aggregate) requireEssentialCriteriaPass(goal);
176
180
  else requireAllCriteriaPass(goal);
177
181
  const snapshot = await readCodexGoalSnapshotInput(args.codexGoalJson, repoRoot);
@@ -218,8 +222,11 @@ export async function checkpointUlwLoop(
218
222
  "ulw_loop_codex_snapshot_mismatch",
219
223
  );
220
224
  }
225
+ if (closesBatch) requireBatchFinalReady(plan, goal);
226
+ if (closesBatch && args.qualityGateJson === undefined)
227
+ throw new UlwLoopError("Validation batch final checkpoint requires --quality-gate-json.", "ULW_LOOP_VALIDATION_BATCH_GATE_REQUIRED");
221
228
  if (final) aggregateCompletion = makeAggregateCompletion(now, evidence, codexGoal);
222
- if (final || aggregateCompletion !== undefined)
229
+ if (final || aggregateCompletion !== undefined || closesBatch) {
223
230
  qualityGate = validateQualityGate(await readJsonInput(args.qualityGateJson, repoRoot), {
224
231
  repoRoot,
225
232
  fs: QUALITY_GATE_FS,
@@ -227,6 +234,8 @@ export async function checkpointUlwLoop(
227
234
  ? { currentAttemptDir: ulwLoopAttemptEvidenceDir(goal.id, goal.attempt, scope) }
228
235
  : {}),
229
236
  });
237
+ requireBatchGate(plan, goal, qualityGate);
238
+ }
230
239
  goal.status = "complete";
231
240
  goal.completedAt = now;
232
241
  goal.evidence = evidence;
@@ -241,6 +250,8 @@ export async function checkpointUlwLoop(
241
250
  await writePlan(repoRoot, plan, scope);
242
251
  const ledgerEntry = buildLedger(now, args, goal, qualityGate, codexGoal, aggregateCompletion);
243
252
  await appendLedger(repoRoot, ledgerEntry, scope);
253
+ const closedBatch = args.status === "complete" ? batchClosedBy(plan, goal.id) : undefined;
254
+ if (closedBatch !== undefined) await appendLedger(repoRoot, { at: now, kind: "batch_closed", goalId: goal.id, message: closedBatch.batchId }, scope);
244
255
  return aggregateCompletion === undefined
245
256
  ? { plan, goal, ledgerEntry }
246
257
  : { plan, goal, ledgerEntry, aggregateCompletion };
@@ -5,7 +5,7 @@ import { UlwLoopError } from "./types.js";
5
5
 
6
6
  type RecordEvidenceCliArgs = { readonly goalId: string; readonly criterionId: string; readonly status: "pass" | "fail" | "blocked"; readonly evidence: string; readonly notes?: string };
7
7
 
8
- const VALUE_FLAGS = new Set("--brief --brief-file --session-id --codex-goal-mode --goal --goal-id --criterion-id --status --evidence --notes --codex-goal-json --quality-gate-json --kind --rationale --title --objective --target-goal-id --source --after-json --directive-json --directive-file --idempotency-key".split(" "));
8
+ const VALUE_FLAGS = new Set("--brief --brief-file --session-id --codex-goal-mode --validation-batch-json --goal --goal-id --criterion-id --status --evidence --notes --codex-goal-json --quality-gate-json --kind --rationale --title --objective --target-goal-id --source --after-json --directive-json --directive-file --idempotency-key --proposals-json".split(" "));
9
9
  const SUBCOMMANDS = new Set("create-goals status complete-goals criteria record-evidence checkpoint steer add-goal record-review-blockers".split(" "));
10
10
 
11
11
  export function hasFlag(argv: readonly string[], flag: string): boolean { return argv.includes(flag); }
@@ -1,9 +1,9 @@
1
+ import { checkpoint } from "./checkpoint-continuation.js";
1
2
  import { hasFlag, readValue } from "./cli-arg-parser.js";
2
3
  import { printJsonError, ULW_LOOP_HELP } from "./cli-output.js";
3
4
  import {
4
5
  addGoal,
5
6
  captureEvidence,
6
- checkpoint,
7
7
  completeGoals,
8
8
  createGoals,
9
9
  criteria,
@@ -2,13 +2,13 @@ import type { UlwLoopCodexGoalMode, UlwLoopItem, UlwLoopPlan } from "./types.js"
2
2
  import { UlwLoopError } from "./types.js";
3
3
 
4
4
  export const ULW_LOOP_HELP = `Usage:
5
- omo ulw-loop create-goals --brief "..." [--brief-file <path>] [--from-stdin] [--codex-goal-mode aggregate|per_story] [--force] [--json]
5
+ omo ulw-loop create-goals --brief "..." [--brief-file <path>] [--from-stdin] [--codex-goal-mode aggregate|per_story] [--validation-batch-json <json-or-path>] [--force] [--json]
6
6
  omo ulw-loop status [--json]
7
7
  omo ulw-loop complete-goals [--retry-failed] [--json]
8
8
  omo ulw-loop criteria --goal-id <id> [--json]
9
9
  omo ulw-loop record-evidence --goal-id <id> --criterion-id <id> --status pass|fail|blocked --evidence "..." [--notes "..."] [--json]
10
- omo ulw-loop checkpoint --goal-id <id> --status complete|failed|blocked --evidence "..." --codex-goal-json <...> [--quality-gate-json <...>] [--json]
11
- omo ulw-loop steer --kind <kind> ... --evidence "..." --rationale "..." [--json]
10
+ omo ulw-loop checkpoint --goal-id <id> --status complete|failed|blocked --evidence "..." --codex-goal-json <...> [--quality-gate-json <...>] [--no-advance] [--json]
11
+ omo ulw-loop steer --kind <kind> ... --evidence "..." --rationale "..." [--proposals-json <json-or-path>] [--json]
12
12
  omo ulw-loop add-goal --title "..." --objective "..." [--json]
13
13
  omo ulw-loop record-review-blockers --goal-id <id> --title "..." --objective "..." --evidence "..." --codex-goal-json <...> [--json]
14
14
 
@@ -1,6 +1,7 @@
1
1
  // biome-ignore-all format: keep this module under the mandated pure LOC budget.
2
2
  import { parseGoalArg, readJsonInput, readValue } from "./cli-arg-parser.js";
3
3
  import { printJson, printStatus } from "./cli-output.js";
4
+ import type { SteerUlwLoopBatchResult } from "./steering-batch.js";
4
5
  import type { SteerUlwLoopResult, UlwLoopSteeringChildGoal, UlwLoopSteeringMutationKind, UlwLoopSteeringProposal, UlwLoopSteeringSource, UlwLoopSuccessCriterionUserModel } from "./types.js";
5
6
  import { ULW_LOOP_STEERING_MUTATION_KINDS, ULW_LOOP_SUCCESS_CRITERION_USER_MODELS, UlwLoopError } from "./types.js";
6
7
 
@@ -31,6 +32,10 @@ function requiredGoal(argv: readonly string[]): string { const value = text(pars
31
32
  function readObject(value: object, key: string): unknown { return Object.entries(value).find(([name]) => name === key)?.[1]; }
32
33
  function isPlain(value: unknown): value is object { return typeof value === "object" && value !== null && !Array.isArray(value); }
33
34
  function objectText(value: object, key: string): string | undefined { const candidate = readObject(value, key); return typeof candidate === "string" ? candidate : undefined; }
35
+ function objectStrings(value: object, key: string): string[] | undefined { const candidate = readObject(value, key); return Array.isArray(candidate) && candidate.every((item) => typeof item === "string") ? candidate : undefined; }
36
+ function objectChildren(value: object, key: string): UlwLoopSteeringChildGoal[] | undefined { const candidate = readObject(value, key); if (!Array.isArray(candidate)) return undefined; const parsed: UlwLoopSteeringChildGoal[] = []; for (const item of candidate) { const next = child(item); if (next === null) return fail(`${key} entries require title/objective.`, "ULW_LOOP_STEERING_CHILD_INVALID", { key }); parsed.push(next); } return parsed; }
37
+ function isProposalKind(value: unknown): value is UlwLoopSteeringMutationKind { return typeof value === "string" && ULW_LOOP_STEERING_MUTATION_KINDS.some((kind) => kind === value); }
38
+ function isProposalSource(value: unknown): value is UlwLoopSteeringSource { return typeof value === "string" && SOURCES.some((source) => source === value); }
34
39
 
35
40
  export function parseSteeringKind(argv: readonly string[]): UlwLoopSteeringMutationKind {
36
41
  const value = readValue(argv, "--kind");
@@ -98,6 +103,43 @@ export function normalizeSteeringProposal(proposal: CliSteeringProposal): CliSte
98
103
  return { kind: proposal.kind, source: proposal.source, evidence, rationale, ...(goalId === undefined ? {} : { goalId }), ...(targetGoalId === undefined ? {} : { targetGoalId }), ...(targetGoalIds === undefined ? {} : { targetGoalIds }), ...(criterionId === undefined ? {} : { criterionId }), ...(title === undefined ? {} : { title }), ...(objective === undefined ? {} : { objective }), ...(childGoals === undefined ? {} : { childGoals }), ...(revisedTitle === undefined ? {} : { revisedTitle }), ...(revisedObjective === undefined ? {} : { revisedObjective }), ...(pendingOrder === undefined ? {} : { pendingOrder }), ...(blockedReason === undefined ? {} : { blockedReason }), ...(proposal.after === undefined ? {} : { after: proposal.after }), ...(directiveText === undefined ? {} : { directiveText }), ...(promptSignature === undefined ? {} : { promptSignature }), ...(idempotencyKey === undefined ? {} : { idempotencyKey }), ...(proposal.now === undefined ? {} : { now: proposal.now }), ...(scenario === undefined ? {} : { scenario }), ...(expectedEvidence === undefined ? {} : { expectedEvidence }), ...(proposal.userModel === undefined ? {} : { userModel: proposal.userModel }) };
99
104
  }
100
105
 
106
+ export async function parseSteeringProposals(argv: readonly string[]): Promise<readonly CliSteeringProposal[]> {
107
+ const input = text(readValue(argv, "--proposals-json"), "--proposals-json");
108
+ if (input === undefined) return [await parseSteeringProposal(argv)];
109
+ if (readValue(argv, "--kind") !== undefined) return fail("--kind and --proposals-json are mutually exclusive.", "ULW_LOOP_STEERING_BATCH_CONFLICT", { flags: ["--kind", "--proposals-json"] });
110
+ const raw = await readJsonInput(input);
111
+ if (!Array.isArray(raw) || raw.length === 0) return fail("--proposals-json must be a non-empty JSON array.", "ULW_LOOP_STEERING_BATCH_ARRAY_REQUIRED", { flag: "--proposals-json" });
112
+ const proposals: CliSteeringProposal[] = [];
113
+ for (const item of raw) proposals.push(normalizeSteeringProposal(proposalFromObject(item)));
114
+ return proposals;
115
+ }
116
+
117
+ function proposalFromObject(value: unknown): CliSteeringProposal {
118
+ if (!isPlain(value)) return fail("--proposals-json entries must be objects.", "ULW_LOOP_STEERING_BATCH_ITEM_INVALID", { flag: "--proposals-json" });
119
+ const kind = readObject(value, "kind"); const source = readObject(value, "source") ?? "cli";
120
+ if (!isProposalKind(kind)) return fail(`Invalid batch steering kind: ${String(kind)}.`, "ULW_LOOP_STEERING_KIND_INVALID", { value: kind });
121
+ if (!isProposalSource(source)) return fail(`Invalid batch steering source: ${String(source)}.`, "ULW_LOOP_STEERING_SOURCE_INVALID", { value: source });
122
+ let proposal: CliSteeringProposal = { kind, source, evidence: objectText(value, "evidence") ?? "", rationale: objectText(value, "rationale") ?? "" };
123
+ const goalId = objectText(value, "goalId"); const targetGoalId = objectText(value, "targetGoalId"); const criterionId = objectText(value, "criterionId");
124
+ const title = objectText(value, "title"); const objective = objectText(value, "objective"); const revisedTitle = objectText(value, "revisedTitle"); const revisedObjective = objectText(value, "revisedObjective");
125
+ const scenario = objectText(value, "scenario"); const expectedEvidence = objectText(value, "expectedEvidence"); const idempotencyKey = objectText(value, "idempotencyKey");
126
+ const targetGoalIds = objectStrings(value, "targetGoalIds"); const pendingOrder = objectStrings(value, "pendingOrder"); const childGoals = objectChildren(value, "childGoals");
127
+ if (goalId !== undefined) proposal = { ...proposal, goalId };
128
+ if (targetGoalId !== undefined) proposal = { ...proposal, targetGoalId };
129
+ if (criterionId !== undefined) proposal = { ...proposal, criterionId };
130
+ if (title !== undefined) proposal = { ...proposal, title };
131
+ if (objective !== undefined) proposal = { ...proposal, objective };
132
+ if (revisedTitle !== undefined) proposal = { ...proposal, revisedTitle };
133
+ if (revisedObjective !== undefined) proposal = { ...proposal, revisedObjective };
134
+ if (scenario !== undefined) proposal = { ...proposal, scenario };
135
+ if (expectedEvidence !== undefined) proposal = { ...proposal, expectedEvidence };
136
+ if (idempotencyKey !== undefined) proposal = { ...proposal, idempotencyKey };
137
+ if (targetGoalIds !== undefined) proposal = { ...proposal, targetGoalIds };
138
+ if (pendingOrder !== undefined) proposal = { ...proposal, pendingOrder };
139
+ if (childGoals !== undefined) proposal = { ...proposal, childGoals };
140
+ return proposal;
141
+ }
142
+
101
143
  export function printSteerResult(result: SteerUlwLoopResult, json: boolean): void {
102
144
  if (json) { printJson({ ok: result.accepted, accepted: result.accepted, rejectedReasons: result.rejectedReasons, deduped: result.deduped, audit: result.audit, plan: result.plan }); return; }
103
145
  const outcome = result.deduped ? "deduped" : result.accepted ? "accepted" : "rejected";
@@ -106,3 +148,10 @@ export function printSteerResult(result: SteerUlwLoopResult, json: boolean): voi
106
148
  if (result.audit.idempotencyKey !== undefined) process.stdout.write(`idempotency-key: ${result.audit.idempotencyKey}\n`);
107
149
  printStatus(result.plan);
108
150
  }
151
+
152
+ export function printSteerBatchResult(result: SteerUlwLoopBatchResult, json: boolean): void {
153
+ if (json) { printJson({ ok: result.accepted, accepted: result.accepted, rejectedReasons: result.rejectedReasons, results: result.results, plan: result.plan }); return; }
154
+ process.stdout.write(`ulw-loop steer batch: ${result.accepted ? "accepted" : "rejected"} ${result.results.length} proposal(s)\n`);
155
+ if (result.rejectedReasons.length > 0) process.stdout.write(`rejected: ${result.rejectedReasons.join("; ")}\n`);
156
+ printStatus(result.plan);
157
+ }
@@ -1,6 +1,5 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
 
3
- import { type CheckpointUlwLoopArgs, checkpointUlwLoop } from "./checkpoint.js";
4
3
  import {
5
4
  hasFlag,
6
5
  parseCodexGoalJson,
@@ -10,7 +9,7 @@ import {
10
9
  readValue,
11
10
  } from "./cli-arg-parser.js";
12
11
  import { blockedDecisionHandoff, normalizeCodexGoalMode, printJson, printStatus } from "./cli-output.js";
13
- import { parseSteeringProposal, printSteerResult } from "./cli-steering.js";
12
+ import { parseSteeringProposals, printSteerBatchResult, printSteerResult } from "./cli-steering.js";
14
13
  import { buildCodexGoalInstruction } from "./codex-goal-instruction.js";
15
14
  import { recordEvidence } from "./evidence.js";
16
15
  import { isEssentialCriterion } from "./goal-status.js";
@@ -19,11 +18,10 @@ import { addUlwLoopGoal, createUlwLoopPlan, startNextUlwLoop, summarizeUlwLoopPl
19
18
  import { readUlwLoopPlan } from "./plan-io.js";
20
19
  import { recordFinalReviewBlockers } from "./review-blockers.js";
21
20
  import { steerUlwLoop } from "./steering.js";
21
+ import { steerUlwLoopBatch } from "./steering-batch.js";
22
22
  import type { UlwLoopItem } from "./types.js";
23
23
  import { UlwLoopError } from "./types.js";
24
24
 
25
- type CheckpointStatus = "complete" | "failed" | "blocked";
26
-
27
25
  export async function createGoals(
28
26
  repoRoot: string,
29
27
  argv: readonly string[],
@@ -42,12 +40,14 @@ export async function createGoals(
42
40
  "ULW_LOOP_BRIEF_REQUIRED",
43
41
  );
44
42
  }
43
+ const validationBatchesJson = readValue(argv, "--validation-batch-json");
45
44
  const plan = await createUlwLoopPlan(
46
45
  repoRoot,
47
46
  {
48
47
  brief,
49
48
  codexGoalMode: normalizeCodexGoalMode(readValue(argv, "--codex-goal-mode")),
50
49
  force: hasFlag(argv, "--force"),
50
+ ...(validationBatchesJson === undefined ? {} : { validationBatchesJson }),
51
51
  },
52
52
  scope,
53
53
  );
@@ -105,44 +105,21 @@ export async function completeGoals(
105
105
  return 0;
106
106
  }
107
107
 
108
- export async function checkpoint(
109
- repoRoot: string,
110
- argv: readonly string[],
111
- json: boolean,
112
- scope?: UlwLoopScope,
113
- ): Promise<number> {
114
- const goalId = required(argv, "--goal-id");
115
- const statusValue = checkpointStatus(required(argv, "--status"));
116
- const evidence = required(argv, "--evidence");
117
- const codexGoalJson = await parseCodexGoalJson(
118
- statusValue === "complete" ? required(argv, "--codex-goal-json") : readValue(argv, "--codex-goal-json"),
119
- );
120
- if (statusValue === "complete" && codexGoalJson === undefined) {
121
- throw new UlwLoopError("Missing --codex-goal-json.", "ULW_LOOP_CODEX_GOAL_JSON_REQUIRED");
122
- }
123
- const qualityGateJson = readValue(argv, "--quality-gate-json");
124
- const args: CheckpointUlwLoopArgs = {
125
- goalId,
126
- status: statusValue,
127
- evidence,
128
- ...(codexGoalJson === undefined ? {} : { codexGoalJson }),
129
- ...(qualityGateJson === undefined ? {} : { qualityGateJson }),
130
- };
131
- const result = await checkpointUlwLoop(repoRoot, args, scope);
132
- if (json) printJson({ ok: true, ...result, summary: summarizeUlwLoopPlan(result.plan) });
133
- else process.stdout.write(`ulw-loop checkpoint: ${result.goal.id} -> ${result.goal.status}\n`);
134
- return 0;
135
- }
136
-
137
108
  export async function steer(
138
109
  repoRoot: string,
139
110
  argv: readonly string[],
140
111
  json: boolean,
141
112
  scope?: UlwLoopScope,
142
113
  ): Promise<number> {
143
- const proposal = await parseSteeringProposal(argv);
144
- const result = await steerUlwLoop(repoRoot, proposal, scope);
145
- printSteerResult(result, json);
114
+ const proposals = await parseSteeringProposals(argv);
115
+ const single = proposals[0];
116
+ if (single !== undefined && proposals.length === 1 && readValue(argv, "--proposals-json") === undefined) {
117
+ const result = await steerUlwLoop(repoRoot, single, scope);
118
+ printSteerResult(result, json);
119
+ return result.accepted ? 0 : 1;
120
+ }
121
+ const result = await steerUlwLoopBatch(repoRoot, proposals, scope);
122
+ printSteerBatchResult(result, json);
146
123
  return result.accepted ? 0 : 1;
147
124
  }
148
125
 
@@ -245,15 +222,6 @@ function required(argv: readonly string[], flag: string): string {
245
222
  throw new UlwLoopError(`Missing ${flag}.`, "ULW_LOOP_ARGUMENT_MISSING", { details: { flag } });
246
223
  }
247
224
 
248
- function checkpointStatus(value: string): CheckpointStatus {
249
- if (value === "complete" || value === "failed" || value === "blocked") return value;
250
- throw new UlwLoopError(
251
- "Missing or invalid --status; expected complete, failed, or blocked.",
252
- "ULW_LOOP_STATUS_INVALID",
253
- { details: { status: value } },
254
- );
255
- }
256
-
257
225
  function findGoal(plan: { readonly goals: readonly UlwLoopItem[] }, goalId: string): UlwLoopItem {
258
226
  const goal = plan.goals.find((candidate) => candidate.id === goalId);
259
227
  if (goal !== undefined) return goal;
@@ -1,3 +1,4 @@
1
+ // biome-ignore-all format: long agent-facing instruction strings are intentionally stable.
1
2
  import {
2
3
  codexGoalMode,
3
4
  expectedCodexObjective,
@@ -60,5 +60,7 @@ export const ULW_LOOP_LEDGER_EVENT_KINDS = [
60
60
  "criterion_failed",
61
61
  "criterion_blocked",
62
62
  "criteria_revised",
63
+ "batch_closed",
64
+ "batch_updated",
63
65
  ] as const satisfies readonly string[];
64
66
  export type UlwLoopLedgerEventKind = (typeof ULW_LOOP_LEDGER_EVENT_KINDS)[number];
@@ -55,6 +55,12 @@ export interface UlwLoopAggregateCompletion {
55
55
  codexGoal?: unknown;
56
56
  }
57
57
 
58
+ export interface UlwLoopValidationBatch {
59
+ readonly batchId: string;
60
+ readonly memberIds: readonly string[];
61
+ readonly finalGoalId: string;
62
+ }
63
+
58
64
  export interface UlwLoopPlan {
59
65
  version: 1;
60
66
  evidenceLayoutVersion?: 2;
@@ -68,6 +74,7 @@ export interface UlwLoopPlan {
68
74
  codexObjectiveAliases?: string[];
69
75
  aggregateCompletion?: UlwLoopAggregateCompletion;
70
76
  activeGoalId?: string;
77
+ validationBatches?: readonly UlwLoopValidationBatch[];
71
78
  goals: UlwLoopItem[];
72
79
  }
73
80
 
@@ -21,6 +21,10 @@ function isResolvedStatus(status: UlwLoopStatus): boolean {
21
21
  return status === "complete";
22
22
  }
23
23
 
24
+ export function isMemberResolved(goal: UlwLoopItem, plan: UlwLoopPlan): boolean {
25
+ return isResolvedStatus(goal.status) || isSupersededResolved(goal, plan);
26
+ }
27
+
24
28
  function isSupersededResolved(goal: UlwLoopItem, plan: UlwLoopPlan): boolean {
25
29
  if (goal.steeringStatus !== "superseded") return false;
26
30
  const replacements = goal.supersededBy ?? [];
@@ -16,6 +16,7 @@ import { appendGoalToPlan, deriveGoalCandidates, makeGoal } from "./plan-goal-fa
16
16
  import { appendLedger, readUlwLoopPlan, withUlwLoopMutationLock, writePlan } from "./plan-io.js";
17
17
  import type { UlwLoopCodexGoalMode, UlwLoopItem, UlwLoopPlan, UlwLoopSuccessCriterion } from "./types.js";
18
18
  import { iso, UlwLoopError } from "./types.js";
19
+ import { parseValidationBatches } from "./validation-batch.js";
19
20
 
20
21
  export { deriveGoalCandidates, seedDefaultSuccessCriteria } from "./plan-goal-factory.js";
21
22
 
@@ -57,7 +58,7 @@ function clearGoalBlockerFields(goal: UlwLoopItem): void {
57
58
 
58
59
  export async function createUlwLoopPlan(
59
60
  repoRoot: string,
60
- args: { brief: string; codexGoalMode?: UlwLoopCodexGoalMode; force?: boolean },
61
+ args: { brief: string; codexGoalMode?: UlwLoopCodexGoalMode; force?: boolean; validationBatchesJson?: string },
61
62
  scope?: UlwLoopScope,
62
63
  ): Promise<UlwLoopPlan> {
63
64
  return withUlwLoopMutationLock(repoRoot, scope, async () => {
@@ -84,6 +85,8 @@ export async function createUlwLoopPlan(
84
85
  codexGoalMode: args.codexGoalMode ?? "aggregate",
85
86
  goals,
86
87
  };
88
+ const validationBatches = await parseValidationBatches(args.validationBatchesJson, goals);
89
+ if (validationBatches !== undefined) plan.validationBatches = validationBatches;
87
90
  if (plan.codexGoalMode === "aggregate") plan.codexObjective = aggregateCodexObjectiveForScope(scope);
88
91
  await mkdir(ulwLoopDir(repoRoot, scope), { recursive: true });
89
92
  await writeFile(
@@ -27,7 +27,12 @@ function isLegacyEnumeratedAggregateObjective(objective: string | undefined): ob
27
27
  }
28
28
 
29
29
  function isSteeringKind(value: unknown): value is UlwLoopLedgerEntry["kind"] {
30
- return value === "steering_accepted" || value === "steering_rejected" || value === "criteria_revised";
30
+ return (
31
+ value === "steering_accepted" ||
32
+ value === "steering_rejected" ||
33
+ value === "criteria_revised" ||
34
+ value === "batch_updated"
35
+ );
31
36
  }
32
37
 
33
38
  export async function withUlwLoopMutationLock<T>(repoRoot: string, fn: () => Promise<T>): Promise<T>;
@@ -112,8 +117,21 @@ export async function writePlan(repoRoot: string, plan: UlwLoopPlan, scope?: Ulw
112
117
  }
113
118
 
114
119
  export async function appendLedger(repoRoot: string, entry: UlwLoopLedgerEntry, scope?: UlwLoopScope): Promise<void> {
120
+ await appendLedgerEntries(repoRoot, [entry], scope);
121
+ }
122
+
123
+ export async function appendLedgerEntries(
124
+ repoRoot: string,
125
+ entries: readonly UlwLoopLedgerEntry[],
126
+ scope?: UlwLoopScope,
127
+ ): Promise<void> {
128
+ if (entries.length === 0) return;
115
129
  await mkdir(ulwLoopDir(repoRoot, scope), { recursive: true });
116
- await appendFile(ulwLoopLedgerPath(repoRoot, scope), `${JSON.stringify(entry)}\n`, "utf8");
130
+ await appendFile(
131
+ ulwLoopLedgerPath(repoRoot, scope),
132
+ `${entries.map((entry) => JSON.stringify(entry)).join("\n")}\n`,
133
+ "utf8",
134
+ );
117
135
  }
118
136
 
119
137
  /**
@@ -1,4 +1,4 @@
1
- import { literal, invalid, textField } from "./quality-gate-fields.js";
1
+ import { invalid, literal, textField } from "./quality-gate-fields.js";
2
2
 
3
3
  export function passedVerdict(value: unknown, field: string): "passed" {
4
4
  if (value === "not_applicable") invalid(`${field} must not be not_applicable.`, field);