oh-my-opencode 4.18.1 → 4.19.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 (217) hide show
  1. package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +39 -16
  2. package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +111 -28
  3. package/README.ja.md +2 -2
  4. package/README.ko.md +2 -2
  5. package/README.md +4 -4
  6. package/README.ru.md +2 -2
  7. package/README.zh-cn.md +2 -2
  8. package/bin/AGENTS.md +33 -0
  9. package/dist/agents/atlas/agent.d.ts +6 -5
  10. package/dist/agents/sisyphus/index.d.ts +5 -0
  11. package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
  12. package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
  13. package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
  14. package/dist/agents/sisyphus-junior/index.d.ts +2 -0
  15. package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
  16. package/dist/agents/types.d.ts +2 -2
  17. package/dist/cli/index.js +690 -139
  18. package/dist/cli-node/index.js +690 -139
  19. package/dist/config/index.d.ts +1 -1
  20. package/dist/config/schema/browser-automation.d.ts +1 -0
  21. package/dist/config/schema/commands.d.ts +1 -3
  22. package/dist/config/schema/default-mode.d.ts +1 -1
  23. package/dist/config/schema/goal.d.ts +7 -0
  24. package/dist/config/schema/hooks.d.ts +1 -1
  25. package/dist/config/schema/oh-my-opencode-config.d.ts +6 -10
  26. package/dist/config/schema/ralph-loop.d.ts +1 -1
  27. package/dist/config/schema.d.ts +1 -1
  28. package/dist/create-hooks.d.ts +1 -1
  29. package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
  30. package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
  31. package/dist/features/builtin-commands/types.d.ts +1 -1
  32. package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
  33. package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
  34. package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
  35. package/dist/hooks/comment-checker/hook.d.ts +8 -1
  36. package/dist/hooks/goal/command-arguments.d.ts +13 -0
  37. package/dist/hooks/goal/controller.d.ts +26 -0
  38. package/dist/hooks/goal/index.d.ts +23 -0
  39. package/dist/hooks/goal/prompt.d.ts +3 -0
  40. package/dist/hooks/goal/store.d.ts +8 -0
  41. package/dist/hooks/goal/tools.d.ts +10 -0
  42. package/dist/hooks/goal/types.d.ts +93 -0
  43. package/dist/hooks/goal/validation.d.ts +4 -0
  44. package/dist/hooks/index.d.ts +1 -0
  45. package/dist/hooks/keyword-detector/hook.d.ts +1 -2
  46. package/dist/hooks/ralph-loop/types.d.ts +1 -1
  47. package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
  48. package/dist/index.js +3706 -4329
  49. package/dist/oh-my-opencode.schema.json +23 -19
  50. package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
  51. package/dist/plugin/chat-message/types.d.ts +23 -10
  52. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
  53. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
  54. package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
  55. package/dist/plugin/messages-transform.d.ts +1 -0
  56. package/dist/plugin/stop-continuation.d.ts +2 -2
  57. package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
  58. package/dist/shared/system-directive.d.ts +0 -1
  59. package/dist/skills/ulw-plan/SKILL.md +11 -7
  60. package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
  61. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  62. package/dist/skills/ulw-research/SKILL.md +3 -3
  63. package/dist/tools/delegate-task/types.d.ts +0 -1
  64. package/dist/tui.js +115 -28
  65. package/package.json +14 -14
  66. package/packages/git-bash-mcp/dist/cli.js +81 -19
  67. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +4 -4
  68. package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +118 -1
  69. package/packages/lsp-core/src/lsp/directory-diagnostics.ts +1 -1
  70. package/packages/lsp-daemon/dist/cli.js +262 -63
  71. package/packages/lsp-daemon/dist/client.js +194 -50
  72. package/packages/lsp-daemon/dist/daemon-client.d.ts +2 -2
  73. package/packages/lsp-daemon/dist/daemon-client.js +26 -2
  74. package/packages/lsp-daemon/dist/ensure-daemon.d.ts +5 -4
  75. package/packages/lsp-daemon/dist/ensure-daemon.js +79 -18
  76. package/packages/lsp-daemon/dist/index.js +262 -63
  77. package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
  78. package/packages/lsp-daemon/dist/proxy.js +79 -23
  79. package/packages/lsp-tools-mcp/dist/cli.js +82 -20
  80. package/packages/lsp-tools-mcp/dist/mcp.js +82 -20
  81. package/packages/lsp-tools-mcp/dist/tools.js +1 -1
  82. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  83. package/packages/omo-codex/plugin/.mcp.json +2 -1
  84. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +352 -17
  85. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  86. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  87. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
  88. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +195 -56
  89. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
  90. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  91. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
  92. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
  93. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
  94. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
  95. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
  96. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  97. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  98. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  99. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  100. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  101. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  102. package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
  103. package/packages/omo-codex/plugin/components/lsp/AGENTS.md +1 -0
  104. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  105. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +113 -31
  106. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  107. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  108. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +1 -0
  109. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
  110. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
  111. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  112. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  113. package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
  114. package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
  115. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
  116. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +148 -61
  117. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  118. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  119. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +4 -72
  120. package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
  121. package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
  122. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +213 -24
  123. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
  124. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +25 -4
  125. package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
  126. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  127. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  128. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  129. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  130. package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +10 -10
  131. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
  132. package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
  133. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  134. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  135. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +50 -33
  136. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +11 -7
  137. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
  138. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  139. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
  140. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
  141. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
  142. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  143. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  144. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
  145. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
  146. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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/package-lock.json +13 -13
  171. package/packages/omo-codex/plugin/package.json +1 -1
  172. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
  173. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
  174. package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
  175. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +13 -20
  176. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
  177. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -2
  178. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
  179. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
  180. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +11 -7
  181. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
  182. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  183. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +2 -2
  184. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
  185. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
  186. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
  187. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
  188. package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
  189. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
  190. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
  191. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +38 -22
  192. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
  193. package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
  194. package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
  195. package/packages/omo-codex/scripts/install-dist/install-local.mjs +353 -18
  196. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
  197. package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -7
  198. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
  199. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  200. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
  201. package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
  202. package/dist/skills/ultraresearch/SKILL.md +0 -10
  203. package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
  204. package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
  205. package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
  206. package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
  207. package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
  208. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
  209. package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
  210. package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
  211. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
  212. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
  213. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
  214. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
  215. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
  216. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
  217. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/agents/openai.yaml +0 -0
