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.
- package/README.ja.md +2 -1
- package/README.ko.md +2 -1
- package/README.md +2 -1
- package/README.ru.md +2 -1
- package/README.zh-cn.md +2 -1
- package/dist/cli/index.js +851 -546
- package/dist/cli-node/index.js +851 -546
- package/package.json +6 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +341 -157
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +7 -4
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/dist/cli.js +42 -32
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/src/plan-checklist.ts +55 -29
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/test/boulder-reader.test.ts +51 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +3 -0
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +4 -0
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/file_scanners.py +6 -2
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +7 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/transcript.py +3 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/types.py +3 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +4 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +10465 -10270
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +3 -0
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +4 -0
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/file_scanners.py +6 -2
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +7 -1
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/transcript.py +3 -1
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/types.py +3 -1
- 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.
|
|
11
|
+
"statusMessage": "(OmO 5.1.1) Checking Bootstrap Provisioning"
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
14
|
}
|
|
@@ -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 {
|
|
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 =
|
|
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.
|
|
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:
|
|
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 pre-tool-use",
|
|
10
10
|
"timeout": 5,
|
|
11
|
-
"statusMessage": "(OmO 5.
|
|
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.
|
|
23
|
+
"statusMessage": "(OmO 5.1.1) Resetting Git Bash MCP Reminder"
|
|
24
24
|
}
|
|
25
25
|
]
|
|
26
26
|
}
|
|
@@ -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.
|
|
11
|
+
"statusMessage": "(OmO 5.1.1) Verifying LazyCodex Executor Evidence"
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
14
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "5.
|
|
4
|
-
"inputDigest": "sha256:
|
|
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.
|
|
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.
|
|
24
|
+
"statusMessage": "(OmO 5.1.1) Resetting LSP Diagnostics Cache"
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
}
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-ultrawork",
|
|
3
|
-
"version": "5.
|
|
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`.
|
|
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
|
|
19
|
-
var
|
|
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
|
-
|
|
36
|
-
let
|
|
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
|
-
|
|
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
|
|
105
|
-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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.
|
|
10
|
+
"statusMessage": "(OmO 5.1.1) Checking Ulw-Execute Continuation"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -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
|
|
16
|
-
const
|
|
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
|
|
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
|
-
|
|
46
|
-
let
|
|
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")
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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 {
|
package/packages/omo-codex/plugin/components/ulw-execute-continuation/test/boulder-reader.test.ts
CHANGED
|
@@ -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(
|