oh-my-opencode 4.16.2 → 4.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/.agents/command/omomomo.md +1 -1
  2. package/.agents/skills/hyperplan/SKILL.md +3 -3
  3. package/.agents/skills/omomomo/SKILL.md +1 -1
  4. package/.agents/skills/pre-publish-review/SKILL.md +3 -0
  5. package/.opencode/command/omomomo.md +1 -1
  6. package/.opencode/skills/hyperplan/SKILL.md +3 -3
  7. package/.opencode/skills/pre-publish-review/SKILL.md +3 -0
  8. package/README.ja.md +4 -4
  9. package/README.ko.md +4 -4
  10. package/README.md +4 -4
  11. package/README.ru.md +4 -4
  12. package/README.zh-cn.md +4 -4
  13. package/dist/agents/hephaestus/agent.d.ts +1 -1
  14. package/dist/agents/hephaestus/gpt-5-6.d.ts +2 -0
  15. package/dist/agents/momus-gpt-5-6.d.ts +1 -0
  16. package/dist/agents/types.d.ts +2 -0
  17. package/dist/cli/index.js +947 -567
  18. package/dist/cli-node/index.js +947 -567
  19. package/dist/features/background-agent/error-classifier.d.ts +5 -0
  20. package/dist/features/builtin-commands/templates/refactor-sections/intro-and-analysis.d.ts +1 -1
  21. package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
  22. package/dist/features/team-mode/test-support/async-test-helpers.d.ts +2 -2
  23. package/dist/index.js +523 -81
  24. package/dist/plugin/chat-message/types.d.ts +4 -0
  25. package/dist/plugin/command-execute-before.d.ts +1 -0
  26. package/dist/plugin/stop-continuation.d.ts +17 -0
  27. package/dist/shared/live-server-route.d.ts +1 -1
  28. package/dist/skills/remove-ai-slops/SKILL.md +2 -2
  29. package/dist/skills/review-work/SKILL.md +1 -1
  30. package/dist/skills/start-work/SKILL.md +5 -2
  31. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  32. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  33. package/dist/skills/ulw-research/SKILL.md +2 -2
  34. package/dist/skills/visual-qa/SKILL.md +1 -1
  35. package/dist/tui.js +195 -41
  36. package/package.json +15 -14
  37. package/packages/git-bash-mcp/package.json +27 -0
  38. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +3 -1
  39. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +465 -248
  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/codegraph/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  44. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  45. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  46. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/AGENTS.md +2 -2
  48. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/cli.js +6 -2
  49. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/codex-hook.js +6 -2
  50. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +2 -2
  51. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  52. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/src/codex-hook.ts +6 -2
  53. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/cli.test.ts +1 -1
  54. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/codex-hook.test.ts +67 -2
  55. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  56. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +0 -13
  58. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +93 -0
  59. package/packages/omo-codex/plugin/components/rules/dist/cli.js +43 -24
  60. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  61. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  62. package/packages/omo-codex/plugin/components/rules/src/codex-hook.ts +2 -1
  63. package/packages/omo-codex/plugin/components/rules/src/dynamic-target-fingerprints.ts +15 -6
  64. package/packages/omo-codex/plugin/components/rules/src/rules-engine-factory.ts +7 -2
  65. package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +2 -2
  66. package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +32 -0
  67. package/packages/omo-codex/plugin/components/rules/test/hephaestus-model-variant.test.ts +106 -0
  68. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  69. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  70. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -79
  71. package/packages/omo-codex/plugin/components/teammode/AGENTS.md +12 -9
  72. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  73. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  74. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +136 -76
  75. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +94 -23
  76. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +94 -14
  77. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-transport.mjs +55 -0
  78. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +72 -18
  79. package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +23 -0
  80. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  81. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  82. package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +23 -40
  83. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +1 -1
  84. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +3 -3
  85. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +7 -7
  86. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +5 -5
  87. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +26 -0
  88. package/packages/omo-codex/plugin/components/ultrawork/agents/{lazycodex-executor.toml → lazycodex-worker-low.toml} +7 -5
  89. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +26 -0
  90. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +20 -48
  91. package/packages/omo-codex/plugin/components/ultrawork/agents/metis.toml +17 -29
  92. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +20 -50
  93. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +22 -43
  94. package/packages/omo-codex/plugin/components/ultrawork/directive.md +55 -28
  95. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  96. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  97. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +55 -28
  98. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
  99. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  100. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +22 -0
  101. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +0 -68
  102. package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +1 -1
  103. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +2 -0
  104. package/packages/omo-codex/plugin/components/ulw-loop/README.md +3 -1
  105. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +55 -28
  106. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +6 -1
  107. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +1 -1
  108. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -2
  109. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +405 -25
  110. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +12 -1
  111. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +4 -2
  112. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +7 -0
  113. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +16 -1
  114. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +1 -0
  115. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.d.ts +6 -0
  116. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +20 -0
  117. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.d.ts +1 -0
  118. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +12 -9
  119. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.d.ts +3 -0
  120. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +148 -0
  121. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.d.ts +2 -0
  122. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +209 -0
  123. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +25 -2
  124. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  125. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +14 -15
  126. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +29 -27
  127. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +6 -1
  128. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +14 -3
  129. package/packages/omo-codex/plugin/components/ulw-loop/src/cli.ts +10 -0
  130. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +12 -1
  131. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +4 -2
  132. package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +27 -1
  133. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +1 -0
  134. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +23 -0
  135. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +16 -9
  136. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +138 -0
  137. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +208 -0
  138. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +12 -0
  139. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-entrypoint.test.ts +4 -1
  140. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +8 -35
  141. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/quality-gate-builder.ts +24 -13
  142. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +5 -2
  143. package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +43 -8
  144. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +55 -2
  145. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +228 -0
  146. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +193 -0
  147. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  148. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  149. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  150. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  151. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  152. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  153. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  154. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  155. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  156. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +18 -0
  157. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  158. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  159. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  160. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  161. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  162. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  163. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  164. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +17 -0
  165. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  166. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +2 -2
  167. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  168. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  169. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  170. package/packages/omo-codex/plugin/model-catalog.json +16 -7
  171. package/packages/omo-codex/plugin/package-lock.json +13 -13
  172. package/packages/omo-codex/plugin/package.json +1 -1
  173. package/packages/omo-codex/plugin/scripts/auto-update.mjs +9 -2
  174. package/packages/omo-codex/plugin/scripts/entry-guard.mjs +26 -0
  175. package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +3 -1
  176. package/packages/omo-codex/plugin/scripts/migrate-codex-config/catalog.mjs +16 -7
  177. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +39 -4
  178. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +24 -4
  179. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +3 -3
  180. package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +2 -2
  181. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +2 -2
  182. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +14 -6
  183. package/packages/omo-codex/plugin/scripts/sync-version.mjs +4 -2
  184. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +3 -3
  185. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +3 -3
  186. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +5 -5
  187. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +9 -3
  188. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +6 -3
  189. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +136 -76
  190. package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +94 -23
  191. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +94 -14
  192. package/packages/omo-codex/plugin/skills/teammode/scripts/team-transport.mjs +55 -0
  193. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +72 -18
  194. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +55 -28
  195. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +14 -15
  196. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +29 -27
  197. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
  198. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  199. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +4 -4
  200. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +4 -4
  201. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +116 -10
  202. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +23 -2
  203. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -1
  204. package/packages/omo-codex/plugin/test/aggregate-model-catalog.test.mjs +4 -4
  205. package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +4 -2
  206. package/packages/omo-codex/plugin/test/auto-update-restart-notice.test.mjs +33 -0
  207. package/packages/omo-codex/plugin/test/auto-update.test.mjs +4 -4
  208. package/packages/omo-codex/plugin/test/component-hook-contract-cases.mjs +2 -2
  209. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +4 -101
  210. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +160 -23
  211. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +71 -0
  212. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +32 -0
  213. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +20 -2
  214. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +1 -1
  215. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -2
  216. package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +345 -0
  217. package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +9 -40
  218. package/packages/omo-codex/plugin/test/ulw-research-skill-contract.test.mjs +4 -277
  219. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +5 -5
  220. package/packages/omo-codex/scripts/install-config.test.mjs +22 -3
  221. package/packages/omo-codex/scripts/install-dist/install-local.mjs +508 -286
  222. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +111 -1
  223. package/packages/shared-skills/skills/remove-ai-slops/SKILL.md +2 -2
  224. package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
  225. package/packages/shared-skills/skills/start-work/SKILL.md +5 -2
  226. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  227. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  228. package/packages/shared-skills/skills/ulw-research/SKILL.md +2 -2
  229. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
  230. package/dist/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
  231. package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +0 -70
  232. package/packages/omo-codex/plugin/test/ulw-research-epistemic-contract.test.mjs +0 -98
  233. package/packages/shared-skills/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
  234. /package/packages/omo-codex/plugin/components/rules/bundled-rules/{hephaestus.md → hephaestus/gpt-5.5.md} +0 -0
