oh-my-opencode 5.0.0-beta.62 → 5.0.0-beta.64

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 (249) hide show
  1. package/THIRD-PARTY-NOTICES.md +8 -6
  2. package/dist/cli/doctor/checks/browser-provider.d.ts +2 -0
  3. package/dist/cli/index.js +70 -41
  4. package/dist/cli-node/index.js +70 -41
  5. package/dist/config/schema/agent-names.d.ts +0 -1
  6. package/dist/config/schema/browser-automation.d.ts +0 -2
  7. package/dist/config/schema/oh-my-opencode-config.d.ts +0 -1
  8. package/dist/hooks/comment-checker/__fixtures__/download-probe.fixture.d.ts +1 -0
  9. package/dist/index.js +89 -588
  10. package/dist/oh-my-opencode.schema.json +4 -5
  11. package/dist/skills/ast-grep/SKILL.md +12 -0
  12. package/dist/skills/debugging/references/tools/playwright-cli.md +79 -161
  13. package/dist/skills/frontend/SKILL.md +1 -1
  14. package/dist/skills/frontend/references/design/aside.md +1 -1
  15. package/dist/skills/frontend/references/design/clone-from-url.md +1 -1
  16. package/dist/skills/frontend/scripts/perfection/lighthouse-audit.py +2 -2
  17. package/dist/skills/review-work/SKILL.md +1 -1
  18. package/dist/skills/ultimate-browsing/ATTRIBUTION.md +1 -82
  19. package/dist/skills/ultimate-browsing/SKILL.md +17 -29
  20. package/dist/skills/ultimate-browsing/engine/AGENTS.md +2 -1
  21. package/dist/skills/ultimate-browsing/engine/templates/package.json +2 -2
  22. package/dist/skills/ultimate-browsing/engine/templates/playwright_mobile_chrome.js +5 -2
  23. package/dist/skills/ultimate-browsing/engine/templates/playwright_real_chrome.js +8 -6
  24. package/dist/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +86 -0
  25. package/dist/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +4 -7
  26. package/dist/skills/ultimate-browsing/references/chrome-stealth.md +86 -100
  27. package/dist/skills/ultimate-browsing/references/insane-search/README.md +9 -3
  28. package/dist/skills/ultimate-browsing/references/insane-search/playwright.md +16 -13
  29. package/dist/skills/ultimate-browsing/scripts/extract_cookies.py +3 -3
  30. package/dist/skills/ulw-execute/SKILL.md +1 -1
  31. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
  32. package/dist/skills/ulw-research/SKILL.md +3 -3
  33. package/dist/skills/visual-qa/AGENTS.md +1 -1
  34. package/dist/skills/visual-qa/SKILL.md +1 -1
  35. package/dist/skills/visual-qa/references/browser-setup.md +75 -0
  36. package/dist/tui.js +18 -11
  37. package/package.json +16 -23
  38. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  39. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -2
  40. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  43. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  44. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  45. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  46. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  47. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  48. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  49. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  50. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  52. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  53. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  54. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  55. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  56. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -2
  58. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +5 -4
  59. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  60. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  61. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  62. package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +1 -1
  63. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  64. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  65. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +13 -0
  66. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +5 -4
  67. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +7 -5
  68. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +7 -2
  69. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +7 -4
  70. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +931 -442
  71. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +6 -0
  72. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +20 -12
  73. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +8 -0
  74. package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.d.ts +4 -0
  75. package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.js +18 -0
  76. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.d.ts +1 -0
  77. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.js +37 -0
  78. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.d.ts +1 -0
  79. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.js +24 -12
  80. package/packages/omo-codex/plugin/components/ulw-loop/dist/ledger.d.ts +4 -0
  81. package/packages/omo-codex/plugin/components/ulw-loop/dist/ledger.js +34 -0
  82. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +1 -0
  83. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +6 -0
  84. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-commit.d.ts +19 -0
  85. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-commit.js +107 -0
  86. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +6 -2
  87. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +54 -25
  88. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.d.ts +10 -3
  89. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.js +38 -31
  90. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +9 -7
  91. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +105 -114
  92. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.d.ts +15 -0
  93. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.js +86 -0
  94. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.d.ts +5 -1
  95. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.js +15 -3
  96. package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +11 -8
  97. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/factory.js +28 -12
  98. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.d.ts +335 -0
  99. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.js +109 -10
  100. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/types.d.ts +3 -0
  101. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-budget-io.d.ts +5 -0
  102. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-budget-io.js +67 -0
  103. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +8 -62
  104. package/packages/omo-codex/plugin/components/ulw-loop/dist/state-lock.d.ts +11 -1
  105. package/packages/omo-codex/plugin/components/ulw-loop/dist/state-lock.js +120 -88
  106. package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.d.ts +3 -2
  107. package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.js +18 -10
  108. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +4 -4
  109. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +3 -2
  110. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +4 -4
  111. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +9 -1
  112. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.d.ts +3 -2
  113. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +11 -11
  114. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +8 -17
  115. package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.d.ts +9 -0
  116. package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.js +41 -0
  117. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  118. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  119. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
  120. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +15 -3
  121. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +7 -4
  122. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +26 -14
  123. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +8 -0
  124. package/packages/omo-codex/plugin/components/ulw-loop/src/driver-objective-ack.ts +20 -0
  125. package/packages/omo-codex/plugin/components/ulw-loop/src/evidence-artifacts.ts +45 -0
  126. package/packages/omo-codex/plugin/components/ulw-loop/src/evidence.ts +23 -16
  127. package/packages/omo-codex/plugin/components/ulw-loop/src/ledger.ts +33 -0
  128. package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +7 -0
  129. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-commit.ts +124 -0
  130. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +67 -54
  131. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-goal-factory.ts +63 -31
  132. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +126 -140
  133. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-log.ts +84 -0
  134. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-missing-recovery.ts +26 -3
  135. package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +11 -7
  136. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/factory.ts +47 -12
  137. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/manifest.ts +124 -10
  138. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/types.ts +3 -0
  139. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-budget-io.ts +59 -0
  140. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +8 -59
  141. package/packages/omo-codex/plugin/components/ulw-loop/src/state-lock.ts +129 -111
  142. package/packages/omo-codex/plugin/components/ulw-loop/src/status-next-actions.ts +19 -10
  143. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +4 -4
  144. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +5 -4
  145. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +13 -1
  146. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +12 -10
  147. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +8 -16
  148. package/packages/omo-codex/plugin/components/ulw-loop/src/success-criteria-input.ts +56 -0
  149. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-snapshot.test.ts +37 -1
  150. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/barrier.ts +13 -0
  151. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-child.ts +65 -0
  152. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-cli-qa.mjs +76 -0
  153. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-crash.ts +23 -0
  154. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-mutations.mjs +58 -0
  155. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-writer.ts +65 -0
  156. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/lease-clock.ts +23 -0
  157. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-commit-log.test.ts +252 -0
  158. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-commit-recovery.test.ts +190 -0
  159. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +50 -35
  160. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-log-newest.test.ts +72 -0
  161. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-completed-plan.test.ts +43 -0
  162. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-driver-states.test.ts +33 -4
  163. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-dx.test.ts +219 -0
  164. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-session-context.test.ts +21 -0
  165. package/packages/omo-codex/plugin/components/ulw-loop/test/state-lock.test.ts +92 -0
  166. package/packages/omo-codex/plugin/components/ulw-loop/test/status-next-actions-surface.test.ts +133 -0
  167. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +12 -12
  168. package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +4 -1
  169. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  170. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  171. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  172. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  173. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  174. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  175. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  176. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  177. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  178. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  179. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  180. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  181. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  182. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  183. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  184. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  185. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  186. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  187. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  188. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  189. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  190. package/packages/omo-codex/plugin/package-lock.json +12 -12
  191. package/packages/omo-codex/plugin/package.json +1 -1
  192. package/packages/omo-codex/plugin/scripts/sync-skills.d.mts +4 -0
  193. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +15 -7
  194. package/packages/omo-codex/plugin/skills/ast-grep/SKILL.md +12 -0
  195. package/packages/omo-codex/plugin/skills/debugging/references/tools/playwright-cli.md +79 -161
  196. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
  197. package/packages/omo-codex/plugin/skills/frontend/references/design/aside.md +1 -1
  198. package/packages/omo-codex/plugin/skills/frontend/references/design/clone-from-url.md +1 -1
  199. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/lighthouse-audit.py +2 -2
  200. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
  201. package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +1 -82
  202. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +17 -29
  203. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -1
  204. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/package.json +2 -2
  205. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/playwright_mobile_chrome.js +5 -2
  206. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/playwright_real_chrome.js +8 -6
  207. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +86 -0
  208. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +4 -7
  209. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +86 -100
  210. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +9 -3
  211. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/playwright.md +16 -13
  212. package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +3 -3
  213. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +5 -4
  214. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +1 -1
  215. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
  216. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +3 -3
  217. package/packages/omo-codex/plugin/skills/visual-qa/AGENTS.md +1 -1
  218. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +1 -1
  219. package/packages/omo-codex/plugin/skills/visual-qa/references/browser-setup.md +75 -0
  220. package/packages/omo-codex/scripts/install-dist/install-local.mjs +6 -4
  221. package/packages/prompts-core/prompts/ultrawork/codex.md +5 -4
  222. package/packages/shared-skills/skills/ast-grep/SKILL.md +12 -0
  223. package/packages/shared-skills/skills/debugging/references/tools/playwright-cli.md +79 -161
  224. package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
  225. package/packages/shared-skills/skills/frontend/references/design/aside.md +1 -1
  226. package/packages/shared-skills/skills/frontend/references/design/clone-from-url.md +1 -1
  227. package/packages/shared-skills/skills/frontend/scripts/perfection/lighthouse-audit.py +2 -2
  228. package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
  229. package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +1 -82
  230. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +17 -29
  231. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -1
  232. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/package.json +2 -2
  233. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/playwright_mobile_chrome.js +5 -2
  234. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/playwright_real_chrome.js +8 -6
  235. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +86 -0
  236. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +4 -7
  237. package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +86 -100
  238. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +9 -3
  239. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/playwright.md +16 -13
  240. package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +3 -3
  241. package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
  242. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
  243. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
  244. package/packages/shared-skills/skills/visual-qa/AGENTS.md +1 -1
  245. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
  246. package/packages/shared-skills/skills/visual-qa/references/browser-setup.md +75 -0
  247. package/dist/skills/visual-qa/references/agent-browser-setup.md +0 -45
  248. package/packages/omo-codex/plugin/skills/visual-qa/references/agent-browser-setup.md +0 -45
  249. package/packages/shared-skills/skills/visual-qa/references/agent-browser-setup.md +0 -45
