lazycodex-ai 4.16.0 → 4.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/cli/codex-ulw-loop.d.ts +8 -0
  2. package/dist/cli/get-local-version/types.d.ts +1 -1
  3. package/dist/cli/index.js +260 -207
  4. package/dist/cli-node/index.js +260 -207
  5. package/docs/reference/web-terminal-visual-qa.md +39 -45
  6. package/package.json +3 -2
  7. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  8. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  9. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  10. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  11. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  12. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  14. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  15. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  16. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  17. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  18. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  19. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus.md +1 -1
  20. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  21. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  22. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
  23. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  24. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -1
  26. package/packages/omo-codex/plugin/components/teammode/AGENTS.md +1 -1
  27. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  28. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +3 -3
  30. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  31. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  32. package/packages/omo-codex/plugin/components/ultrawork/directive.md +19 -10
  33. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  34. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +19 -10
  36. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +13 -0
  37. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +2 -0
  38. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +22 -1
  39. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +4 -0
  40. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +19 -10
  41. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +15 -2
  42. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +2 -1
  43. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +89 -27
  44. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +6 -0
  45. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +55 -9
  46. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.d.ts +15 -0
  47. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.js +33 -0
  48. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +10 -3
  49. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +15 -11
  50. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  51. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  52. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +3 -1
  53. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +7 -7
  54. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +17 -2
  55. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +2 -1
  56. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +59 -11
  57. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-snapshot.ts +38 -0
  58. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +11 -3
  59. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +15 -7
  60. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +16 -0
  61. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +260 -2
  62. package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +1 -1
  63. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-snapshot.test.ts +124 -0
  64. package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +101 -2
  65. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  78. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  79. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  80. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  81. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  82. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  83. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  84. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  85. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  86. package/packages/omo-codex/plugin/package-lock.json +13 -13
  87. package/packages/omo-codex/plugin/package.json +1 -1
  88. package/packages/omo-codex/plugin/scripts/auto-update.mjs +64 -17
  89. package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +10 -6
  90. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +186 -20
  91. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +51 -3
  92. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +33 -5
  93. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +1 -1
  94. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +9 -9
  95. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +7 -3
  96. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +1 -1
  97. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +24 -2
  98. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/README.md +2 -2
  99. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
  100. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +1 -1
  101. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +1 -1
  102. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +1 -1
  103. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
  104. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -3
  105. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +3 -3
  106. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +19 -10
  107. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +3 -1
  108. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +7 -7
  109. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +13 -0
  110. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +2 -0
  111. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +1 -1
  112. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +14 -18
  113. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +4 -4
  114. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +1 -1
  115. package/packages/omo-codex/plugin/test/auto-update.test.mjs +35 -1
  116. package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +1 -1
  117. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +22 -9
  118. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +275 -19
  119. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +33 -0
  120. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
  121. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +4 -2
  122. package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +52 -0
  123. package/packages/omo-codex/scripts/install-dist/install-local.mjs +65 -39
  124. package/packages/shared-skills/skills/frontend/SKILL.md +9 -9
  125. package/packages/shared-skills/skills/frontend/references/design/README.md +7 -3
  126. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +1 -1
  127. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +24 -2
  128. package/packages/shared-skills/skills/frontend/references/designpowers/README.md +2 -2
  129. package/packages/shared-skills/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
  130. package/packages/shared-skills/skills/review-work/SKILL.md +3 -1
  131. package/packages/shared-skills/skills/start-work/SKILL.md +4 -2
  132. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -1
  133. package/packages/shared-skills/skills/visual-qa/SKILL.md +13 -17
  134. package/script/qa/strip-ansi.mjs +10 -0
  135. package/script/qa/web-terminal-visual-qa.mjs +112 -195
  136. package/script/qa/xterm-live-terminal.mjs +180 -0
  137. package/script/qa/web-terminal-renderer.mjs +0 -218
@@ -96,6 +96,8 @@ Every spawn starts with `TASK:`, then DELIVERABLE / SCOPE / VERIFY inside `messa
96
96
  multi_agent_v1.spawn_agent({"message":"TASK: act as an explorer. DELIVERABLE: ... SCOPE: ... VERIFY: ...","agent_type":"explorer","fork_context":false})
97
97
  ```