@@ -17,15 +17,73 @@ const agentSchemaKeys = new Set([
17
17
 
18
18
  const lazycodexAgentInvariants = new Map([
19
19
  [
20
- "lazycodex-executor.toml",
20
+ "explorer.toml",
21
+ {
22
+ model: "gpt-5.6-luna",
23
+ effort: "low",
24
+ includes: [/Read-only/, /working tree/, /rg/],
25
+ },
26
+ ],
27
+ [
28
+ "librarian.toml",
29
+ {
30
+ model: "gpt-5.6-luna",
31
+ effort: "low",
32
+ includes: [/Read-only/, /SHA-pinned GitHub permalink/, /external/],
33
+ },
34
+ ],
35
+ [
36
+ "metis.toml",
37
+ {
38
+ model: "gpt-5.6-sol",
39
+ effort: "high",
40
+ includes: [/pre-planning analyst/i, /contradictions/, /Read-only/],
41
+ },
42
+ ],
43
+ [
44
+ "momus.toml",
45
+ {
46
+ model: "gpt-5.6-sol",
47
+ effort: "ultra",
48
+ includes: [/plan reviewer/i, /OKAY, ITERATE, or REJECT/, /Read-only/],
49
+ },
50
+ ],
51
+ [
52
+ "plan.toml",
53
+ {
54
+ model: "gpt-5.6-sol",
55
+ effort: "max",
56
+ includes: [/strategic planning consultant/i, /\.omo\/plans\/<slug>\.md/, /never implements/i],
57
+ },
58
+ ],
59
+ [
60
+ "lazycodex-worker-low.toml",
21
61
  {
62
+ model: "gpt-5.6-luna",
22
63
  effort: "high",
23
- includes: [/EVIDENCE_RECORDED: <path>/, /scenario/i, /artifact/i],
64
+ includes: [/EVIDENCE_RECORDED: <path>/, /low-difficulty/i, /smallest correct change/i],
65
+ },
66
+ ],
67
+ [
68
+ "lazycodex-worker-medium.toml",
69
+ {
70
+ model: "gpt-5.6-luna",
71
+ effort: "max",
72
+ includes: [/EVIDENCE_RECORDED: <path>/, /medium-difficulty/i, /smallest correct change/i],
73
+ },
74
+ ],
75
+ [
76
+ "lazycodex-worker-high.toml",
77
+ {
78
+ model: "gpt-5.6-sol",
79
+ effort: "max",
80
+ includes: [/EVIDENCE_RECORDED: <path>/, /high-difficulty/i, /smallest correct change/i],
24
81
  },
25
82
  ],
26
83
  [
27
84
  "lazycodex-clone-fidelity-reviewer.toml",
28
85
  {
86
+ model: "gpt-5.6-sol",
29
87
  effort: "xhigh",
30
88
  includes: [/recommendation/, /blockers/, /\.omo\/evidence\/<goal>-clone-fidelity\.md/],
31
89
  },
@@ -33,22 +91,25 @@ const lazycodexAgentInvariants = new Map([
33
91
  [
34
92
  "lazycodex-code-reviewer.toml",
35
93
  {
94
+ model: "gpt-5.6-sol",
36
95
  effort: "xhigh",
37
- includes: [/codeQualityStatus/, /recommendation/, /\.omo\/evidence\/<goal>-code-review\.md/],
96
+ includes: [/codeQualityStatus/, /recommendation/, /<attemptDir>\/<goalId>-code-review\.md/, /currentAttemptDir/],
38
97
  },
39
98
  ],
40
99
  [
41
100
  "lazycodex-qa-executor.toml",
42
101
  {
43
- effort: "medium",
44
- includes: [/not_applicable/, /surfaceEvidence/, /adversarialCases/],
102
+ model: "gpt-5.6-luna",
103
+ effort: "high",
104
+ includes: [/not_applicable/, /surfaceEvidence/, /adversarialCases/, /<attemptDir>\/<goalId>-manual-qa\.md/],
45
105
  },
46
106
  ],
47
107
  [
48
108
  "lazycodex-gate-reviewer.toml",
49
109
  {
50
- effort: "xhigh",
51
- includes: [/APPROVE\/REJECT/, /blockers/, /\.omo\/evidence\/<goal>-gate-review\.md/],
110
+ model: "gpt-5.6-sol",
111
+ effort: "high",
112
+ includes: [/APPROVE\/REJECT/, /blockers/, /<attemptDir>\/<goalId>-gate-review\.md/, /currentAttemptDir/],
52
113
  },
53
114
  ],
54
115
  ]);
@@ -69,9 +130,11 @@ test("#given bundled Codex agents #when components/ultrawork/agents directory is
69
130
  "explorer.toml",
70
131
  "lazycodex-clone-fidelity-reviewer.toml",
71
132
  "lazycodex-code-reviewer.toml",
72
- "lazycodex-executor.toml",
73
133
  "lazycodex-gate-reviewer.toml",
74
134
  "lazycodex-qa-executor.toml",
135
+ "lazycodex-worker-high.toml",
136
+ "lazycodex-worker-low.toml",
137
+ "lazycodex-worker-medium.toml",
75
138
  "librarian.toml",
76
139
  "metis.toml",
77
140
  "momus.toml",
@@ -94,11 +157,29 @@ test("#given bundled Codex agents #when components/ultrawork/agents directory is
94
157
  }
95
158
  });
96
159
 
160
+ test("#given bundled agent TOMLs #when nickname_candidates are inspected #then they use only the codex-accepted charset", async () => {
161
+ // given: codex_app_server ignores a role whose nickname has characters outside
162
+ // ASCII letters, digits, spaces, hyphens, underscores (observed live in task-15 QA)
163
+ const agentsDir = join(root, "components", "ultrawork", "agents");
164
+ const files = (await readdir(agentsDir)).filter((name) => name.endsWith(".toml"));
165
+
166
+ // when/then
167
+ for (const file of files) {
168
+ const text = await readFile(join(agentsDir, file), "utf8");
169
+ for (const match of text.matchAll(/nickname_candidates\s*=\s*\[([^\]]*)\]/g)) {
170
+ for (const nickname of match[1].matchAll(/"([^"]*)"/g)) {
171
+ assert.match(nickname[1], /^[A-Za-z0-9 _-]+$/, `${file}: nickname "${nickname[1]}"`);
172
+ }
173
+ }
174
+ }
175
+ });
176
+
97
177
  test("#given planner agent prompt #when inspected #then generated artifacts stay under .omo", async () => {
98
178
  const prompt = await readFile(join(root, "components", "ultrawork", "agents", "plan.toml"), "utf8");
99
179
 
100
180
  assert.match(prompt, /\.omo\/plans\/<slug>\.md/);
101
- assert.match(prompt, /\.omo\/evidence\/task-<N>-<slug>\.<ext>/);
181
+ assert.match(prompt, /<attemptDir>\/task-<N>-<slug>\.<ext>/);
182
+ assert.match(prompt, /\.omo\/evidence\/ulw\/<session>\/<goalId>\/a<attempt>/);
102
183
  assert.doesNotMatch(prompt, /(?<!\.omo\/)plans\/<slug>\.md/);
103
184
  assert.doesNotMatch(prompt, /(?<!\.omo\/)evidence\/task-/);
104
185
  });
@@ -109,7 +190,8 @@ test("#given lazycodex agent prompts #when inspected #then each role pins model
109
190
  for (const [fileName, invariant] of lazycodexAgentInvariants) {
110
191
  const prompt = await readFile(join(agentsDir, fileName), "utf8");
111
192
 
112
- assert.match(prompt, /^model\s*=\s*"gpt-5\.5"$/m);
193
+ const escapedModel = invariant.model.replace(/\./g, "\\.");
194
+ assert.match(prompt, new RegExp(`^model\\s*=\\s*"${escapedModel}"$`, "m"));
113
195
  assert.match(prompt, new RegExp(`^model_reasoning_effort\\s*=\\s*"${invariant.effort}"$`, "m"));
114
196
  assert.doesNotMatch(prompt, /^tools\s*=/m);
115
197
  assert.doesNotMatch(prompt, /^blocking\s*=/m);
@@ -161,3 +243,27 @@ test("#given LazyCodex reviewer prompts #when inspected #then anti-slop review c
161
243
  "gate reviewer must perform the overfit/slop pass directly before checking report coverage",
162
244
  );
163
245
  });
246
+
247
+ test("#given done-gate reviewer prompts #when inspected #then burden of proof is approve-unless-cited and reject priors are gone", async () => {
248
+ const agentsDir = join(root, "components", "ultrawork", "agents");
249
+ const gateReviewer = await readFile(join(agentsDir, "lazycodex-gate-reviewer.toml"), "utf8");
250
+ const qaExecutor = await readFile(join(agentsDir, "lazycodex-qa-executor.toml"), "utf8");
251
+ const codeReviewer = await readFile(join(agentsDir, "lazycodex-code-reviewer.toml"), "utf8");
252
+
253
+ assert.match(gateReviewer, /APPROVE unless you can cite/);
254
+ assert.match(gateReviewer, /violatedCriterion/);
255
+ assert.match(gateReviewer, /evidencePointer/);
256
+ assert.match(gateReviewer, /top blockers inline/);
257
+ assert.match(gateReviewer, /is a NOTE, not a blocker/);
258
+ assert.match(gateReviewer, /You do NOT check/);
259
+ assert.doesNotMatch(gateReviewer, /Assume the work has already failed/);
260
+ assert.doesNotMatch(gateReviewer, /Return exactly one recommendation: APPROVE\/REJECT\./);
261
+
262
+ assert.match(qaExecutor, /one-line reason/);
263
+ assert.match(qaExecutor, /rejecting a legitimately untriggered class is itself an error/);
264
+ assert.doesNotMatch(qaExecutor, /Trust nothing\./);
265
+
266
+ assert.match(codeReviewer, /MEDIUM by default/);
267
+ assert.doesNotMatch(codeReviewer, /Treat useless tests or needless production complexity as CRITICAL\/HIGH/);
268
+ });
269
+
@@ -48,6 +48,22 @@ test("#given isolated components #when hooks are inspected #then commands stay i
48
48
  assert.equal(await exists("scripts/migrate-codex-config.mjs"), true);
49
49
  });
50
50
 
51
+ test("#given aggregate Stop hooks #when inspected #then start-work continuation and ulw-loop resume are separate groups", async () => {
52
+ // given
53
+ const manifests = await readAggregateHookManifests();
54
+
55
+ // when
56
+ const stopCommands = manifests
57
+ .filter(({ hooks }) => hooks.hooks.Stop)
58
+ .flatMap(({ hooks }) => hooks.hooks.Stop)
59
+ .flatMap((group) => group.hooks.map((handler) => handler.command));
60
+
61
+ // then
62
+ assert.equal(stopCommands.length, 2);
63
+ assert.ok(stopCommands.some((command) => command.includes("start-work-continuation/dist/cli.js")));
64
+ assert.ok(stopCommands.some((command) => command.includes("ulw-loop/dist/cli.js\" hook stop")));
65
+ });
66
+
51
67
  test("#given aggregate SubagentStop hooks #when inspected #then start-work and LazyCodex executor verifier are separate groups", async () => {
52
68
  // given
53
69
  const manifests = await readAggregateHookManifests();
@@ -64,7 +80,7 @@ test("#given aggregate SubagentStop hooks #when inspected #then start-work and L
64
80
  // then
65
81
  assert.equal(subagentStopGroups.length, 2);
66
82
  assert.equal(subagentStopGroups[0]?.matcher, undefined);
67
- assert.equal(subagentStopGroups[1]?.matcher, "^lazycodex-executor$");
83
+ assert.equal(subagentStopGroups[1]?.matcher, "^lazycodex-worker-(low|medium|high)$");
68
84
  assert.equal(verifierGroups.length, 1);
69
85
  assert.equal(verifierGroups[0]?.groupIndex, 0);
70
86
  assert.equal(verifierGroups[0]?.handler.timeout, 10);
@@ -158,7 +174,12 @@ test("#given aggregate OMO plugin is enabled #when hooks are inspected #then she
158
174
  assert.match(text, /Resetting Git Bash MCP Reminder/);
159
175
  assert.match(text, /components\/ulw-loop\/dist\/cli\.js/);
160
176
  assert.match(text, /hook pre-tool-use/);
161
- assert.deepEqual(preToolUseGroups.map((group) => group.matcher), ["^Bash$", "^create_goal$"]);
177
+ assert.deepEqual(preToolUseGroups.map((group) => group.matcher), [
178
+ "^Bash$",
179
+ "^create_goal$",
180
+ "^(spawn_agent|collaborationspawn_agent|collaboration\\.spawn_agent)$",
181
+ ]);
182
+ assert.match(text, /hook pre-tool-use-spawn/);
162
183
  });
163
184
 
164
185
  test("#given aggregate OMO plugin has a dedicated ultrawork trigger #when hooks are inspected #then ulw-loop does not duplicate ultrawork injection", async () => {
@@ -17,7 +17,7 @@ test("#given aggregate plugin manifest #when inspected #then it owns the omo nam
17
17
  // then
18
18
  assert.equal(manifest.name, "omo");
19
19
  assert(Array.isArray(hookPaths));
20
- assert.equal(hookPaths.length, 21);
20
+ assert.equal(hookPaths.length, 23);
21
21
  assert(hookPaths.every((hookPath) => typeof hookPath === "string" && hookPath.startsWith("./hooks/")));
22
22
  assert(!hookPaths.includes("./hooks/hooks.json"));
23
23
  assert(!hookPaths.includes("./hooks/user-prompt-submit-selecting-lazycodex-workflow.json"));
@@ -8,17 +8,17 @@ import { root } from "./aggregate-plugin-fixture.mjs";
8
8
  test("#given bundled model catalog #when inspected #then default verifier and worker roles are pinned", async () => {
9
9
  const catalog = JSON.parse(await readFile(join(root, "model-catalog.json"), "utf8"));
10
10
 
11
- assert.equal(catalog.current.model, "gpt-5.5");
12
- assert.equal(catalog.current.model_context_window, 400000);
11
+ assert.equal(catalog.current.model, "gpt-5.6-sol");
12
+ assert.equal(catalog.current.model_context_window, 372000);
13
13
  assert.equal(catalog.current.model_reasoning_effort, "high");
14
14
  assert.equal(catalog.current.plan_mode_reasoning_effort, "xhigh");
15
15
  assert.deepEqual(catalog.roles.default, catalog.current);
16
16
  assert.deepEqual(catalog.roles.verifier, {
17
- model: "gpt-5.5",
17
+ model: "gpt-5.6-sol",
18
18
  model_reasoning_effort: "high",
19
19
  });
20
20
  assert.deepEqual(catalog.roles.worker, {
21
- model: "gpt-5.5",
21
+ model: "gpt-5.6-sol",
22
22
  model_reasoning_effort: "high",
23
23
  });
24
24
  });
@@ -34,7 +34,8 @@ test('#given synced skills and bundled rules #when role-specific agents are spaw
34
34
  const promptFiles = skillEntries
35
35
  .filter((entry) => entry.isDirectory())
36
36
  .map((entry) => join(skillsDir, entry.name, "SKILL.md"));
37
- promptFiles.push(join(root, "components", "rules", "bundled-rules", "hephaestus.md"));
37
+ promptFiles.push(join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.5.md"));
38
+ promptFiles.push(join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.6.md"));
38
39
 
39
40
  const missingForkContext = [];
40
41
  for (const promptPath of promptFiles) {
@@ -53,7 +54,8 @@ test("#given long-running orchestration prompts #when waiting on child agents #t
53
54
  join(root, "skills", "ulw-loop", "references", "full-workflow.md"),
54
55
  join(root, "skills", "review-work", "SKILL.md"),
55
56
  join(root, "skills", "start-work", "SKILL.md"),
56
- join(root, "components", "rules", "bundled-rules", "hephaestus.md"),
57
+ join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.5.md"),
58
+ join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.6.md"),
57
59
  ];
58
60
 
59
61
  const missingLivenessGuidance = [];
@@ -224,3 +224,36 @@ test("#given completed pending update #when hook session-start runs as CLI #then
224
224
  assert.equal(repeat.status, 0);
225
225
  assert.equal(repeat.stdout, "");
226
226
  });
227
+
228
+ test("#given the script reached through a symlinked directory #when hook session-start runs as CLI #then the entry guard still fires", async () => {
229
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-restart-notice-symlink-"));
230
+ const env = autoUpdateEnv(root, {
231
+ LAZYCODEX_CURRENT_VERSION: "1.0.1",
232
+ LAZYCODEX_LATEST_VERSION: "1.0.1",
233
+ LAZYCODEX_CONFIG_MIGRATION_DISABLED: "1",
234
+ });
235
+ await writeFile(env.LAZYCODEX_AUTO_UPDATE_STATE_PATH, JSON.stringify({
236
+ lastCheckedAt: Date.now() - 1_000,
237
+ lastStatus: "success",
238
+ pendingNotice: { fromVersion: "1.0.0", toVersion: "1.0.1", startedAt: 1 },
239
+ }));
240
+ // Simulate a symlinked plugin cache dir: argv[1] spells the symlink, while
241
+ // import.meta.url resolves to the real path. Seen in the wild; the old
242
+ // `pathToFileURL(process.argv[1])` guard never matched and the hook was a
243
+ // silent no-op.
244
+ const { symlink } = await import("node:fs/promises");
245
+ const { dirname } = await import("node:path");
246
+ const linkDir = join(root, "linked-scripts");
247
+ await symlink(dirname(SCRIPT_PATH), linkDir, "dir");
248
+ const linkedScript = join(linkDir, "auto-update.mjs");
249
+
250
+ const result = spawnSync(process.execPath, [linkedScript, "hook", "session-start"], {
251
+ encoding: "utf8",
252
+ env: { ...process.env, ...env },
253
+ });
254
+
255
+ assert.equal(result.status, 0);
256
+ const lines = result.stdout.split("\n").filter((line) => line.length > 0);
257
+ assert.equal(lines.length, 1, `expected one SessionStart JSON line, got stdout=${JSON.stringify(result.stdout)} stderr=${JSON.stringify(result.stderr)}`);
258
+ assert.equal(JSON.parse(lines[0]).hookSpecificOutput.hookEventName, "SessionStart");
259
+ });
@@ -225,7 +225,7 @@ test("#given test command override #when running check #then records state and l
225
225
  toVersion: "1.0.1",
226
226
  },
227
227
  ]);
228
- assert.match(await readFile(join(env.CODEX_HOME, "config.toml"), "utf8"), /model = "gpt-5\.5"/);
228
+ assert.match(await readFile(join(env.CODEX_HOME, "config.toml"), "utf8"), /model = "gpt-5\.6-sol"/);
229
229
  });
230
230
 
231
231
  test("#given failed waited update #when retry window passes #then next update is not blocked by success throttle", async () => {
@@ -279,7 +279,7 @@ test("#given active lock #when running check #then skips concurrent update", asy
279
279
 
280
280
  assert.equal(result.started, false);
281
281
  assert.equal(result.reason, "locked");
282
- assert.match(await readFile(join(root, "codex-home", "config.toml"), "utf8"), /model_context_window = 400000/);
282
+ assert.match(await readFile(join(root, "codex-home", "config.toml"), "utf8"), /model_context_window = 372000/);
283
283
  });
284
284
 
285
285
  test("#given stale lock #when running check #then removes lock and runs update", async () => {
@@ -645,8 +645,8 @@ test("#given throttled updater and stale Codex config #when running check #then
645
645
  const content = await readFile(join(codexHome, "config.toml"), "utf8");
646
646
  assert.equal(result.started, false);
647
647
  assert.equal(result.reason, "throttled");
648
- assert.match(content, /model = "gpt-5\.5"/);
649
- assert.match(content, /model_context_window = 400000/);
648
+ assert.match(content, /model = "gpt-5\.6-sol"/);
649
+ assert.match(content, /model_context_window = 372000/);
650
650
  assert.match(content, /model_reasoning_effort = "high"/);
651
651
  assert.match(content, /plan_mode_reasoning_effort = "xhigh"/);
652
652
  assert.doesNotMatch(content, /gpt-5\.2/);
@@ -180,12 +180,12 @@ export function componentHookContractCases(tempRoot) {
180
180
  },
181
181
  },
182
182
  {
183
- name: "lazycodex executor verifier subagent-stop blocks missing evidence",
183
+ name: "lazycodex worker verifier subagent-stop blocks missing evidence",
184
184
  component: "lazycodex-executor-verify",
185
185
  event: "subagent-stop",
186
186
  payload: {
187
187
  hook_event_name: "SubagentStop",
188
- agent_type: "lazycodex-executor",
188
+ agent_type: "lazycodex-worker-medium",
189
189
  agent_id: "agent-task12",
190
190
  session_id: "s-task12",
191
191
  transcript_path: join(tempRoot, "transcript.jsonl"),
@@ -7,109 +7,12 @@ import { fileURLToPath } from "node:url";
7
7
  const pluginRoot = dirname(dirname(fileURLToPath(import.meta.url)));
8
8
  const sharedSkillsRoot = join(pluginRoot, "..", "..", "shared-skills");
9
9
 
10
- async function readSkill(name) {
11
- return readFile(join(sharedSkillsRoot, "skills", name, "SKILL.md"), "utf8");
12
- }
13
-
14
- test("#given synced lcx-report-bug skill #when inspected #then it files LazyCodex bug issues with generated labels", async () => {
15
- // given
16
- const skillRoot = join(sharedSkillsRoot, "skills", "lcx-report-bug");
17
-
18
- // when
19
- const skill = await readFile(join(skillRoot, "SKILL.md"), "utf8");
20
- const interfaceMetadata = await readFile(join(skillRoot, "agents", "openai.yaml"), "utf8");
21
-
22
- // then
23
- assert.match(skill, /^---\r?\nname: lcx-report-bug\r?\n/m);
24
- assert.match(skill, /Never create a PR or push a branch against `code-yeongyu\/lazycodex`/);
25
- assert.match(skill, /gh pr create --repo openai\/codex/);
26
- assert.doesNotMatch(skill, /gh pr create --repo "\$TARGET_REPO"/);
27
- assert.doesNotMatch(skill, /gh pr create --repo code-yeongyu\/lazycodex/);
28
- assert.match(interfaceMetadata, /display_name: "lcx-report-bug \(omo\)"/);
29
- assert.match(interfaceMetadata, /- "lazycodex bug"/);
30
- assert.match(interfaceMetadata, /- "openai codex bug"/);
31
- });
32
-
33
- test("#given synced lcx-contribute-bug-fix skill #when inspected #then it delivers LazyCodex fixes as issues and upstream fixes as fork PRs", async () => {
34
- // given
35
- const skillRoot = join(sharedSkillsRoot, "skills", "lcx-contribute-bug-fix");
36
-
37
- // when
38
- const skill = await readFile(join(skillRoot, "SKILL.md"), "utf8");
39
- const interfaceMetadata = await readFile(join(skillRoot, "agents", "openai.yaml"), "utf8");
40
-
41
- // then
42
- assert.match(skill, /^---\r?\nname: lcx-contribute-bug-fix\r?\n/m);
43
- assert.match(skill, /NEVER open a PR or push a branch against this repo/);
44
- assert.match(skill, /gh issue create --repo code-yeongyu\/lazycodex/);
45
- assert.match(skill, /gh pr create --repo openai\/codex/);
46
- assert.doesNotMatch(skill, /gh pr create --repo "\$TARGET_REPO"/);
47
- assert.doesNotMatch(skill, /gh pr create --repo code-yeongyu\/lazycodex/);
48
- assert.match(interfaceMetadata, /display_name: "lcx-contribute-bug-fix \(omo\)"/);
49
- assert.match(interfaceMetadata, /- "contribute a bug fix"/);
50
- assert.match(interfaceMetadata, /- "fix bug pr"/);
51
- });
52
-
53
- test("#given synced lcx-doctor skill #when inspected #then it diagnoses installs against latest temp-root sources without mutating them", async () => {
54
- // given
55
- const skillRoot = join(sharedSkillsRoot, "skills", "lcx-doctor");
56
-
57
- // when
58
- const skill = await readFile(join(skillRoot, "SKILL.md"), "utf8");
59
- const interfaceMetadata = await readFile(join(skillRoot, "agents", "openai.yaml"), "utf8");
60
-
61
- // then
62
- assert.match(skill, /^---\r?\nname: lcx-doctor\r?\n/m);
63
- assert.match(interfaceMetadata, /display_name: "lcx-doctor \(omo\)"/);
64
- assert.match(interfaceMetadata, /- "lazycodex doctor"/);
65
- assert.match(interfaceMetadata, /- "lazycodex health check"/);
66
- });
67
-
68
- test("#given lcx source-sync skills #when inspected #then source caches are validated before reuse", async () => {
69
- // given
70
- const skillNames = ["lcx-doctor", "lcx-report-bug", "lcx-contribute-bug-fix"];
71
-
72
- for (const skillName of skillNames) {
10
+ test("#given lcx skills #when frontmatter is inspected #then each exposes the loader-parsed skill name", async () => {
11
+ for (const skillName of ["lcx-report-bug", "lcx-contribute-bug-fix", "lcx-doctor"]) {
73
12
  // when
74
- const skill = await readSkill(skillName);
13
+ const skill = await readFile(join(sharedSkillsRoot, "skills", skillName, "SKILL.md"), "utf8");
75
14
 
76
15
  // then
77
- assert.match(skill, /LAZYCODEX_SOURCE_ROOT="\$\{LAZYCODEX_SOURCE_ROOT:-\$\{TMPDIR:-\/tmp\}\/lazycodex-sources\}"/);
78
- assert.match(skill, /git -C "\$DEST" rev-parse --is-inside-work-tree/);
79
- assert.match(skill, /git -C "\$DEST" config --get remote\.origin\.url/);
80
- assert.match(skill, /DEST\.corrupt\.\$\(date \+%Y%m%d%H%M%S\)/);
81
- assert.doesNotMatch(skill, /if \[ ! -d "\$DEST\/\.git" \]; then/);
82
- assert.doesNotMatch(skill, /sync_latest_source code-yeongyu\/lazycodex \/tmp\/lazycodex-source/);
83
- assert.doesNotMatch(skill, /sync_latest_source openai\/codex \/tmp\/openai-codex-source/);
16
+ assert.match(skill, new RegExp(`^name: ${skillName}$`, "m"), `${skillName}: frontmatter must expose ${skillName}`);
84
17
  }
85
18
  });
86
-
87
- test("#given lcx-doctor skill #when inspected #then aggregate hook validation follows the current manifest", async () => {
88
- // given
89
- const skill = await readSkill("lcx-doctor");
90
-
91
- // then
92
- assert.match(skill, /\.codex-plugin\/plugin\.json/);
93
- assert.match(skill, /manifest declares a `hooks` array/);
94
- assert.match(skill, /validate every direct hook path declared by the manifest/);
95
- assert.match(skill, /`hooks\/hooks\.json` only when the manifest declares it/);
96
- assert.match(skill, /do not require retired paths such as `components\/workflow-selector`/);
97
- assert.match(skill, /`hooks\/user-prompt-submit-selecting-lazycodex-workflow\.json` unless the current manifest declares them/);
98
- assert.doesNotMatch(skill, /Plugin payload present and non-empty: `hooks\/hooks\.json`/);
99
- });
100
-
101
- test("#given lcx-doctor skill #when inspected #then runtime probes and materialized payloads distinguish expected rewrites from missing files", async () => {
102
- // given
103
- const skill = await readSkill("lcx-doctor");
104
-
105
- // then
106
- assert.match(skill, /manifest-declared runtime payload/);
107
- assert.match(skill, /`dist\/cli\/index\.js` and `dist\/cli-node\/index\.js`/);
108
- assert.match(skill, /install-time materialization rewrites as expected/);
109
- assert.match(skill, /absolute `\.mcp\.json` runtime paths/);
110
- assert.match(skill, /Missing or zero-byte rewritten targets are FAIL/);
111
- assert.match(skill, /Use the configured Codex default model/);
112
- assert.match(skill, /unless the user explicitly passed a model override/);
113
- assert.match(skill, /never force a guessed\/rejected model such as `gpt-5\.5-codex-mini`/);
114
- assert.doesNotMatch(skill, /--model gpt-5\.5-codex-mini/);
115
- });