@@ -0,0 +1,252 @@
1
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { mkdtemp, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+ import { ulwLoopDir } from "../src/paths.js";
7
+ import { createUlwLoopPlan } from "../src/plan-crud.js";
8
+ import * as io from "../src/plan-io.js";
9
+ import { childWriter, cleanupWriters } from "./fixtures/commit-child.js";
10
+ import { seedCrash } from "./fixtures/commit-crash.js";
11
+ import { leaseClock } from "./fixtures/lease-clock.js";
12
+
13
+ let root: string;
14
+ let dir: string;
15
+ const scope = { sessionId: "commit-log" };
16
+ beforeEach(async () => {
17
+ root = await mkdtemp(join(tmpdir(), "ulw-commit-"));
18
+ dir = ulwLoopDir(root, scope);
19
+ });
20
+ afterEach(async () => {
21
+ await cleanupWriters();
22
+ await rm(root, { recursive: true, force: true });
23
+ });
24
+ function snapshot() {
25
+ return Object.fromEntries(
26
+ ["goals.json", "ledger.jsonl", "brief.md"].map((name) => [
27
+ name,
28
+ existsSync(join(dir, name)) ? readFileSync(join(dir, name), "utf8") : null,
29
+ ]),
30
+ );
31
+ }
32
+
33
+ describe("#given an immutable plan and audit log", () => {
34
+ it("#when (7a) a writer dies after publishing #then reads reconcile without writes and locked reads repair views", async () => {
35
+ const next = await seedCrash(root, scope, false);
36
+ const before = snapshot();
37
+ expect(await io.readUlwLoopPlan(root, scope)).toEqual(next);
38
+ expect(io.readLedger(root, scope).at(-1)?.id).toBe(`${next.revision}-0`);
39
+ expect(snapshot()).toEqual(before);
40
+ await io.withUlwLoopMutationLock(root, scope, () => io.readUlwLoopPlan(root, scope));
41
+ expect(JSON.parse(readFileSync(join(dir, "goals.json"), "utf8"))).toEqual(next);
42
+ expect(readFileSync(join(dir, "brief.md"), "utf8")).toBe(next.brief);
43
+ });
44
+ it("#when (7b) first creation has no goals cache #then existence and reads use the log", async () => {
45
+ const next = await seedCrash(root, scope, true);
46
+ expect(io.planExists(root, scope)).toBe(true);
47
+ expect(await io.readUlwLoopPlan(root, scope)).toEqual(next);
48
+ });
49
+ it("#when (8) a legacy plan first commits #then revision zero and legacy ledger and brief survive", async () => {
50
+ const plan = await createUlwLoopPlan(root, { brief: "legacy" }, scope);
51
+ delete plan.revision;
52
+ delete plan.brief;
53
+ delete plan.ledgerResetRevision;
54
+ await rm(join(dir, "revisions"), { recursive: true, force: true });
55
+ writeFileSync(join(dir, "goals.json"), JSON.stringify(plan));
56
+ writeFileSync(join(dir, "ledger.jsonl"), '{"at":"legacy","kind":"goal_started"}\n{"torn":');
57
+ expect(io.readLedger(root, scope)[0]).toMatchObject({ revision: 0, id: "legacy-1" });
58
+ const { commit } = await import("../src/plan-commit.js");
59
+ await io.withUlwLoopMutationLock(root, scope, async () => {
60
+ const legacy = await io.readUlwLoopPlan(root, scope);
61
+ await commit(root, scope, { plan: legacy, entries: [] });
62
+ });
63
+ expect(readdirSync(join(dir, "revisions"))).toEqual(["00000001.json"]);
64
+ expect(io.readLedger(root, scope)[0]?.id).toBe("legacy-1");
65
+ expect(readFileSync(join(dir, "brief.md"), "utf8")).toBe("legacy\n");
66
+ });
67
+ it("#when (9) force recreation succeeds #then the reset is a new revision with filtered views", async () => {
68
+ const first = await createUlwLoopPlan(root, { brief: "old" }, scope);
69
+ const next = await createUlwLoopPlan(root, { brief: "new", force: true }, scope);
70
+ expect(next.revision).toBe((first.revision ?? 0) + 1);
71
+ expect(next.ledgerResetRevision).toBe(next.revision);
72
+ expect(io.readLedger(root, scope)).toHaveLength(1);
73
+ expect(readFileSync(join(dir, "brief.md"), "utf8")).toBe(next.brief);
74
+ expect(readdirSync(join(dir, "revisions"))).toHaveLength(2);
75
+ });
76
+ it("#when (12) hard links are unsupported #then publication fails closed without a record or view changes", async () => {
77
+ await createUlwLoopPlan(root, { brief: "old" }, scope);
78
+ const before = snapshot();
79
+ const { withCommitHooks, commit } = await import("../src/plan-commit.js");
80
+ await expect(
81
+ withCommitHooks(
82
+ {
83
+ link: () => {
84
+ throw Object.assign(new Error("unsupported"), { code: "EPERM" });
85
+ },
86
+ },
87
+ () =>
88
+ io.withUlwLoopMutationLock(root, scope, async () => {
89
+ const plan = await io.readUlwLoopPlan(root, scope);
90
+ plan.brief = "forbidden";
91
+ await commit(root, scope, { plan, entries: [] });
92
+ }),
93
+ ),
94
+ ).rejects.toMatchObject({ code: "ULW_LOOP_PUBLISH_UNSUPPORTED_FS" });
95
+ expect(snapshot()).toEqual(before);
96
+ expect(readdirSync(join(dir, "revisions"))).toHaveLength(1);
97
+ });
98
+ });
99
+
100
+ describe("#given paused writers in separate processes", () => {
101
+ it.each(["write", "force"])(
102
+ "#when (5)/(9) B wins %s after A's precheck #then A loses without publishing or changing views",
103
+ async (mode) => {
104
+ await createUlwLoopPlan(root, { brief: "initial" }, scope);
105
+ const { withCommitHooks, commit } = await import("../src/plan-commit.js");
106
+ let successor: ReturnType<typeof snapshot> | undefined;
107
+ let writes = 0;
108
+ const clock = leaseClock();
109
+ await expect(
110
+ withCommitHooks(
111
+ {
112
+ beforeCommit: async () => {
113
+ clock.advance(201);
114
+ const writer = childWriter(root, mode, clock.now());
115
+ expect(await writer.exited).toBe(0);
116
+ successor = snapshot();
117
+ },
118
+ writeView: async () => {
119
+ writes += 1;
120
+ throw new Error("loser wrote a view");
121
+ },
122
+ },
123
+ () =>
124
+ io.withUlwLoopMutationLock(
125
+ root,
126
+ scope,
127
+ async () => {
128
+ const plan = await io.readUlwLoopPlan(root, scope);
129
+ plan.brief = "A\n";
130
+ await commit(root, scope, { plan, entries: [{ at: "A", kind: "goal_added", message: "A" }] });
131
+ },
132
+ { clock, leaseMs: 200, heartbeatMs: 0 },
133
+ ),
134
+ ),
135
+ ).rejects.toMatchObject({ code: "ULW_LOOP_LOCK_LOST" });
136
+ expect(writes).toBe(0);
137
+ expect(snapshot()).toEqual(successor);
138
+ const published = JSON.parse(readFileSync(join(dir, "revisions", "00000002.json"), "utf8"));
139
+ expect(published.plan).toEqual(await io.readUlwLoopPlan(root, scope));
140
+ expect(published.ledger).toHaveLength(1);
141
+ expect(io.readLedger(root, scope).at(-1)).toEqual(published.ledger[0]);
142
+ expect(published.ledger[0].kind).toBe(mode === "force" ? "plan_created" : "goal_added");
143
+ expect(io.readLedger(root, scope).some((entry) => entry.message === "A")).toBe(false);
144
+ expect(readdirSync(join(dir, "revisions"))).toEqual(["00000001.json", "00000002.json"]);
145
+ },
146
+ );
147
+ it("#when (6) A wins before the valid holder B #then B retries once from A's revision", async () => {
148
+ await createUlwLoopPlan(root, { brief: "initial" }, scope);
149
+ const { withCommitHooks, commit } = await import("../src/plan-commit.js");
150
+ let writer: ReturnType<typeof childWriter> | undefined;
151
+ const clock = leaseClock();
152
+ await withCommitHooks(
153
+ {
154
+ beforeCommit: async () => {
155
+ clock.advance(201);
156
+ writer = childWriter(root, "mirror", clock.now());
157
+ await writer.wait("before");
158
+ },
159
+ },
160
+ () =>
161
+ io.withUlwLoopMutationLock(
162
+ root,
163
+ scope,
164
+ async () => {
165
+ const plan = await io.readUlwLoopPlan(root, scope);
166
+ plan.codexObjective = "A";
167
+ await commit(root, scope, { plan, entries: [{ at: "A", kind: "goal_added", message: "A" }] });
168
+ },
169
+ { clock, leaseMs: 200, heartbeatMs: 0 },
170
+ ),
171
+ );
172
+ if (writer === undefined) throw new Error("B not started");
173
+ writer.child.send("resume");
174
+ expect(await writer.exited).toBe(0);
175
+ expect((await io.readUlwLoopPlan(root, scope)).codexObjective).toBe("A|B");
176
+ expect(
177
+ io
178
+ .readLedger(root, scope)
179
+ .slice(-2)
180
+ .map((entry) => entry.message),
181
+ ).toEqual(["A", "B"]);
182
+ expect(readdirSync(join(dir, "revisions"))).toHaveLength(3);
183
+ });
184
+ it("#when (7c) a child dies after an acknowledged partial temp write #then every public view remains complete", async () => {
185
+ await createUlwLoopPlan(root, { brief: "initial" }, scope);
186
+ const before = snapshot();
187
+ const writer = childWriter(root, "partial");
188
+ await writer.wait("partial");
189
+ writer.child.kill("SIGKILL");
190
+ await writer.exited;
191
+ const after = snapshot();
192
+ const published = JSON.parse(readFileSync(join(dir, "revisions", "00000002.json"), "utf8"));
193
+ expect([before["goals.json"], `${JSON.stringify(published.plan, null, 2)}\n`]).toContain(after["goals.json"]);
194
+ expect([before["brief.md"], published.plan.brief]).toContain(after["brief.md"]);
195
+ expect(after["ledger.jsonl"]).toBe(before["ledger.jsonl"]);
196
+ for (const line of readFileSync(join(dir, "ledger.jsonl"), "utf8").trim().split("\n"))
197
+ expect(() => JSON.parse(line)).not.toThrow();
198
+ expect(io.readLedger(root, scope).at(-1)?.message).toBe("B");
199
+ });
200
+ it.each(["accepted", "rejected"])(
201
+ "#when (11) %s steering dies after link #then its audit survives and accepted retries dedupe",
202
+ async (mode) => {
203
+ const original = await createUlwLoopPlan(root, { brief: "initial" }, scope);
204
+ const writer = childWriter(root, mode);
205
+ await writer.wait("linked");
206
+ writer.child.kill("SIGKILL");
207
+ await writer.exited;
208
+ expect(io.readLedger(root, scope).at(-1)?.kind).toBe(`steering_${mode}`);
209
+ const prior = await io.findAcceptedSteeringLedgerEntry(root, "crash-key", scope);
210
+ if (mode === "rejected") {
211
+ expect(prior).toBeUndefined();
212
+ expect(await io.readUlwLoopPlan(root, scope)).toEqual({ ...original, revision: 2 });
213
+ } else {
214
+ const { steerUlwLoop } = await import("../src/steering.js");
215
+ const result = await steerUlwLoop(
216
+ root,
217
+ {
218
+ kind: "annotate_ledger",
219
+ source: "cli",
220
+ evidence: "observed",
221
+ rationale: "audit",
222
+ idempotencyKey: "crash-key",
223
+ },
224
+ scope,
225
+ );
226
+ expect(result.deduped).toBe(true);
227
+ expect(io.readLedger(root, scope)).toHaveLength(2);
228
+ expect(readdirSync(join(dir, "revisions"))).toHaveLength(2);
229
+ }
230
+ },
231
+ );
232
+ it("#when (10) retry fails before commit #then no goal_retried audit escapes", async () => {
233
+ const { startNextUlwLoop } = await import("../src/plan-crud.js");
234
+ const plan = await createUlwLoopPlan(root, { brief: "initial" }, scope);
235
+ const { commit, withCommitHooks } = await import("../src/plan-commit.js");
236
+ for (const goal of plan.goals) goal.status = "failed";
237
+ await commit(root, scope, { plan, entries: [] });
238
+ const before = snapshot();
239
+ await expect(
240
+ withCommitHooks(
241
+ {
242
+ beforeCommit: () => {
243
+ throw new Error("stop before commit");
244
+ },
245
+ },
246
+ () => startNextUlwLoop(root, { retryFailed: true }, scope),
247
+ ),
248
+ ).rejects.toThrow("stop before commit");
249
+ expect(snapshot()).toEqual(before);
250
+ expect(io.readLedger(root, scope).some((entry) => entry.kind === "goal_retried")).toBe(false);
251
+ });
252
+ });
@@ -0,0 +1,190 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+ import type { PreToolUsePayload } from "../src/codex-hook.js";
7
+ import { ulwLoopDir } from "../src/paths.js";
8
+ import { commit, materialize, withCommitHooks } from "../src/plan-commit.js";
9
+ import { createUlwLoopPlan } from "../src/plan-crud.js";
10
+ import {
11
+ planExists,
12
+ readLedger,
13
+ readUlwLoopPlan,
14
+ withMutationLockOptions,
15
+ withUlwLoopMutationLock,
16
+ } from "../src/plan-io.js";
17
+ import { applySpawnBudgetGuards } from "../src/spawn-guard.js";
18
+ import { runStopResumeHook } from "../src/stop-resume-hook.js";
19
+ import { childWriter, cleanupWriters } from "./fixtures/commit-child.js";
20
+ import { leaseClock } from "./fixtures/lease-clock.js";
21
+
22
+ let root: string;
23
+ let dir: string;
24
+ const scope = { sessionId: "commit-log" };
25
+ beforeEach(async () => {
26
+ root = await mkdtemp(join(tmpdir(), "ulw-recovery-"));
27
+ dir = ulwLoopDir(root, scope);
28
+ });
29
+ afterEach(async () => {
30
+ await cleanupWriters();
31
+ vi.unstubAllEnvs();
32
+ await rm(root, { recursive: true, force: true });
33
+ });
34
+ function signal() {
35
+ let resolve: () => void = () => {
36
+ throw new Error("signal not initialized");
37
+ };
38
+ const promise = new Promise<void>((done, reject) => {
39
+ const timeout = setTimeout(() => reject(new Error("signal timed out")), 5_000);
40
+ resolve = () => {
41
+ clearTimeout(timeout);
42
+ done();
43
+ };
44
+ });
45
+ return { promise, resolve };
46
+ }
47
+ function views() {
48
+ return ["goals.json", "ledger.jsonl", "brief.md"].map((name) => readFileSync(join(dir, name), "utf8"));
49
+ }
50
+ describe("#given crash recovery through public surfaces", () => {
51
+ it("#when (7b) only revision one exists #then both hooks and existence gates use it", async () => {
52
+ const plan = await createUlwLoopPlan(root, { brief: "unfinished" }, scope);
53
+ for (const name of ["goals.json", "ledger.jsonl", "brief.md"]) await rm(join(dir, name));
54
+ expect(readdirSync(join(dir, "revisions"))).toEqual(["00000001.json"]);
55
+ expect(planExists(root, scope)).toBe(true);
56
+ expect((await readUlwLoopPlan(root, scope)).revision).toBe(1);
57
+ const payload = {
58
+ session_id: scope.sessionId,
59
+ turn_id: "turn",
60
+ cwd: root,
61
+ model: "test",
62
+ permission_mode: "default",
63
+ };
64
+ const stop = {
65
+ ...payload,
66
+ hook_event_name: "Stop",
67
+ transcript_path: join(root, "transcript"),
68
+ stop_hook_active: false,
69
+ };
70
+ expect(JSON.parse(runStopResumeHook(stop)).decision).toBe("block");
71
+ expect(JSON.parse(readFileSync(join(dir, `auto-resume-${plan.goals[0]?.id}.json`), "utf8")).ledgerLineCount).toBe(
72
+ 1,
73
+ );
74
+ vi.stubEnv("OMO_SPAWN_FANOUT_LIMIT", "1");
75
+ vi.stubEnv("PLUGIN_DATA", join(root, "plugin-data"));
76
+ const spawn: PreToolUsePayload = {
77
+ ...payload,
78
+ hook_event_name: "PreToolUse",
79
+ transcript_path: null,
80
+ tool_name: "spawn_agent",
81
+ tool_use_id: "tool",
82
+ tool_input: {},
83
+ };
84
+ expect(applySpawnBudgetGuards(spawn)).toBe("");
85
+ expect(JSON.parse(applySpawnBudgetGuards(spawn)).hookSpecificOutput.permissionDecision).toBe("deny");
86
+ });
87
+ it("#when (7c) two materializers overlap #then every public inode is complete even if a view regresses", async () => {
88
+ const plan = await createUlwLoopPlan(root, { brief: "old" }, scope);
89
+ const old = views();
90
+ await withCommitHooks(
91
+ {
92
+ afterCommit: () => {
93
+ throw new Error("crash");
94
+ },
95
+ },
96
+ () =>
97
+ commit(root, scope, {
98
+ plan: { ...plan, brief: "middle\n" },
99
+ entries: [{ at: "middle", kind: "goal_added" }],
100
+ }),
101
+ ).catch((error: unknown) => {
102
+ expect(error).toBeInstanceOf(Error);
103
+ });
104
+ const middleViews = [
105
+ `${JSON.stringify(await readUlwLoopPlan(root, scope), null, 2)}\n`,
106
+ `${readLedger(root, scope)
107
+ .map((entry) => JSON.stringify(entry))
108
+ .join("\n")}\n`,
109
+ "middle\n",
110
+ ];
111
+ const partial = signal();
112
+ const resume = signal();
113
+ const first = withCommitHooks(
114
+ {
115
+ writeView: async (path, content) => {
116
+ if (path.endsWith("ledger.jsonl")) {
117
+ await writeFile(path, content.slice(0, 5));
118
+ partial.resolve();
119
+ await resume.promise;
120
+ }
121
+ await writeFile(path, content);
122
+ },
123
+ },
124
+ () => materialize(dir),
125
+ );
126
+ await partial.promise;
127
+ expect(readFileSync(join(dir, "ledger.jsonl"), "utf8")).toBe(old[1]);
128
+ const latest = await readUlwLoopPlan(root, scope);
129
+ await commit(root, scope, { plan: { ...latest, brief: "new\n" }, entries: [{ at: "new", kind: "goal_added" }] });
130
+ const newViews = views();
131
+ resume.resolve();
132
+ await first;
133
+ const final = views();
134
+ expect(() => JSON.parse(final[0] ?? "")).not.toThrow();
135
+ for (const line of (final[1] ?? "").trim().split("\n")) expect(() => JSON.parse(line)).not.toThrow();
136
+ for (const [index, content] of final.entries()) expect([middleViews[index], newViews[index]]).toContain(content);
137
+ expect(
138
+ readLedger(root, scope)
139
+ .map((entry) => entry.at)
140
+ .slice(-2),
141
+ ).toEqual(["middle", "new"]);
142
+ });
143
+ it("#when (9) a force-recreator loses before its first possible view write #then its successor stays untouched", async () => {
144
+ await createUlwLoopPlan(root, { brief: "old" }, scope);
145
+ let successor: string[] = [];
146
+ let writes = 0;
147
+ const clock = leaseClock();
148
+ await expect(
149
+ withCommitHooks(
150
+ {
151
+ beforeMutation: async () => {
152
+ clock.advance(201);
153
+ const writer = childWriter(root, "force", clock.now());
154
+ expect(await writer.exited).toBe(0);
155
+ successor = views();
156
+ },
157
+ writeView: async (path, content) => {
158
+ writes += 1;
159
+ await writeFile(path, content);
160
+ },
161
+ },
162
+ () =>
163
+ withMutationLockOptions({ clock, leaseMs: 200, heartbeatMs: 0 }, () =>
164
+ createUlwLoopPlan(root, { brief: "A", force: true }, scope),
165
+ ),
166
+ ),
167
+ ).rejects.toMatchObject({ code: "ULW_LOOP_LOCK_LOST" });
168
+ expect(writes).toBe(0);
169
+ expect(views()).toEqual(successor);
170
+ expect(readLedger(root, scope)).toHaveLength(1);
171
+ });
172
+ it("#when a locked ledger-only read follows a crash #then all views are repaired without another revision", async () => {
173
+ const plan = await createUlwLoopPlan(root, { brief: "old" }, scope);
174
+ await expect(
175
+ withCommitHooks(
176
+ {
177
+ afterCommit: () => {
178
+ throw new Error("crash");
179
+ },
180
+ },
181
+ () => commit(root, scope, { plan: { ...plan, brief: "new\n" }, entries: [] }),
182
+ ),
183
+ ).rejects.toThrow("crash");
184
+ await withUlwLoopMutationLock(root, scope, async () => {
185
+ expect(readLedger(root, scope)).toHaveLength(1);
186
+ });
187
+ expect(readFileSync(join(dir, "brief.md"), "utf8")).toBe("new\n");
188
+ expect(readdirSync(join(dir, "revisions"))).toHaveLength(2);
189
+ });
190
+ });