oh-my-opencode 4.18.2 → 4.19.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 (307) 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 +8 -8
  7. package/README.ru.md +6 -6
  8. package/README.zh-cn.md +6 -6
  9. package/dist/agents/atlas/agent.d.ts +7 -6
  10. package/dist/agents/gpt-prompt-identity.d.ts +3 -0
  11. package/dist/agents/sisyphus/gpt-5-5.d.ts +1 -1
  12. package/dist/agents/sisyphus/gpt-task-system-guide.d.ts +1 -0
  13. package/dist/agents/sisyphus/index.d.ts +5 -0
  14. package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
  15. package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
  16. package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
  17. package/dist/agents/sisyphus-junior/gpt-5-5.d.ts +2 -2
  18. package/dist/agents/sisyphus-junior/index.d.ts +2 -0
  19. package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
  20. package/dist/agents/types.d.ts +2 -2
  21. package/dist/cli/index.js +781 -215
  22. package/dist/cli-node/index.js +781 -215
  23. package/dist/config/index.d.ts +1 -1
  24. package/dist/config/schema/agent-overrides.d.ts +48 -48
  25. package/dist/config/schema/browser-automation.d.ts +1 -0
  26. package/dist/config/schema/categories.d.ts +6 -6
  27. package/dist/config/schema/codegraph.d.ts +2 -0
  28. package/dist/config/schema/commands.d.ts +1 -3
  29. package/dist/config/schema/default-mode.d.ts +1 -1
  30. package/dist/config/schema/fallback-models.d.ts +5 -5
  31. package/dist/config/schema/goal.d.ts +7 -0
  32. package/dist/config/schema/hooks.d.ts +1 -1
  33. package/dist/config/schema/oh-my-opencode-config.d.ts +56 -58
  34. package/dist/config/schema/ralph-loop.d.ts +1 -1
  35. package/dist/config/schema.d.ts +1 -1
  36. package/dist/create-hooks.d.ts +1 -1
  37. package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
  38. package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
  39. package/dist/features/builtin-commands/types.d.ts +1 -1
  40. package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
  41. package/dist/hooks/codegraph-bootstrap/hook.d.ts +3 -2
  42. package/dist/hooks/goal/command-arguments.d.ts +13 -0
  43. package/dist/hooks/goal/controller.d.ts +26 -0
  44. package/dist/hooks/goal/index.d.ts +23 -0
  45. package/dist/hooks/goal/prompt.d.ts +3 -0
  46. package/dist/hooks/goal/store.d.ts +8 -0
  47. package/dist/hooks/goal/tools.d.ts +10 -0
  48. package/dist/hooks/goal/types.d.ts +93 -0
  49. package/dist/hooks/goal/validation.d.ts +4 -0
  50. package/dist/hooks/index.d.ts +1 -0
  51. package/dist/hooks/keyword-detector/hook.d.ts +1 -2
  52. package/dist/hooks/ralph-loop/types.d.ts +1 -1
  53. package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +1 -1
  54. package/dist/hooks/start-work/notepad-scaffold.d.ts +10 -0
  55. package/dist/index.js +5398 -5109
  56. package/dist/oh-my-opencode.schema.json +34 -19
  57. package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
  58. package/dist/plugin/chat-message/types.d.ts +23 -10
  59. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
  60. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
  61. package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
  62. package/dist/plugin/stop-continuation.d.ts +2 -2
  63. package/dist/shared/omo-process-sweep.d.ts +18 -0
  64. package/dist/shared/system-directive.d.ts +0 -1
  65. package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
  66. package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
  67. package/dist/skills/ulw-plan/SKILL.md +31 -8
  68. package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
  69. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  70. package/dist/skills/ulw-research/SKILL.md +3 -3
  71. package/dist/testing/create-plugin-module.d.ts +1 -0
  72. package/dist/tools/delegate-task/types.d.ts +0 -1
  73. package/dist/tui.js +138 -80
  74. package/package.json +14 -14
  75. package/packages/git-bash-mcp/dist/cli.js +54 -8
  76. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +60 -0
  77. package/packages/lsp-core/src/lsp/client.ts +14 -1
  78. package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +2 -2
  79. package/packages/lsp-core/src/mcp.ts +9 -0
  80. package/packages/lsp-daemon/dist/cli.js +319 -51
  81. package/packages/lsp-daemon/dist/client.js +136 -48
  82. package/packages/lsp-daemon/dist/daemon-client.d.ts +0 -10
  83. package/packages/lsp-daemon/dist/daemon-client.js +4 -34
  84. package/packages/lsp-daemon/dist/daemon-failure-result.d.ts +3 -0
  85. package/packages/lsp-daemon/dist/daemon-failure-result.js +38 -0
  86. package/packages/lsp-daemon/dist/daemon-request-error.d.ts +14 -0
  87. package/packages/lsp-daemon/dist/daemon-request-error.js +26 -0
  88. package/packages/lsp-daemon/dist/daemon-server.js +5 -1
  89. package/packages/lsp-daemon/dist/index.js +137 -48
  90. package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
  91. package/packages/lsp-daemon/dist/proxy.js +1 -0
  92. package/packages/lsp-daemon/dist/version-reap.d.ts +26 -0
  93. package/packages/lsp-daemon/dist/version-reap.js +182 -0
  94. package/packages/lsp-tools-mcp/dist/cli.js +61 -10
  95. package/packages/lsp-tools-mcp/dist/lsp/manager.js +6 -1
  96. package/packages/lsp-tools-mcp/dist/mcp.js +61 -10
  97. package/packages/lsp-tools-mcp/dist/tools.js +6 -1
  98. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  99. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +416 -25
  100. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  101. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  102. package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +22 -2
  103. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
  104. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +638 -267
  105. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +178 -89
  106. package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
  107. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +3 -1
  108. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +3 -1
  109. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +98 -32
  110. package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +5 -0
  111. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +15 -6
  112. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +6 -2
  113. package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +1 -0
  114. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
  115. package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +148 -0
  116. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +31 -2
  117. package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +1 -1
  118. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +65 -1
  119. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -7
  120. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +1 -1
  121. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +57 -3
  122. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +41 -0
  123. package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +109 -8
  124. package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +2 -2
  125. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +43 -0
  126. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  127. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  128. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  129. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  130. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  131. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  132. package/packages/omo-codex/plugin/components/lsp/AGENTS.md +1 -0
  133. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  134. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +80 -40
  135. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  136. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  137. package/packages/omo-codex/plugin/components/rules/dist/cli.js +11 -0
  138. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  139. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  140. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +11 -0
  141. package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +60 -0
  142. package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
  143. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +147 -60
  144. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  145. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  146. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +3 -71
  147. package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
  148. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +198 -23
  149. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
  150. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +6 -5
  151. package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
  152. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  153. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  154. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  155. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  156. package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +10 -10
  157. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +2 -2
  158. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +2 -2
  159. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
  160. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +1 -1
  161. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +2 -2
  162. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
  163. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
  164. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +1 -1
  165. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  166. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  167. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +31 -8
  168. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
  169. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  170. package/packages/omo-codex/plugin/components/ultrawork/src/codex-hook.ts +1 -1
  171. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook-trigger-policy.test.ts +42 -0
  172. package/packages/omo-codex/plugin/components/ulw-loop/README.md +6 -4
  173. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.d.ts +21 -0
  174. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +64 -0
  175. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +15 -2
  176. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.js +1 -1
  177. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +2 -1
  178. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
  179. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +3 -3
  180. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.d.ts +3 -0
  181. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +85 -0
  182. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +0 -1
  183. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -33
  184. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +998 -593
  185. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +1 -0
  186. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.d.ts +1 -1
  187. package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.js +2 -0
  188. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +6 -0
  189. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.d.ts +1 -0
  190. package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.js +3 -0
  191. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +1 -0
  192. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +4 -0
  193. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +1 -0
  194. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +10 -2
  195. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +1 -1
  196. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.d.ts +15 -0
  197. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +84 -0
  198. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +5 -0
  199. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +81 -0
  200. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +7 -59
  201. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +12 -4
  202. package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.d.ts +9 -0
  203. package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.js +122 -0
  204. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  205. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  206. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +8 -6
  207. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +84 -0
  208. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +13 -2
  209. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-arg-parser.ts +1 -1
  210. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +1 -1
  211. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +3 -3
  212. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +49 -0
  213. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +13 -45
  214. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +1 -0
  215. package/packages/omo-codex/plugin/components/ulw-loop/src/constants.ts +2 -0
  216. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +7 -0
  217. package/packages/omo-codex/plugin/components/ulw-loop/src/goal-status.ts +4 -0
  218. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +4 -1
  219. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +20 -2
  220. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +1 -1
  221. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +125 -0
  222. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +59 -0
  223. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +6 -58
  224. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +12 -4
  225. package/packages/omo-codex/plugin/components/ulw-loop/src/validation-batch.ts +122 -0
  226. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +76 -0
  227. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-checkpoint-continuation.test.ts +111 -0
  228. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-steering-batch.test.ts +67 -0
  229. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-validation-batch.test.ts +57 -0
  230. package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +1 -0
  231. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +152 -0
  232. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +29 -0
  233. package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch-checkpoint.test.ts +147 -0
  234. package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch.test.ts +73 -0
  235. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  236. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  237. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  238. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  239. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  240. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  241. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  242. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  243. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  244. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  245. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  246. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +2 -2
  247. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  248. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +2 -2
  249. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  250. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +2 -2
  251. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  252. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  253. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  254. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  255. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  256. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  257. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  258. package/packages/omo-codex/plugin/package-lock.json +41 -41
  259. package/packages/omo-codex/plugin/package.json +1 -1
  260. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
  261. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
  262. package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
  263. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +3 -18
  264. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +43 -0
  265. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
  266. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +8 -6
  267. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +31 -8
  268. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
  269. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  270. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +11 -11
  271. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +26 -0
  272. package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +41 -0
  273. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
  274. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
  275. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
  276. package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
  277. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
  278. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +3 -18
  279. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
  280. package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
  281. package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
  282. package/packages/omo-codex/scripts/install-dist/install-local.mjs +401 -22
  283. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
  284. package/packages/omo-codex/scripts/install-marketplace-cache.test.mjs +4 -1
  285. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
  286. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
  287. package/packages/shared-skills/skills/ulw-plan/SKILL.md +31 -8
  288. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
  289. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  290. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
  291. package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
  292. package/dist/skills/ultraresearch/SKILL.md +0 -10
  293. package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
  294. package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
  295. package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
  296. package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
  297. package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
  298. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
  299. package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
  300. package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
  301. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
  302. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
  303. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
  304. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
  305. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
  306. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
  307. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/agents/openai.yaml +0 -0