98
98
 
99
+ If your tool list has a flat `spawn_agent` with a required `task_name` instead of `multi_agent_v1.*` (`multi_agent_v2`), rewrite: add `"task_name":"<lowercase_digits_underscores>"`, replace `"fork_context":false` with `"fork_turns":"none"`, and `wait_agent` takes only `timeout_ms`, returning on any child mailbox activity (finished agents end on their own — skip the close step).
100
+
99
101
  Roles: `explorer`, `librarian`, `metis`, `momus`. Spawn long plan/reviewer agents in the background and poll with short waits; require the child to send `WORKING: <task> - <phase>` before long passes and `BLOCKED: <reason>` only when progress stops. A wait timeout only means no new mailbox update arrived; treat a running child as alive. Fall back only when the child completed without the deliverable, is ack-only after followup, explicitly `BLOCKED:`, or no longer running; then respawn a smaller `fork_context: false` job. Close each agent after integrating its result.
100
102
 
101
103
  ## Stop rules
@@ -19,7 +19,7 @@ This skill may include examples copied from the OpenCode harness. In Codex, do n
19
19
 
20
20
  Role-specific behavior must be described in a self-contained `message`. Use `fork_context: false` to start the child with only the initial prompt (no parent history); use `fork_context: true` only when full parent history is truly required. Include any required conversation context, files, diffs, constraints, and requested skill names directly in the spawned agent's `message`. OMO installs these selectable agent roles into `~/.codex/agents/`: `explorer`, `librarian`, `plan`, `momus`, `metis`, `lazycodex-code-reviewer`, `lazycodex-qa-executor`, and `lazycodex-gate-reviewer` - pass the matching name as `agent_type` so the child gets that role's model and instructions. If the spawn tool exposes no `agent_type` parameter, omit it and describe the role inside `message`. If a code block below conflicts with this section, this section wins.
21
21
 
22
- On `multi_agent_v2` sessions the same `agent_type` applies (the OMO installer exposes it) with `fork_turns` instead of `fork_context`. If a code block below conflicts with this section, this section wins.
22
+ Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If `multi_agent_v1.*` tools exist, use the table above as written. If instead a flat `spawn_agent` with a required `task_name` exists (`multi_agent_v2`), rewrite every `multi_agent_v1.*` example: `multi_agent_v1.spawn_agent({...,"fork_context":false})` becomes `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})` (`"all"` only when full parent history is truly required); `send_input` becomes `send_message`; do not call `close_agent`/`resume_agent` (finished agents end on their own; `followup_task` re-tasks one, `interrupt_agent` stops one); `wait_agent` takes only `timeout_ms` and returns on any child mailbox activity. `agent_type` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
23
23
 
24
24
  When translating `load_skills=[...]`, include the requested skill names in the spawned agent's `message`. If a code block below conflicts with this section, this section wins.
25
25
 
@@ -19,7 +19,7 @@ This skill may include examples copied from the OpenCode harness. In Codex, do n
19
19
 
20
20
  Role-specific behavior must be described in a self-contained `message`. Use `fork_context: false` to start the child with only the initial prompt (no parent history); use `fork_context: true` only when full parent history is truly required. Include any required conversation context, files, diffs, constraints, and requested skill names directly in the spawned agent's `message`. OMO installs these selectable agent roles into `~/.codex/agents/`: `explorer`, `librarian`, `plan`, `momus`, `metis`, `lazycodex-code-reviewer`, `lazycodex-qa-executor`, and `lazycodex-gate-reviewer` - pass the matching name as `agent_type` so the child gets that role's model and instructions. If the spawn tool exposes no `agent_type` parameter, omit it and describe the role inside `message`. If a code block below conflicts with this section, this section wins.
21
21
 
