lazycodex-ai 5.0.1 → 5.1.1

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 (80) hide show
  1. package/README.ja.md +2 -1
  2. package/README.ko.md +2 -1
  3. package/README.md +2 -1
  4. package/README.ru.md +2 -1
  5. package/README.zh-cn.md +2 -1
  6. package/dist/cli/index.js +851 -546
  7. package/dist/cli-node/index.js +851 -546
  8. package/package.json +6 -1
  9. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  10. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +341 -157
  11. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  12. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +7 -4
  14. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  15. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  16. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  17. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  18. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  19. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  21. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  22. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  24. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  26. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  28. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  30. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  31. package/packages/omo-codex/plugin/components/ulw-execute-continuation/AGENTS.md +1 -1
  32. package/packages/omo-codex/plugin/components/ulw-execute-continuation/dist/cli.js +42 -32
  33. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  34. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/ulw-execute-continuation/src/plan-checklist.ts +55 -29
  36. package/packages/omo-codex/plugin/components/ulw-execute-continuation/test/boulder-reader.test.ts +51 -0
  37. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  38. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  39. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  40. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  41. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  42. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  60. package/packages/omo-codex/plugin/package-lock.json +12 -12
  61. package/packages/omo-codex/plugin/package.json +1 -1
  62. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  63. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +1 -1
  64. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +3 -0
  65. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +4 -0
  66. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/file_scanners.py +6 -2
  67. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +7 -1
  68. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/transcript.py +3 -1
  69. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/types.py +3 -1
  70. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +4 -0
  71. package/packages/omo-codex/scripts/install-dist/install-local.mjs +10465 -10270
  72. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  73. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +1 -1
  74. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +3 -0
  75. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +4 -0
  76. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/file_scanners.py +6 -2
  77. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +7 -1
  78. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/transcript.py +3 -1
  79. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/types.py +3 -1
  80. package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +4 -0
@@ -8,7 +8,7 @@
8
8
  "command": "node \"${PLUGIN_ROOT}/components/bootstrap/dist/cli.js\" hook session-start",
9
9
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\bootstrap.ps1\"",
10
10
  "timeout": 30,
11
- "statusMessage": "(OmO 5.0.1) Checking Bootstrap Provisioning"
11
+ "statusMessage": "(OmO 5.1.1) Checking Bootstrap Provisioning"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-bootstrap",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
4
4
  "description": "Codex SessionStart bootstrap component that provisions LazyCodex runtime dependencies from a detached worker.",
5
5
  "type": "module",
6
6
  "private": true,
@@ -1,6 +1,6 @@
1
1
  import { copyFile, mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
- import { readDefaultRoleConfig } from "../../../../src/install/codex-default-role-config.ts";
3
+ import { readCodexAgentConfig, unmanagedAgentOverrideWarnings } from "../../../../src/install/codex-agent-config.ts";
4
4
 
5
5
  // These relative imports resolve at BUILD time in the monorepo; esbuild
6
6
  // inlines the installer source modules into dist/cli.js so PLUGIN_ROOT ships
@@ -95,20 +95,23 @@ async function linkBundledAgentsStep(options: WorkerSetupOptions): Promise<Agent
95
95
  await stageBundledAgents(options.pluginRoot, stageRoot);
96
96
  const preservedReasoning = await capturePreservedAgentReasoning({ codexHome: options.codexHome });
97
97
  const preservedServiceTier = await capturePreservedAgentServiceTier({ codexHome: options.codexHome });
98
- const config = readDefaultRoleConfig({ env: options.env });
98
+ const config = readCodexAgentConfig({ env: options.env });
99
99
  const linked = await linkCachedPluginAgents({
100
100
  codexHome: options.codexHome,
101
101
  pluginRoot: stageRoot,
102
102
  preservedReasoning,
103
103
  preservedServiceTier,
104
- defaultRoleEnabled: config.enabled,
104
+ defaultRoleEnabled: config.defaultRoleEnabled,
105
+ agentOverrides: config.agentOverrides,
105
106
  });
107
+ const managedAgentNames = new Set(linked.map((link) => agentNameFromToml(link.name)));
108
+ const warnings = [...config.warnings, ...unmanagedAgentOverrideWarnings(config.agentOverrides, managedAgentNames)];
106
109
  const agentConfigs = linked
107
110
  .map((link) => ({ configFile: `./agents/${link.name}`, name: agentNameFromToml(link.name) }))
108
111
  .sort((left, right) => left.name.localeCompare(right.name));
109
112
  return {
110
113
  agentConfigs,
111
- degraded: config.warnings.map((reason) => ({ component: "agents-config", hint: BOOTSTRAP_DOCTOR_HINT, reason })),
114
+ degraded: warnings.map((reason) => ({ component: "agents-config", hint: BOOTSTRAP_DOCTOR_HINT, reason })),
112
115
  };
113
116
  } catch (error) {
114
117
  return {
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
10
10
  "timeout": 30,
11
- "statusMessage": "(OmO 5.0.1) Checking Comments"
11
+ "statusMessage": "(OmO 5.1.1) Checking Comments"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-comment-checker",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
4
4
  "description": "Codex plugin that runs comment-checker after edit-like PostToolUse hooks.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use",
10
10
  "timeout": 5,
11
- "statusMessage": "(OmO 5.0.1) Recommending Git Bash MCP"
11
+ "statusMessage": "(OmO 5.1.1) Recommending Git Bash MCP"
12
12
  }
13
13
  ]
