oh-my-opencode 4.8.1 → 4.9.1

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 (509) hide show
  1. package/dist/agents/prometheus/system-prompt.d.ts +1 -1
  2. package/dist/agents/sisyphus/claude-fable-5.d.ts +19 -0
  3. package/dist/agents/sisyphus/claude-opus-4-7.d.ts +3 -1
  4. package/dist/agents/sisyphus/claude-opus-4-8.d.ts +19 -0
  5. package/dist/agents/sisyphus/index.d.ts +4 -0
  6. package/dist/agents/types.d.ts +2 -2
  7. package/dist/cli/doctor/checks/codex.d.ts +1 -0
  8. package/dist/cli/doctor/checks/dependencies.d.ts +2 -2
  9. package/dist/cli/doctor/checks/tools-gh.d.ts +8 -1
  10. package/dist/cli/doctor/index.d.ts +1 -0
  11. package/dist/cli/doctor/types.d.ts +2 -0
  12. package/dist/cli/index.js +1908 -787
  13. package/dist/cli/install-codex/codex-config-permissions.d.ts +1 -1
  14. package/dist/cli/install-codex/codex-config-plugins.d.ts +1 -0
  15. package/dist/cli/install-codex/codex-config-toml.d.ts +1 -0
  16. package/dist/cli/install-codex/codex-installer-bin-dir.d.ts +8 -0
  17. package/dist/cli/install-codex/install-codex.d.ts +1 -8
  18. package/dist/cli/install-codex/lsp-daemon-reaper.d.ts +5 -0
  19. package/dist/cli/sparkshell-condense.d.ts +10 -0
  20. package/dist/cli/sparkshell-parse.d.ts +3 -0
  21. package/dist/cli/sparkshell-session-context.d.ts +20 -0
  22. package/dist/cli/sparkshell-spark.d.ts +23 -0
  23. package/dist/cli/sparkshell.d.ts +8 -1
  24. package/dist/cli-node/index.js +92564 -0
  25. package/dist/config/schema/agent-names.d.ts +2 -0
  26. package/dist/config/schema/hooks.d.ts +0 -2
  27. package/dist/config/schema/keyword-detector.d.ts +0 -6
  28. package/dist/config/schema/oh-my-opencode-config.d.ts +2 -4
  29. package/dist/create-hooks.d.ts +0 -2
  30. package/dist/features/background-agent/parent-wake-dedupe.d.ts +2 -0
  31. package/dist/features/background-agent/parent-wake-flush-runner.d.ts +2 -0
  32. package/dist/features/background-agent/parent-wake-prompt-dispatch.d.ts +1 -0
  33. package/dist/features/background-agent/parent-wake-session-history.d.ts +4 -0
  34. package/dist/features/background-agent/parent-wake-session-inspector.d.ts +1 -0
  35. package/dist/features/builtin-commands/templates/handoff.d.ts +1 -1
  36. package/dist/features/builtin-skills/index.d.ts +1 -1
  37. package/dist/features/builtin-skills/skills.d.ts +4 -0
  38. package/dist/features/opencode-runtime-skills/source-server.d.ts +16 -1
  39. package/dist/features/opencode-skill-loader/skill-definition-record.d.ts +2 -0
  40. package/dist/features/team-mode/tools/lifecycle-test-fixture.d.ts +2 -0
  41. package/dist/features/team-mode/types.d.ts +1 -0
  42. package/dist/features/tmux-subagent/failed-readiness-cache.d.ts +28 -0
  43. package/dist/features/tmux-subagent/manager.d.ts +1 -9
  44. package/dist/features/tmux-subagent/resolve-server-url.d.ts +3 -0
  45. package/dist/hooks/anthropic-context-window-limit-recovery/executor.d.ts +1 -1
  46. package/dist/hooks/index.d.ts +0 -1
  47. package/dist/hooks/keyword-detector/constants.d.ts +0 -4
  48. package/dist/hooks/keyword-detector/ultrawork/source-detector.d.ts +1 -1
  49. package/dist/index.js +9001 -1795
  50. package/dist/oh-my-opencode.schema.json +2 -4
  51. package/dist/plugin/chat-params.d.ts +1 -8
  52. package/dist/plugin/hooks/create-core-hooks.d.ts +0 -2
  53. package/dist/plugin/hooks/create-session-hooks.d.ts +0 -2
  54. package/dist/plugin/hooks/create-transform-hooks.d.ts +1 -2
  55. package/dist/plugin/messages-transform.d.ts +0 -1
  56. package/dist/shared/model-availability.d.ts +10 -2
  57. package/dist/shared/module-resolution-failure.d.ts +7 -0
  58. package/package.json +25 -18
  59. package/packages/ast-grep-mcp/dist/cli.js +2 -10
  60. package/packages/git-bash-mcp/dist/cli.js +11 -4
  61. package/packages/lsp-daemon/dist/cli.d.ts +2 -0
  62. package/packages/lsp-daemon/dist/cli.js +3711 -0
  63. package/packages/lsp-daemon/dist/daemon-client.d.ts +19 -0
  64. package/packages/lsp-daemon/dist/daemon-client.js +114 -0
  65. package/packages/lsp-daemon/dist/daemon-server.d.ts +12 -0
  66. package/packages/lsp-daemon/dist/daemon-server.js +106 -0
  67. package/packages/lsp-daemon/dist/ensure-daemon.d.ts +21 -0
  68. package/packages/lsp-daemon/dist/ensure-daemon.js +97 -0
  69. package/packages/lsp-daemon/dist/index.d.ts +5 -0
  70. package/packages/lsp-daemon/dist/index.js +3573 -0
  71. package/packages/lsp-daemon/dist/lock.d.ts +7 -0
  72. package/packages/lsp-daemon/dist/lock.js +61 -0
  73. package/packages/lsp-daemon/dist/package.json +6 -0
  74. package/packages/lsp-daemon/dist/paths.d.ts +11 -0
  75. package/packages/lsp-daemon/dist/paths.js +49 -0
  76. package/packages/lsp-daemon/dist/proxy.d.ts +10 -0
  77. package/packages/lsp-daemon/dist/proxy.js +61 -0
  78. package/packages/lsp-daemon/dist/request-routing.d.ts +9 -0
  79. package/packages/lsp-daemon/dist/request-routing.js +44 -0
  80. package/packages/lsp-daemon/dist/run-daemon.d.ts +1 -0
  81. package/packages/lsp-daemon/dist/run-daemon.js +11 -0
  82. package/packages/lsp-daemon/dist/socket-jsonrpc.d.ts +5 -0
  83. package/packages/lsp-daemon/dist/socket-jsonrpc.js +25 -0
  84. package/packages/lsp-daemon/package.json +38 -0
  85. package/packages/lsp-tools-mcp/dist/cli.js +0 -0
  86. package/packages/lsp-tools-mcp/dist/lsp/client-wrapper.js +40 -17
  87. package/packages/lsp-tools-mcp/dist/lsp/client.js +11 -9
  88. package/packages/lsp-tools-mcp/dist/lsp/config-loader.js +5 -5
  89. package/packages/lsp-tools-mcp/dist/lsp/directory-diagnostics.js +5 -3
  90. package/packages/lsp-tools-mcp/dist/lsp/effective-extension.d.ts +1 -0
  91. package/packages/lsp-tools-mcp/dist/lsp/effective-extension.js +8 -0
  92. package/packages/lsp-tools-mcp/dist/lsp/infer-extension.js +3 -2
  93. package/packages/lsp-tools-mcp/dist/lsp/language-mappings.js +1 -0
  94. package/packages/lsp-tools-mcp/dist/lsp/server-definitions.js +12 -0
  95. package/packages/lsp-tools-mcp/dist/lsp/server-install-state.d.ts +12 -0
  96. package/packages/lsp-tools-mcp/dist/lsp/server-install-state.js +51 -0
  97. package/packages/lsp-tools-mcp/dist/lsp/workspace-edit.js +2 -1
  98. package/packages/lsp-tools-mcp/dist/request-context.d.ts +7 -0
  99. package/packages/lsp-tools-mcp/dist/request-context.js +14 -0
  100. package/packages/lsp-tools-mcp/dist/tools.js +44 -1
  101. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +46 -33
  102. package/packages/omo-codex/plugin/.mcp.json +1 -1
  103. package/packages/omo-codex/plugin/components/comment-checker/dist/apply-patch.d.ts +7 -0
  104. package/packages/omo-codex/plugin/components/comment-checker/dist/apply-patch.js +173 -0
  105. package/packages/omo-codex/plugin/components/comment-checker/dist/cli.d.ts +2 -0
  106. package/packages/omo-codex/plugin/components/comment-checker/dist/cli.js +10 -0
  107. package/packages/omo-codex/plugin/components/comment-checker/dist/codex-hook.d.ts +22 -0
  108. package/packages/omo-codex/plugin/components/comment-checker/dist/codex-hook.js +165 -0
  109. package/packages/omo-codex/plugin/components/comment-checker/dist/core-values.d.ts +1 -0
  110. package/packages/omo-codex/plugin/components/comment-checker/dist/core-values.js +1 -0
  111. package/packages/omo-codex/plugin/components/comment-checker/dist/core.d.ts +5 -0
  112. package/packages/omo-codex/plugin/components/comment-checker/dist/core.js +4 -0
  113. package/packages/omo-codex/plugin/components/comment-checker/dist/hook-input.d.ts +6 -0
  114. package/packages/omo-codex/plugin/components/comment-checker/dist/hook-input.js +10 -0
  115. package/packages/omo-codex/plugin/components/comment-checker/dist/record.d.ts +2 -0
  116. package/packages/omo-codex/plugin/components/comment-checker/dist/record.js +11 -0
  117. package/packages/omo-codex/plugin/components/comment-checker/dist/request-extractor.d.ts +3 -0
  118. package/packages/omo-codex/plugin/components/comment-checker/dist/request-extractor.js +104 -0
  119. package/packages/omo-codex/plugin/components/comment-checker/dist/runner.d.ts +26 -0
  120. package/packages/omo-codex/plugin/components/comment-checker/dist/runner.js +144 -0
  121. package/packages/omo-codex/plugin/components/comment-checker/dist/types.d.ts +43 -0
  122. package/packages/omo-codex/plugin/components/comment-checker/dist/types.js +1 -0
  123. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  124. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  125. package/packages/omo-codex/plugin/components/git-bash/dist/cli.d.ts +2 -0
  126. package/packages/omo-codex/plugin/components/git-bash/dist/cli.js +29 -0
  127. package/packages/omo-codex/plugin/components/git-bash/dist/codex-hook.d.ts +28 -0
  128. package/packages/omo-codex/plugin/components/git-bash/dist/codex-hook.js +137 -0
  129. package/packages/omo-codex/plugin/components/git-bash/dist/index.d.ts +1 -0
  130. package/packages/omo-codex/plugin/components/git-bash/dist/index.js +1 -0
  131. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  132. package/packages/omo-codex/plugin/components/git-bash/package.json +5 -2
  133. package/packages/omo-codex/plugin/components/lsp/.mcp.json +1 -1
  134. package/packages/omo-codex/plugin/components/lsp/dist/cli.d.ts +2 -0
  135. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +42 -0
  136. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook-cli.d.ts +2 -0
  137. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook-cli.js +40 -0
  138. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.d.ts +16 -0
  139. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +180 -0
  140. package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.d.ts +12 -0
  141. package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.js +95 -0
  142. package/packages/omo-codex/plugin/components/lsp/dist/mutated-file-paths.d.ts +6 -0
  143. package/packages/omo-codex/plugin/components/lsp/dist/mutated-file-paths.js +79 -0
  144. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  145. package/packages/omo-codex/plugin/components/lsp/package.json +7 -7
  146. package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-daemon.mjs +68 -0
  147. package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-tools.mjs +45 -22
  148. package/packages/omo-codex/plugin/components/lsp/src/cli.ts +1 -1
  149. package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +1 -1
  150. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +6 -2
  151. package/packages/omo-codex/plugin/components/lsp/src/lsp-session-state.ts +4 -0
  152. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-unavailable.test.ts +68 -0
  153. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +8 -20
  154. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus.md +69 -96
  155. package/packages/omo-codex/plugin/components/rules/dist/cli.d.ts +2 -0
  156. package/packages/omo-codex/plugin/components/rules/dist/cli.js +118 -0
  157. package/packages/omo-codex/plugin/components/rules/dist/codex-hook-options.d.ts +5 -0
  158. package/packages/omo-codex/plugin/components/rules/dist/codex-hook-options.js +1 -0
  159. package/packages/omo-codex/plugin/components/rules/dist/codex-hook.d.ts +47 -0
  160. package/packages/omo-codex/plugin/components/rules/dist/codex-hook.js +127 -0
  161. package/packages/omo-codex/plugin/components/rules/dist/config.d.ts +2 -0
  162. package/packages/omo-codex/plugin/components/rules/dist/config.js +100 -0
  163. package/packages/omo-codex/plugin/components/rules/dist/context-pressure.d.ts +2 -0
  164. package/packages/omo-codex/plugin/components/rules/dist/context-pressure.js +26 -0
  165. package/packages/omo-codex/plugin/components/rules/dist/debug-log.d.ts +8 -0
  166. package/packages/omo-codex/plugin/components/rules/dist/debug-log.js +36 -0
  167. package/packages/omo-codex/plugin/components/rules/dist/dynamic-target-fingerprints.d.ts +7 -0
  168. package/packages/omo-codex/plugin/components/rules/dist/dynamic-target-fingerprints.js +65 -0
  169. package/packages/omo-codex/plugin/components/rules/dist/event-budget.d.ts +3 -0
  170. package/packages/omo-codex/plugin/components/rules/dist/event-budget.js +14 -0
  171. package/packages/omo-codex/plugin/components/rules/dist/hook-output.d.ts +2 -0
  172. package/packages/omo-codex/plugin/components/rules/dist/hook-output.js +24 -0
  173. package/packages/omo-codex/plugin/components/rules/dist/path-utils.d.ts +4 -0
  174. package/packages/omo-codex/plugin/components/rules/dist/path-utils.js +24 -0
  175. package/packages/omo-codex/plugin/components/rules/dist/persistent-cache.d.ts +13 -0
  176. package/packages/omo-codex/plugin/components/rules/dist/persistent-cache.js +172 -0
  177. package/packages/omo-codex/plugin/components/rules/dist/post-compact-budget.d.ts +6 -0
  178. package/packages/omo-codex/plugin/components/rules/dist/post-compact-budget.js +74 -0
  179. package/packages/omo-codex/plugin/components/rules/dist/post-compact-claim.d.ts +4 -0
  180. package/packages/omo-codex/plugin/components/rules/dist/post-compact-claim.js +6 -0
  181. package/packages/omo-codex/plugin/components/rules/dist/post-compact-directive.d.ts +1 -0
  182. package/packages/omo-codex/plugin/components/rules/dist/post-compact-directive.js +32 -0
  183. package/packages/omo-codex/plugin/components/rules/dist/post-compact-state.d.ts +13 -0
  184. package/packages/omo-codex/plugin/components/rules/dist/post-compact-state.js +29 -0
  185. package/packages/omo-codex/plugin/components/rules/dist/rules/cache.d.ts +9 -0
  186. package/packages/omo-codex/plugin/components/rules/dist/rules/cache.js +51 -0
  187. package/packages/omo-codex/plugin/components/rules/dist/rules/constants.d.ts +70 -0
  188. package/packages/omo-codex/plugin/components/rules/dist/rules/constants.js +101 -0
  189. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-dynamic-cache.d.ts +5 -0
  190. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-dynamic-cache.js +60 -0
  191. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-dynamic-loader.d.ts +6 -0
  192. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-dynamic-loader.js +61 -0
  193. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-loader.d.ts +7 -0
  194. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-loader.js +60 -0
  195. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-paths.d.ts +11 -0
  196. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-paths.js +75 -0
  197. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-static-loader.d.ts +6 -0
  198. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-static-loader.js +29 -0
  199. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-types.d.ts +44 -0
  200. package/packages/omo-codex/plugin/components/rules/dist/rules/engine-types.js +1 -0
  201. package/packages/omo-codex/plugin/components/rules/dist/rules/engine.d.ts +5 -0
  202. package/packages/omo-codex/plugin/components/rules/dist/rules/engine.js +85 -0
  203. package/packages/omo-codex/plugin/components/rules/dist/rules/errors.d.ts +6 -0
  204. package/packages/omo-codex/plugin/components/rules/dist/rules/errors.js +12 -0
  205. package/packages/omo-codex/plugin/components/rules/dist/rules/finder-cache.d.ts +14 -0
  206. package/packages/omo-codex/plugin/components/rules/dist/rules/finder-cache.js +51 -0
  207. package/packages/omo-codex/plugin/components/rules/dist/rules/finder-paths.d.ts +6 -0
  208. package/packages/omo-codex/plugin/components/rules/dist/rules/finder-paths.js +33 -0
  209. package/packages/omo-codex/plugin/components/rules/dist/rules/finder-sources.d.ts +5 -0
  210. package/packages/omo-codex/plugin/components/rules/dist/rules/finder-sources.js +40 -0
  211. package/packages/omo-codex/plugin/components/rules/dist/rules/finder.d.ts +28 -0
  212. package/packages/omo-codex/plugin/components/rules/dist/rules/finder.js +146 -0
  213. package/packages/omo-codex/plugin/components/rules/dist/rules/formatter.d.ts +7 -0
  214. package/packages/omo-codex/plugin/components/rules/dist/rules/formatter.js +112 -0
  215. package/packages/omo-codex/plugin/components/rules/dist/rules/matcher.d.ts +18 -0
  216. package/packages/omo-codex/plugin/components/rules/dist/rules/matcher.js +93 -0
  217. package/packages/omo-codex/plugin/components/rules/dist/rules/ordering.d.ts +3 -0
  218. package/packages/omo-codex/plugin/components/rules/dist/rules/ordering.js +27 -0
  219. package/packages/omo-codex/plugin/components/rules/dist/rules/parser-frontmatter.d.ts +7 -0
  220. package/packages/omo-codex/plugin/components/rules/dist/rules/parser-frontmatter.js +30 -0
  221. package/packages/omo-codex/plugin/components/rules/dist/rules/parser-yaml.d.ts +2 -0
  222. package/packages/omo-codex/plugin/components/rules/dist/rules/parser-yaml.js +237 -0
  223. package/packages/omo-codex/plugin/components/rules/dist/rules/parser.d.ts +3 -0
  224. package/packages/omo-codex/plugin/components/rules/dist/rules/parser.js +31 -0
  225. package/packages/omo-codex/plugin/components/rules/dist/rules/plugin-root.d.ts +1 -0
  226. package/packages/omo-codex/plugin/components/rules/dist/rules/plugin-root.js +48 -0
  227. package/packages/omo-codex/plugin/components/rules/dist/rules/project-root.d.ts +1 -0
  228. package/packages/omo-codex/plugin/components/rules/dist/rules/project-root.js +23 -0
  229. package/packages/omo-codex/plugin/components/rules/dist/rules/scanner.d.ts +14 -0
  230. package/packages/omo-codex/plugin/components/rules/dist/rules/scanner.js +111 -0
  231. package/packages/omo-codex/plugin/components/rules/dist/rules/sources.d.ts +3 -0
  232. package/packages/omo-codex/plugin/components/rules/dist/rules/sources.js +9 -0
  233. package/packages/omo-codex/plugin/components/rules/dist/rules/truncator.d.ts +18 -0
  234. package/packages/omo-codex/plugin/components/rules/dist/rules/truncator.js +59 -0
  235. package/packages/omo-codex/plugin/components/rules/dist/rules/types.d.ts +126 -0
  236. package/packages/omo-codex/plugin/components/rules/dist/rules/types.js +8 -0
  237. package/packages/omo-codex/plugin/components/rules/dist/rules-engine-factory.d.ts +6 -0
  238. package/packages/omo-codex/plugin/components/rules/dist/rules-engine-factory.js +20 -0
  239. package/packages/omo-codex/plugin/components/rules/dist/session-state-lock.d.ts +3 -0
  240. package/packages/omo-codex/plugin/components/rules/dist/session-state-lock.js +41 -0
  241. package/packages/omo-codex/plugin/components/rules/dist/sparkshell-awareness.d.ts +10 -0
  242. package/packages/omo-codex/plugin/components/rules/dist/sparkshell-awareness.js +90 -0
  243. package/packages/omo-codex/plugin/components/rules/dist/static-injection.d.ts +3 -0
  244. package/packages/omo-codex/plugin/components/rules/dist/static-injection.js +128 -0
  245. package/packages/omo-codex/plugin/components/rules/dist/tool-paths.d.ts +6 -0
  246. package/packages/omo-codex/plugin/components/rules/dist/tool-paths.js +168 -0
  247. package/packages/omo-codex/plugin/components/rules/dist/transcript-rule-filter.d.ts +4 -0
  248. package/packages/omo-codex/plugin/components/rules/dist/transcript-rule-filter.js +49 -0
  249. package/packages/omo-codex/plugin/components/rules/dist/transcript-search.d.ts +4 -0
  250. package/packages/omo-codex/plugin/components/rules/dist/transcript-search.js +91 -0
  251. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  252. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  253. package/packages/omo-codex/plugin/components/rules/src/codex-hook.ts +4 -2
  254. package/packages/omo-codex/plugin/components/rules/src/config.ts +13 -0
  255. package/packages/omo-codex/plugin/components/rules/src/event-budget.ts +17 -0
  256. package/packages/omo-codex/plugin/components/rules/src/persistent-cache.ts +4 -1
  257. package/packages/omo-codex/plugin/components/rules/src/post-compact-directive.ts +39 -0
  258. package/packages/omo-codex/plugin/components/rules/src/rules/constants.ts +16 -0
  259. package/packages/omo-codex/plugin/components/rules/src/rules/engine.ts +8 -0
  260. package/packages/omo-codex/plugin/components/rules/src/rules/types.ts +4 -0
  261. package/packages/omo-codex/plugin/components/rules/src/sparkshell-awareness.ts +53 -4
  262. package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +127 -7
  263. package/packages/omo-codex/plugin/components/rules/src/transcript-rule-filter.ts +9 -1
  264. package/packages/omo-codex/plugin/components/rules/test/bundled-rules.test.ts +4 -2
  265. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-budget.test.ts +7 -2
  266. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-context.test.ts +9 -9
  267. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-dedup.test.ts +10 -4
  268. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-directive.test.ts +241 -0
  269. package/packages/omo-codex/plugin/components/rules/test/event-budget.test.ts +168 -0
  270. package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +4 -0
  271. package/packages/omo-codex/plugin/components/rules/test/sparkshell-awareness.test.ts +86 -3
  272. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +15 -15
  273. package/packages/omo-codex/plugin/components/start-work-continuation/dist/boulder-reader.d.ts +16 -0
  274. package/packages/omo-codex/plugin/components/start-work-continuation/dist/boulder-reader.js +146 -0
  275. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.d.ts +2 -0
  276. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +49 -0
  277. package/packages/omo-codex/plugin/components/start-work-continuation/dist/codex-hook.d.ts +2 -0
  278. package/packages/omo-codex/plugin/components/start-work-continuation/dist/codex-hook.js +80 -0
  279. package/packages/omo-codex/plugin/components/start-work-continuation/dist/directive.d.ts +1 -0
  280. package/packages/omo-codex/plugin/components/start-work-continuation/dist/directive.js +2 -0
  281. package/packages/omo-codex/plugin/components/start-work-continuation/dist/index.d.ts +5 -0
  282. package/packages/omo-codex/plugin/components/start-work-continuation/dist/index.js +3 -0
  283. package/packages/omo-codex/plugin/components/start-work-continuation/dist/types.d.ts +20 -0
  284. package/packages/omo-codex/plugin/components/start-work-continuation/dist/types.js +1 -0
  285. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  286. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  287. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +24 -2
  288. package/packages/omo-codex/plugin/components/telemetry/dist/atomic-write.d.ts +1 -0
  289. package/packages/omo-codex/plugin/components/telemetry/dist/atomic-write.js +18 -0
  290. package/packages/omo-codex/plugin/components/telemetry/dist/cli.d.ts +2 -0
  291. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +62 -0
  292. package/packages/omo-codex/plugin/components/telemetry/dist/codex-hook.d.ts +15 -0
  293. package/packages/omo-codex/plugin/components/telemetry/dist/codex-hook.js +42 -0
  294. package/packages/omo-codex/plugin/components/telemetry/dist/data-path.d.ts +10 -0
  295. package/packages/omo-codex/plugin/components/telemetry/dist/data-path.js +35 -0
  296. package/packages/omo-codex/plugin/components/telemetry/dist/diagnostics.d.ts +12 -0
  297. package/packages/omo-codex/plugin/components/telemetry/dist/diagnostics.js +108 -0
  298. package/packages/omo-codex/plugin/components/telemetry/dist/env-flags.d.ts +4 -0
  299. package/packages/omo-codex/plugin/components/telemetry/dist/env-flags.js +31 -0
  300. package/packages/omo-codex/plugin/components/telemetry/dist/posthog-activity-state.d.ts +8 -0
  301. package/packages/omo-codex/plugin/components/telemetry/dist/posthog-activity-state.js +68 -0
  302. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.d.ts +21 -0
  303. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +133 -0
  304. package/packages/omo-codex/plugin/components/telemetry/dist/product-identity.d.ts +8 -0
  305. package/packages/omo-codex/plugin/components/telemetry/dist/product-identity.js +29 -0
  306. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  307. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  308. package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +5 -13
  309. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +61 -185
  310. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
  311. package/packages/omo-codex/plugin/components/ultrawork/directive.md +122 -117
  312. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.d.ts +2 -0
  313. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +48 -0
  314. package/packages/omo-codex/plugin/components/ultrawork/dist/codex-hook.d.ts +7 -0
  315. package/packages/omo-codex/plugin/components/ultrawork/dist/codex-hook.js +122 -0
  316. package/packages/omo-codex/plugin/components/ultrawork/dist/directive.d.ts +1 -0
  317. package/packages/omo-codex/plugin/components/ultrawork/dist/directive.js +2 -0
  318. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  319. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  320. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +20 -11
  321. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +17 -11
  322. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +2 -5
  323. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +0 -71
  324. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.d.ts +16 -0
  325. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +200 -0
  326. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.d.ts +17 -0
  327. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.js +97 -0
  328. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.d.ts +4 -0
  329. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +183 -0
  330. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +6 -0
  331. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +55 -0
  332. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.d.ts +12 -0
  333. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +145 -0
  334. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.d.ts +2 -0
  335. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +39 -0
  336. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.d.ts +13 -0
  337. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +100 -0
  338. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +26 -0
  339. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +97 -0
  340. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-hook.d.ts +28 -0
  341. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-hook.js +145 -0
  342. package/packages/omo-codex/plugin/components/ulw-loop/dist/command-types.d.ts +34 -0
  343. package/packages/omo-codex/plugin/components/ulw-loop/dist/command-types.js +1 -0
  344. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.d.ts +16 -0
  345. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.js +41 -0
  346. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +95 -0
  347. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.js +1 -0
  348. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.d.ts +31 -0
  349. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.js +119 -0
  350. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.d.ts +12 -0
  351. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.js +69 -0
  352. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +16 -0
  353. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +59 -0
  354. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +48 -0
  355. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +119 -0
  356. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +8 -0
  357. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +89 -0
  358. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.d.ts +6 -0
  359. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +123 -0
  360. package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.d.ts +16 -0
  361. package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +70 -0
  362. package/packages/omo-codex/plugin/components/ulw-loop/dist/runtime.d.ts +10 -0
  363. package/packages/omo-codex/plugin/components/ulw-loop/dist/runtime.js +13 -0
  364. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +63 -0
  365. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.js +1 -0
  366. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.d.ts +6 -0
  367. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +292 -0
  368. package/packages/omo-codex/plugin/components/ulw-loop/dist/types.d.ts +5 -0
  369. package/packages/omo-codex/plugin/components/ulw-loop/dist/types.js +5 -0
  370. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  371. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  372. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +14 -14
  373. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +24 -25
  374. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +17 -3
  375. package/packages/omo-codex/plugin/components/ulw-loop/src/cli.ts +2 -1
  376. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +1 -1
  377. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-entrypoint.test.ts +95 -0
  378. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +0 -96
  379. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +23 -0
  380. package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +46 -0
  381. package/packages/omo-codex/plugin/hooks/hooks.json +16 -16
  382. package/packages/omo-codex/plugin/package-lock.json +10 -9
  383. package/packages/omo-codex/plugin/package.json +27 -26
  384. package/packages/omo-codex/plugin/scripts/auto-update.mjs +64 -15
  385. package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +16 -0
  386. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +82 -18
  387. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +2 -2
  388. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +23 -11
  389. package/packages/omo-codex/plugin/scripts/sync-version.mjs +94 -0
  390. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +9 -9
  391. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +16 -1
  392. package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +93 -0
  393. package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +11 -0
  394. package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +17 -13
  395. package/packages/omo-codex/plugin/skills/lsp-setup/SKILL.md +139 -0
  396. package/packages/omo-codex/plugin/skills/lsp-setup/references/bash/README.md +60 -0
  397. package/packages/omo-codex/plugin/skills/lsp-setup/references/c-cpp/README.md +61 -0
  398. package/packages/omo-codex/plugin/skills/lsp-setup/references/csharp/README.md +71 -0
  399. package/packages/omo-codex/plugin/skills/lsp-setup/references/dart/README.md +48 -0
  400. package/packages/omo-codex/plugin/skills/lsp-setup/references/elixir/README.md +51 -0
  401. package/packages/omo-codex/plugin/skills/lsp-setup/references/go/README.md +57 -0
  402. package/packages/omo-codex/plugin/skills/lsp-setup/references/haskell/README.md +57 -0
  403. package/packages/omo-codex/plugin/skills/lsp-setup/references/java/README.md +57 -0
  404. package/packages/omo-codex/plugin/skills/lsp-setup/references/julia/README.md +60 -0
  405. package/packages/omo-codex/plugin/skills/lsp-setup/references/kotlin/README.md +59 -0
  406. package/packages/omo-codex/plugin/skills/lsp-setup/references/lua/README.md +66 -0
  407. package/packages/omo-codex/plugin/skills/lsp-setup/references/php/README.md +62 -0
  408. package/packages/omo-codex/plugin/skills/lsp-setup/references/python/README.md +71 -0
  409. package/packages/omo-codex/plugin/skills/lsp-setup/references/ruby/README.md +53 -0
  410. package/packages/omo-codex/plugin/skills/lsp-setup/references/rust/README.md +59 -0
  411. package/packages/omo-codex/plugin/skills/lsp-setup/references/swift/README.md +51 -0
  412. package/packages/omo-codex/plugin/skills/lsp-setup/references/terraform/README.md +62 -0
  413. package/packages/omo-codex/plugin/skills/lsp-setup/references/typescript/README.md +77 -0
  414. package/packages/omo-codex/plugin/skills/lsp-setup/references/yaml/README.md +70 -0
  415. package/packages/omo-codex/plugin/skills/lsp-setup/references/zig/README.md +49 -0
  416. package/packages/omo-codex/plugin/skills/lsp-setup/scripts/detect-lsp.ts +210 -0
  417. package/packages/omo-codex/plugin/skills/lsp-setup/scripts/lsp-server-table.ts +177 -0
  418. package/packages/omo-codex/plugin/skills/lsp-setup/scripts/tsconfig.json +17 -0
  419. package/packages/omo-codex/plugin/skills/lsp-setup/scripts/verify-lsp.ts +147 -0
  420. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +9 -9
  421. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +10 -10
  422. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +20 -22
  423. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +38 -61
  424. package/packages/omo-codex/plugin/skills/ultraresearch/SKILL.md +135 -677
  425. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +14 -14
  426. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +24 -25
  427. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +20 -11
  428. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +17 -11
  429. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +9 -9
  430. package/packages/omo-codex/plugin/test/aggregate-build.test.mjs +2 -1
  431. package/packages/omo-codex/plugin/test/aggregate-mcp.test.mjs +1 -1
  432. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +5 -5
  433. package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +6 -6
  434. package/packages/omo-codex/plugin/test/auto-update-restart-notice.test.mjs +194 -0
  435. package/packages/omo-codex/plugin/test/auto-update.test.mjs +17 -0
  436. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +15 -44
  437. package/packages/omo-codex/plugin/test/lsp-prebuild-layouts.test.mjs +140 -0
  438. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +189 -7
  439. package/packages/omo-codex/plugin/test/start-work-skill.test.mjs +9 -31
  440. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +68 -4
  441. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +119 -0
  442. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +11 -112
  443. package/packages/omo-codex/plugin/test/sync-version.test.mjs +68 -0
  444. package/packages/omo-codex/plugin/test/ultraresearch-skill-contract.test.mjs +126 -0
  445. package/packages/omo-codex/plugin/test/ulw-plan-skill.test.mjs +2 -2
  446. package/packages/omo-codex/scripts/install/bin-dir.mjs +20 -0
  447. package/packages/omo-codex/scripts/install/bin-links.mjs +43 -6
  448. package/packages/omo-codex/scripts/install/cache.mjs +4 -0
  449. package/packages/omo-codex/scripts/install/config.mjs +4 -4
  450. package/packages/omo-codex/scripts/install/delegated-command.mjs +5 -1
  451. package/packages/omo-codex/scripts/install/git-bash-mcp-env.mjs +28 -0
  452. package/packages/omo-codex/scripts/install/git-bash.mjs +12 -4
  453. package/packages/omo-codex/scripts/install/git-bash.test.mjs +39 -4
  454. package/packages/omo-codex/scripts/install/hook-targets.mjs +46 -0
  455. package/packages/omo-codex/scripts/install/multi-agent-v2-config.mjs +12 -2
  456. package/packages/omo-codex/scripts/install/process.mjs +1 -0
  457. package/packages/omo-codex/scripts/install-bin-links.test.mjs +131 -3
  458. package/packages/omo-codex/scripts/install-config-git-bash.test.mjs +91 -0
  459. package/packages/omo-codex/scripts/install-config.test.mjs +50 -44
  460. package/packages/omo-codex/scripts/install-delegated-command.test.mjs +78 -0
  461. package/packages/omo-codex/scripts/install-git-bash-mcp-env.test.mjs +93 -0
  462. package/packages/omo-codex/scripts/install-hook-targets.test.mjs +100 -0
  463. package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +3 -1
  464. package/packages/omo-codex/scripts/install-local.mjs +7 -18
  465. package/packages/omo-codex/scripts/install-local.test.mjs +34 -1
  466. package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +16 -1
  467. package/packages/shared-skills/skills/lcx-doctor/SKILL.md +93 -0
  468. package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +11 -0
  469. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +17 -13
  470. package/packages/shared-skills/skills/lsp-setup/SKILL.md +139 -0
  471. package/packages/shared-skills/skills/lsp-setup/references/bash/README.md +60 -0
  472. package/packages/shared-skills/skills/lsp-setup/references/c-cpp/README.md +61 -0
  473. package/packages/shared-skills/skills/lsp-setup/references/csharp/README.md +71 -0
  474. package/packages/shared-skills/skills/lsp-setup/references/dart/README.md +48 -0
  475. package/packages/shared-skills/skills/lsp-setup/references/elixir/README.md +51 -0
  476. package/packages/shared-skills/skills/lsp-setup/references/go/README.md +57 -0
  477. package/packages/shared-skills/skills/lsp-setup/references/haskell/README.md +57 -0
  478. package/packages/shared-skills/skills/lsp-setup/references/java/README.md +57 -0
  479. package/packages/shared-skills/skills/lsp-setup/references/julia/README.md +60 -0
  480. package/packages/shared-skills/skills/lsp-setup/references/kotlin/README.md +59 -0
  481. package/packages/shared-skills/skills/lsp-setup/references/lua/README.md +66 -0
  482. package/packages/shared-skills/skills/lsp-setup/references/php/README.md +62 -0
  483. package/packages/shared-skills/skills/lsp-setup/references/python/README.md +71 -0
  484. package/packages/shared-skills/skills/lsp-setup/references/ruby/README.md +53 -0
  485. package/packages/shared-skills/skills/lsp-setup/references/rust/README.md +59 -0
  486. package/packages/shared-skills/skills/lsp-setup/references/swift/README.md +51 -0
  487. package/packages/shared-skills/skills/lsp-setup/references/terraform/README.md +62 -0
  488. package/packages/shared-skills/skills/lsp-setup/references/typescript/README.md +77 -0
  489. package/packages/shared-skills/skills/lsp-setup/references/yaml/README.md +70 -0
  490. package/packages/shared-skills/skills/lsp-setup/references/zig/README.md +49 -0
  491. package/packages/shared-skills/skills/lsp-setup/scripts/detect-lsp.ts +210 -0
  492. package/packages/shared-skills/skills/lsp-setup/scripts/lsp-server-table.ts +177 -0
  493. package/packages/shared-skills/skills/lsp-setup/scripts/tsconfig.json +17 -0
  494. package/packages/shared-skills/skills/lsp-setup/scripts/verify-lsp.ts +147 -0
  495. package/packages/shared-skills/skills/remove-ai-slops/SKILL.md +1 -1
  496. package/packages/shared-skills/skills/review-work/SKILL.md +10 -14
  497. package/packages/shared-skills/skills/start-work/SKILL.md +30 -59
  498. package/packages/shared-skills/skills/ultraresearch/SKILL.md +126 -667
  499. package/dist/hooks/anthropic-effort/hook.d.ts +0 -26
  500. package/dist/hooks/anthropic-effort/index.d.ts +0 -1
  501. package/dist/hooks/keyword-detector/analyze/default.d.ts +0 -12
  502. package/dist/hooks/keyword-detector/analyze/index.d.ts +0 -1
  503. package/dist/hooks/keyword-detector/search/default.d.ts +0 -12
  504. package/dist/hooks/keyword-detector/search/index.d.ts +0 -1
  505. package/dist/hooks/thinking-block-validator/hook.d.ts +0 -12
  506. package/dist/hooks/thinking-block-validator/index.d.ts +0 -1
  507. package/packages/omo-codex/plugin/components/ultrawork/test/directive-contract.test.ts +0 -18
  508. package/packages/omo-codex/plugin/test/global-review-debug-gate.test.mjs +0 -29
  509. package/packages/omo-codex/plugin/test/subagent-guidance.test.mjs +0 -151