22
- On `multi_agent_v2` sessions the same `agent_type` applies (the OMO installer exposes it) with `fork_turns` instead of `fork_context`. If a code block below conflicts with this section, this section wins.
22
+ Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If `multi_agent_v1.*` tools exist, use the table above as written. If instead a flat `spawn_agent` with a required `task_name` exists (`multi_agent_v2`), rewrite every `multi_agent_v1.*` example: `multi_agent_v1.spawn_agent({...,"fork_context":false})` becomes `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})` (`"all"` only when full parent history is truly required); `send_input` becomes `send_message`; do not call `close_agent`/`resume_agent` (finished agents end on their own; `followup_task` re-tasks one, `interrupt_agent` stops one); `wait_agent` takes only `timeout_ms` and returns on any child mailbox activity. `agent_type` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
23
23
 
24
24
  When translating `load_skills=[...]`, include the requested skill names in the spawned agent's `message`. If a code block below conflicts with this section, this section wins.
25
25
 
@@ -79,32 +79,28 @@ For reference-fidelity work, repeat the capture and diff for every referenced vi
79
79
 
80
80
  ### TUI
81
81
 
82
- 1. Capture plain text and an ANSI-preserving copy:
83
-
84
- ```
85
- tmux capture-pane -p > capture.txt
86
- tmux capture-pane -e -p > capture-ansi.txt
87
- ```
88
-
89
- 2. When the TUI evidence will be attached to a PR or reviewed visually, render
90
- the capture through the browser helper from the repository root:
82
+ 1. Render the TUI through the REAL xterm.js web terminal and screenshot it -
83
+ NEVER `tmux capture-pane`, which degrades truecolor and misaligns wide (CJK)
84
+ glyphs. Run the command in a real pty and capture the browser render from the
85
+ repository root:
91
86
 
92
87
  ```
93
88
  node script/qa/web-terminal-visual-qa.mjs --title "TUI Visual QA" \
94
- --from-file capture.txt \
89
+ --command "<tui-command>" \
90
+ --input "{ArrowDown}" --input "{Enter}" \
95
91
  --evidence-dir .omo/evidence/<slug>/tui-web-terminal
96
92
  ```
97
93
 
98
- This produces `terminal.png`, `terminal.html`, `terminal.txt`,
99
- `terminal-ansi.txt`, and `metadata.json`. Treat this as the standard TUI visual
100
- artifact pattern for terminal screenshots. If the project is outside this repo,
101
- copy the same pattern: terminal capture -> browser-rendered page -> PNG +
102
- metadata with cleanup receipt.
94
+ Replay a saved raw stream with `--from-file <capture.ansi>` instead of
95
+ `--command`. This produces `terminal.png` (the true-color artifact),
96
+ `terminal.txt`, `terminal-ansi.txt`, and `metadata.json`. Treat this as the
97
+ standard TUI visual artifact pattern. Outside this repo, copy the pattern:
98
+ real pty -> xterm.js in a browser -> PNG + metadata with cleanup receipt.
103
99
 
104
- 3. Run the check with the REAL terminal width and keep the JSON:
100
+ 2. Run the width check on the produced text and keep the JSON:
105
101
 
106
102
  ```
107
- node "$SKILL_DIR/scripts/visual-qa.mjs" tui-check capture.txt --cols <N>
103
+ node "$SKILL_DIR/scripts/visual-qa.mjs" tui-check .omo/evidence/<slug>/tui-web-terminal/terminal.txt --cols <N>
108
104
  ```
109
105
 
110
106
  Key fields: `maxWidth`, `overflowLines[]`, `borderMisaligned`, `wideCharColumns[]`, `hasAnsi`.
@@ -68,7 +68,7 @@ test("#given aggregate SubagentStop hooks #when inspected #then start-work and L
68
68
  assert.equal(verifierGroups.length, 1);
69
69
  assert.equal(verifierGroups[0]?.groupIndex, 0);
70
70
  assert.equal(verifierGroups[0]?.handler.timeout, 10);
71
- assert.equal(verifierGroups[0]?.handler.statusMessage, "(OmO) Verifying LazyCodex Executor Evidence");
71
+ assert.match(verifierGroups[0]?.handler.statusMessage ?? "", /^\(OmO [^)]+\) Verifying LazyCodex Executor Evidence$/);
72
72
  });
