oh-my-opencode 4.18.2 → 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 (164) hide show
  1. package/README.ja.md +2 -2
  2. package/README.ko.md +2 -2
  3. package/README.md +4 -4
  4. package/README.ru.md +2 -2
  5. package/README.zh-cn.md +2 -2
  6. package/dist/agents/atlas/agent.d.ts +6 -5
  7. package/dist/agents/sisyphus/index.d.ts +5 -0
  8. package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
  9. package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
  10. package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
  11. package/dist/agents/sisyphus-junior/index.d.ts +2 -0
  12. package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
  13. package/dist/agents/types.d.ts +2 -2
  14. package/dist/cli/index.js +648 -136
  15. package/dist/cli-node/index.js +648 -136
  16. package/dist/config/index.d.ts +1 -1
  17. package/dist/config/schema/browser-automation.d.ts +1 -0
  18. package/dist/config/schema/commands.d.ts +1 -3
  19. package/dist/config/schema/default-mode.d.ts +1 -1
  20. package/dist/config/schema/goal.d.ts +7 -0
  21. package/dist/config/schema/hooks.d.ts +1 -1
  22. package/dist/config/schema/oh-my-opencode-config.d.ts +6 -10
  23. package/dist/config/schema/ralph-loop.d.ts +1 -1
  24. package/dist/config/schema.d.ts +1 -1
  25. package/dist/create-hooks.d.ts +1 -1
  26. package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
  27. package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
  28. package/dist/features/builtin-commands/types.d.ts +1 -1
  29. package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
  30. package/dist/hooks/goal/command-arguments.d.ts +13 -0
  31. package/dist/hooks/goal/controller.d.ts +26 -0
  32. package/dist/hooks/goal/index.d.ts +23 -0
  33. package/dist/hooks/goal/prompt.d.ts +3 -0
  34. package/dist/hooks/goal/store.d.ts +8 -0
  35. package/dist/hooks/goal/tools.d.ts +10 -0
  36. package/dist/hooks/goal/types.d.ts +93 -0
  37. package/dist/hooks/goal/validation.d.ts +4 -0
  38. package/dist/hooks/index.d.ts +1 -0
  39. package/dist/hooks/keyword-detector/hook.d.ts +1 -2
  40. package/dist/hooks/ralph-loop/types.d.ts +1 -1
  41. package/dist/index.js +3515 -4365
  42. package/dist/oh-my-opencode.schema.json +23 -19
  43. package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
  44. package/dist/plugin/chat-message/types.d.ts +23 -10
  45. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
  46. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
  47. package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
  48. package/dist/plugin/stop-continuation.d.ts +2 -2
  49. package/dist/shared/system-directive.d.ts +0 -1
  50. package/dist/skills/ulw-plan/SKILL.md +11 -7
  51. package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
  52. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  53. package/dist/skills/ulw-research/SKILL.md +3 -3
  54. package/dist/tools/delegate-task/types.d.ts +0 -1
  55. package/dist/tui.js +73 -25
  56. package/package.json +14 -14
  57. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  58. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +352 -17
  59. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  60. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  61. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
  62. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +97 -30
  63. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  64. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
  65. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  66. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  67. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  68. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  69. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  70. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  71. package/packages/omo-codex/plugin/components/lsp/AGENTS.md +1 -0
  72. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  73. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  74. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  75. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  76. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  77. package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
  78. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +147 -60
  79. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  80. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  81. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +3 -71
  82. package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
  83. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +198 -23
  84. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
  85. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +6 -5
  86. package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
  87. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  88. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  89. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  90. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  91. package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +10 -10
  92. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
  93. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  94. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  95. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +11 -7
  96. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
  97. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  98. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  99. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  106. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  107. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  108. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  109. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  110. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  111. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  112. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  113. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  114. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  115. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  116. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  117. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  118. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  119. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  120. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  121. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  122. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  123. package/packages/omo-codex/plugin/package-lock.json +13 -13
  124. package/packages/omo-codex/plugin/package.json +1 -1
  125. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
  126. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
  127. package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
  128. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +3 -18
  129. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +11 -7
  130. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
  131. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  132. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +2 -2
  133. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
  134. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
  135. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
  136. package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
  137. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
  138. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +3 -18
  139. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
  140. package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
  141. package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
  142. package/packages/omo-codex/scripts/install-dist/install-local.mjs +353 -18
  143. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
  144. package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -7
  145. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
  146. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  147. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
  148. package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
  149. package/dist/skills/ultraresearch/SKILL.md +0 -10
  150. package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
  151. package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
  152. package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
  153. package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
  154. package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
  155. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
  156. package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
  157. package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
  158. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
  159. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
  160. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
  161. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
  162. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
  163. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
  164. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/agents/openai.yaml +0 -0