@@ -0,0 +1,69 @@
1
+ import { ulwLoopGoalsRelativePath, ulwLoopLedgerRelativePath } from "./paths.js";
2
+ export const ULW_LOOP_AGGREGATE_CODEX_OBJECTIVE = aggregateCodexObjectiveForScope();
3
+ export function aggregateCodexObjectiveForScope(scope) {
4
+ return `Complete the durable ulw-loop plan in ${ulwLoopGoalsRelativePath(scope)}, including later accepted/appended stories, under the original brief constraints; use ${ulwLoopLedgerRelativePath(scope)} as the audit trail.`;
5
+ }
6
+ export function codexGoalMode(plan) {
7
+ return plan.codexGoalMode ?? "per_story";
8
+ }
9
+ function isResolvedStatus(status) {
10
+ return status === "complete";
11
+ }
12
+ function isSupersededResolved(goal, plan) {
13
+ if (goal.steeringStatus !== "superseded")
14
+ return false;
15
+ const replacements = goal.supersededBy ?? [];
16
+ if (replacements.length === 0)
17
+ return false;
18
+ return replacements.every((id) => {
19
+ const replacement = plan.goals.find((candidate) => candidate.id === id);
20
+ return replacement !== undefined && isResolvedStatus(replacement.status);
21
+ });
22
+ }
23
+ function isCompletionBlocking(goal, plan) {
24
+ if (goal.steeringStatus === "superseded")
25
+ return !isSupersededResolved(goal, plan);
26
+ if (goal.steeringStatus === "blocked")
27
+ return true;
28
+ return !isResolvedStatus(goal.status);
29
+ }
30
+ function isCompletionBlockingForFinalCandidate(candidate, finalCandidate, plan) {
31
+ if (candidate.id === finalCandidate.id)
32
+ return false;
33
+ if (candidate.steeringStatus === "superseded") {
34
+ const replacements = candidate.supersededBy ?? [];
35
+ if (replacements.length === 0)
36
+ return true;
37
+ return !replacements.every((id) => {
38
+ if (id === finalCandidate.id)
39
+ return true;
40
+ const replacement = plan.goals.find((goal) => goal.id === id);
41
+ return replacement !== undefined && isResolvedStatus(replacement.status);
42
+ });
43
+ }
44
+ return isCompletionBlocking(candidate, plan);
45
+ }
46
+ export function isUlwLoopDone(plan) {
47
+ if (plan.aggregateCompletion?.status === "complete")
48
+ return true;
49
+ return plan.goals.every((goal) => !isCompletionBlocking(goal, plan));
50
+ }
51
+ export function isFinalRunCompletionCandidate(plan, goal) {
52
+ return (isCompletionBlocking(goal, plan) &&
53
+ plan.goals.every((candidate) => !isCompletionBlockingForFinalCandidate(candidate, goal, plan)));
54
+ }
55
+ export function aggregateCodexObjective(plan) {
56
+ return plan.codexObjective ?? ULW_LOOP_AGGREGATE_CODEX_OBJECTIVE;
57
+ }
58
+ export function expectedCodexObjective(plan, goal) {
59
+ return codexGoalMode(plan) === "aggregate" ? aggregateCodexObjective(plan) : goal.objective;
60
+ }
61
+ export function compatibleCodexObjectives(plan) {
62
+ return [aggregateCodexObjective(plan), ...(plan.codexObjectiveAliases ?? [])];
63
+ }
64
+ export function hasAllCriteriaPass(goal) {
65
+ return goal.successCriteria.length > 0 && goal.successCriteria.every((criterion) => criterion.status === "pass");
66
+ }
67
+ export function firstUnresolvedCriterion(goal) {
68
+ return goal.successCriteria.find((criterion) => criterion.status !== "pass");
69
+ }
@@ -0,0 +1,16 @@
1
+ export interface UlwLoopScope {
2
+ readonly sessionId?: string | null;
3
+ }
4
+ type EnvMap = Readonly<Record<string, string | undefined>>;
5
+ export declare function normalizeUlwLoopSessionId(sessionId: string | null | undefined): string | null;
6
+ export declare function resolveUlwLoopSessionIdFromEnv(env?: EnvMap): string | null;
7
+ export declare function ulwLoopRelativeDir(scope?: UlwLoopScope): string;
8
+ export declare function ulwLoopDir(repoRoot: string, scope?: UlwLoopScope): string;
9
+ export declare function ulwLoopBriefRelativePath(scope?: UlwLoopScope): string;
10
+ export declare function ulwLoopGoalsRelativePath(scope?: UlwLoopScope): string;
11
+ export declare function ulwLoopLedgerRelativePath(scope?: UlwLoopScope): string;
12
+ export declare function ulwLoopBriefPath(repoRoot: string, scope?: UlwLoopScope): string;
13
+ export declare function ulwLoopGoalsPath(repoRoot: string, scope?: UlwLoopScope): string;
14
+ export declare function ulwLoopLedgerPath(repoRoot: string, scope?: UlwLoopScope): string;
15
+ export declare function repoRelative(absolutePath: string, repoRoot: string): string;
16
+ export {};
@@ -0,0 +1,59 @@
1
+ import { join } from "node:path";
2
+ import { ULW_LOOP_BRIEF, ULW_LOOP_DIR, ULW_LOOP_GOALS, ULW_LOOP_LEDGER } from "./types.js";
3
+ const SESSION_ENV_KEYS = ["OMO_ULW_LOOP_SESSION_ID", "CODEX_SESSION_ID", "CODEX_THREAD_ID"];
4
+ export function normalizeUlwLoopSessionId(sessionId) {
5
+ const trimmed = sessionId?.trim();
6
+ if (!trimmed)
7
+ return null;
8
+ const pathSegments = trimmed
9
+ .split(/[\\/]+/)
10
+ .filter((segment) => segment.length > 0 && segment !== "." && segment !== "..");
11
+ const candidate = (pathSegments.length > 0 ? pathSegments.join("-") : trimmed)
12
+ .replace(/[^A-Za-z0-9._-]+/g, "-")
13
+ .replace(/-+/g, "-")
14
+ .replace(/^\.+/, "")
15
+ .replace(/^[.-]+|[.-]+$/g, "");
16
+ return candidate.length > 0 ? candidate : null;
17
+ }
18
+ export function resolveUlwLoopSessionIdFromEnv(env = process.env) {
19
+ for (const key of SESSION_ENV_KEYS) {
20
+ const normalized = normalizeUlwLoopSessionId(env[key]);
21
+ if (normalized !== null)
22
+ return normalized;
23
+ }
24
+ return null;
25
+ }
26
+ export function ulwLoopRelativeDir(scope) {
27
+ const sessionId = normalizeUlwLoopSessionId(scope?.sessionId);
28
+ return sessionId === null ? ULW_LOOP_DIR : `${ULW_LOOP_DIR}/${sessionId}`;
29
+ }
30
+ export function ulwLoopDir(repoRoot, scope) {
31
+ return join(repoRoot, ulwLoopRelativeDir(scope));
32
+ }
33
+ export function ulwLoopBriefRelativePath(scope) {
34
+ return `${ulwLoopRelativeDir(scope)}/${ULW_LOOP_BRIEF}`;
35
+ }
36
+ export function ulwLoopGoalsRelativePath(scope) {
37
+ return `${ulwLoopRelativeDir(scope)}/${ULW_LOOP_GOALS}`;
38
+ }
39
+ export function ulwLoopLedgerRelativePath(scope) {
40
+ return `${ulwLoopRelativeDir(scope)}/${ULW_LOOP_LEDGER}`;
41
+ }
42
+ export function ulwLoopBriefPath(repoRoot, scope) {
43
+ return join(ulwLoopDir(repoRoot, scope), ULW_LOOP_BRIEF);
44
+ }
45
+ export function ulwLoopGoalsPath(repoRoot, scope) {
46
+ return join(ulwLoopDir(repoRoot, scope), ULW_LOOP_GOALS);
47
+ }
48
+ export function ulwLoopLedgerPath(repoRoot, scope) {
49
+ return join(ulwLoopDir(repoRoot, scope), ULW_LOOP_LEDGER);
50
+ }
51
+ export function repoRelative(absolutePath, repoRoot) {
52
+ const slashPrefix = `${repoRoot}/`;
53
+ const backslashPrefix = `${repoRoot}\\`;
54
+ if (absolutePath.startsWith(slashPrefix))
55
+ return absolutePath.slice(slashPrefix.length).split("\\").join("/");
56
+ if (absolutePath.startsWith(backslashPrefix))
57
+ return absolutePath.slice(backslashPrefix.length).split("\\").join("/");
58
+ return absolutePath.split("\\").join("/");
59
+ }
@@ -0,0 +1,48 @@
1
+ import { type UlwLoopScope } from "./paths.js";
2
+ import type { UlwLoopCodexGoalMode, UlwLoopItem, UlwLoopPlan, UlwLoopSuccessCriterion } from "./types.js";
3
+ export type UlwLoopPlanSummary = {
4
+ readonly total: number;
5
+ readonly pending: number;
6
+ readonly in_progress: number;
7
+ readonly complete: number;
8
+ readonly failed: number;
9
+ readonly blocked: number;
10
+ readonly review_blocked: number;
11
+ readonly needs_user_decision: number;
12
+ readonly superseded: number;
13
+ readonly criteria: {
14
+ readonly total: number;
15
+ readonly pass: number;
16
+ readonly pending: number;
17
+ readonly fail: number;
18
+ readonly blocked: number;
19
+ };
20
+ };
21
+ export declare function seedDefaultSuccessCriteria(goalIndex: number, objective: string): UlwLoopSuccessCriterion[];
22
+ export declare function deriveGoalCandidates(brief: string): Array<{
23
+ title: string;
24
+ objective: string;
25
+ }>;
26
+ export declare function createUlwLoopPlan(repoRoot: string, args: {
27
+ brief: string;
28
+ codexGoalMode?: UlwLoopCodexGoalMode;
29
+ force?: boolean;
30
+ }, scope?: UlwLoopScope): Promise<UlwLoopPlan>;
31
+ export declare function addUlwLoopGoal(repoRoot: string, args: {
32
+ title: string;
33
+ objective: string;
34
+ }, scope?: UlwLoopScope): Promise<{
35
+ plan: UlwLoopPlan;
36
+ goal: UlwLoopItem;
37
+ }>;
38
+ export declare function startNextUlwLoop(repoRoot: string, args?: {
39
+ retryFailed?: boolean;
40
+ }, scope?: UlwLoopScope): Promise<{
41
+ plan: UlwLoopPlan;
42
+ goal: UlwLoopItem;
43
+ resumed: boolean;
44
+ } | {
45
+ done: true;
46
+ plan: UlwLoopPlan;
47
+ }>;
48
+ export declare function summarizeUlwLoopPlan(plan: UlwLoopPlan): UlwLoopPlanSummary;
@@ -0,0 +1,119 @@
1
+ // biome-ignore-all format: keep this port under the mandated pure LOC budget.
2
+ import { existsSync } from "node:fs";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
+ import { aggregateCodexObjectiveForScope, isUlwLoopDone } from "./goal-status.js";
5
+ import { ulwLoopBriefPath, ulwLoopBriefRelativePath, ulwLoopDir, ulwLoopGoalsPath, ulwLoopGoalsRelativePath, ulwLoopLedgerPath, ulwLoopLedgerRelativePath } from "./paths.js";
6
+ import { appendLedger, readUlwLoopPlan, withUlwLoopMutationLock, writePlan } from "./plan-io.js";
7
+ import { iso, UlwLoopError } from "./types.js";
8
+ function cleanLine(line) { return line.replace(/^\s*(?:[-*+]\s+|\d+[.)]\s+)/, "").trim(); }
9
+ function normalizeObjective(value) { return value.replace(/\s+/g, " ").trim(); }
10
+ function titleFromObjective(objective, fallback) { const firstLine = objective.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? fallback; return firstLine.length > 72 ? `${firstLine.slice(0, 69).trimEnd()}...` : firstLine; }
11
+ function normalizeGoalId(title, index) { const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 36).replace(/-+$/g, ""); return `G${String(index + 1).padStart(3, "0")}${slug ? `-${slug}` : ""}`; }
12
+ function assertNonEmpty(value, label) { const trimmed = value?.trim(); if (!trimmed)
13
+ throw new UlwLoopError(`Missing ${label}.`, "ULW_LOOP_ARGUMENT_MISSING"); return trimmed; }
14
+ function truncateObjective(objective) { return objective.length > 80 ? `${objective.slice(0, 77).trimEnd()}...` : objective; }
15
+ export function seedDefaultSuccessCriteria(goalIndex, objective) {
16
+ const subject = truncateObjective(normalizeObjective(objective) || `Goal ${goalIndex + 1}`);
17
+ const rows = [
18
+ ["C001", "happy", `happy path for: ${subject}`, `Replace via revise_criterion with observable happy-path proof for goal ${goalIndex + 1}.`],
19
+ ["C002", "edge", "edge case (boundary/empty/malformed)", `Replace via revise_criterion with boundary or malformed-input proof for: ${subject}.`],
20
+ ["C003", "regression", "regression: adjacent surface still works", `Replace via revise_criterion with regression proof for neighboring behavior after: ${subject}.`],
21
+ ];
22
+ return rows.map(([id, userModel, scenario, expectedEvidence]) => ({ id, scenario, userModel, expectedEvidence, capturedEvidence: null, status: "pending" }));
23
+ }
24
+ export function deriveGoalCandidates(brief) {
25
+ const bulletGoals = brief.split(/\r?\n/).map((line) => ({ original: line, cleaned: normalizeObjective(cleanLine(line)) })).filter(({ cleaned }) => cleaned.length > 0 && cleaned.length <= 1200).filter(({ original, cleaned }, index, all) => /^\s*(?:[-*+]\s+|\d+[.)]\s+)/.test(original) && all.findIndex((candidate) => candidate.cleaned === cleaned) === index).map(({ cleaned }) => cleaned);
26
+ const paragraphs = brief.split(/\n\s*\n/).map(normalizeObjective).filter((paragraph) => paragraph.length > 0 && !paragraph.startsWith("#"));
27
+ const selected = (bulletGoals.length > 0 ? bulletGoals : paragraphs).length > 0 ? (bulletGoals.length > 0 ? bulletGoals : paragraphs) : ["Complete the requested project objective."];
28
+ return selected.map((objective, index) => ({ title: titleFromObjective(objective, `Goal ${index + 1}`), objective }));
29
+ }
30
+ function makeGoal(title, objective, index, now) {
31
+ const cleanTitle = assertNonEmpty(title, "title");
32
+ const cleanObjective = assertNonEmpty(objective, "objective");
33
+ return { id: normalizeGoalId(cleanTitle, index), title: cleanTitle, objective: cleanObjective, status: "pending", successCriteria: seedDefaultSuccessCriteria(index, cleanObjective), attempt: 0, createdAt: now, updatedAt: now };
34
+ }
35
+ function appendGoalToPlan(plan, title, objective, now) {
36
+ const goal = makeGoal(title, objective, plan.goals.length, now);
37
+ plan.goals.push(goal);
38
+ plan.updatedAt = now;
39
+ return goal;
40
+ }
41
+ function isScheduleEligible(goal) { return goal.steeringStatus !== "superseded" && goal.steeringStatus !== "blocked"; }
42
+ function clearGoalBlockerFields(goal) {
43
+ for (const key of ["blockedReason", "blockerSignature", "blockerOccurrenceCount", "requiredExternalDecision", "nonRetriable", "failedAt", "failureReason"])
44
+ delete goal[key];
45
+ }
46
+ export async function createUlwLoopPlan(repoRoot, args, scope) {
47
+ return withUlwLoopMutationLock(repoRoot, scope, async () => {
48
+ if (!args.force && existsSync(ulwLoopGoalsPath(repoRoot, scope))) {
49
+ const existing = await readUlwLoopPlan(repoRoot, scope);
50
+ if (isUlwLoopDone(existing))
51
+ throw completedPlanExistsError(scope);
52
+ throw new UlwLoopError(`Refusing to overwrite existing ${ulwLoopGoalsRelativePath(scope)}; pass --force to recreate it.`, "ULW_LOOP_PLAN_EXISTS");
53
+ }
54
+ const now = iso();
55
+ const goals = deriveGoalCandidates(args.brief).map((goal, index) => makeGoal(goal.title, goal.objective, index, now));
56
+ const plan = { version: 1, createdAt: now, updatedAt: now, briefPath: ulwLoopBriefRelativePath(scope), goalsPath: ulwLoopGoalsRelativePath(scope), ledgerPath: ulwLoopLedgerRelativePath(scope), codexGoalMode: args.codexGoalMode ?? "aggregate", goals };
57
+ if (plan.codexGoalMode === "aggregate")
58
+ plan.codexObjective = aggregateCodexObjectiveForScope(scope);
59
+ await mkdir(ulwLoopDir(repoRoot, scope), { recursive: true });
60
+ await writeFile(ulwLoopBriefPath(repoRoot, scope), args.brief.endsWith("\n") ? args.brief : `${args.brief}\n`, "utf8");
61
+ await writePlan(repoRoot, plan, scope);
62
+ await writeFile(ulwLoopLedgerPath(repoRoot, scope), "", "utf8");
63
+ await appendLedger(repoRoot, { at: now, kind: "plan_created", message: `${goals.length} goal(s) created` }, scope);
64
+ return plan;
65
+ });
66
+ }
67
+ function completedPlanExistsError(scope) {
68
+ return new UlwLoopError([
69
+ `Existing ulw-loop aggregate is already complete at ${ulwLoopGoalsRelativePath(scope)}.`,
70
+ "Start a new run with `omo ulw-loop create-goals --session-id <new-id> ...` to isolate fresh state.",
71
+ "Use --force only when you intentionally want to overwrite the completed evidence.",
72
+ ].join(" "), "ULW_LOOP_PLAN_EXISTS_COMPLETE");
73
+ }
74
+ export async function addUlwLoopGoal(repoRoot, args, scope) {
75
+ return withUlwLoopMutationLock(repoRoot, scope, async () => {
76
+ const plan = await readUlwLoopPlan(repoRoot, scope);
77
+ const now = iso();
78
+ const goal = appendGoalToPlan(plan, args.title, args.objective, now);
79
+ await writePlan(repoRoot, plan, scope);
80
+ await appendLedger(repoRoot, { at: now, kind: "goal_added", goalId: goal.id, status: goal.status, message: goal.title }, scope);
81
+ return { plan, goal };
82
+ });
83
+ }
84
+ export async function startNextUlwLoop(repoRoot, args = {}, scope) {
85
+ return withUlwLoopMutationLock(repoRoot, scope, async () => {
86
+ const plan = await readUlwLoopPlan(repoRoot, scope);
87
+ const now = iso();
88
+ if (plan.aggregateCompletion?.status === "complete")
89
+ return { done: true, plan };
90
+ const existing = plan.goals.find((goal) => goal.status === "in_progress" && isScheduleEligible(goal));
91
+ if (existing) {
92
+ await appendLedger(repoRoot, { at: now, kind: "goal_resumed", goalId: existing.id, status: existing.status, message: "Resuming active ulw-loop" }, scope);
93
+ return { plan, goal: existing, resumed: true };
94
+ }
95
+ let next = plan.goals.find((goal) => goal.status === "pending" && isScheduleEligible(goal));
96
+ if (!next && args.retryFailed) {
97
+ next = plan.goals.find((goal) => goal.status === "failed" && !goal.nonRetriable && isScheduleEligible(goal));
98
+ if (next)
99
+ await appendLedger(repoRoot, { at: now, kind: "goal_retried", goalId: next.id, status: "pending", ...(next.failureReason ? { message: next.failureReason } : {}) }, scope);
100
+ }
101
+ if (!next)
102
+ return { done: true, plan };
103
+ next.status = "in_progress";
104
+ next.attempt += 1;
105
+ next.startedAt = now;
106
+ clearGoalBlockerFields(next);
107
+ next.updatedAt = now;
108
+ plan.activeGoalId = next.id;
109
+ plan.updatedAt = now;
110
+ await writePlan(repoRoot, plan, scope);
111
+ await appendLedger(repoRoot, { at: now, kind: "goal_started", goalId: next.id, status: next.status, message: `Attempt ${next.attempt}` }, scope);
112
+ return { plan, goal: next, resumed: false };
113
+ });
114
+ }
115
+ export function summarizeUlwLoopPlan(plan) {
116
+ const countStatus = (status) => plan.goals.filter((goal) => goal.status === status).length;
117
+ const countCriteria = (status) => plan.goals.reduce((sum, goal) => sum + goal.successCriteria.filter((criterion) => criterion.status === status).length, 0);
118
+ return { total: plan.goals.length, pending: countStatus("pending"), in_progress: countStatus("in_progress"), complete: countStatus("complete"), failed: countStatus("failed"), blocked: countStatus("blocked"), review_blocked: countStatus("review_blocked"), needs_user_decision: countStatus("needs_user_decision"), superseded: plan.goals.filter((goal) => goal.steeringStatus === "superseded").length, criteria: { total: plan.goals.reduce((sum, goal) => sum + goal.successCriteria.length, 0), pass: countCriteria("pass"), pending: countCriteria("pending"), fail: countCriteria("fail"), blocked: countCriteria("blocked") } };
119
+ }
@@ -0,0 +1,8 @@
1
+ import { type UlwLoopScope } from "./paths.js";
2
+ import type { UlwLoopLedgerEntry, UlwLoopPlan } from "./types.js";
3
+ export declare function withUlwLoopMutationLock<T>(repoRoot: string, fn: () => Promise<T>): Promise<T>;
4
+ export declare function withUlwLoopMutationLock<T>(repoRoot: string, scope: UlwLoopScope | undefined, fn: () => Promise<T>): Promise<T>;
5
+ export declare function readUlwLoopPlan(repoRoot: string, scope?: UlwLoopScope): Promise<UlwLoopPlan>;
6
+ export declare function writePlan(repoRoot: string, plan: UlwLoopPlan, scope?: UlwLoopScope): Promise<void>;
7
+ export declare function appendLedger(repoRoot: string, entry: UlwLoopLedgerEntry, scope?: UlwLoopScope): Promise<void>;
8
+ export declare function readSteeringLedgerEntries(repoRoot: string, scope?: UlwLoopScope): Promise<UlwLoopLedgerEntry[]>;
@@ -0,0 +1,89 @@
1
+ import { appendFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { aggregateCodexObjectiveForScope } from "./goal-status.js";
3
+ import { repoRelative, ulwLoopDir, ulwLoopGoalsPath, ulwLoopLedgerPath, ulwLoopRelativeDir, } from "./paths.js";
4
+ import { iso, ULW_LOOP_DIR, ULW_LOOP_GOALS, ULW_LOOP_LEDGER, UlwLoopError } from "./types.js";
5
+ const LEGACY_OBJECTIVE_PREFIX = `Complete all ulw-loop stories in ${ULW_LOOP_DIR}/${ULW_LOOP_GOALS}: `;
6
+ const LEGACY_OBJECTIVE = `Complete all ulw-loop stories listed in ${ULW_LOOP_DIR}/${ULW_LOOP_GOALS}. Use ${ULW_LOOP_DIR}/${ULW_LOOP_LEDGER} as the durable audit trail.`;
7
+ const locks = new Map();
8
+ function hasCode(error, code) {
9
+ return error instanceof Error && "code" in error && error.code === code;
10
+ }
11
+ function isLegacyEnumeratedAggregateObjective(objective) {
12
+ return objective === LEGACY_OBJECTIVE || Boolean(objective?.startsWith(LEGACY_OBJECTIVE_PREFIX));
13
+ }
14
+ function isSteeringKind(value) {
15
+ return value === "steering_accepted" || value === "steering_rejected" || value === "criteria_revised";
16
+ }
17
+ export async function withUlwLoopMutationLock(repoRoot, scopeOrFn, maybeFn) {
18
+ const scope = typeof scopeOrFn === "function" ? undefined : scopeOrFn;
19
+ const fn = typeof scopeOrFn === "function" ? scopeOrFn : maybeFn;
20
+ if (fn === undefined)
21
+ throw new UlwLoopError("Missing ulw-loop mutation body.", "ULW_LOOP_LOCK_BODY_MISSING");
22
+ const lockKey = `${repoRoot}\0${ulwLoopRelativeDir(scope)}`;
23
+ const prior = locks.get(lockKey) ?? Promise.resolve();
24
+ const run = prior.then(fn, fn);
25
+ locks.set(lockKey, run.catch(() => undefined));
26
+ return run;
27
+ }
28
+ export async function readUlwLoopPlan(repoRoot, scope) {
29
+ const path = ulwLoopGoalsPath(repoRoot, scope);
30
+ let raw;
31
+ try {
32
+ raw = await readFile(path, "utf8");
33
+ }
34
+ catch (error) {
35
+ if (!hasCode(error, "ENOENT"))
36
+ throw error;
37
+ throw new UlwLoopError(`No ulw-loop plan found at ${repoRelative(path, repoRoot)}. Run \`omo ulw-loop create-goals ...\` first.`, "ULW_LOOP_PLAN_MISSING", { cause: error });
38
+ }
39
+ const parsed = JSON.parse(raw);
40
+ if (parsed.version !== 1 || !Array.isArray(parsed.goals)) {
41
+ throw new UlwLoopError(`Invalid ulw-loop plan at ${repoRelative(path, repoRoot)}.`, "ULW_LOOP_PLAN_INVALID");
42
+ }
43
+ const previousObjective = parsed.codexObjective;
44
+ if ((parsed.codexGoalMode ?? "per_story") === "aggregate" &&
45
+ isLegacyEnumeratedAggregateObjective(previousObjective)) {
46
+ const now = iso();
47
+ parsed.codexObjective = aggregateCodexObjectiveForScope(scope);
48
+ parsed.codexObjectiveAliases = [...new Set([...(parsed.codexObjectiveAliases ?? []), previousObjective])];
49
+ parsed.updatedAt = now;
50
+ await writePlan(repoRoot, parsed, scope);
51
+ await appendLedger(repoRoot, {
52
+ at: now,
53
+ kind: "aggregate_objective_migrated",
54
+ message: "Migrated legacy enumerated aggregate Codex objective to the stable pointer objective.",
55
+ before: { codexObjective: previousObjective },
56
+ after: { codexObjective: parsed.codexObjective },
57
+ }, scope);
58
+ }
59
+ return parsed;
60
+ }
61
+ export async function writePlan(repoRoot, plan, scope) {
62
+ await mkdir(ulwLoopDir(repoRoot, scope), { recursive: true });
63
+ const path = ulwLoopGoalsPath(repoRoot, scope);
64
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
65
+ await writeFile(tmpPath, `${JSON.stringify(plan, null, 2)}\n`, "utf8");
66
+ await rename(tmpPath, path);
67
+ }
68
+ export async function appendLedger(repoRoot, entry, scope) {
69
+ await mkdir(ulwLoopDir(repoRoot, scope), { recursive: true });
70
+ await appendFile(ulwLoopLedgerPath(repoRoot, scope), `${JSON.stringify(entry)}\n`, "utf8");
71
+ }
72
+ export async function readSteeringLedgerEntries(repoRoot, scope) {
73
+ let raw;
74
+ try {
75
+ raw = await readFile(ulwLoopLedgerPath(repoRoot, scope), "utf8");
76
+ }
77
+ catch (error) {
78
+ if (hasCode(error, "ENOENT"))
79
+ return [];
80
+ throw error;
81
+ }
82
+ const entries = [];
83
+ for (const line of raw.split(/\r?\n/).filter(Boolean)) {
84
+ const entry = JSON.parse(line);
85
+ if (isSteeringKind(entry.kind))
86
+ entries.push(entry);
87
+ }
88
+ return entries;
89
+ }
@@ -0,0 +1,6 @@
1
+ import type { UlwLoopItem, UlwLoopPlan, UlwLoopQualityGate } from "./types.js";
2
+ export declare function validateQualityGate(input: unknown): UlwLoopQualityGate;
3
+ export declare function normalizeBlockerEvidence(evidence: string): string;
4
+ export declare function classifyExternalAuthorizationBlocker(evidence: string): string | null;
5
+ export declare function sameBlockerOccurrences(plan: UlwLoopPlan, signature: string): number;
6
+ export declare function clearGoalBlockerFields(goal: UlwLoopItem): void;
@@ -0,0 +1,123 @@
1
+ import { UlwLoopError } from "./types.js";
2
+ const BLOCKER_FIELD_KEYS = "blocker blockerSignature blockerEvidence blockerOccurrences blockedAt".split(" ");
3
+ const URL_PATTERN = /https?:\/\/\S+/g;
4
+ const PUNCTUATION_PATTERN = /[`"'()[\]{}:,;]/g;
5
+ const WHITESPACE_PATTERN = /\s+/g;
6
+ const AUTH_PATTERN = /\b(auth\w*|credential\w*|token|permission\w*|scope\w*|access|unauthorized|forbidden|401|403)\b/;
7
+ const MISSING_PATTERN = /\b(unset|missing|required|requires|without|omit\w*|not set|not available|no read packages|read packages)\b/;
8
+ const GHCR_PATTERN = /\b(ghcr|github container registry|read packages|imagepullsecret|package api|anonymous|container image)\b/;
9
+ const GHCR_401_PATTERN = /\b(401|unauthorized|anonymous pull|authentication required)\b/;
10
+ const GHCR_403_PATTERN = /\b(403|forbidden|read packages|package api)\b/;
11
+ const UNCONDITIONAL_APPROVAL_PATTERN = /\bUNCONDITIONAL\s+APPROVAL\b/i;
12
+ function invalid(message, field) {
13
+ throw new UlwLoopError(message, "ULW_LOOP_QUALITY_GATE_INVALID", { details: { field } });
14
+ }
15
+ function isRecord(value) {
16
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17
+ }
18
+ function section(value, field) {
19
+ return isRecord(value) ? value : invalid(`Final quality gate is missing ${field} evidence.`, field);
20
+ }
21
+ function nonEmptyString(value, field) {
22
+ return typeof value === "string" && value.trim() !== ""
23
+ ? value
24
+ : invalid(`Final quality gate requires non-empty ${field}.`, field);
25
+ }
26
+ function numberField(value, field) {
27
+ return typeof value === "number" && Number.isFinite(value)
28
+ ? value
29
+ : invalid(`Final quality gate requires numeric ${field}.`, field);
30
+ }
31
+ function stringArray(value, field) {
32
+ if (!Array.isArray(value) || value.length === 0)
33
+ return invalid(`Final quality gate requires ${field}.`, field);
34
+ return value.map((item) => nonEmptyString(item, field));
35
+ }
36
+ function normalizeReviewerField({ value, field, expectedValue, evidenceApproved, }) {
37
+ if (typeof value === "string") {
38
+ const trimmed = value.trim();
39
+ if (trimmed === "") {
40
+ if (evidenceApproved)
41
+ return expectedValue;
42
+ invalid(`${field} must be ${expectedValue} or codeReview.evidence should include UNCONDITIONAL APPROVAL.`, field);
43
+ }
44
+ if (trimmed === expectedValue)
45
+ return expectedValue;
46
+ invalid(`${field} must be ${expectedValue}.`, field);
47
+ }
48
+ if (value === undefined) {
49
+ if (evidenceApproved)
50
+ return expectedValue;
51
+ invalid(`${field} must be ${expectedValue} or codeReview.evidence should include UNCONDITIONAL APPROVAL.`, field);
52
+ }
53
+ invalid(`${field} must be ${expectedValue}.`, field);
54
+ }
55
+ export function validateQualityGate(input) {
56
+ const gate = section(input, "qualityGate");
57
+ const cleaner = section(gate["aiSlopCleaner"], "aiSlopCleaner");
58
+ const verification = section(gate["verification"], "verification");
59
+ const review = section(gate["codeReview"], "codeReview");
60
+ const coverage = section(gate["criteriaCoverage"], "criteriaCoverage");
61
+ if (cleaner["status"] !== "passed")
62
+ invalid("aiSlopCleaner.status must be passed.", "aiSlopCleaner.status");
63
+ if (verification["status"] !== "passed")
64
+ invalid("verification.status must be passed.", "verification.status");
65
+ const totalCriteria = numberField(coverage["totalCriteria"], "criteriaCoverage.totalCriteria");
66
+ const passCount = numberField(coverage["passCount"], "criteriaCoverage.passCount");
67
+ if (passCount < totalCriteria)
68
+ invalid("criteriaCoverage.passCount must cover totalCriteria.", "criteriaCoverage.passCount");
69
+ const commands = stringArray(verification["commands"], "verification.commands");
70
+ const covered = stringArray(coverage["adversarialClassesCovered"], "criteriaCoverage.adversarialClassesCovered");
71
+ const cleanerEvidence = nonEmptyString(cleaner["evidence"], "aiSlopCleaner.evidence");
72
+ const verificationEvidence = nonEmptyString(verification["evidence"], "verification.evidence");
73
+ const reviewEvidence = nonEmptyString(review["evidence"], "codeReview.evidence");
74
+ const approvalEvidence = UNCONDITIONAL_APPROVAL_PATTERN.test(reviewEvidence);
75
+ const recommendation = normalizeReviewerField({
76
+ value: review["recommendation"],
77
+ field: "codeReview.recommendation",
78
+ expectedValue: "APPROVE",
79
+ evidenceApproved: approvalEvidence,
80
+ });
81
+ const architectStatus = normalizeReviewerField({
82
+ value: review["architectStatus"],
83
+ field: "codeReview.architectStatus",
84
+ expectedValue: "CLEAR",
85
+ evidenceApproved: approvalEvidence,
86
+ });
87
+ const result = {
88
+ aiSlopCleaner: { status: "passed", evidence: cleanerEvidence },
89
+ verification: { status: "passed", commands, evidence: verificationEvidence },
90
+ codeReview: { recommendation, architectStatus, evidence: reviewEvidence },
91
+ };
92
+ Object.assign(result, { criteriaCoverage: { totalCriteria, passCount, adversarialClassesCovered: covered } });
93
+ return result;
94
+ }
95
+ export function normalizeBlockerEvidence(evidence) {
96
+ const withoutUrls = evidence.toLowerCase().replace(URL_PATTERN, " ");
97
+ const withoutPunctuation = withoutUrls.replace(PUNCTUATION_PATTERN, " ");
98
+ return withoutPunctuation.replace(WHITESPACE_PATTERN, " ").trim();
99
+ }
100
+ export function classifyExternalAuthorizationBlocker(evidence) {
101
+ const normalized = normalizeBlockerEvidence(evidence);
102
+ if (!normalized || !AUTH_PATTERN.test(normalized) || !MISSING_PATTERN.test(normalized))
103
+ return null;
104
+ if (!GHCR_PATTERN.test(normalized))
105
+ return "EXTERNAL_AUTHORIZATION_REQUIRED";
106
+ const status401 = GHCR_401_PATTERN.test(normalized) ? "HTTP_401_ANONYMOUS" : null;
107
+ const status403 = GHCR_403_PATTERN.test(normalized) ? "HTTP_403_NO_READ_PACKAGES" : null;
108
+ const status = [status401, status403].filter((part) => part !== null).join("+");
109
+ return `GHCR_PULL_ACCESS:${status || "AUTHORIZATION_REQUIRED"}:GHCR_VISIBILITY_OR_CREDENTIAL_REQUIRED`;
110
+ }
111
+ function nestedBlockerSignature(goal) {
112
+ const blocker = Reflect.get(goal, "blocker");
113
+ const signature = isRecord(blocker) ? blocker["signature"] : null;
114
+ return typeof signature === "string" ? signature : null;
115
+ }
116
+ export function sameBlockerOccurrences(plan, signature) {
117
+ return plan.goals.filter((goal) => goal.blockerSignature === signature || nestedBlockerSignature(goal) === signature)
118
+ .length;
119
+ }
120
+ export function clearGoalBlockerFields(goal) {
121
+ for (const key of BLOCKER_FIELD_KEYS)
122
+ Reflect.deleteProperty(goal, key);
123
+ }
@@ -0,0 +1,16 @@
1
+ import type { UlwLoopScope } from "./paths.js";
2
+ import type { UlwLoopItem, UlwLoopLedgerEntry, UlwLoopPlan } from "./types.js";
3
+ export interface RecordFinalReviewBlockersArgs {
4
+ readonly goalId: string;
5
+ readonly title: string;
6
+ readonly objective: string;
7
+ readonly evidence: string;
8
+ readonly codexGoalJson: string;
9
+ }
10
+ export interface RecordFinalReviewBlockersResult {
11
+ readonly plan: UlwLoopPlan;
12
+ readonly blockedGoal: UlwLoopItem;
13
+ readonly newGoal: UlwLoopItem;
14
+ readonly ledgerEntries: UlwLoopLedgerEntry[];
15
+ }
16
+ export declare function recordFinalReviewBlockers(repoRoot: string, args: RecordFinalReviewBlockersArgs, scope?: UlwLoopScope): Promise<RecordFinalReviewBlockersResult>;
@@ -0,0 +1,70 @@
1
+ // biome-ignore-all format: compact port must stay within the requested pure LOC budget.
2
+ import { readCodexGoalSnapshotInput, reconcileCodexGoalSnapshot } from "./codex-goal-snapshot.js";
3
+ import { codexGoalMode, compatibleCodexObjectives, expectedCodexObjective, isFinalRunCompletionCandidate } from "./goal-status.js";
4
+ import { seedDefaultSuccessCriteria } from "./plan-crud.js";
5
+ import { appendLedger, readUlwLoopPlan, withUlwLoopMutationLock, writePlan } from "./plan-io.js";
6
+ import { iso, UlwLoopError } from "./types.js";
7
+ const BLOCKER_FIELDS = "blockedReason blockerSignature blockerOccurrenceCount requiredExternalDecision nonRetriable failedAt failureReason completedAt blocker blockerEvidence blockerOccurrences blockedAt".split(" ");
8
+ function ulwLoopError(message, code) {
9
+ throw new UlwLoopError(message, code);
10
+ }
11
+ function nextGoalId(plan) {
12
+ const max = plan.goals.reduce((current, goal) => {
13
+ const digits = /^G(\d+)/u.exec(goal.id)?.[1];
14
+ return digits === undefined ? current : Math.max(current, Number(digits));
15
+ }, 0);
16
+ return `G${String(max + 1).padStart(3, "0")}`;
17
+ }
18
+ function appendBlockerGoal(plan, args, now) {
19
+ const index = plan.goals.length;
20
+ const goal = {
21
+ id: nextGoalId(plan),
22
+ title: args.title,
23
+ objective: args.objective,
24
+ status: "pending",
25
+ successCriteria: seedDefaultSuccessCriteria(index, args.objective),
26
+ attempt: 0,
27
+ createdAt: now,
28
+ updatedAt: now,
29
+ };
30
+ plan.goals.push(goal);
31
+ return goal;
32
+ }
33
+ export async function recordFinalReviewBlockers(repoRoot, args, scope) {
34
+ return withUlwLoopMutationLock(repoRoot, scope, async () => {
35
+ const plan = await readUlwLoopPlan(repoRoot, scope);
36
+ const goal = plan.goals.find((candidate) => candidate.id === args.goalId);
37
+ if (goal === undefined)
38
+ ulwLoopError(`Unknown ulw-loop id: ${args.goalId}`, "ulw_loop_goal_not_found");
39
+ if (goal.status !== "in_progress")
40
+ ulwLoopError(`${goal.id} is ${goal.status}.`, "ulw_loop_goal_not_in_progress");
41
+ if (!isFinalRunCompletionCandidate(plan, goal))
42
+ ulwLoopError(`${goal.id} is not final.`, "ulw_loop_not_final_story");
43
+ const snapshot = await readCodexGoalSnapshotInput(args.codexGoalJson, repoRoot);
44
+ const aggregate = codexGoalMode(plan) === "aggregate";
45
+ const reconciliation = reconcileCodexGoalSnapshot(snapshot, { expectedObjective: expectedCodexObjective(plan, goal), ...(aggregate ? { acceptedObjectives: compatibleCodexObjectives(plan) } : {}), allowedStatuses: ["active"], requireSnapshot: true, requireComplete: false });
46
+ if (!reconciliation.ok)
47
+ ulwLoopError(reconciliation.errors.join(" "), "ulw_loop_codex_snapshot_mismatch");
48
+ const now = iso();
49
+ for (const field of BLOCKER_FIELDS)
50
+ Reflect.deleteProperty(goal, field);
51
+ goal.status = "review_blocked";
52
+ goal.reviewBlockedAt = now;
53
+ goal.evidence = args.evidence;
54
+ goal.updatedAt = now;
55
+ if (plan.activeGoalId === goal.id)
56
+ delete plan.activeGoalId;
57
+ const newGoal = appendBlockerGoal(plan, args, now);
58
+ plan.updatedAt = now;
59
+ const codexGoal = reconciliation.snapshot.raw;
60
+ const blockedEntry = { at: now, kind: "goal_review_blocked", goalId: goal.id, status: goal.status, evidence: args.evidence, codexGoal };
61
+ const addedEntry = { at: now, kind: "goal_added", goalId: newGoal.id, status: newGoal.status, evidence: args.evidence, message: newGoal.title };
62
+ const summaryEntry = { at: now, kind: "goal_review_blocked", goalId: goal.id, status: goal.status, evidence: args.evidence, codexGoal, message: `Review blockers recorded; appended ${newGoal.id}.` };
63
+ Reflect.set(summaryEntry, "kind", "blocker_recorded");
64
+ const ledgerEntries = [blockedEntry, addedEntry, summaryEntry];
65
+ await writePlan(repoRoot, plan, scope);
66
+ for (const entry of ledgerEntries)
67
+ await appendLedger(repoRoot, entry, scope);
68
+ return { plan, blockedGoal: goal, newGoal, ledgerEntries };
69
+ });
70
+ }
@@ -0,0 +1,10 @@
1
+ export interface UlwLoopErrorOptions {
2
+ readonly cause?: unknown;
3
+ readonly details?: Record<string, unknown>;
4
+ }
5
+ export declare class UlwLoopError extends Error {
6
+ readonly code: string;
7
+ readonly details?: Record<string, unknown>;
8
+ constructor(message: string, code: string, opts?: UlwLoopErrorOptions);
9
+ }
10
+ export declare function iso(): string;