@@ -0,0 +1,455 @@
1
+ export function findTomlSection(config, headerLine) {
2
+ const targetHeaderPath = parseTomlTableHeader(headerLine);
3
+ const lines = config.match(/[^\n]*\n?|$/g) ?? [];
4
+ let offset = 0;
5
+ let start = -1;
6
+ let multilineQuote = null;
7
+ for (const line of lines) {
8
+ if (line.length === 0) break;
9
+ const multilineScan = scanTomlMultilineLine(line, multilineQuote);
10
+ multilineQuote = multilineScan.nextQuote;
11
+ if (multilineScan.wasInside) {
12
+ offset += line.length;
13
+ continue;
14
+ }
15
+ if (start === -1) {
16
+ if (tomlTableHeaderMatches(line, targetHeaderPath)) start = offset;
17
+ } else if (isTomlTableHeaderLine(line)) {
18
+ return { start, end: offset, text: config.slice(start, offset) };
19
+ }
20
+ offset += line.length;
21
+ }
22
+ if (start === -1) return null;
23
+ return { start, end: config.length, text: config.slice(start) };
24
+ }
25
+
26
+ export function hasTomlSetting(config, keyPath) {
27
+ const targetPath = parseTomlDottedKey(keyPath);
28
+ if (!targetPath) return false;
29
+
30
+ let tablePath = [];
31
+ let multilineQuote = null;
32
+ for (const line of config.split("\n")) {
33
+ const multilineScan = scanTomlMultilineLine(line, multilineQuote);
34
+ multilineQuote = multilineScan.nextQuote;
35
+ if (multilineScan.wasInside) continue;
36
+ const normalizedLine = stripUnquotedInlineComment(line).trim();
37
+ if (normalizedLine.length === 0) continue;
38
+
39
+ const headerPath = parseTomlTableHeader(normalizedLine);
40
+ if (headerPath) {
41
+ tablePath = headerPath;
42
+ continue;
43
+ }
44
+ if (isTomlTableHeaderLine(normalizedLine)) {
45
+ tablePath = null;
46
+ continue;
47
+ }
48
+ if (!tablePath) continue;
49
+
50
+ const assignmentIndex = findUnquotedAssignment(normalizedLine);
51
+ if (assignmentIndex < 0) continue;
52
+ const settingPath = parseTomlDottedKey(normalizedLine.slice(0, assignmentIndex).trim());
53
+ if (!settingPath) continue;
54
+ const fullPath = [...tablePath, ...settingPath];
55
+ if (fullPath.length !== targetPath.length) continue;
56
+ if (fullPath.every((part, index) => part === targetPath[index])) return true;
57
+ }
58
+ return false;
59
+ }
60
+
61
+ export function replaceOrInsertRootTomlSetting(config, keyPath, value) {
62
+ const targetPath = parseTomlDottedKey(keyPath);
63
+ if (!targetPath) return config;
64
+ const match = findRootTomlSetting(config, targetPath);
65
+ if (match) {
66
+ const commentIndex = findUnquotedComment(match.lineBody, match.assignmentIndex + 1);
67
+ const comment = commentIndex === -1 ? "" : ` ${match.lineBody.slice(commentIndex).trimStart()}`;
68
+ const replacement = `${match.lineBody.slice(0, match.assignmentIndex + 1)} ${value}${comment}${match.newline}`;
69
+ return config.slice(0, match.offset) + replacement + config.slice(match.offset + match.line.length);
70
+ }
71
+ const firstTable = findFirstTomlTableStart(config);
72
+ const root = config.slice(0, firstTable).trimEnd();
73
+ const suffix = config.slice(firstTable);
74
+ const replacement = `${root}${root.length > 0 ? "\n" : ""}${keyPath} = ${value}\n`;
75
+ if (suffix.length === 0) return replacement;
76
+ return `${replacement.trimEnd()}\n\n${suffix.trimStart()}`;
77
+ }
78
+
79
+ export function replaceOrInsertTomlSectionSetting(config, section, keyPath, value) {
80
+ const targetPath = parseTomlDottedKey(keyPath);
81
+ if (!targetPath) return config;
82
+ const lines = section.text.match(/[^\n]*\n?|$/g) ?? [];
83
+ let offset = 0;
84
+ let multilineQuote = null;
85
+ for (const line of lines) {
86
+ if (line.length === 0) break;
87
+ const multilineScan = scanTomlMultilineLine(line, multilineQuote);
88
+ multilineQuote = multilineScan.nextQuote;
89
+ if (multilineScan.wasInside) {
90
+ offset += line.length;
91
+ continue;
92
+ }
93
+ const assignmentIndex = findUnquotedAssignment(line);
94
+ if (assignmentIndex >= 0) {
95
+ const settingPath = parseTomlDottedKey(line.slice(0, assignmentIndex).trim());
96
+ if (settingPath && tomlPathMatches(settingPath, targetPath)) {
97
+ const replacement = replaceTomlAssignmentValue(line, assignmentIndex, value);
98
+ const patched = section.text.slice(0, offset) + replacement + section.text.slice(offset + line.length);
99
+ return config.slice(0, section.start) + patched + config.slice(section.end);
100
+ }
101
+ }
102
+ offset += line.length;
103
+ }
104
+ const headerEnd = section.text.indexOf("\n");
105
+ const insertAt = headerEnd === -1 ? section.text.length : headerEnd + 1;
106
+ const patched = `${section.text.slice(0, insertAt)}${headerEnd === -1 ? "\n" : ""}${keyPath} = ${value}\n${section.text.slice(insertAt)}`;
107
+ return config.slice(0, section.start) + patched + config.slice(section.end);
108
+ }
109
+
110
+ export function removeTomlSectionSetting(config, section, keyPath, expectedValue) {
111
+ const targetPath = parseTomlDottedKey(keyPath);
112
+ if (!targetPath) return config;
113
+ const lines = section.text.match(/[^\n]*\n?|$/g) ?? [];
114
+ let offset = 0;
115
+ let multilineQuote = null;
116
+ for (const line of lines) {
117
+ if (line.length === 0) break;
118
+ const multilineScan = scanTomlMultilineLine(line, multilineQuote);
119
+ multilineQuote = multilineScan.nextQuote;
120
+ if (multilineScan.wasInside) {
121
+ offset += line.length;
122
+ continue;
123
+ }
124
+ const assignmentIndex = findUnquotedAssignment(line);
125
+ if (assignmentIndex >= 0) {
126
+ const settingPath = parseTomlDottedKey(line.slice(0, assignmentIndex).trim());
127
+ if (settingPath && tomlPathMatches(settingPath, targetPath)) {
128
+ const lineBody = line.endsWith("\n") ? line.slice(0, -1) : line;
129
+ const commentIndex = findUnquotedComment(lineBody, assignmentIndex + 1);
130
+ const valueEnd = commentIndex === -1 ? lineBody.length : commentIndex;
131
+ if (lineBody.slice(assignmentIndex + 1, valueEnd).trim() !== expectedValue) return config;
132
+ const patched = section.text.slice(0, offset) + section.text.slice(offset + line.length);
133
+ return config.slice(0, section.start) + patched + config.slice(section.end);
134
+ }
135
+ }
136
+ offset += line.length;
137
+ }
138
+ return config;
139
+ }
140
+
141
+ export function removeRootTomlSetting(config, keyPath, expectedValue) {
142
+ const targetPath = parseTomlDottedKey(keyPath);
143
+ if (!targetPath) return config;
144
+ const match = findRootTomlSetting(config, targetPath);
145
+ if (!match) return config;
146
+ const commentIndex = findUnquotedComment(match.lineBody, match.assignmentIndex + 1);
147
+ const valueEnd = commentIndex === -1 ? match.lineBody.length : commentIndex;
148
+ if (match.lineBody.slice(match.assignmentIndex + 1, valueEnd).trim() !== expectedValue) return config;
149
+ return config.slice(0, match.offset) + config.slice(match.offset + match.line.length);
150
+ }
151
+
152
+ function tomlTableHeaderMatches(line, targetHeaderPath) {
153
+ if (!targetHeaderPath) return false;
154
+ const candidateHeaderPath = parseTomlTableHeader(line);
155
+ if (!candidateHeaderPath || candidateHeaderPath.length !== targetHeaderPath.length) return false;
156
+ return candidateHeaderPath.every((part, index) => part === targetHeaderPath[index]);
157
+ }
158
+
159
+ function isTomlTableHeaderLine(line) {
160
+ const normalizedLine = stripUnquotedInlineComment(line).trim();
161
+ return normalizedLine.startsWith("[") && normalizedLine.endsWith("]");
162
+ }
163
+
164
+ function scanTomlMultilineLine(line, currentQuote) {
165
+ if (currentQuote) {
166
+ return {
167
+ wasInside: true,
168
+ nextQuote: findTomlMultilineDelimiter(line, currentQuote, 0) === -1 ? currentQuote : null,
169
+ };
170
+ }
171
+
172
+ let quote = null;
173
+ let index = 0;
174
+ while (index < line.length) {
175
+ const char = line[index];
176
+ if (quote === '"') {
177
+ if (char === "\\") {
178
+ index += 2;
179
+ continue;
180
+ }
181
+ if (char === '"') quote = null;
182
+ index += 1;
183
+ continue;
184
+ }
185
+ if (quote === "'") {
186
+ if (char === "'") quote = null;
187
+ index += 1;
188
+ continue;
189
+ }
190
+ if (char === "#") break;
191
+ const delimiter = line.startsWith('"""', index) ? '"""' : line.startsWith("'''", index) ? "'''" : null;
192
+ if (delimiter) {
193
+ const closingIndex = findTomlMultilineDelimiter(line, delimiter, index + delimiter.length);
194
+ return { wasInside: false, nextQuote: closingIndex === -1 ? delimiter : null };
195
+ }
196
+ if (char === '"' || char === "'") quote = char;
197
+ index += 1;
198
+ }
199
+ return { wasInside: false, nextQuote: null };
200
+ }
201
+
202
+ function findTomlMultilineDelimiter(line, delimiter, startIndex) {
203
+ let index = line.indexOf(delimiter, startIndex);
204
+ while (index !== -1) {
205
+ if (delimiter === "'''" || countPrecedingBackslashes(line, index) % 2 === 0) return index;
206
+ index = line.indexOf(delimiter, index + 1);
207
+ }
208
+ return -1;
209
+ }
210
+
211
+ function countPrecedingBackslashes(line, index) {
212
+ let count = 0;
213
+ let cursor = index - 1;
214
+ while (cursor >= 0 && line[cursor] === "\\") {
215
+ count += 1;
216
+ cursor -= 1;
217
+ }
218
+ return count;
219
+ }
220
+
221
+ function findRootTomlSetting(config, targetPath) {
222
+ const lines = config.match(/[^\n]*\n?|$/g) ?? [];
223
+ let offset = 0;
224
+ let multilineQuote = null;
225
+ for (const line of lines) {
226
+ if (line.length === 0) break;
227
+ const multilineScan = scanTomlMultilineLine(line, multilineQuote);
228
+ multilineQuote = multilineScan.nextQuote;
229
+ if (multilineScan.wasInside) {
230
+ offset += line.length;
231
+ continue;
232
+ }
233
+ if (isTomlTableHeaderLine(line)) break;
234
+ const assignmentIndex = findUnquotedAssignment(line);
235
+ if (assignmentIndex >= 0) {
236
+ const settingPath = parseTomlDottedKey(line.slice(0, assignmentIndex).trim());
237
+ if (settingPath && tomlPathMatches(settingPath, targetPath)) {
238
+ const newline = line.endsWith("\n") ? "\n" : "";
239
+ return { line, lineBody: newline ? line.slice(0, -1) : line, newline, offset, assignmentIndex };
240
+ }
241
+ }
242
+ offset += line.length;
243
+ }
244
+ return null;
245
+ }
246
+
247
+ function findFirstTomlTableStart(config) {
248
+ const lines = config.match(/[^\n]*\n?|$/g) ?? [];
249
+ let offset = 0;
250
+ let multilineQuote = null;
251
+ for (const line of lines) {
252
+ if (line.length === 0) break;
253
+ const multilineScan = scanTomlMultilineLine(line, multilineQuote);
254
+ multilineQuote = multilineScan.nextQuote;
255
+ if (!multilineScan.wasInside && isTomlTableHeaderLine(line)) return offset;
256
+ offset += line.length;
257
+ }
258
+ return config.length;
259
+ }
260
+
261
+ function findUnquotedComment(line, startIndex) {
262
+ let quote = null;
263
+ let index = startIndex;
264
+ while (index < line.length) {
265
+ const char = line[index];
266
+ if (quote === '"') {
267
+ if (char === "\\") {
268
+ index += 2;
269
+ continue;
270
+ }
271
+ if (char === '"') quote = null;
272
+ index += 1;
273
+ continue;
274
+ }
275
+ if (quote === "'") {
276
+ if (char === "'") quote = null;
277
+ index += 1;
278
+ continue;
279
+ }
280
+ if (char === '"' || char === "'") quote = char;
281
+ else if (char === "#") return index;
282
+ index += 1;
283
+ }
284
+ return -1;
285
+ }
286
+
287
+ function tomlPathMatches(candidate, target) {
288
+ return candidate.length === target.length && candidate.every((part, index) => part === target[index]);
289
+ }
290
+
291
+ function replaceTomlAssignmentValue(line, assignmentIndex, value) {
292
+ const newline = line.endsWith("\n") ? "\n" : "";
293
+ const lineBody = newline ? line.slice(0, -1) : line;
294
+ const commentIndex = findUnquotedComment(lineBody, assignmentIndex + 1);
295
+ const comment = commentIndex === -1 ? "" : ` ${lineBody.slice(commentIndex).trimStart()}`;
296
+ return `${lineBody.slice(0, assignmentIndex + 1)} ${value}${comment}${newline}`;
297
+ }
298
+
299
+ function parseTomlTableHeader(line) {
300
+ const normalizedLine = stripUnquotedInlineComment(line).trim();
301
+ if (!normalizedLine.startsWith("[") || !normalizedLine.endsWith("]") || normalizedLine.startsWith("[[")) {
302
+ return null;
303
+ }
304
+ return parseTomlDottedKey(normalizedLine.slice(1, -1).trim());
305
+ }
306
+
307
+ function stripUnquotedInlineComment(line) {
308
+ let quote = null;
309
+ let index = 0;
310
+ while (index < line.length) {
311
+ const char = line[index];
312
+ if (quote === '"') {
313
+ if (char === "\\") {
314
+ index += 2;
315
+ continue;
316
+ }
317
+ if (char === '"') quote = null;
318
+ index += 1;
319
+ continue;
320
+ }
321
+ if (quote === "'") {
322
+ if (char === "'") quote = null;
323
+ index += 1;
324
+ continue;
325
+ }
326
+ if (char === '"' || char === "'") {
327
+ quote = char;
328
+ index += 1;
329
+ continue;
330
+ }
331
+ if (char === "#") return line.slice(0, index);
332
+ index += 1;
333
+ }
334
+ return line;
335
+ }
336
+
337
+ function findUnquotedAssignment(line) {
338
+ let quote = null;
339
+ let index = 0;
340
+ while (index < line.length) {
341
+ const char = line[index];
342
+ if (quote === '"') {
343
+ if (char === "\\") {
344
+ index += 2;
345
+ continue;
346
+ }
347
+ if (char === '"') quote = null;
348
+ index += 1;
349
+ continue;
350
+ }
351
+ if (quote === "'") {
352
+ if (char === "'") quote = null;
353
+ index += 1;
354
+ continue;
355
+ }
356
+ if (char === '"' || char === "'") {
357
+ quote = char;
358
+ index += 1;
359
+ continue;
360
+ }
361
+ if (char === "=") return index;
362
+ index += 1;
363
+ }
364
+ return -1;
365
+ }
366
+
367
+ function parseTomlDottedKey(input) {
368
+ const parts = [];
369
+ let index = 0;
370
+ while (index < input.length) {
371
+ index = skipWhitespace(input, index);
372
+ const parsedKey = parseTomlKeyPart(input, index);
373
+ if (!parsedKey) return null;
374
+ parts.push(parsedKey.value);
375
+ index = skipWhitespace(input, parsedKey.nextIndex);
376
+ if (index === input.length) return parts;
377
+ if (input[index] !== ".") return null;
378
+ index += 1;
379
+ }
380
+ return parts.length > 0 ? parts : null;
381
+ }
382
+
383
+ function parseTomlKeyPart(input, startIndex) {
384
+ const quote = input[startIndex];
385
+ if (quote === "'") return parseLiteralTomlString(input, startIndex);
386
+ if (quote === '"') return parseBasicTomlString(input, startIndex);
387
+ return parseBareTomlKey(input, startIndex);
388
+ }
389
+
390
+ function parseLiteralTomlString(input, startIndex) {
391
+ let index = startIndex + 1;
392
+ let value = "";
393
+ while (index < input.length) {
394
+ const char = input[index];
395
+ if (char === "'") return { value, nextIndex: index + 1 };
396
+ value += char;
397
+ index += 1;
398
+ }
399
+ return null;
400
+ }
401
+
402
+ function parseBasicTomlString(input, startIndex) {
403
+ let index = startIndex + 1;
404
+ let value = "";
405
+ while (index < input.length) {
406
+ const char = input[index];
407
+ if (char === '"') return { value, nextIndex: index + 1 };
408
+ if (char !== "\\") {
409
+ value += char;
410
+ index += 1;
411
+ continue;
412
+ }
413
+ const escaped = parseBasicTomlEscape(input, index);
414
+ if (!escaped) return null;
415
+ value += escaped.value;
416
+ index = escaped.nextIndex;
417
+ }
418
+ return null;
419
+ }
420
+
421
+ function parseBasicTomlEscape(input, backslashIndex) {
422
+ const escape = input[backslashIndex + 1];
423
+ if (escape === undefined) return null;
424
+ if (escape === "b") return { value: "\b", nextIndex: backslashIndex + 2 };
425
+ if (escape === "t") return { value: "\t", nextIndex: backslashIndex + 2 };
426
+ if (escape === "n") return { value: "\n", nextIndex: backslashIndex + 2 };
427
+ if (escape === "f") return { value: "\f", nextIndex: backslashIndex + 2 };
428
+ if (escape === "r") return { value: "\r", nextIndex: backslashIndex + 2 };
429
+ if (escape === '"') return { value: '"', nextIndex: backslashIndex + 2 };
430
+ if (escape === "\\") return { value: "\\", nextIndex: backslashIndex + 2 };
431
+ if (escape === "u") return parseUnicodeEscape(input, backslashIndex + 2, 4);
432
+ if (escape === "U") return parseUnicodeEscape(input, backslashIndex + 2, 8);
433
+ return null;
434
+ }
435
+
436
+ function parseUnicodeEscape(input, digitsStart, digitCount) {
437
+ const digits = input.slice(digitsStart, digitsStart + digitCount);
438
+ if (digits.length !== digitCount || !/^[0-9A-Fa-f]+$/.test(digits)) return null;
439
+ const codePoint = Number.parseInt(digits, 16);
440
+ if (codePoint > 0x10ffff) return null;
441
+ return { value: String.fromCodePoint(codePoint), nextIndex: digitsStart + digitCount };
442
+ }
443
+
444
+ function parseBareTomlKey(input, startIndex) {
445
+ let index = startIndex;
446
+ while (index < input.length && /[A-Za-z0-9_-]/.test(input[index])) index += 1;
447
+ if (index === startIndex) return null;
448
+ return { value: input.slice(startIndex, index), nextIndex: index };
449
+ }
450
+
451
+ function skipWhitespace(input, startIndex) {
452
+ let index = startIndex;
453
+ while (index < input.length && /\s/.test(input[index])) index += 1;
454
+ return index;
455
+ }
@@ -14,6 +14,9 @@ const ignoredSkillSourceDirNames = new Set([".mypy_cache", ".omo", ".pytest_cach
14
14
  const ignoredSkillSourceFileNames = new Set([".gitignore", ".npmignore", "pyrightconfig.json"]);
15
15
  const skillSources = [
16
16
  ["comment-checker", "components/comment-checker/skills/comment-checker"],
17
+ ["lcx-contribute-bug-fix", "components/lcx/skills/lcx-contribute-bug-fix"],
18
+ ["lcx-doctor", "components/lcx/skills/lcx-doctor"],
19
+ ["lcx-report-bug", "components/lcx/skills/lcx-report-bug"],
17
20
  ["lsp", "components/lsp/skills/lsp"],
18
21
  ["rules", "components/rules/skills/rules"],
19
22
  ["teammode", "components/teammode/skills/teammode"],
@@ -22,7 +25,6 @@ const skillSources = [
22
25
  ["ultrawork", "components/ultrawork/skills/ultrawork"],
23
26
  ];
24
27
  const componentSkillNames = new Set(skillSources.map(([name]) => name));
25
- const codexHiddenSharedSkillNames = new Set(["ultraresearch"]);
26
28
  const skillDisplayPrefix = "(OmO) ";
27
29
 
28
30
  function shouldCopySkillSource(source) {
@@ -182,22 +184,6 @@ prefixes when identity is needed.
182
184
  `;
183
185
 
184
186
  function applyCodexSkillOverlays(skillName, content) {
185
- if (skillName === "ulw-research") {
186
- return content
187
- .replace(
188
- ", the legacy alias 'ultraresearch', any 'ulw' research wording,",
189
- ", any 'ulw' research wording,",
190
- )
191
- .replace(
192
- 'the legacy alias "ultraresearch" (also `/ultraresearch`, `$ultraresearch`), ',
193
- "",
194
- )
195
- .replace(
196
- "answer those normally, and mention that `ulw-research` is available (legacy alias: `ultraresearch`) when a question would clearly benefit from it.",
197
- "answer those normally, and mention that `ulw-research` is available when a question would clearly benefit from it.",
198
- )
199
- .replace("# Ultraresearch Synthesis: <query>", "# ULW-Research Synthesis: <query>");
200
- }
201
187
  if (skillName === "start-work") {
202
188
  return content
203
189
  .replace(startWorkOriginalCompletion, startWorkCodexCompletion)
@@ -270,7 +256,6 @@ async function syncSkills() {
270
256
 
271
257
  for (const skillName of sharedSkillNames) {
272
258
  if (componentSkillNames.has(skillName)) continue;
273
- if (codexHiddenSharedSkillNames.has(skillName)) continue;
274
259
  await cp(join(sharedSkillsRoot, skillName), join(skillsRoot, skillName), {
275
260
  filter: shouldCopySkillSource,
276
261
  recursive: true,
@@ -85,6 +85,49 @@ describe("getCodexOmoConfig", () => {
85
85
  expect(result.codegraph?.excluded_roots).toEqual(["/tmp/omo-research", "/private/tmp/omo-research"])
86
86
  })
87
87
 
88
+ it("#given no codegraph.daemon key #when loading config #then daemon defaults to off", () => {
89
+ // given
90
+ const homeDir = createTemporaryDirectory("omo-codex-shared-daemon-default-home-")
91
+ const cwd = createTemporaryDirectory("omo-codex-shared-daemon-default-project-")
92
+ writeOmoConfig(homeDir, JSON.stringify({ codegraph: { enabled: true } }))
93
+
94
+ // when
95
+ const result = getCodexOmoConfig({ cwd, homeDir, env: {} })
96
+
97
+ // then
98
+ expect(result.codegraph?.daemon).toBeUndefined()
99
+ expect(result.codegraph?.daemon === true).toBe(false)
100
+ expect(result.warnings).toEqual([])
101
+ })
102
+
103
+ it("#given codex SOT sets codegraph.daemon=true #when loading config #then daemon opt-in is returned", () => {
104
+ // given
105
+ const homeDir = createTemporaryDirectory("omo-codex-shared-daemon-on-home-")
106
+ const cwd = createTemporaryDirectory("omo-codex-shared-daemon-on-project-")
107
+ writeOmoConfig(homeDir, JSON.stringify({ "[codex]": { codegraph: { daemon: true } } }))
108
+
109
+ // when
110
+ const result = getCodexOmoConfig({ cwd, homeDir, env: {} })
111
+
112
+ // then
113
+ expect(result.codegraph?.daemon).toBe(true)
114
+ expect(result.warnings).toEqual([])
115
+ })
116
+
117
+ it("#given codex SOT sets codegraph.daemon to a non-boolean #when loading config #then the value is rejected with a warning", () => {
118
+ // given
119
+ const homeDir = createTemporaryDirectory("omo-codex-shared-daemon-invalid-home-")
120
+ const cwd = createTemporaryDirectory("omo-codex-shared-daemon-invalid-project-")
121
+ writeOmoConfig(homeDir, JSON.stringify({ "[codex]": { codegraph: { daemon: "yes" } } }))
122
+
123
+ // when
124
+ const result = getCodexOmoConfig({ cwd, homeDir, env: {} })
125
+
126
+ // then
127
+ expect(result.codegraph?.daemon).toBeUndefined()
128
+ expect(result.warnings).toContain("config.[codex].codegraph.daemon must be a boolean")
129
+ })
130
+
88
131
  it("#given legacy env override and SOT value #when loading config #then env wins over the SOT", () => {
89
132
  // given
90
133
  const homeDir = createTemporaryDirectory("omo-codex-shared-env-")
@@ -19,6 +19,10 @@
19
19
  * Usage:
20
20
  * bun run scripts/check-no-excuse-rules.ts <file-or-dir>...
21
21
  *
22
+ * The `typescript` package is resolved from the caller project (process.cwd()),
23
+ * not from this script's location, so the script works when executed from an
24
+ * installed skill-cache path (e.g. ~/.codex/...) against a project checkout.
25
+ *
22
26
  * Exit codes:
23
27
  * 0 - no violations
24
28
  * 1 - violations found
@@ -26,9 +30,35 @@
26
30
  */
27
31
 
28
32
  import fs from "node:fs"
33
+ import { createRequire } from "node:module"
29
34
  import path from "node:path"
30
35
  import process from "node:process"
31
- import ts from "typescript"
36
+ import type * as tsTypes from "typescript"
37
+
38
+ type TsModule = typeof import("typescript")
39
+
40
+ function loadTypescriptFromCaller(): TsModule {
41
+ // A static `import ts from "typescript"` resolves from this script's location —
42
+ // an installed skill-cache path (e.g. ~/.codex/...) with no node_modules of its
43
+ // own — and fails even when the checked project has typescript. Resolve from the
44
+ // caller project (cwd) instead. When the caller has no local typescript, Bun
45
+ // falls back to a version-only stub from its global install cache instead of
46
+ // throwing, so the loaded module's API shape is verified as well.
47
+ const callerRequire = createRequire(path.join(process.cwd(), "no-excuse-anchor.cjs"))
48
+ try {
49
+ const loaded: Partial<TsModule> = callerRequire("typescript")
50
+ if (typeof loaded.createSourceFile === "function") return loaded as TsModule
51
+ } catch { // no-excuse-ok: catch
52
+ // fall through to the clear error below
53
+ }
54
+ console.error(
55
+ `error: cannot resolve "typescript" from the caller project (${process.cwd()}). ` +
56
+ "Install it in the project being checked (e.g. `bun add -d typescript`) and re-run.",
57
+ )
58
+ process.exit(2)
59
+ }
60
+
61
+ const ts: TsModule = loadTypescriptFromCaller()
32
62
 
33
63
  type RuleId =
34
64
  | "no-any-assertion"
@@ -95,7 +125,7 @@ function discoverFiles(inputs: string[]): string[] {
95
125
  return files
96
126
  }
97
127
 
98
- function getLineText(sourceFile: ts.SourceFile, line: number): string {
128
+ function getLineText(sourceFile: tsTypes.SourceFile, line: number): string {
99
129
  const lineStarts = sourceFile.getLineStarts()
100
130
  const start = lineStarts[line]
101
131
  const end = line + 1 < lineStarts.length ? lineStarts[line + 1] : sourceFile.getEnd()
@@ -107,17 +137,17 @@ function analyzeFile(filePath: string): Violation[] {
107
137
  const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true)
108
138
  const violations: Violation[] = []
109
139
 
110
- function pos(node: ts.Node): { line: number; column: number } {
140
+ function pos(node: tsTypes.Node): { line: number; column: number } {
111
141
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile))
112
142
  return { line: line + 1, column: character + 1 }
113
143
  }
114
144
 
115
- function lineHasOptOut(node: ts.Node): boolean {
145
+ function lineHasOptOut(node: tsTypes.Node): boolean {
116
146
  const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile))
117
147
  return OPT_OUT_RE.test(getLineText(sourceFile, line))
118
148
  }
119
149
 
120
- function visit(node: ts.Node): void {
150
+ function visit(node: tsTypes.Node): void {
121
151
  // ── as any / as unknown ──
122
152
  if (ts.isAsExpression(node)) {
123
153
  const typeText = node.type.getText(sourceFile)
@@ -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