@@ -0,0 +1,171 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+
3
+ export type PlanChecklist = {
4
+ readonly completed: number;
5
+ readonly remaining: number;
6
+ readonly total: number;
7
+ readonly nextTaskLabel: string | null;
8
+ };
9
+
10
+ const TODO_HEADING_PATTERN = /^##[ \t]+TODOs(?:[ \t]+#+)?[ \t]*$/i;
11
+ const FINAL_VERIFICATION_HEADING_PATTERN = /^##[ \t]+Final Verification Wave(?:[ \t]+#+)?[ \t]*$/i;
12
+ const SECTION_BOUNDARY_HEADING_PATTERN = /^#{1,2}(?:[ \t]+|$)/;
13
+ const FENCE_PATTERN = /^[ \t]{0,3}(`{3,}|~{3,})(.*)$/;
14
+ const SIMPLE_CHECKBOX_PATTERN = /^[-*][ \t]*\[[ \t]*([xX]?)[ \t]*\][ \t]+(.+)$/;
15
+ const TODO_CHECKBOX_PATTERN = /^- \[([ xX])\] ([1-9]\d*\. .+)$/;
16
+ const FINAL_WAVE_CHECKBOX_PATTERN = /^- \[([ xX])\] (F[1-9]\d*\. .+)$/i;
17
+
18
+ type ChecklistSection = "todo" | "final-wave" | "other";
19
+
20
+ type ParsedCheckbox = {
21
+ readonly checked: boolean;
22
+ readonly label: string;
23
+ };
24
+
25
+ type MarkdownFence = {
26
+ readonly marker: "`" | "~";
27
+ readonly length: number;
28
+ };
29
+
30
+ export function getPlanChecklist(planPath: string): PlanChecklist {
31
+ if (!existsSync(planPath)) return emptyChecklist();
32
+
33
+ try {
34
+ return parsePlanChecklist(readFileSync(planPath, "utf8"));
35
+ } catch (error) {
36
+ if (error instanceof Error) return emptyChecklist();
37
+ throw error;
38
+ }
39
+ }
40
+
41
+ export function parsePlanChecklist(markdown: string): PlanChecklist {
42
+ const lines = markdown.split(/\r?\n/);
43
+ if (!hasStructuredSection(lines)) return parseSimpleChecklist(lines);
44
+
45
+ let completed = 0;
46
+ let remaining = 0;
47
+ let nextTaskLabel: string | null = null;
48
+ let section: ChecklistSection = "other";
49
+ let fence: MarkdownFence | null = null;
50
+
51
+ for (const line of lines) {
52
+ if (fence !== null) {
53
+ if (isClosingFence(line, fence)) fence = null;
54
+ continue;
55
+ }
56
+ const openingFence = parseOpeningFence(line);
57
+ if (openingFence !== null) {
58
+ fence = openingFence;
59
+ continue;
60
+ }
61
+
62
+ if (SECTION_BOUNDARY_HEADING_PATTERN.test(line)) {
63
+ section = parseStructuredSectionHeading(line);
64
+ continue;
65
+ }
66
+ if (section === "other") continue;
67
+
68
+ const checkbox = parseStructuredCheckbox(line, section);
69
+ if (checkbox === null) continue;
70
+ if (checkbox.checked) completed += 1;
71
+ else {
72
+ remaining += 1;
73
+ nextTaskLabel = nextTaskLabel ?? checkbox.label;
74
+ }
75
+ }
76
+
77
+ return { completed, remaining, total: completed + remaining, nextTaskLabel };
78
+ }
79
+
80
+ function hasStructuredSection(lines: readonly string[]): boolean {
81
+ let fence: MarkdownFence | null = null;
82
+ for (const line of lines) {
83
+ if (fence !== null) {
84
+ if (isClosingFence(line, fence)) fence = null;
85
+ continue;
86
+ }
87
+ const openingFence = parseOpeningFence(line);
88
+ if (openingFence !== null) {
89
+ fence = openingFence;
90
+ continue;
91
+ }
92
+ if (parseStructuredSectionHeading(line) !== "other") return true;
93
+ }
94
+ return false;
95
+ }
96
+
97
+ function parseSimpleChecklist(lines: readonly string[]): PlanChecklist {
98
+ let completed = 0;
99
+ let remaining = 0;
100
+ let nextTaskLabel: string | null = null;
101
+ let fence: MarkdownFence | null = null;
102
+
103
+ for (const line of lines) {
104
+ if (fence !== null) {
105
+ if (isClosingFence(line, fence)) fence = null;
106
+ continue;
107
+ }
108
+ const openingFence = parseOpeningFence(line);
109
+ if (openingFence !== null) {
110
+ fence = openingFence;
111
+ continue;
112
+ }
113
+
114
+ const checkbox = parseSimpleTopLevelCheckbox(line);
115
+ if (checkbox === null) continue;
116
+ if (checkbox.checked) completed += 1;
117
+ else {
118
+ remaining += 1;
119
+ nextTaskLabel = nextTaskLabel ?? checkbox.label;
120
+ }
121
+ }
122
+
123
+ return { completed, remaining, total: completed + remaining, nextTaskLabel };
124
+ }
125
+
126
+ function parseStructuredSectionHeading(line: string): ChecklistSection {
127
+ if (TODO_HEADING_PATTERN.test(line)) return "todo";
128
+ if (FINAL_VERIFICATION_HEADING_PATTERN.test(line)) return "final-wave";
129
+ return "other";
130
+ }
131
+
132
+ function parseStructuredCheckbox(line: string, section: "todo" | "final-wave"): ParsedCheckbox | null {
133
+ const pattern = section === "todo" ? TODO_CHECKBOX_PATTERN : FINAL_WAVE_CHECKBOX_PATTERN;
134
+ const match = line.match(pattern);
135
+ const marker = match?.[1];
136
+ const label = match?.[2];
137
+ if (marker === undefined || label === undefined) return null;
138
+ return { checked: marker.toLowerCase() === "x", label };
139
+ }
140
+
141
+ function parseSimpleTopLevelCheckbox(line: string): ParsedCheckbox | null {
142
+ const match = line.match(SIMPLE_CHECKBOX_PATTERN);
143
+ const marker = match?.[1];
144
+ const label = match?.[2];
145
+ if (marker === undefined || label === undefined) return null;
146
+ return { checked: marker.toLowerCase() === "x", label };
147
+ }
148
+
149
+ function parseOpeningFence(line: string): MarkdownFence | null {
150
+ const match = line.match(FENCE_PATTERN);
151
+ const run = match?.[1];
152
+ const info = match?.[2];
153
+ const marker = run?.charAt(0);
154
+ if (
155
+ run === undefined ||
156
+ info === undefined ||
157
+ (marker !== "`" && marker !== "~") ||
158
+ (marker === "`" && info.includes("`"))
159
+ )
160
+ return null;
161
+ return { marker, length: run.length };
162
+ }
163
+
164
+ function isClosingFence(line: string, fence: MarkdownFence): boolean {
165
+ const run = line.match(/^[ \t]{0,3}(`{3,}|~{3,})[ \t]*$/)?.[1];
166
+ return run?.charAt(0) === fence.marker && run.length >= fence.length;
167
+ }
168
+
169
+ function emptyChecklist(): PlanChecklist {
170
+ return { completed: 0, remaining: 0, total: 0, nextTaskLabel: null };
171
+ }
@@ -1,40 +1,44 @@
1
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterEach, describe, expect, it } from "vitest";
5
5
  import { getPlanChecklist, readContinuationState } from "../src/boulder-reader.js";
6
6
 
7
7
  const cleanupRoots: string[] = [];
8
+ const SCAFFOLD_PLAN_MARKDOWN = readFileSync(new URL("./fixtures/plan-scaffold.md", import.meta.url), "utf8");
8
9
 
9
10
  afterEach(() => {
10
11
  for (const root of cleanupRoots.splice(0)) rmSync(root, { recursive: true, force: true });
11
12
  });
12
13
 
13
14
  describe("start-work plan checklist consumption", () => {
14
- it("#given top-level completed and incomplete checkboxes #when parsed #then counts remaining and total", () => {
15
+ it("#given scaffold headings and canonical numbered rows #when parsed #then structured totals and next label match", () => {
15
16
  // given
16
- const planPath = createPlan(
17
- ["# Plan", "", "## TODOs", "- [ ] First", "- [x] Done", "- [X] Also done", "- [ ] Second"].join("\n"),
18
- );
17
+ const planPath = createPlan(SCAFFOLD_PLAN_MARKDOWN);
19
18
 
20
19
  // when
21
20
  const checklist = getPlanChecklist(planPath);
22
21
 
23
22
  // then
24
- expect(checklist).toEqual({ completed: 2, remaining: 2, total: 4, nextTaskLabel: "First" });
23
+ expect(checklist).toEqual({
24
+ completed: 2,
25
+ remaining: 2,
26
+ total: 4,
27
+ nextTaskLabel: "1. Implement checklist parser parity",
28
+ });
25
29
  });
26
30
 
27
31
  it("#given nested checkboxes #when parsed #then ignores non-column-zero items", () => {
28
32
  // given
29
33
  const planPath = createPlan(
30
- ["## TODOs", "- [ ] Top-level", " - [ ] Nested", "\t- [ ] Tab nested", "- [x] Complete"].join("\n"),
34
+ ["## TODOs", "- [ ] 1. Top-level", " - [ ] Nested", "\t- [ ] Tab nested", "- [x] 2. Complete"].join("\n"),
31
35
  );
32
36
 
33
37
  // when
34
38
  const checklist = getPlanChecklist(planPath);
35
39
 
36
40
  // then
37
- expect(checklist).toEqual({ completed: 1, remaining: 1, total: 2, nextTaskLabel: "Top-level" });
41
+ expect(checklist).toEqual({ completed: 1, remaining: 1, total: 2, nextTaskLabel: "1. Top-level" });
38
42
  });
39
43
 
40
44
  it("#given checkboxes outside counted sections #when parsed #then ignores unrelated top-level tasks", () => {
@@ -44,12 +48,12 @@ describe("start-work plan checklist consumption", () => {
44
48
  "# Plan",
45
49
  "- [ ] Preamble task",
46
50
  "## TODOs",
47
- "- [ ] Build hook",
51
+ "- [ ] 1. Build hook",
48
52
  "## Acceptance Criteria",
49
53
  "- [ ] Acceptance item",
50
54
  "## Final Verification Wave",
51
- "- [x] Run tests",
52
- "- [ ] Run smoke",
55
+ "- [x] F1. Run tests",
56
+ "- [ ] F2. Run smoke",
53
57
  ].join("\n"),
54
58
  );
55
59
 
@@ -57,18 +61,184 @@ describe("start-work plan checklist consumption", () => {
57
61
  const checklist = getPlanChecklist(planPath);
58
62
 
59
63
  // then
60
- expect(checklist).toEqual({ completed: 1, remaining: 2, total: 3, nextTaskLabel: "Build hook" });
64
+ expect(checklist).toEqual({ completed: 1, remaining: 2, total: 3, nextTaskLabel: "1. Build hook" });
61
65
  });
62
66
 
63
67
  it("#given all top-level tasks complete #when parsed #then next task is null", () => {
64
68
  // given
65
- const planPath = createPlan(["## TODOs", "- [x] First", "- [X] Second"].join("\n"));
69
+ const planPath = createPlan(
70
+ ["## Todos", "- [x] 1. First", "- [X] 2. Second", "## Final verification wave", "- [x] F1. Final"].join("\n"),
71
+ );
72
+
73
+ // when
74
+ const checklist = getPlanChecklist(planPath);
75
+
76
+ // then
77
+ expect(checklist).toEqual({ completed: 3, remaining: 0, total: 3, nextTaskLabel: null });
78
+ });
79
+
80
+ it("#given no structured headings #when parsed #then legacy top-level checkbox fallback remains", () => {
81
+ // given
82
+ const planPath = createPlan(["# Plan", "- [ ] First", "- [x] Done", " - [ ] Nested"].join("\n"));
83
+
84
+ // when
85
+ const checklist = getPlanChecklist(planPath);
86
+
87
+ // then
88
+ expect(checklist).toEqual({ completed: 1, remaining: 1, total: 2, nextTaskLabel: "First" });
89
+ });
90
+
91
+ it("#given a heading-free legacy star checklist #when parsed #then fallback behavior is preserved", () => {
92
+ // given
93
+ const planPath = createPlan(["# Plan", "* [ ] First", "* [x] Done", " * [ ] Nested"].join("\n"));
94
+
95
+ // when
96
+ const checklist = getPlanChecklist(planPath);
97
+
98
+ // then
99
+ expect(checklist).toEqual({ completed: 1, remaining: 1, total: 2, nextTaskLabel: "First" });
100
+ });
101
+
102
+ it("#given noncanonical structured rows #when parsed #then only exact positive-number grammar is counted", () => {
103
+ // given
104
+ const planPath = createPlan(
105
+ [
106
+ "## Todos",
107
+ "- [ ] 0. Zero is invalid",
108
+ "- [ ] 01. Leading zero is invalid",
109
+ "* [ ] 2. Star marker is invalid",
110
+ "-[ ] 3. Missing spaces are invalid",
111
+ "- [ ] 4. Canonical implementation",
112
+ "## Final verification wave",
113
+ "- [ ] F0. Zero final verifier is invalid",
114
+ "- [ ] F01. Leading zero final verifier is invalid",
115
+ "- [x] F2. Canonical final verifier",
116
+ ].join("\n"),
117
+ );
66
118
 
67
119
  // when
68
120
  const checklist = getPlanChecklist(planPath);
69
121
 
70
122
  // then
71
- expect(checklist).toEqual({ completed: 2, remaining: 0, total: 2, nextTaskLabel: null });
123
+ expect(checklist).toEqual({
124
+ completed: 1,
125
+ remaining: 1,
126
+ total: 2,
127
+ nextTaskLabel: "4. Canonical implementation",
128
+ });
129
+ });
130
+
131
+ it("#given fenced examples and a higher-level heading #when parsed #then section scope excludes them", () => {
132
+ // given
133
+ const planPath = createPlan(
134
+ [
135
+ "## Todos",
136
+ "- [ ] 1. Counted implementation",
137
+ "```md",
138
+ "- [ ] 2. Fenced example",
139
+ "```",
140
+ "# Appendix",
141
+ "- [ ] 3. Appendix checkbox",
142
+ "## Final verification wave",
143
+ "- [x] F1. Counted verifier",
144
+ ].join("\n"),
145
+ );
146
+
147
+ // when
148
+ const checklist = getPlanChecklist(planPath);
149
+
150
+ // then
151
+ expect(checklist).toEqual({
152
+ completed: 1,
153
+ remaining: 1,
154
+ total: 2,
155
+ nextTaskLabel: "1. Counted implementation",
156
+ });
157
+ });
158
+
159
+ it("#given a child heading inside TODOs #when parsed #then canonical rows remain in the parent section", () => {
160
+ // given
161
+ const planPath = createPlan(["## TODOs", "- [x] 1. First task", "### Notes", "- [ ] 2. Second task"].join("\n"));
162
+
163
+ // when
164
+ const checklist = getPlanChecklist(planPath);
165
+
166
+ // then
167
+ expect(checklist).toEqual({ completed: 1, remaining: 1, total: 2, nextTaskLabel: "2. Second task" });
168
+ });
169
+
170
+ it("#given a four-backtick fence containing triple-backtick examples #when parsed #then shorter fences do not close it", () => {
171
+ // given
172
+ const planPath = createPlan(
173
+ [
174
+ "## Todos",
175
+ "- [x] 1. Counted implementation",
176
+ "````md",
177
+ "```ts",
178
+ "- [ ] 2. Fenced example",
179
+ "```",
180
+ "````",
181
+ "## Final verification wave",
182
+ "- [ ] F1. Counted verifier",
183
+ ].join("\n"),
184
+ );
185
+
186
+ // when
187
+ const checklist = getPlanChecklist(planPath);
188
+
189
+ // then
190
+ expect(checklist).toEqual({
191
+ completed: 1,
192
+ remaining: 1,
193
+ total: 2,
194
+ nextTaskLabel: "F1. Counted verifier",
195
+ });
196
+ });
197
+
198
+ it("#given structured headings with ATX closing markers #when parsed #then canonical tasks remain structured", () => {
199
+ // given
200
+ const planPath = createPlan(
201
+ ["## TODOs ##", "- [ ] 1. Implement", "## Final Verification Wave ###", "- [ ] F1. Verify"].join("\n"),
202
+ );
203
+
204
+ // when
205
+ const checklist = getPlanChecklist(planPath);
206
+
207
+ // then
208
+ expect(checklist).toEqual({
209
+ completed: 0,
210
+ remaining: 2,
211
+ total: 2,
212
+ nextTaskLabel: "1. Implement",
213
+ });
214
+ });
215
+
216
+ it("#given inline backtick code before a canonical task #when parsed #then it does not open a fence", () => {
217
+ // given
218
+ const planPath = createPlan(["## TODOs", "```example```", "- [ ] 1. Implement"].join("\n"));
219
+
220
+ // when
221
+ const checklist = getPlanChecklist(planPath);
222
+
223
+ // then
224
+ expect(checklist.total).toBe(1);
225
+ expect(checklist.nextTaskLabel).toBe("1. Implement");
226
+ });
227
+
228
+ it("#given a heading-free fenced checkbox example #when parsed #then legacy fallback ignores it", () => {
229
+ // given
230
+ const planPath = createPlan(["````md", "- [ ] 1. Example only", "````"].join("\n"));
231
+
232
+ // when
233
+ const checklist = getPlanChecklist(planPath);
234
+
235
+ // then
236
+ expect(checklist).toEqual({
237
+ completed: 0,
238
+ remaining: 0,
239
+ total: 0,
240
+ nextTaskLabel: null,
241
+ });
72
242
  });