73
73
 
74
74
  test("#given aggregate PostCompact hooks #when hooks are inspected #then LSP diagnostics cache reset is registered", async () => {
@@ -84,7 +84,7 @@ test("#given aggregate PostCompact hooks #when hooks are inspected #then LSP dia
84
84
 
85
85
  // then
86
86
  assert.equal(lspPostCompactHooks.length, 1);
87
- assert.equal(lspPostCompactHooks[0]?.handler.statusMessage, "(OmO) Resetting LSP Diagnostics Cache");
87
+ assert.match(lspPostCompactHooks[0]?.handler.statusMessage ?? "", /^\(OmO [^)]+\) Resetting LSP Diagnostics Cache$/);
88
88
  });
89
89
 
90
90
  test("#given aggregate hook commands #when inspected #then every command exposes a Codex status message", async () => {
@@ -218,7 +218,7 @@ test("#given aggregate PostToolUse hooks #when inspected #then CodeGraph init gu
218
218
  // then
219
219
  assert.equal(codegraphPostToolUseHooks.length, 1);
220
220
  assert.equal(codegraphPostToolUseHooks[0]?.matcher, "^(codegraph[._].*|mcp__codegraph__.*)$");
221
- assert.equal(codegraphPostToolUseHooks[0]?.handler.statusMessage, "(OmO) Checking CodeGraph Init Guidance");
221
+ assert.match(codegraphPostToolUseHooks[0]?.handler.statusMessage ?? "", /^\(OmO [^)]+\) Checking CodeGraph Init Guidance$/);
222
222
  });
223
223
 
224
224
  test("#given aggregate PostToolUse hooks #when inspected #then thread title hygiene is registered for created Codex threads", async () => {
@@ -235,7 +235,7 @@ test("#given aggregate PostToolUse hooks #when inspected #then thread title hygi
235
235
  // then
236
236
  assert.equal(threadTitleHooks.length, 1);
237
237
  assert.equal(threadTitleHooks[0]?.matcher, "^(create_thread|codex_app\\.create_thread)$");
238
- assert.equal(threadTitleHooks[0]?.handler.statusMessage, "(OmO) Checking Thread Title Hygiene");
238
+ assert.match(threadTitleHooks[0]?.handler.statusMessage ?? "", /^\(OmO [^)]+\) Checking Thread Title Hygiene$/);
239
239
  });
240
240
 
