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.
- package/README.ja.md +2 -2
- package/README.ko.md +2 -2
- package/README.md +4 -4
- package/README.ru.md +2 -2
- package/README.zh-cn.md +2 -2
- package/dist/agents/atlas/agent.d.ts +6 -5
- package/dist/agents/sisyphus/index.d.ts +5 -0
- package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
- package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
- package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
- package/dist/agents/sisyphus-junior/index.d.ts +2 -0
- package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
- package/dist/agents/types.d.ts +2 -2
- package/dist/cli/index.js +648 -136
- package/dist/cli-node/index.js +648 -136
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schema/browser-automation.d.ts +1 -0
- package/dist/config/schema/commands.d.ts +1 -3
- package/dist/config/schema/default-mode.d.ts +1 -1
- package/dist/config/schema/goal.d.ts +7 -0
- package/dist/config/schema/hooks.d.ts +1 -1
- package/dist/config/schema/oh-my-opencode-config.d.ts +6 -10
- package/dist/config/schema/ralph-loop.d.ts +1 -1
- package/dist/config/schema.d.ts +1 -1
- package/dist/create-hooks.d.ts +1 -1
- package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
- package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
- package/dist/hooks/goal/command-arguments.d.ts +13 -0
- package/dist/hooks/goal/controller.d.ts +26 -0
- package/dist/hooks/goal/index.d.ts +23 -0
- package/dist/hooks/goal/prompt.d.ts +3 -0
- package/dist/hooks/goal/store.d.ts +8 -0
- package/dist/hooks/goal/tools.d.ts +10 -0
- package/dist/hooks/goal/types.d.ts +93 -0
- package/dist/hooks/goal/validation.d.ts +4 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/keyword-detector/hook.d.ts +1 -2
- package/dist/hooks/ralph-loop/types.d.ts +1 -1
- package/dist/index.js +3515 -4365
- package/dist/oh-my-opencode.schema.json +23 -19
- package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
- package/dist/plugin/chat-message/types.d.ts +23 -10
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
- package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
- package/dist/plugin/stop-continuation.d.ts +2 -2
- package/dist/shared/system-directive.d.ts +0 -1
- package/dist/skills/ulw-plan/SKILL.md +11 -7
- package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
- package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/dist/skills/ulw-research/SKILL.md +3 -3
- package/dist/tools/delegate-task/types.d.ts +0 -1
- package/dist/tui.js +73 -25
- package/package.json +14 -14
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +352 -17
- 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/codegraph/AGENTS.md +7 -6
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +97 -30
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
- 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/AGENTS.md +1 -0
- 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/start-work-continuation/AGENTS.md +8 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +147 -60
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +3 -71
- package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +198 -23
- package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +6 -5
- package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
- 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/AGENTS.md +10 -10
- package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
- 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/ultrawork/skills/ulw-plan/SKILL.md +11 -7
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- 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-codegraph-init-guidance.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/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-checking-codegraph-bootstrap.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-start-work-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-checking-start-work-continuation.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 +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +3 -18
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +11 -7
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +3 -18
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
- package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +353 -18
- package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -7
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
- package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
- package/dist/skills/ultraresearch/SKILL.md +0 -10
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
- package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
- package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
- /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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
|
38
|
+
let section = "other";
|
|
39
|
+
let fence = null;
|
|
53
40
|
for (const line of lines) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 (
|
|
51
|
+
if (SECTION_BOUNDARY_HEADING_PATTERN.test(line)) {
|
|
52
|
+
section = parseStructuredSectionHeading(line);
|
|
60
53
|
continue;
|
|
61
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
215
|
-
var START_WORK_CONTINUATION_DIRECTIVE =
|
|
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
|
|
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.
|
|
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.
|
|
22
|
+
"statusMessage": "(OmO 4.19.0) Checking Start-Work Continuation"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -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
|
-
|
|
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
|
+
}
|