14
14
  }
@@ -20,7 +20,7 @@
20
20
  "type": "command",
21
21
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
22
22
  "timeout": 5,
23
- "statusMessage": "(OmO 5.0.1) Resetting Git Bash MCP Reminder"
23
+ "statusMessage": "(OmO 5.1.1) Resetting Git Bash MCP Reminder"
24
24
  }
25
25
  ]
26
26
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-git-bash-hook",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
4
4
  "description": "Codex hook component that reminds Windows sessions to prefer the OMO git_bash MCP.",
5
5
  "type": "module",
6
6
  "private": true,
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/components/lazycodex-executor-verify/dist/cli.js\" hook subagent-stop",
10
10
  "timeout": 10,
11
- "statusMessage": "(OmO 5.0.1) Verifying LazyCodex Executor Evidence"
11
+ "statusMessage": "(OmO 5.1.1) Verifying LazyCodex Executor Evidence"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-lazycodex-executor-verify",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
4
4
  "description": "Codex SubagentStop evidence verifier for LazyCodex executor completions.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "5.0.1",
4
- "inputDigest": "sha256:88aa873d30cdfeb3fd564cb466008d972119d6c7c72176021c38a615b8069998",
3
+ "version": "5.1.1",
4
+ "inputDigest": "sha256:74ce38926eea0fd287b9c3d6ad38c42d0715b796e803790ad88daf464d1a0313",
5
5
  "outputs": [
6
6
  {
7
7
  "path": "cli.d.ts",
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
10
10
  "timeout": 60,
11
- "statusMessage": "(OmO 5.0.1) Checking LSP Diagnostics"
11
+ "statusMessage": "(OmO 5.1.1) Checking LSP Diagnostics"
12
12
  }
13
13
  ]
14
14
  }
@@ -21,7 +21,7 @@
21
21
  "type": "command",
22
22
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
23
23
  "timeout": 5,
24
- "statusMessage": "(OmO 5.0.1) Resetting LSP Diagnostics Cache"
24
+ "statusMessage": "(OmO 5.1.1) Resetting LSP Diagnostics Cache"
25
25
  }
26
26
  ]
27
27
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-lsp",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
4
4
  "description": "Codex plugin that exposes Language Server Protocol tools and post-edit diagnostics.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.1) Loading Project Rules"
10
+ "statusMessage": "(OmO 5.1.1) Loading Project Rules"
11
11
  }
12
12
  ]
13
13
  }
@@ -19,7 +19,7 @@
19
19
  "type": "command",
20
20
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
21
21
  "timeout": 10,
22
- "statusMessage": "(OmO 5.0.1) Loading Project Rules"
22
+ "statusMessage": "(OmO 5.1.1) Loading Project Rules"
23
23
  }
24
24
  ]
25
25
  }
@@ -32,7 +32,7 @@
32
32
  "type": "command",