@@ -1,37 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // components/start-work-continuation/src/cli.ts
4
- import { readFileSync as readFileSync3 } from "node:fs";
4
+ import { readFileSync as readFileSync4 } from "node:fs";
5
5
  import { stdin as processStdin, stdout as processStdout } from "node:process";
6
6
 
7
7
  // components/start-work-continuation/src/boulder-reader.ts
8
- import { existsSync, readFileSync } from "node:fs";
8
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
9
9
  import { isAbsolute, join, relative, resolve } from "node:path";
10
- var TODO_HEADING = "TODOs";
11
- var FINAL_VERIFICATION_HEADING = "Final Verification Wave";
12
- var CHECKBOX_PREFIX_LENGTH = "- [ ] ".length;
13
- var SESSION_ID_PREFIX_PATTERN = /^(codex|opencode):/;
14
- function readContinuationState(cwd, sessionId) {
15
- const boulderPath = getBoulderFilePath(cwd);
16
- const boulderState = readBoulderState(boulderPath);
17
- if (boulderState === null)
18
- return null;
19
- const work = getWorkForSession(boulderState, normalizeSessionId(sessionId, "codex"));
20
- if (work === null || !isContinuableStatus(work.status))
21
- return null;
22
- const planPath = resolveBoulderPlanPathForWork(cwd, work);
23
- const checklist = getPlanChecklist(planPath);
24
- if (checklist.total === 0)
25
- return null;
26
- return {
27
- planName: work.planName,
28
- planPath,
29
- boulderPath,
30
- ledgerPath: join(cwd, ".omo", "start-work", "ledger.jsonl"),
31
- worktreePath: work.worktreePath ?? null,
32
- checklist
33
- };
34
- }
10
+
11
+ // components/start-work-continuation/src/plan-checklist.ts
12
+ import { existsSync, readFileSync } from "node:fs";
13
+ var TODO_HEADING_PATTERN = /^##[ \t]+TODOs(?:[ \t]+#+)?[ \t]*$/i;
14
+ var FINAL_VERIFICATION_HEADING_PATTERN = /^##[ \t]+Final Verification Wave(?:[ \t]+#+)?[ \t]*$/i;
15
+ var SECTION_BOUNDARY_HEADING_PATTERN = /^#{1,2}(?:[ \t]+|$)/;
16
+ var FENCE_PATTERN = /^[ \t]{0,3}(`{3,}|~{3,})(.*)$/;
17
+ var SIMPLE_CHECKBOX_PATTERN = /^[-*][ \t]*\[[ \t]*([xX]?)[ \t]*\][ \t]+(.+)$/;
18
+ var TODO_CHECKBOX_PATTERN = /^- \[([ xX])\] ([1-9]\d*\. .+)$/;
19
+ var FINAL_WAVE_CHECKBOX_PATTERN = /^- \[([ xX])\] (F[1-9]\d*\. .+)$/i;
35
20
  function getPlanChecklist(planPath) {
36
21
  if (!existsSync(planPath))
37
22
  return emptyChecklist();
@@ -45,34 +30,155 @@ function getPlanChecklist(planPath) {
45
30
  }
46
31
  function parsePlanChecklist(markdown) {
47
32
  const lines = markdown.split(/\r?\n/);
48
- const hasCountedSections = lines.some((line) => isCountedHeading(parseLevelTwoHeading(line)));
33
+ if (!hasStructuredSection(lines))
34
+ return parseSimpleChecklist(lines);
49
35
  let completed = 0;
50
36
  let remaining = 0;
51
37
  let nextTaskLabel = null;
52
- let isCountedSection = !hasCountedSections;
38
+ let section = "other";
39
+ let fence = null;
53
40
  for (const line of lines) {
54
- const heading = parseLevelTwoHeading(line);
55
- if (heading !== null) {
56
- isCountedSection = isCountedHeading(heading);
41
+ if (fence !== null) {
42
+ if (isClosingFence(line, fence))
43
+ fence = null;
44
+ continue;
45
+ }
46
+ const openingFence = parseOpeningFence(line);
47
+ if (openingFence !== null) {
48
+ fence = openingFence;
57
49
  continue;
58
50
  }
59
- if (!isCountedSection)
51
+ if (SECTION_BOUNDARY_HEADING_PATTERN.test(line)) {
52
+ section = parseStructuredSectionHeading(line);
60
53
  continue;
61
- const checkbox = parseTopLevelCheckbox(line);
54
+ }
55
+ if (section === "other")
56
+ continue;
57
+ const checkbox = parseStructuredCheckbox(line, section);
62
58
  if (checkbox === null)
63
59
  continue;
64
- if (checkbox.checked) {
60
+ if (checkbox.checked)
65
61
  completed += 1;
66
- } else {
62
+ else {
67
63
  remaining += 1;
68
64
  nextTaskLabel = nextTaskLabel ?? checkbox.label;
69
65
  }
70
66
  }
71
67
  return { completed, remaining, total: completed + remaining, nextTaskLabel };
72
68
  }
69
+ function hasStructuredSection(lines) {
70
+ let fence = null;
71
+ for (const line of lines) {
72
+ if (fence !== null) {
73
+ if (isClosingFence(line, fence))
74
+ fence = null;
75
+ continue;
76
+ }
77
+ const openingFence = parseOpeningFence(line);
78
+ if (openingFence !== null) {
79
+ fence = openingFence;
80
+ continue;
81
+ }
82
+ if (parseStructuredSectionHeading(line) !== "other")
83
+ return true;
84
+ }
85
+ return false;
86
+ }
87
+ function parseSimpleChecklist(lines) {
88
+ let completed = 0;
89
+ let remaining = 0;
90
+ let nextTaskLabel = null;
91
+ let fence = null;
92
+ for (const line of lines) {
93
+ if (fence !== null) {
94
+ if (isClosingFence(line, fence))
95
+ fence = null;
96
+ continue;
97
+ }
98
+ const openingFence = parseOpeningFence(line);
99
+ if (openingFence !== null) {
100
+ fence = openingFence;
101
+ continue;
102
+ }
103
+ const checkbox = parseSimpleTopLevelCheckbox(line);
104
+ if (checkbox === null)
105
+ continue;
106
+ if (checkbox.checked)
107
+ completed += 1;
108
+ else {
109
+ remaining += 1;
110
+ nextTaskLabel = nextTaskLabel ?? checkbox.label;
111
+ }
112
+ }
113
+ return { completed, remaining, total: completed + remaining, nextTaskLabel };
114
+ }
115
+ function parseStructuredSectionHeading(line) {
116
+ if (TODO_HEADING_PATTERN.test(line))
117
+ return "todo";
118
+ if (FINAL_VERIFICATION_HEADING_PATTERN.test(line))
119
+ return "final-wave";
120
+ return "other";
121
+ }
122
+ function parseStructuredCheckbox(line, section) {
123
+ const pattern = section === "todo" ? TODO_CHECKBOX_PATTERN : FINAL_WAVE_CHECKBOX_PATTERN;
124
+ const match = line.match(pattern);
125
+ const marker = match?.[1];
126
+ const label = match?.[2];
127
+ if (marker === undefined || label === undefined)
128
+ return null;
129
+ return { checked: marker.toLowerCase() === "x", label };
130
+ }
131
+ function parseSimpleTopLevelCheckbox(line) {
132
+ const match = line.match(SIMPLE_CHECKBOX_PATTERN);
133
+ const marker = match?.[1];
134
+ const label = match?.[2];
135
+ if (marker === undefined || label === undefined)
136
+ return null;
137
+ return { checked: marker.toLowerCase() === "x", label };
138
+ }
139
+ function parseOpeningFence(line) {
140
+ const match = line.match(FENCE_PATTERN);
141
+ const run = match?.[1];
142
+ const info = match?.[2];
143
+ const marker = run?.charAt(0);
144
+ if (run === undefined || info === undefined || marker !== "`" && marker !== "~" || marker === "`" && info.includes("`"))
145
+ return null;
146
+ return { marker, length: run.length };
147
+ }
148
+ function isClosingFence(line, fence) {
149
+ const run = line.match(/^[ \t]{0,3}(`{3,}|~{3,})[ \t]*$/)?.[1];
150
+ return run?.charAt(0) === fence.marker && run.length >= fence.length;
151
+ }
152
+ function emptyChecklist() {
153
+ return { completed: 0, remaining: 0, total: 0, nextTaskLabel: null };
154
+ }
155
+
156
+ // components/start-work-continuation/src/boulder-reader.ts
157
+ var SESSION_ID_PREFIX_PATTERN = /^(codex|opencode):/;
158
+ function readContinuationState(cwd, sessionId) {
159
+ const boulderPath = getBoulderFilePath(cwd);
160
+ const boulderState = readBoulderState(boulderPath);
161
+ if (boulderState === null)
162
+ return null;
163
+ const work = getWorkForSession(boulderState, normalizeSessionId(sessionId, "codex"));
164
+ if (work === null || !isContinuableStatus(work.status))
165
+ return null;
166
+ const planPath = resolveBoulderPlanPathForWork(cwd, work);
167
+ const checklist = getPlanChecklist(planPath);
168
+ if (checklist.total === 0)
169
+ return null;
170
+ return {
171
+ planName: work.planName,
172
+ planPath,
173
+ boulderPath,
174
+ ledgerPath: join(cwd, ".omo", "start-work", "ledger.jsonl"),
175
+ worktreePath: work.worktreePath ?? null,
176
+ checklist
177
+ };
178
+ }
73
179
  function readBoulderState(path) {
74
180
  try {
75
- const parsed = JSON.parse(readFileSync(path, "utf8"));
181
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
76
182
  return parseBoulderState(parsed);
77
183
  } catch (error) {
78
184
  if (error instanceof Error)
@@ -150,27 +256,11 @@ function resolveBoulderPlanPathForWork(cwd, work) {
150
256
  return absolutePlanPath;
151
257
  }
152
258
  const worktreePlanPath = resolve(resolveTrackedPath(cwd, worktreePath), relativePlanPath);
153
- return existsSync(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
259
+ return existsSync2(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
154
260
  }
155
261
  function resolveTrackedPath(baseDirectory, trackedPath) {
156
262
  return isAbsolute(trackedPath) ? resolve(trackedPath) : resolve(baseDirectory, trackedPath);
157
263
  }
158
- function parseTopLevelCheckbox(line) {
159
- if (line.startsWith("- [ ] "))
160
- return { checked: false, label: line.slice(CHECKBOX_PREFIX_LENGTH) };
161
- if (line.startsWith("- [x] ") || line.startsWith("- [X] ")) {
162
- return { checked: true, label: line.slice(CHECKBOX_PREFIX_LENGTH) };
163
- }
164
- return null;
165
- }
166
- function parseLevelTwoHeading(line) {
167
- if (!line.startsWith("## "))
168
- return null;
169
- return line.slice("## ".length).trim();
170
- }
171
- function isCountedHeading(heading) {
172
- return heading === TODO_HEADING || heading === FINAL_VERIFICATION_HEADING;
173
- }
174
264
  function parseBoulderWorkStatus(value) {
175
265
  if (value === "active" || value === "paused" || value === "completed" || value === "abandoned")
176
266
  return value;
@@ -203,16 +293,13 @@ function isContinuableStatus(status) {
203
293
  function getBoulderFilePath(cwd) {
204
294
  return join(cwd, ".omo", "boulder.json");
205
295
  }
206
- function emptyChecklist() {
207
- return { completed: 0, remaining: 0, total: 0, nextTaskLabel: null };
208
- }
209
296
  function isRecord(value) {
210
297
  return typeof value === "object" && value !== null && !Array.isArray(value);
211
298
  }
212
299
 
213
300
  // components/start-work-continuation/src/directive.ts
214
- import { readFileSync as readFileSync2 } from "node:fs";
215
- var START_WORK_CONTINUATION_DIRECTIVE = readFileSync2(new URL("../directive.md", import.meta.url), "utf8");
301
+ import { readFileSync as readFileSync3 } from "node:fs";
302
+ var START_WORK_CONTINUATION_DIRECTIVE = readFileSync3(new URL("../directive.md", import.meta.url), "utf8");
216
303
 
217
304
  // components/start-work-continuation/src/codex-hook.ts
218
305
  function runStopHook(input, fs) {
@@ -285,7 +372,7 @@ function isRecord2(value) {
285
372
  // components/start-work-continuation/src/cli.ts
286
373
  var nodeFileSystem = {
287
374
  readFileSync(path, encoding) {
288
- return readFileSync3(path, encoding);
375
+ return readFileSync4(path, encoding);
289
376
  }
290
377
  };
291
378
  var command = process.argv[2];
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook stop",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 4.18.2) Checking Start-Work Continuation"
10
+ "statusMessage": "(OmO 4.19.0) Checking Start-Work Continuation"
11
11
  }
12
12
  ]
13
13
  }
@@ -19,7 +19,7 @@
19
19
  "type": "command",
20
20
  "command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook subagent-stop",
21
21
  "timeout": 10,
22
- "statusMessage": "(OmO 4.18.2) Checking Start-Work Continuation"
22
+ "statusMessage": "(OmO 4.19.0) Checking Start-Work Continuation"
23
23
  }
24
24
  ]
25
25
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-start-work-continuation",
3
- "version": "4.18.2",
3
+ "version": "4.19.0",
4
4
  "description": "Codex Stop hook continuation injector for omo-codex start-work plans.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -1,12 +1,9 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { isAbsolute, join, relative, resolve } from "node:path";
3
+ import { getPlanChecklist, type PlanChecklist } from "./plan-checklist.js";
3
4
 
4
- export type PlanChecklist = {
5
- readonly completed: number;
6
- readonly remaining: number;
7
- readonly total: number;
8
- readonly nextTaskLabel: string | null;
9
- };
5
+ export type { PlanChecklist } from "./plan-checklist.js";
6
+ export { getPlanChecklist } from "./plan-checklist.js";
10
7
 
11
8
  type BoulderWorkStatus = "active" | "paused" | "completed" | "abandoned";
12
9
 
@@ -35,9 +32,6 @@ export type ContinuationState = {
35
32
  readonly checklist: PlanChecklist;
36
33
  };
37
34
 
38
- const TODO_HEADING = "TODOs";
39
- const FINAL_VERIFICATION_HEADING = "Final Verification Wave";
40
- const CHECKBOX_PREFIX_LENGTH = "- [ ] ".length;
41
35
  const SESSION_ID_PREFIX_PATTERN = /^(codex|opencode):/;
42
36
 
43
37
  export function readContinuationState(cwd: string, sessionId: string): ContinuationState | null {
@@ -62,47 +56,6 @@ export function readContinuationState(cwd: string, sessionId: string): Continuat
62
56
  };
63
57
  }
64
58
 
65
- export function getPlanChecklist(planPath: string): PlanChecklist {
66
- if (!existsSync(planPath)) return emptyChecklist();
67
-
68
- try {
69
- return parsePlanChecklist(readFileSync(planPath, "utf8"));
70
- } catch (error) {
71
- if (error instanceof Error) return emptyChecklist();
72
- throw error;
73
- }
74
- }
75
-
76
- function parsePlanChecklist(markdown: string): PlanChecklist {
77
- const lines = markdown.split(/\r?\n/);
78
- const hasCountedSections = lines.some((line) => isCountedHeading(parseLevelTwoHeading(line)));
79
- let completed = 0;
80
- let remaining = 0;
81
- let nextTaskLabel: string | null = null;
82
- let isCountedSection = !hasCountedSections;
83
-
84
- for (const line of lines) {
85
- const heading = parseLevelTwoHeading(line);
86
- if (heading !== null) {
87
- isCountedSection = isCountedHeading(heading);
88
- continue;
89
- }
90
- if (!isCountedSection) continue;
91
-
92
- const checkbox = parseTopLevelCheckbox(line);
93
- if (checkbox === null) continue;
94
-
95
- if (checkbox.checked) {
96
- completed += 1;
97
- } else {
98
- remaining += 1;
99
- nextTaskLabel = nextTaskLabel ?? checkbox.label;
100
- }
101
- }
102
-
103
- return { completed, remaining, total: completed + remaining, nextTaskLabel };
104
- }
105
-
106
59
  function readBoulderState(path: string): BoulderState | null {
107
60
  try {
108
61
  const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
@@ -193,23 +146,6 @@ function resolveTrackedPath(baseDirectory: string, trackedPath: string): string
193
146
  return isAbsolute(trackedPath) ? resolve(trackedPath) : resolve(baseDirectory, trackedPath);
194
147
  }
195
148
 
196
- function parseTopLevelCheckbox(line: string): { readonly checked: boolean; readonly label: string } | null {
197
- if (line.startsWith("- [ ] ")) return { checked: false, label: line.slice(CHECKBOX_PREFIX_LENGTH) };
198
- if (line.startsWith("- [x] ") || line.startsWith("- [X] ")) {
199
- return { checked: true, label: line.slice(CHECKBOX_PREFIX_LENGTH) };
200
- }
201
- return null;
202
- }
203
-
204
- function parseLevelTwoHeading(line: string): string | null {
205
- if (!line.startsWith("## ")) return null;
206
- return line.slice("## ".length).trim();
207
- }
208
-
209
- function isCountedHeading(heading: string | null): boolean {
210
- return heading === TODO_HEADING || heading === FINAL_VERIFICATION_HEADING;
211
- }
212
-
213
149
  function parseBoulderWorkStatus(value: unknown): BoulderWorkStatus | undefined {
214
150
  if (value === "active" || value === "paused" || value === "completed" || value === "abandoned") return value;
215
151
  return undefined;
@@ -243,10 +179,6 @@ function getBoulderFilePath(cwd: string): string {
243
179
  return join(cwd, ".omo", "boulder.json");
244
180
  }
245
181
 
246
- function emptyChecklist(): PlanChecklist {
247
- return { completed: 0, remaining: 0, total: 0, nextTaskLabel: null };
248
- }
249
-
250
182
  function isRecord(value: unknown): value is Record<string, unknown> {
251
183
  return typeof value === "object" && value !== null && !Array.isArray(value);
252
184
  }
@@ -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
+ }