241
241
  test("#given aggregate plugin packaging #when inspected #then hooks and compatibility sentinels stay Python-free", async () => {
@@ -88,7 +88,7 @@ export function findSpawnAgentCallsWithoutForkContextFalse(content) {
88
88
  const regex = /spawn_agent\(([^)]*)\)/g;
89
89
  for (const match of content.matchAll(regex)) {
90
90
  const call = match[0];
91
- if (!/"fork_context"\s*:\s*false|fork_context:\s*false|fork_context=false/.test(call)) {
91
+ if (!/"fork_context"\s*:\s*false|fork_context:\s*false|fork_context=false|"fork_turns"\s*:\s*"none"|fork_turns:\s*"none"|fork_turns="none"/.test(call)) {
92
92
  missingForkContext.push(call);
93
93
  }
94
94
  }
@@ -2,9 +2,10 @@ import assert from "node:assert/strict";
2
2
  import { mkdir, mkdtemp, readFile, symlink, utimes, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
+ import { PassThrough } from "node:stream";
5
6
  import test from "node:test";
6
7
 
7
- import { resolveAutoUpdatePlan, resolveLazyCodexUpdatePlan, runAutoUpdateCheck } from "../scripts/auto-update.mjs";
8
+ import { readSessionModelFromStdin, resolveAutoUpdatePlan, resolveLazyCodexUpdatePlan, runAutoUpdateCheck } from "../scripts/auto-update.mjs";
8
9
  import { detectInstallFlow } from "../scripts/install-flow.mjs";
9
10
  import { resolveSpawnInvocation } from "../scripts/spawn-command.mjs";
10
11
 
@@ -20,6 +21,12 @@ function autoUpdateEnv(root, extra = {}) {
20
21
  };
21
22
  }
22
23
 
24
+ function stdinFrom(payload) {
25
+ const stream = new PassThrough();
26
+ stream.end(payload);
27
+ return stream;
28
+ }
29
+
23
30
  test("#given auto update is disabled #when resolving plan #then no command is scheduled", () => {
24
31
  const plan = resolveAutoUpdatePlan({
25
32
  env: { LAZYCODEX_AUTO_UPDATE_DISABLED: "1" },
@@ -31,6 +38,33 @@ test("#given auto update is disabled #when resolving plan #then no command is sc
31
38
  assert.equal(plan.reason, "disabled");
32
39
  });
33
40
 
41
+ test("#given SessionStart payload with model #when reading stdin #then returns the model", async () => {
42
+ const payload = JSON.stringify({
43
+ hook_event_name: "SessionStart",
44
+ session_id: "s-1",
45
+ cwd: "/tmp",
46
+ model: "gpt-5.6-terra",
47
+ permission_mode: "default",
48
+ source: "startup",
49
+ });
50
+
51
+ assert.equal(await readSessionModelFromStdin(stdinFrom(payload)), "gpt-5.6-terra");
52
+ });
53
+
54
+ test("#given empty or malformed stdin #when reading session model #then returns null", async () => {
55
+ assert.equal(await readSessionModelFromStdin(stdinFrom("")), null);
56
+ assert.equal(await readSessionModelFromStdin(stdinFrom("not json")), null);
57
+ assert.equal(await readSessionModelFromStdin(stdinFrom(JSON.stringify({ model: " " }))), null);
58
+ assert.equal(await readSessionModelFromStdin(stdinFrom(JSON.stringify({ session_id: "s-1" }))), null);
59
+ assert.equal(await readSessionModelFromStdin(null), null);
60
+ });
61
+
62
+ test("#given a TTY stdin #when reading session model #then returns null without waiting", async () => {
63
+ const stream = stdinFrom("{}");
64
+ stream.isTTY = true;
65
+ assert.equal(await readSessionModelFromStdin(stream), null);
66
+ });
67
+
34
68
  test("#given stale state #when resolving plan #then installer update command is scheduled", () => {
35
69
  const plan = resolveAutoUpdatePlan({
36
70
  env: { LAZYCODEX_CURRENT_VERSION: "1.0.0", LAZYCODEX_LATEST_VERSION: "1.0.1" },
@@ -171,7 +171,7 @@ test("#given the bootstrap component #when its SessionStart registration is insp
171
171
  assert.equal(typeof handler.timeout, "number");
172
172
  assert(handler.timeout <= 60, `${label} bootstrap timeout must stay <= 60 seconds`);
173
173
  assert.equal(typeof handler.statusMessage, "string");
174
- assert.match(handler.statusMessage, /^\(OmO\) .+$/);
174
+ assert.match(handler.statusMessage, /^\(OmO [^)]+\) .+$/);
175
175
  }
176
176
  });
177
177
 
@@ -128,7 +128,7 @@ function collectCommandHooks(hooks, source, version) {
128
128
  return commandHooks;
129
129
  }
130
130
 
131
- test("#given hook status label #when formatting #then prefixes OmO display namespace", async () => {
131
+ test("#given hook status label #when formatting #then prefixes OmO display namespace and version", async () => {
132
132
  // given
133
133
  const version = (await readRepoJson("package.json")).version;
134
134
  const label = "Checking Comments";
@@ -137,10 +137,10 @@ test("#given hook status label #when formatting #then prefixes OmO display names
137
137
  const message = formatLazyCodexHookStatusMessage(version, label);
138
138
 
139
139
  // then
140
- assert.equal(message, "(OmO) Checking Comments");
140
+ assert.equal(message, `(OmO ${version}) Checking Comments`);
141
141
  });
142
142
 
143
- test("#given hook status label with blank version #when formatting #then still prefixes OmO display namespace", () => {
143
+ test("#given hook status label with blank version #when formatting #then uses local OmO display version", () => {
144
144
  // given
145
145
  const version = " ";
146
146
  const label = "Checking Comments";
@@ -149,7 +149,7 @@ test("#given hook status label with blank version #when formatting #then still p
149
149
  const message = formatLazyCodexHookStatusMessage(version, label);
150
150
 
151
151
  // then
152
- assert.equal(message, "(OmO) Checking Comments");
152
+ assert.equal(message, "(OmO local) Checking Comments");
153
153
  });
154
154
 
155
155
  test("#given loose legacy status label #when normalizing #then removes OMO wording and title-cases label", async () => {
@@ -163,7 +163,7 @@ test("#given loose legacy status label #when normalizing #then removes OMO wordi
163
163
 
164
164
  // then
165
165
  assert.equal(normalized, "Checking Comments");
166
- assert.equal(message, "(OmO) Checking Comments");
166
+ assert.equal(message, `(OmO ${version}) Checking Comments`);
167
167
  });
168
168
 
169
169
  test("#given LazyCodex appears inside hook label #when normalizing #then product casing is preserved", async () => {
@@ -177,7 +177,7 @@ test("#given LazyCodex appears inside hook label #when normalizing #then product
177
177
 
178
178
  // then
179
179
  assert.equal(normalized, "Verifying LazyCodex Executor Evidence");
180
- assert.equal(message, "(OmO) Verifying LazyCodex Executor Evidence");
180
+ assert.equal(message, `(OmO ${version}) Verifying LazyCodex Executor Evidence`);
181
181
  });
182
182
 
183
183
  test("#given MCP appears inside hook label #when normalizing #then protocol casing is preserved", () => {
@@ -190,7 +190,20 @@ test("#given MCP appears inside hook label #when normalizing #then protocol casi
190
190
 
191
191
  // then
192
192
  assert.equal(normalized, "Recommending Git Bash MCP");
193
- assert.equal(message, "(OmO) Recommending Git Bash MCP");
193
+ assert.equal(message, "(OmO 4.10.0) Recommending Git Bash MCP");
194
+ });
195
+
196
+ test("#given versioned OmO status label #when normalizing #then it does not duplicate display prefix", () => {
197
+ // given
198
+ const label = "(OmO 4.16.0) checking comments";
199
+
200
+ // when
201
+ const parsed = parseLazyCodexHookStatusMessage(label);
202
+ const message = formatLazyCodexHookStatusMessage("4.16.1", label);
203
+
204
+ // then
205
+ assert.deepEqual(parsed, { version: "4.16.0", label: "checking comments" });
206
+ assert.equal(message, "(OmO 4.16.1) Checking Comments");
194
207
  });
195
208
  test("#given aggregate comment-checker hook #when status is inspected #then it uses OmO comments label", async () => {
196
209
  // given
@@ -201,7 +214,7 @@ test("#given aggregate comment-checker hook #when status is inspected #then it u
201
214
  const commentCheckerHook = hooks.find((hook) => hook.command.includes("components/comment-checker/dist/cli.js"));
202
215
 
203
216
  // then
204
- assert.equal(commentCheckerHook?.statusMessage, formatLazyCodexHookStatusMessage("", "Checking Comments"));
217
+ assert.equal(commentCheckerHook?.statusMessage, formatLazyCodexHookStatusMessage(commentCheckerHook?.version ?? "", "Checking Comments"));
205
218
  assert.doesNotMatch(JSON.stringify(aggregateManifests), /checking\s+OMO\s+comments/i);
206
219
  });
207
220
 
@@ -231,6 +244,6 @@ test("#given aggregate and component hooks #when status messages are inspected #
231
244
  const actualLabels = new Set(commandHooks.map((hook) => parseLazyCodexHookStatusMessage(hook.statusMessage)?.label));
232
245
  assert.deepEqual([...expectedLabels.values()].filter((label) => !actualLabels.has(label)), []);
233
246
  for (const hook of commandHooks) {
234
- assert.match(hook.statusMessage, /^\(OmO\) /);
247
+ assert.match(hook.statusMessage, /^\(OmO [^)]+\) /);
235
248
  }
236
249
  });