33
33
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
34
34
  "timeout": 10,
35
- "statusMessage": "(OmO 5.0.1) Matching Project Rules"
35
+ "statusMessage": "(OmO 5.1.1) Matching Project Rules"
36
36
  }
37
37
  ]
38
38
  }
@@ -45,7 +45,7 @@
45
45
  "type": "command",
46
46
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
47
47
  "timeout": 10,
48
- "statusMessage": "(OmO 5.0.1) Resetting Project Rule Cache"
48
+ "statusMessage": "(OmO 5.1.1) Resetting Project Rule Cache"
49
49
  }
50
50
  ]
51
51
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-rules",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
4
4
  "description": "Codex plugin that injects project rule files into model context through lifecycle hooks.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -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 5.0.1) Checking Thread Title Hygiene"
11
+ "statusMessage": "(OmO 5.1.1) Checking Thread Title Hygiene"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-teammode",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
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 5.0.1) Recording Session Telemetry"
10
+ "statusMessage": "(OmO 5.1.1) Recording Session Telemetry"
11
11
  }
12
12
  ]
13
13
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-telemetry",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
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",
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 5.0.1) Checking Ultrawork Trigger"
10
+ "statusMessage": "(OmO 5.1.1) Checking Ultrawork Trigger"
11
11
  }
12
12
  ]
13
13
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-ultrawork",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
4
4
  "description": "Codex plugin that injects the ultrawork orchestration directive and ships LazyCodex planning, review, QA, and gate agent roles.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -32,7 +32,7 @@ Conventions for human contributors and AI agents working on this repository.
32
32
  ## Layout
33
33
 
34
34
  - `src/boulder-reader.ts`: reads `.omo/boulder.json`, resolves the active work for the session, re-exports `getPlanChecklist`/`PlanChecklist` from `plan-checklist.ts`. `readContinuationState` returns null in four cases: `boulder.json` is missing or unparseable, no work matches the session, the work status is not continuable (only `active` and `paused` continue - `completed`/`abandoned` stop), or `checklist.total === 0`.
35
- - `src/plan-checklist.ts`: `PlanChecklist` (`completed`/`remaining`/`total`/`nextTaskLabel`) and `getPlanChecklist`/`parsePlanChecklist`. Counts structured `## TODOs` rows (`N. <title>`) and `## Final Verification Wave` rows (`F<number>. <title>`); falls back to simple top-level `- [ ]`/`- [x]` checkboxes. Skips fenced blocks and respects `#`/`##` section boundaries.
35
+ - `src/plan-checklist.ts`: `PlanChecklist` (`completed`/`remaining`/`total`/`nextTaskLabel`) and `getPlanChecklist`/`parsePlanChecklist`. Mirrors `@oh-my-opencode/boulder-state`'s grammar: counts structured `## TODOs` rows (`N` / `T<n>[.<n>[a]]`) and `## Final Verification Wave` rows (`F<n>` / `H<n>`), each followed by `.`, space, `-` or em-dash; falls back to simple top-level checkboxes when there are no structured sections, or when they count nothing while checkboxes sit under another heading. `[~]` (blocked) counts toward `total` only, never `completed` or `remaining`. Skips fenced blocks and respects `#`/`##` section boundaries.
36
36
  - `src/codex-hook.ts`: Stop hook; fills nine placeholders into `directive.md` - `PLAN_NAME`, `PLAN_PATH`, `BOULDER_PATH`, `REMAINING_COUNT`, `TOTAL_COUNT`, `NEXT_TASK_LABEL`, `WORKTREE_BLOCK`, `LEDGER_PATH`, `SESSION_ID`. `WORKTREE_BLOCK` renders empty when the work has no worktree. When `remaining === 0`, `nextTaskLabel` is null and renders as "none (final gate pending)". SubagentStop is intentionally accepted by the CLI as a no-op for compatibility, but never injects a root plan.
37
37
  - **Context-pressure suppression:** the hook reads `input.transcript_path` through the injected `ReadonlyFileSystem` and returns `""` (no continuation) when the transcript carries any context-pressure marker (`context compacted`, `context_length_exceeded`, `context_too_large`, `codex ran out of room in the model's context window`, and related phrasings). This is the safety valve against an infinite continuation loop once the context window is exhausted; it is pinned by a `#given context-window pressure` test.