73
243
  });
74
244
 
@@ -77,7 +247,7 @@ describe("start-work boulder state reader", () => {
77
247
  // given
78
248
  const workspace = createWorkspace({
79
249
  boulderJson: createBoulderJson({ status: "active", sessionIds: ["codex:sess_abc"] }),
80
- planMarkdown: "# Plan\n\n## TODOs\n- [ ] First\n- [x] Done\n- [ ] Second\n",
250
+ planMarkdown: SCAFFOLD_PLAN_MARKDOWN,
81
251
  });
82
252
 
83
253
  // when
@@ -90,7 +260,12 @@ describe("start-work boulder state reader", () => {
90
260
  boulderPath: join(workspace, ".omo", "boulder.json"),
91
261
  ledgerPath: join(workspace, ".omo", "start-work", "ledger.jsonl"),
92
262
  worktreePath: null,
93
- checklist: { completed: 1, remaining: 2, total: 3, nextTaskLabel: "First" },
263
+ checklist: {
264
+ completed: 2,
265
+ remaining: 2,
266
+ total: 4,
267
+ nextTaskLabel: "1. Implement checklist parser parity",
268
+ },
94
269
  });
95
270
  });
96
271
 
@@ -98,7 +273,7 @@ describe("start-work boulder state reader", () => {
98
273
  // given
99
274
  const workspace = createWorkspace({
100
275
  boulderJson: createBoulderJson({ status: "completed", sessionIds: ["codex:sess_abc"] }),
101
- planMarkdown: "# Plan\n\n## TODOs\n- [ ] First\n",
276
+ planMarkdown: "# Plan\n\n## TODOs\n- [ ] 1. First\n",
102
277
  });
103
278
 
104
279
  // when
@@ -112,7 +287,7 @@ describe("start-work boulder state reader", () => {
112
287
  // given
113
288
  const workspace = createWorkspace({
114
289
  boulderJson: createBoulderJson({ status: "paused", sessionIds: ["codex:sess_abc"] }),
115
- planMarkdown: "# Plan\n\n## TODOs\n- [ ] First\n",
290
+ planMarkdown: "# Plan\n\n## TODOs\n- [ ] 1. First\n",
116
291
  });
117
292
 
118
293
  // when
@@ -120,14 +295,28 @@ describe("start-work boulder state reader", () => {
120
295
 
121
296
  // then
122
297
  expect(state?.planName).toBe("launch-plan");
123
- expect(state?.checklist).toEqual({ completed: 0, remaining: 1, total: 1, nextTaskLabel: "First" });
298
+ expect(state?.checklist).toEqual({ completed: 0, remaining: 1, total: 1, nextTaskLabel: "1. First" });
299
+ });
300
+
301
+ it("#given active codex work with no remaining checklist items #when state is read #then final gate continuation remains present", () => {
302
+ // given
303
+ const workspace = createWorkspace({
304
+ boulderJson: createBoulderJson({ status: "active", sessionIds: ["codex:sess_abc"] }),
305
+ planMarkdown: "# Plan\n\n## TODOs\n- [x] 1. First\n",
306
+ });
307
+
308
+ // when
309
+ const state = readContinuationState(workspace, "sess_abc");
310
+
311
+ // then
312
+ expect(state?.checklist).toEqual({ completed: 1, remaining: 0, total: 1, nextTaskLabel: null });
124
313
  });
125
314
 
126
- it("#given no remaining top-level checklist items #when state is read #then continuation is absent", () => {
315
+ it("#given active codex work with no readable checklist #when state is read #then continuation remains absent", () => {
127
316
  // given
128
317
  const workspace = createWorkspace({
129
318
  boulderJson: createBoulderJson({ status: "active", sessionIds: ["codex:sess_abc"] }),
130
- planMarkdown: "# Plan\n\n## TODOs\n- [x] First\n",
319
+ planMarkdown: "# Plan\n\nNo checklist yet.\n",
131
320
  });
132
321
 
133
322
  // when
@@ -141,7 +330,7 @@ describe("start-work boulder state reader", () => {
141
330
  // given
142
331
  const workspace = createWorkspace({
143
332
  boulderJson: "{",
144
- planMarkdown: "# Plan\n\n## TODOs\n- [ ] First\n",
333
+ planMarkdown: "# Plan\n\n## TODOs\n- [ ] 1. First\n",
145
334
  });
146
335
 
147
336
  // when
@@ -155,7 +344,7 @@ describe("start-work boulder state reader", () => {
155
344
  // given
156
345
  const workspace = createWorkspace({
157
346
  boulderJson: createBoulderJson({ status: "active", sessionIds: ["sess_abc"] }),
158
- planMarkdown: "# Plan\n\n## TODOs\n- [ ] First\n",
347
+ planMarkdown: "# Plan\n\n## TODOs\n- [ ] 1. First\n",
159
348
  });
160
349
 
161
350
  // when
@@ -187,7 +376,7 @@ describe("start-work boulder state reader", () => {
187
376
  started_at: "2026-06-12T00:00:00.000Z",
188
377
  session_ids: ["codex:sess_abc"],
189
378
  }),
190
- planMarkdown: "# Plan\n\n## TODOs\n- [ ] First\n",
379
+ planMarkdown: "# Plan\n\n## TODOs\n- [ ] 1. First\n",
191
380
  });
192
381
 
193
382
  // when
@@ -6,6 +6,7 @@ import { execPath } from "node:process";
6
6
  import { afterEach, describe, expect, it } from "vitest";
7
7
 
8
8
  const cleanupRoots: string[] = [];
9
+ const SCAFFOLD_PLAN_MARKDOWN = readFileSync(new URL("./fixtures/plan-scaffold.md", import.meta.url), "utf8");
9
10
 
10
11
  afterEach(() => {
11
12
  for (const root of cleanupRoots.splice(0)) rmSync(root, { recursive: true, force: true });
@@ -25,8 +26,8 @@ describe("start-work continuation CLI", () => {
25
26
  expect(result.status).toBe(0);
26
27
  const output = parseStopHookOutput(result.stdout);
27
28
  expect(output.decision).toBe("block");
28
- expect(output.reason).toContain("Remaining top-level checkboxes: `2` of `3`");
29
- expect(output.reason).toContain("Next incomplete task: `Task one`");
29
+ expect(output.reason).toContain("Remaining top-level checkboxes: `2` of `4`");
30
+ expect(output.reason).toContain("Next incomplete task: `1. Implement checklist parser parity`");
30
31
  expect(output.reason).toContain("Your session id in boulder.json: `codex:s1`");
31
32
  });
32
33
 
@@ -106,7 +107,7 @@ function createWorkspace(sessionIds: readonly string[]): string {
106
107
  const root = mkdtempSync(join(tmpdir(), "codex-continuation-cli-"));
107
108
  cleanupRoots.push(root);
108
109
  mkdirSync(join(root, ".omo", "plans"), { recursive: true });
109
- writeFileSync(join(root, ".omo", "plans", "plan.md"), "## TODOs\n\n- [ ] Task one\n- [x] Done\n- [ ] Task two\n");
110
+ writeFileSync(join(root, ".omo", "plans", "plan.md"), SCAFFOLD_PLAN_MARKDOWN);
110
111
  const work = {
111
112
  work_id: "w1",
112
113
  active_plan: ".omo/plans/plan.md",
@@ -1,4 +1,4 @@
1
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterEach, describe, expect, it } from "vitest";
@@ -8,6 +8,7 @@ import type { ReadonlyFileSystem, StopInput } from "../src/types.js";
8
8
 
9
9
  const DEFAULT_WORKSPACE = "/repo";
10
10
  const cleanupRoots: string[] = [];
11
+ const SCAFFOLD_PLAN_MARKDOWN = readFileSync(new URL("./fixtures/plan-scaffold.md", import.meta.url), "utf8");
11
12
 
12
13
  afterEach(() => {
13
14
  for (const root of cleanupRoots.splice(0)) rmSync(root, { recursive: true, force: true });
@@ -49,7 +50,7 @@ describe("start-work Stop hook", () => {
49
50
  status: "active",
50
51
  worktreePath: "/tmp/worktree",
51
52
  }),
52
- planMarkdown: ["# Plan", "", "## TODOs", "- [ ] First", "- [x] Done", "- [ ] Second"].join("\n"),
53
+ planMarkdown: SCAFFOLD_PLAN_MARKDOWN,
53
54
  });
54
55
  const fs = createMemoryFs();
55
56
 
@@ -62,13 +63,33 @@ describe("start-work Stop hook", () => {
62
63
  expect(parsed.reason).toContain("- Plan: `launch-plan`");
63
64
  expect(parsed.reason).toContain(`- Plan file: \`${join(workspace, ".omo", "plans", "plan.md")}\``);
64
65
  expect(parsed.reason).toContain(`- Boulder state: \`${join(workspace, ".omo", "boulder.json")}\``);
65
- expect(parsed.reason).toContain("- Remaining top-level checkboxes: `2` of `3`");
66
- expect(parsed.reason).toContain("- Next incomplete task: `First`");
66
+ expect(parsed.reason).toContain("- Remaining top-level checkboxes: `2` of `4`");
67
+ expect(parsed.reason).toContain("- Next incomplete task: `1. Implement checklist parser parity`");
67
68
  expect(parsed.reason).toContain("- Worktree: `/tmp/worktree`");
68
69
  expect(parsed.reason).toContain(`- Ledger: \`${join(workspace, ".omo", "start-work", "ledger.jsonl")}\``);
69
70
  expect(parsed.reason).toContain("- Your session id in boulder.json: `codex:sess_abc`");
70
71
  });
71
72
 
73
+ it("#given active codex work with zero remaining tasks #when hook runs #then blocks for the final gate", () => {
74
+ // given
75
+ const workspace = createWorkspace({
76
+ boulderJson: createBoulderJson({ sessionIds: ["codex:sess_abc"], status: "active" }),
77
+ planMarkdown: ["# Plan", "", "## TODOs", "- [x] 1. Done"].join("\n"),
78
+ });
79
+ const fs = createMemoryFs();
80
+
81
+ // when
82
+ const output = runStopHook(createStopInput(workspace), fs);
83
+
84
+ // then
85
+ const parsed = parseBlockOutput(output);
86
+ expect(parsed.decision).toBe("block");
87
+ expect(parsed.reason).toContain("- Remaining top-level checkboxes: `0` of `1`");
88
+ expect(parsed.reason).toContain("- Next incomplete task: `none (final gate pending)`");
89
+ expect(parsed.reason).toContain("When the remaining count is `0`, skip checkbox execution");
90
+ expect(parsed.reason).toContain("re-read the ledger record and verify the exact lane/SHA pair");
91
+ });
92
+
72
93
  it("#given context-window pressure in transcript #when hook runs #then it does not inject continuation text", () => {
73
94
  // given
74
95
  const transcriptPath = "/repo/transcript.jsonl";
@@ -0,0 +1,15 @@
1
+ # Scaffold Parser Parity
2
+
3
+ ## Todos
4
+ - [ ] 1. Implement checklist parser parity
5
+ - [ ] Nested acceptance detail
6
+ - [x] 2. Preserve completed implementation rows
7
+ - [ ] Missing numeric prefix must be ignored
8
+
9
+ ## Acceptance Criteria
10
+ - [ ] Outside tracked sections must be ignored
11
+
12
+ ## Final verification wave
13
+ - [ ] F1. Exercise the Codex Stop surface
14
+ - [X] F2. Preserve completed final verification rows
15
+ - [ ] 3. Wrong final-wave label must be ignored
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
10
10
  "timeout": 10,
11
- "statusMessage": "(OmO 4.18.1) Checking Thread Title Hygiene"
11
+ "statusMessage": "(OmO 4.19.0) Checking Thread Title Hygiene"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-teammode",
3
- "version": "4.18.1",
3
+ "version": "4.19.0",
4
4
  "description": "Codex team-mode hook component that keeps background thread titles descriptive after create_thread.",
5
5
  "type": "module",
6
6
  "private": true,
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 4.18.1) Recording Session Telemetry"
10
+ "statusMessage": "(OmO 4.19.0) Recording Session Telemetry"
11
11
  }
12
12
  ]
13
13
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-telemetry",
3
- "version": "4.18.1",
3
+ "version": "4.19.0",
4
4
  "description": "Codex plugin component that emits omo-codex anonymous daily-active telemetry on SessionStart.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",