38
38
  - `directive.md`: directive template with placeholders, applied per invocation.
@@ -14,9 +14,10 @@ var TODO_HEADING_PATTERN = /^##[ \t]+TODOs(?:[ \t]+#+)?[ \t]*$/i;
14
14
  var FINAL_VERIFICATION_HEADING_PATTERN = /^##[ \t]+Final Verification Wave(?:[ \t]+#+)?[ \t]*$/i;
15
15
  var SECTION_BOUNDARY_HEADING_PATTERN = /^#{1,2}(?:[ \t]+|$)/;
16
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;
17
+ var SIMPLE_CHECKBOX_PATTERN = /^[-*][ \t]*\[[ \t]*([xX~]?)[ \t]*\][ \t]+(.+)$/;
18
+ var STRUCTURED_CHECKBOX_PATTERN = /^- \[([ xX~])\] (.+)$/;
19
+ var TODO_TASK_LABEL_PATTERN = /^([1-9]\d*|T[1-9]\d*(?:\.[1-9]\d*[a-z]?)?)(?:\.[ \t]+|[ \t]+(?:[-\u2014][ \t]+)?)(.+)$/i;
20
+ var FINAL_WAVE_TASK_LABEL_PATTERN = /^([FH][1-9]\d*(?:\.[1-9]\d*[a-z]?)?)(?:\.[ \t]+|[ \t]+(?:[-\u2014][ \t]+)?)(.+)$/i;
20
21
  function getPlanChecklist(planPath) {
21
22
  if (!existsSync(planPath))
22
23
  return emptyChecklist();
@@ -32,9 +33,8 @@ function parsePlanChecklist(markdown) {
32
33
  const lines = markdown.split(/\r?\n/);
33
34
  if (!hasStructuredSection(lines))
34
35
  return parseSimpleChecklist(lines);
35
- let completed = 0;
36
- let remaining = 0;
37
- let nextTaskLabel = null;
36
+ const counter = emptyCounter();
37
+ let hasUntrackedTopLevelCheckbox = false;
38
38
  let section = "other";
39
39
  let fence = null;
40
40
  for (const line of lines) {
@@ -52,19 +52,30 @@ function parsePlanChecklist(markdown) {
52
52
  section = parseStructuredSectionHeading(line);
53
53
  continue;
54
54
  }
55
- if (section === "other")
55
+ if (section === "other") {
56
+ if (parseSimpleTopLevelCheckbox(line) !== null)
57
+ hasUntrackedTopLevelCheckbox = true;
56
58
  continue;
57
- const checkbox = parseStructuredCheckbox(line, section);
58
- if (checkbox === null)
59
- continue;
60
- if (checkbox.checked)
61
- completed += 1;
62
- else {
63
- remaining += 1;
64
- nextTaskLabel = nextTaskLabel ?? checkbox.label;
65
59
  }
60
+ const checkbox = parseStructuredCheckbox(line, section);
61
+ if (checkbox !== null)
62
+ countCheckbox(counter, checkbox);
63
+ }
64
+ if (counter.total === 0 && hasUntrackedTopLevelCheckbox)
65
+ return parseSimpleChecklist(lines);
66
+ return { ...counter };
67
+ }
68
+ function emptyCounter() {
69
+ return { completed: 0, remaining: 0, total: 0, nextTaskLabel: null };
70
+ }
71
+ function countCheckbox(counter, checkbox) {
72
+ counter.total += 1;
73
+ if (checkbox.status === "done")
74
+ counter.completed += 1;
75
+ else if (checkbox.status === "open") {
76
+ counter.remaining += 1;
77
+ counter.nextTaskLabel = counter.nextTaskLabel ?? checkbox.label;
66
78
  }
67
- return { completed, remaining, total: completed + remaining, nextTaskLabel };
68
79
  }
69
80
  function hasStructuredSection(lines) {
70
81
  let fence = null;
@@ -85,9 +96,7 @@ function hasStructuredSection(lines) {
85
96
  return false;
86
97
  }
87
98
  function parseSimpleChecklist(lines) {
88
- let completed = 0;
89
- let remaining = 0;
90
- let nextTaskLabel = null;
99
+ const counter = emptyCounter();
91
100
  let fence = null;
92
101
  for (const line of lines) {
93
102
  if (fence !== null) {
@@ -101,16 +110,10 @@ function parseSimpleChecklist(lines) {
101
110
  continue;
102
111
  }
103
112
  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
- }
113
+ if (checkbox !== null)
114
+ countCheckbox(counter, checkbox);
112
115
  }
113
- return { completed, remaining, total: completed + remaining, nextTaskLabel };
116
+ return { ...counter };
114
117
  }
115
118
  function parseStructuredSectionHeading(line) {
116
119
  if (TODO_HEADING_PATTERN.test(line))
@@ -120,13 +123,15 @@ function parseStructuredSectionHeading(line) {
120
123
  return "other";
121
124
  }
122
125
  function parseStructuredCheckbox(line, section) {
123
- const pattern = section === "todo" ? TODO_CHECKBOX_PATTERN : FINAL_WAVE_CHECKBOX_PATTERN;
124
- const match = line.match(pattern);
126
+ const match = line.match(STRUCTURED_CHECKBOX_PATTERN);
125
127
  const marker = match?.[1];
126
128
  const label = match?.[2];
127
129
  if (marker === undefined || label === undefined)
128
130
  return null;
129
- return { checked: marker.toLowerCase() === "x", label };
131
+ const labelPattern = section === "todo" ? TODO_TASK_LABEL_PATTERN : FINAL_WAVE_TASK_LABEL_PATTERN;
132
+ if (!labelPattern.test(label))
133
+ return null;
134
+ return { status: parseCheckboxStatus(marker), label };
130
135
  }
131
136
  function parseSimpleTopLevelCheckbox(line) {
132
137
  const match = line.match(SIMPLE_CHECKBOX_PATTERN);
@@ -134,7 +139,12 @@ function parseSimpleTopLevelCheckbox(line) {
134
139
  const label = match?.[2];
135
140
  if (marker === undefined || label === undefined)
136
141
  return null;
137
- return { checked: marker.toLowerCase() === "x", label };
142
+ return { status: parseCheckboxStatus(marker), label };
143
+ }
144
+ function parseCheckboxStatus(marker) {
145
+ if (marker.toLowerCase() === "x")
146
+ return "done";
147
+ return marker === "~" ? "in-progress" : "open";
138
148
  }
139
149
  function parseOpeningFence(line) {
140
150
  const match = line.match(FENCE_PATTERN);
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/ulw-execute-continuation/dist/cli.js\" hook stop",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.1) Checking Ulw-Execute Continuation"
10
+ "statusMessage": "(OmO 5.1.1) Checking Ulw-Execute Continuation"
11
11
  }
12
12
  ]
13
13
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-ulw-execute-continuation",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
4
4
  "description": "Codex Stop hook continuation injector for omo-codex ulw-execute plans.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -11,17 +11,31 @@ const TODO_HEADING_PATTERN = /^##[ \t]+TODOs(?:[ \t]+#+)?[ \t]*$/i;
11
11
  const FINAL_VERIFICATION_HEADING_PATTERN = /^##[ \t]+Final Verification Wave(?:[ \t]+#+)?[ \t]*$/i;
12
12
  const SECTION_BOUNDARY_HEADING_PATTERN = /^#{1,2}(?:[ \t]+|$)/;
13
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;
14
+ const SIMPLE_CHECKBOX_PATTERN = /^[-*][ \t]*\[[ \t]*([xX~]?)[ \t]*\][ \t]+(.+)$/;
15
+ const STRUCTURED_CHECKBOX_PATTERN = /^- \[([ xX~])\] (.+)$/;
16
+ const TODO_TASK_LABEL_PATTERN =
17
+ /^([1-9]\d*|T[1-9]\d*(?:\.[1-9]\d*[a-z]?)?)(?:\.[ \t]+|[ \t]+(?:[-\u2014][ \t]+)?)(.+)$/i;
18
+ const FINAL_WAVE_TASK_LABEL_PATTERN =
19
+ /^([FH][1-9]\d*(?:\.[1-9]\d*[a-z]?)?)(?:\.[ \t]+|[ \t]+(?:[-\u2014][ \t]+)?)(.+)$/i;
17
20
 
18
21
  type ChecklistSection = "todo" | "final-wave" | "other";
19
22
 
23
+ // Mirrors @oh-my-opencode/boulder-state's plan grammar: `[~]` marks a blocked task that counts toward
24
+ // the total but is neither completed nor remaining work.
25
+ type CheckboxStatus = "open" | "done" | "in-progress";
26
+
20
27
  type ParsedCheckbox = {
21
- readonly checked: boolean;
28
+ readonly status: CheckboxStatus;
22
29
  readonly label: string;
23
30
  };
24
31
 
32
+ type ChecklistCounter = {
33
+ completed: number;
34
+ remaining: number;
35
+ total: number;
36
+ nextTaskLabel: string | null;
37
+ };
38
+
25
39
  type MarkdownFence = {
26
40
  readonly marker: "`" | "~";
27
41
  readonly length: number;
@@ -42,9 +56,8 @@ export function parsePlanChecklist(markdown: string): PlanChecklist {
42
56
  const lines = markdown.split(/\r?\n/);
43
57
  if (!hasStructuredSection(lines)) return parseSimpleChecklist(lines);
44
58
 
45
- let completed = 0;
46
- let remaining = 0;
47
- let nextTaskLabel: string | null = null;
59
+ const counter = emptyCounter();
60
+ let hasUntrackedTopLevelCheckbox = false;
48
61
  let section: ChecklistSection = "other";
49
62
  let fence: MarkdownFence | null = null;
50
63
 
@@ -63,18 +76,32 @@ export function parsePlanChecklist(markdown: string): PlanChecklist {
63
76
  section = parseStructuredSectionHeading(line);
64
77
  continue;
65
78
  }
66
- if (section === "other") continue;
79
+ if (section === "other") {
80
+ if (parseSimpleTopLevelCheckbox(line) !== null) hasUntrackedTopLevelCheckbox = true;
81
+ continue;
82
+ }
67
83
 
68
84
  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
- }
85
+ if (checkbox !== null) countCheckbox(counter, checkbox);
75
86
  }
76
87
 
77
- return { completed, remaining, total: completed + remaining, nextTaskLabel };
88
+ // Canonical headings that hold only template text while the real rows live under another heading
89
+ // would otherwise count nothing; fall back to every top-level checkbox in that case.
90
+ if (counter.total === 0 && hasUntrackedTopLevelCheckbox) return parseSimpleChecklist(lines);
91
+ return { ...counter };
92
+ }
93
+
94
+ function emptyCounter(): ChecklistCounter {
95
+ return { completed: 0, remaining: 0, total: 0, nextTaskLabel: null };
96
+ }
97
+
98
+ function countCheckbox(counter: ChecklistCounter, checkbox: ParsedCheckbox): void {
99
+ counter.total += 1;
100
+ if (checkbox.status === "done") counter.completed += 1;
101
+ else if (checkbox.status === "open") {
102
+ counter.remaining += 1;
103
+ counter.nextTaskLabel = counter.nextTaskLabel ?? checkbox.label;
104
+ }
78
105
  }
79
106
 
80
107
  function hasStructuredSection(lines: readonly string[]): boolean {
@@ -95,9 +122,7 @@ function hasStructuredSection(lines: readonly string[]): boolean {
95
122
  }
96
123
 
97
124
  function parseSimpleChecklist(lines: readonly string[]): PlanChecklist {
98
- let completed = 0;
99
- let remaining = 0;
100
- let nextTaskLabel: string | null = null;
125
+ const counter = emptyCounter();
101
126
  let fence: MarkdownFence | null = null;
102
127
 
103
128
  for (const line of lines) {
@@ -112,15 +137,10 @@ function parseSimpleChecklist(lines: readonly string[]): PlanChecklist {
112
137
  }
113
138
 
114
139
  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
- }
140
+ if (checkbox !== null) countCheckbox(counter, checkbox);
121
141
  }
122
142
 
123
- return { completed, remaining, total: completed + remaining, nextTaskLabel };
143
+ return { ...counter };
124
144
  }
125
145
 
126
146
  function parseStructuredSectionHeading(line: string): ChecklistSection {
@@ -130,12 +150,13 @@ function parseStructuredSectionHeading(line: string): ChecklistSection {
130
150
  }
131
151
 
132
152
  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);
153
+ const match = line.match(STRUCTURED_CHECKBOX_PATTERN);
135
154
  const marker = match?.[1];
136
155
  const label = match?.[2];
137
156
  if (marker === undefined || label === undefined) return null;
138
- return { checked: marker.toLowerCase() === "x", label };
157
+ const labelPattern = section === "todo" ? TODO_TASK_LABEL_PATTERN : FINAL_WAVE_TASK_LABEL_PATTERN;
158
+ if (!labelPattern.test(label)) return null;
159
+ return { status: parseCheckboxStatus(marker), label };
139
160
  }
140
161
 
141
162
  function parseSimpleTopLevelCheckbox(line: string): ParsedCheckbox | null {
@@ -143,7 +164,12 @@ function parseSimpleTopLevelCheckbox(line: string): ParsedCheckbox | null {
143
164
  const marker = match?.[1];
144
165
  const label = match?.[2];
145
166
  if (marker === undefined || label === undefined) return null;
146
- return { checked: marker.toLowerCase() === "x", label };
167
+ return { status: parseCheckboxStatus(marker), label };
168
+ }
169
+
170
+ function parseCheckboxStatus(marker: string): CheckboxStatus {
171
+ if (marker.toLowerCase() === "x") return "done";
172
+ return marker === "~" ? "in-progress" : "open";
147
173
  }
148
174
 
149
175
  function parseOpeningFence(line: string): MarkdownFence | null {
@@ -64,6 +64,57 @@ describe("ulw-execute plan checklist consumption", () => {
64
64
  expect(checklist).toEqual({ completed: 1, remaining: 2, total: 3, nextTaskLabel: "1. Build hook" });
65
65
  });
66
66
 
67
+ it("#given T, F and H task IDs and blocked rows #when parsed #then they count like the shared boulder-state parser", () => {
68
+ // given
69
+ const planPath = createPlan(
70
+ [
71
+ "## TODOs",
72
+ "- [x] T1.1 Completed implementation",
73
+ "- [~] T1.2 \u2014 Blocked on a user decision",
74
+ "- [ ] T6.3a Remaining implementation",
75
+ "## Final Verification Wave",
76
+ "- [ ] F1 \u2014 Plan compliance audit",
77
+ "- [x] H1 \u2014 User approval",
78
+ ].join("\n"),
79
+ );
80
+
81
+ // when
82
+ const checklist = getPlanChecklist(planPath);
83
+
84
+ // then
85
+ expect(checklist).toEqual({
86
+ completed: 2,
87
+ remaining: 2,
88
+ total: 5,
89
+ nextTaskLabel: "T6.3a Remaining implementation",
90
+ });
91
+ });
92
+
93
+ it("#given template-only counted sections and rows under another heading #when parsed #then top-level rows are counted", () => {
94
+ // given
95
+ const planPath = createPlan(
96
+ [
97
+ "## TODOs",
98
+ "Template task guidance.",
99
+ "## Progress Tracker",
100
+ "- [x] T1.1 \u2014 Schema",
101
+ "- [~] T5.1 \u2014 Verification engine \u2014 BLOCKED: user decision",
102
+ "- [ ] F5 \u2014 Delta verification",
103
+ ].join("\n"),
104
+ );
105
+
106
+ // when
107
+ const checklist = getPlanChecklist(planPath);
108
+
109
+ // then
110
+ expect(checklist).toEqual({
111
+ completed: 1,
112
+ remaining: 1,
113
+ total: 3,
114
+ nextTaskLabel: "F5 \u2014 Delta verification",
115
+ });
116
+ });
117
+
67
118
  it("#given all top-level tasks complete #when parsed #then next task is null", () => {
68
119
  // given
69
120
  const planPath = createPlan(