oh-my-opencode 4.11.1 → 4.12.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/.agents/skills/codex-qa/scripts/lib/app-server-client.mjs +132 -82
- package/.agents/skills/codex-qa/scripts/lib/app-server-client.test.js +48 -0
- package/.agents/skills/opencode-qa/scripts/serve-wake-split-probe.sh +361 -84
- package/.agents/skills/work-with-pr/SKILL.md +19 -5
- package/.opencode/skills/work-with-pr/SKILL.md +19 -5
- package/dist/agents/types.d.ts +7 -6
- package/dist/cli/index.js +438 -306
- package/dist/cli-node/index.js +438 -306
- package/dist/features/background-agent/parent-wake-flush-runner.d.ts +1 -0
- package/dist/features/background-agent/parent-wake-history-state.d.ts +1 -0
- package/dist/features/background-agent/parent-wake-session-history.d.ts +1 -47
- package/dist/features/background-agent/parent-wake-session-message.d.ts +47 -0
- package/dist/index.js +245 -69
- package/dist/plugin/tool-execute-before.d.ts +2 -0
- package/dist/plugin-handlers/plugin-components-loader.d.ts +1 -0
- package/dist/skills/ulw-plan/SKILL.md +2 -1
- package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
- package/dist/skills/ulw-plan/references/intent-clear.md +2 -2
- package/dist/tools/delegate-task/sync-session-poller.d.ts +1 -2
- package/dist/tools/delegate-task/sync-session-turns.d.ts +3 -0
- package/dist/tui.js +16 -7
- package/package.json +13 -13
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +22 -2
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +25 -9
- 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/dist/cli.js +10 -3
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +9 -2
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +7 -2
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +2 -1
- package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +9 -9
- package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +94 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-node-support.test.ts +33 -0
- 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/comment-checker/test/package-smoke.test.ts +2 -71
- 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/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +26 -104
- 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/rules/test/package-smoke.test.ts +12 -92
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +157 -234
- 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 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +227 -15
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +62 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +34 -3
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +139 -0
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +111 -0
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +250 -0
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +220 -0
- 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/test-support/package-smoke-fixture.ts +158 -0
- 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 +2 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +76 -49
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +17 -0
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +17 -0
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +17 -0
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +17 -0
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +17 -0
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +16 -0
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +16 -0
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +16 -0
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +16 -0
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +16 -0
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +17 -0
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +16 -0
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +16 -0
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +16 -0
- package/packages/omo-codex/plugin/package-lock.json +12 -22
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +14 -11
- package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +15 -4
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +37 -0
- package/packages/omo-codex/plugin/skills/ast-grep/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/comment-checker/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/debugging/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/frontend/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/git-master/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/init-deep/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lsp/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/lsp-setup/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/programming/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/refactor/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/remove-ai-slops/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/review-work/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/rules/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/start-work/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/teammode/SKILL.md +139 -0
- package/packages/omo-codex/plugin/skills/teammode/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +111 -0
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +250 -0
- package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +220 -0
- package/packages/omo-codex/plugin/skills/ultraresearch/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/ulw-loop/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +2 -2
- package/packages/omo-codex/plugin/skills/visual-qa/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +34 -33
- package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +5 -2
- package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +13 -0
- package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +1 -1
- package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +14 -6
- package/packages/omo-codex/plugin/test/bootstrap-ps-guard.test.mjs +7 -3
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/display-metadata.test.mjs +83 -0
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +30 -19
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -0
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +9 -4
- package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +88 -0
- package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +240 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +385 -206
- package/packages/omo-codex/scripts/install-hook-targets.test.mjs +93 -0
- package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +2 -2
- package/packages/omo-codex/scripts/install-local-bun-global-update.test.mjs +63 -0
- package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +109 -1
- package/packages/omo-codex/scripts/install-local.mjs +1 -0
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +2 -1
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +2 -2
- package/packages/omo-codex/plugin/hooks/hooks.json +0 -214
|
@@ -1,295 +1,218 @@
|
|
|
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 readFileSync3 } 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 { join as join2 } from "node:path";
|
|
9
|
-
|
|
10
|
-
// ../../boulder-state/src/constants.ts
|
|
11
|
-
var BOULDER_DIR = ".omo";
|
|
12
|
-
var BOULDER_FILE = "boulder.json";
|
|
13
|
-
var BOULDER_STATE_PATH = `${BOULDER_DIR}/${BOULDER_FILE}`;
|
|
14
|
-
var NOTEPAD_DIR = "notepads";
|
|
15
|
-
var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
|
|
16
|
-
// ../../boulder-state/src/plan-checklist.ts
|
|
17
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
18
|
-
|
|
19
|
-
var UNCHECKED_PATTERN = /^- \[ \] /;
|
|
9
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
20
10
|
var TODO_HEADING = "TODOs";
|
|
21
11
|
var FINAL_VERIFICATION_HEADING = "Final Verification Wave";
|
|
12
|
+
var CHECKBOX_PREFIX_LENGTH = "- [ ] ".length;
|
|
13
|
+
var SESSION_ID_PREFIX_PATTERN = /^(codex|opencode):/;
|
|
14
|
+
function readContinuationState(cwd, sessionId) {
|
|
15
|
+
const boulderPath = getBoulderFilePath(cwd);
|
|
16
|
+
const boulderState = readBoulderState(boulderPath);
|
|
17
|
+
if (boulderState === null)
|
|
18
|
+
return null;
|
|
19
|
+
const work = getWorkForSession(boulderState, normalizeSessionId(sessionId, "codex"));
|
|
20
|
+
if (work === null || !isContinuableStatus(work.status))
|
|
21
|
+
return null;
|
|
22
|
+
const planPath = resolveBoulderPlanPathForWork(cwd, work);
|
|
23
|
+
const checklist = getPlanChecklist(planPath);
|
|
24
|
+
if (checklist.remaining === 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
|
+
}
|
|
22
35
|
function getPlanChecklist(planPath) {
|
|
23
|
-
if (!existsSync(planPath))
|
|
36
|
+
if (!existsSync(planPath))
|
|
24
37
|
return emptyChecklist();
|
|
25
|
-
}
|
|
26
38
|
try {
|
|
27
|
-
return parsePlanChecklist(readFileSync(planPath, "
|
|
39
|
+
return parsePlanChecklist(readFileSync(planPath, "utf8"));
|
|
28
40
|
} catch (error) {
|
|
29
|
-
if (error instanceof Error)
|
|
41
|
+
if (error instanceof Error)
|
|
30
42
|
return emptyChecklist();
|
|
31
|
-
}
|
|
32
43
|
throw error;
|
|
33
44
|
}
|
|
34
45
|
}
|
|
35
46
|
function parsePlanChecklist(markdown) {
|
|
36
47
|
const lines = markdown.split(/\r?\n/);
|
|
37
|
-
const hasCountedSections = lines.some(
|
|
48
|
+
const hasCountedSections = lines.some((line) => isCountedHeading(parseLevelTwoHeading(line)));
|
|
49
|
+
let completed = 0;
|
|
38
50
|
let remaining = 0;
|
|
39
|
-
let total = 0;
|
|
40
51
|
let nextTaskLabel = null;
|
|
41
52
|
let isCountedSection = !hasCountedSections;
|
|
42
53
|
for (const line of lines) {
|
|
43
54
|
const heading = parseLevelTwoHeading(line);
|
|
44
55
|
if (heading !== null) {
|
|
45
56
|
isCountedSection = isCountedHeading(heading);
|
|
46
|
-
}
|
|
47
|
-
if (!isCountedSection || !CHECKBOX_PATTERN.test(line)) {
|
|
48
57
|
continue;
|
|
49
58
|
}
|
|
50
|
-
|
|
51
|
-
if (!UNCHECKED_PATTERN.test(line)) {
|
|
59
|
+
if (!isCountedSection)
|
|
52
60
|
continue;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
const checkbox = parseTopLevelCheckbox(line);
|
|
62
|
+
if (checkbox === null)
|
|
63
|
+
continue;
|
|
64
|
+
if (checkbox.checked) {
|
|
65
|
+
completed += 1;
|
|
66
|
+
} else {
|
|
67
|
+
remaining += 1;
|
|
68
|
+
nextTaskLabel = nextTaskLabel ?? checkbox.label;
|
|
57
69
|
}
|
|
58
70
|
}
|
|
59
|
-
return {
|
|
60
|
-
completed: total - remaining,
|
|
61
|
-
remaining,
|
|
62
|
-
total,
|
|
63
|
-
nextTaskLabel
|
|
64
|
-
};
|
|
71
|
+
return { completed, remaining, total: completed + remaining, nextTaskLabel };
|
|
65
72
|
}
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return line.slice("## ".length).trim();
|
|
75
|
-
}
|
|
76
|
-
function isCountedHeading(heading) {
|
|
77
|
-
return heading === TODO_HEADING || heading === FINAL_VERIFICATION_HEADING;
|
|
78
|
-
}
|
|
79
|
-
function emptyChecklist() {
|
|
80
|
-
return { completed: 0, remaining: 0, total: 0, nextTaskLabel: null };
|
|
81
|
-
}
|
|
82
|
-
// ../../boulder-state/src/storage/path.ts
|
|
83
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
84
|
-
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
85
|
-
function getBoulderFilePath(directory) {
|
|
86
|
-
return join(directory, BOULDER_DIR, BOULDER_FILE);
|
|
87
|
-
}
|
|
88
|
-
function resolveTrackedPath(baseDirectory, trackedPath) {
|
|
89
|
-
return isAbsolute(trackedPath) ? resolve(trackedPath) : resolve(baseDirectory, trackedPath);
|
|
90
|
-
}
|
|
91
|
-
function resolveBoulderPlanPath(directory, state) {
|
|
92
|
-
const absolutePlanPath = resolveTrackedPath(directory, state.active_plan);
|
|
93
|
-
const worktreePath = state.worktree_path?.trim();
|
|
94
|
-
if (!worktreePath) {
|
|
95
|
-
return absolutePlanPath;
|
|
96
|
-
}
|
|
97
|
-
const absoluteDirectory = resolve(directory);
|
|
98
|
-
const relativePlanPath = relative(absoluteDirectory, absolutePlanPath);
|
|
99
|
-
if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute(relativePlanPath)) {
|
|
100
|
-
return absolutePlanPath;
|
|
101
|
-
}
|
|
102
|
-
const absoluteWorktreePath = resolveTrackedPath(directory, worktreePath);
|
|
103
|
-
const worktreePlanPath = resolve(absoluteWorktreePath, relativePlanPath);
|
|
104
|
-
return existsSync2(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
|
|
105
|
-
}
|
|
106
|
-
function resolveBoulderPlanPathForWork(directory, work) {
|
|
107
|
-
return resolveBoulderPlanPath(directory, work);
|
|
108
|
-
}
|
|
109
|
-
// ../../boulder-state/src/storage/shared.ts
|
|
110
|
-
var RESERVED_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
111
|
-
var SESSION_ID_PREFIX_PATTERN = /^(codex|opencode):/;
|
|
112
|
-
function normalizeSessionId(sessionId, platform = "opencode") {
|
|
113
|
-
if (SESSION_ID_PREFIX_PATTERN.test(sessionId)) {
|
|
114
|
-
return sessionId;
|
|
73
|
+
function readBoulderState(path) {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
76
|
+
return parseBoulderState(parsed);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error instanceof Error)
|
|
79
|
+
return null;
|
|
80
|
+
throw error;
|
|
115
81
|
}
|
|
116
|
-
return `${platform}:${sessionId}`;
|
|
117
82
|
}
|
|
118
|
-
function
|
|
119
|
-
if (!value)
|
|
83
|
+
function parseBoulderState(value) {
|
|
84
|
+
if (!isRecord(value))
|
|
120
85
|
return null;
|
|
86
|
+
const works = [];
|
|
87
|
+
const worksValue = value["works"];
|
|
88
|
+
const hasWorksMap = isRecord(worksValue);
|
|
89
|
+
if (hasWorksMap) {
|
|
90
|
+
for (const workValue of Object.values(worksValue)) {
|
|
91
|
+
const work = parseBoulderWork(workValue);
|
|
92
|
+
if (work !== null)
|
|
93
|
+
works.push(work);
|
|
94
|
+
}
|
|
121
95
|
}
|
|
122
|
-
const
|
|
123
|
-
|
|
96
|
+
const mirrorWork = parseBoulderWork(value);
|
|
97
|
+
if (works.length === 0 && mirrorWork === null)
|
|
98
|
+
return null;
|
|
99
|
+
return { works, mirrorWork, hasWorksMap };
|
|
124
100
|
}
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
101
|
+
function parseBoulderWork(value) {
|
|
102
|
+
if (!isRecord(value))
|
|
103
|
+
return null;
|
|
104
|
+
const activePlan = value["active_plan"];
|
|
105
|
+
const planName = value["plan_name"];
|
|
106
|
+
if (typeof activePlan !== "string")
|
|
107
|
+
return null;
|
|
108
|
+
const status = parseBoulderWorkStatus(value["status"]);
|
|
109
|
+
const sessionIds = parseSessionIds(value["session_ids"]);
|
|
110
|
+
const worktreePath = value["worktree_path"];
|
|
111
|
+
const startedAt = value["started_at"];
|
|
112
|
+
const updatedAt = value["updated_at"];
|
|
128
113
|
return {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
status:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
updated_at: state.updated_at,
|
|
137
|
-
session_ids: Array.isArray(state.session_ids) ? [...state.session_ids] : [],
|
|
138
|
-
session_origins: state.session_origins,
|
|
139
|
-
agent: state.agent,
|
|
140
|
-
worktree_path: state.worktree_path,
|
|
141
|
-
task_sessions: state.task_sessions
|
|
114
|
+
activePlan,
|
|
115
|
+
planName: typeof planName === "string" ? planName : activePlan,
|
|
116
|
+
sessionIds,
|
|
117
|
+
...status === undefined ? {} : { status },
|
|
118
|
+
...typeof startedAt === "string" ? { startedAt } : {},
|
|
119
|
+
...typeof updatedAt === "string" ? { updatedAt } : {},
|
|
120
|
+
...typeof worktreePath === "string" ? { worktreePath } : {}
|
|
142
121
|
};
|
|
143
122
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
state.agent = work.agent;
|
|
155
|
-
state.worktree_path = work.worktree_path;
|
|
156
|
-
state.task_sessions = work.task_sessions ? { ...work.task_sessions } : {};
|
|
157
|
-
}
|
|
158
|
-
function selectMirrorWork(state) {
|
|
159
|
-
const works = state.works ? Object.values(state.works) : [];
|
|
160
|
-
if (works.length === 0) {
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
if (state.active_work_id) {
|
|
164
|
-
const matched = works.find((work) => work.work_id === state.active_work_id);
|
|
165
|
-
if (matched) {
|
|
166
|
-
return matched;
|
|
123
|
+
function getWorkForSession(state, normalizedSessionId) {
|
|
124
|
+
let newestWork = null;
|
|
125
|
+
let newestWorkMs = 0;
|
|
126
|
+
for (const work of state.works) {
|
|
127
|
+
if (!work.sessionIds.includes(normalizedSessionId))
|
|
128
|
+
continue;
|
|
129
|
+
const workMs = parseIsoToMs(work.updatedAt ?? work.startedAt) ?? 0;
|
|
130
|
+
if (newestWork === null || workMs > newestWorkMs) {
|
|
131
|
+
newestWork = work;
|
|
132
|
+
newestWorkMs = workMs;
|
|
167
133
|
}
|
|
168
134
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return rightMs - leftMs;
|
|
173
|
-
});
|
|
174
|
-
return sorted[0] ?? null;
|
|
175
|
-
}
|
|
176
|
-
// ../../boulder-state/src/storage/read-state.ts
|
|
177
|
-
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
178
|
-
function readBoulderState(directory) {
|
|
179
|
-
const filePath = getBoulderFilePath(directory);
|
|
180
|
-
if (!existsSync3(filePath)) {
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
try {
|
|
184
|
-
const content = readFileSync2(filePath, "utf-8");
|
|
185
|
-
const parsed = JSON.parse(content);
|
|
186
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
normalizeState(parsed);
|
|
190
|
-
const state = parsed;
|
|
191
|
-
const mirrorWork = selectMirrorWork(state);
|
|
192
|
-
if (mirrorWork) {
|
|
193
|
-
state.active_work_id = mirrorWork.work_id;
|
|
194
|
-
projectWorkToMirror(state, mirrorWork);
|
|
195
|
-
}
|
|
196
|
-
return state;
|
|
197
|
-
} catch {
|
|
135
|
+
if (newestWork !== null)
|
|
136
|
+
return newestWork;
|
|
137
|
+
if (state.hasWorksMap)
|
|
198
138
|
return null;
|
|
139
|
+
if (state.mirrorWork?.sessionIds.includes(normalizedSessionId) === true)
|
|
140
|
+
return state.mirrorWork;
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
function resolveBoulderPlanPathForWork(cwd, work) {
|
|
144
|
+
const absolutePlanPath = resolveTrackedPath(cwd, work.activePlan);
|
|
145
|
+
const worktreePath = work.worktreePath?.trim();
|
|
146
|
+
if (worktreePath === undefined || worktreePath.length === 0)
|
|
147
|
+
return absolutePlanPath;
|
|
148
|
+
const relativePlanPath = relative(resolve(cwd), absolutePlanPath);
|
|
149
|
+
if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute(relativePlanPath)) {
|
|
150
|
+
return absolutePlanPath;
|
|
199
151
|
}
|
|
152
|
+
const worktreePlanPath = resolve(resolveTrackedPath(cwd, worktreePath), relativePlanPath);
|
|
153
|
+
return existsSync(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
|
|
200
154
|
}
|
|
201
|
-
function
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
sessionOrigins[soleSessionId] = "direct";
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (!state.task_sessions || typeof state.task_sessions !== "object" || Array.isArray(state.task_sessions)) {
|
|
213
|
-
state.task_sessions = {};
|
|
155
|
+
function resolveTrackedPath(baseDirectory, trackedPath) {
|
|
156
|
+
return isAbsolute(trackedPath) ? resolve(trackedPath) : resolve(baseDirectory, trackedPath);
|
|
157
|
+
}
|
|
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) };
|
|
214
163
|
}
|
|
215
|
-
|
|
164
|
+
return null;
|
|
216
165
|
}
|
|
217
|
-
function
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
target.session_origins = sessionOrigins;
|
|
166
|
+
function parseLevelTwoHeading(line) {
|
|
167
|
+
if (!line.startsWith("## "))
|
|
168
|
+
return null;
|
|
169
|
+
return line.slice("## ".length).trim();
|
|
222
170
|
}
|
|
223
|
-
function
|
|
224
|
-
return
|
|
171
|
+
function isCountedHeading(heading) {
|
|
172
|
+
return heading === TODO_HEADING || heading === FINAL_VERIFICATION_HEADING;
|
|
225
173
|
}
|
|
226
|
-
function
|
|
227
|
-
if (
|
|
228
|
-
return;
|
|
229
|
-
|
|
230
|
-
for (const work of Object.values(works)) {
|
|
231
|
-
if (work && typeof work === "object" && !Array.isArray(work)) {
|
|
232
|
-
normalizeSessionFields(work);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
174
|
+
function parseBoulderWorkStatus(value) {
|
|
175
|
+
if (value === "active" || value === "paused" || value === "completed" || value === "abandoned")
|
|
176
|
+
return value;
|
|
177
|
+
return;
|
|
235
178
|
}
|
|
236
|
-
function
|
|
237
|
-
if (
|
|
238
|
-
return Object.values(state.works);
|
|
239
|
-
}
|
|
240
|
-
if (!state.active_plan || !state.plan_name || !state.started_at) {
|
|
179
|
+
function parseSessionIds(value) {
|
|
180
|
+
if (!Array.isArray(value))
|
|
241
181
|
return [];
|
|
182
|
+
const sessionIds = [];
|
|
183
|
+
for (const item of value) {
|
|
184
|
+
if (typeof item === "string")
|
|
185
|
+
sessionIds.push(normalizeSessionId(item));
|
|
242
186
|
}
|
|
243
|
-
return
|
|
187
|
+
return sessionIds;
|
|
244
188
|
}
|
|
245
|
-
function
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
251
|
-
let newestWork = null;
|
|
252
|
-
let newestWorkMs = 0;
|
|
253
|
-
for (const work of getBoulderWorks(state)) {
|
|
254
|
-
if (!work.session_ids.includes(normalizedSessionId)) {
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
const workMs = parseIsoToMs(work.updated_at ?? work.started_at) ?? 0;
|
|
258
|
-
if (!newestWork || workMs > newestWorkMs) {
|
|
259
|
-
newestWork = work;
|
|
260
|
-
newestWorkMs = workMs;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
if (newestWork) {
|
|
264
|
-
return newestWork;
|
|
265
|
-
}
|
|
266
|
-
return state.session_ids.includes(normalizedSessionId) ? buildWorkFromMirror(state) : null;
|
|
189
|
+
function normalizeSessionId(sessionId, platform = "opencode") {
|
|
190
|
+
if (SESSION_ID_PREFIX_PATTERN.test(sessionId))
|
|
191
|
+
return sessionId;
|
|
192
|
+
return `${platform}:${sessionId}`;
|
|
267
193
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const work = getWorkForSession(cwd, normalizeSessionId(sessionId, "codex"));
|
|
271
|
-
if (work === null || !isContinuableStatus(work.status))
|
|
272
|
-
return null;
|
|
273
|
-
const planPath = resolveBoulderPlanPathForWork(cwd, work);
|
|
274
|
-
const checklist = getPlanChecklist(planPath);
|
|
275
|
-
if (checklist.remaining === 0)
|
|
194
|
+
function parseIsoToMs(value) {
|
|
195
|
+
if (value === undefined)
|
|
276
196
|
return null;
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
planPath,
|
|
280
|
-
boulderPath: getBoulderFilePath(cwd),
|
|
281
|
-
ledgerPath: join2(cwd, ".omo", "start-work", "ledger.jsonl"),
|
|
282
|
-
worktreePath: work.worktree_path ?? null,
|
|
283
|
-
checklist
|
|
284
|
-
};
|
|
197
|
+
const parsed = Date.parse(value);
|
|
198
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
285
199
|
}
|
|
286
200
|
function isContinuableStatus(status) {
|
|
287
201
|
return status === "active" || status === "paused";
|
|
288
202
|
}
|
|
203
|
+
function getBoulderFilePath(cwd) {
|
|
204
|
+
return join(cwd, ".omo", "boulder.json");
|
|
205
|
+
}
|
|
206
|
+
function emptyChecklist() {
|
|
207
|
+
return { completed: 0, remaining: 0, total: 0, nextTaskLabel: null };
|
|
208
|
+
}
|
|
209
|
+
function isRecord(value) {
|
|
210
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
211
|
+
}
|
|
289
212
|
|
|
290
213
|
// components/start-work-continuation/src/directive.ts
|
|
291
|
-
import { readFileSync as
|
|
292
|
-
var START_WORK_CONTINUATION_DIRECTIVE =
|
|
214
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
215
|
+
var START_WORK_CONTINUATION_DIRECTIVE = readFileSync2(new URL("../directive.md", import.meta.url), "utf8");
|
|
293
216
|
|
|
294
217
|
// components/start-work-continuation/src/codex-hook.ts
|
|
295
218
|
function runStopHook(input, fs) {
|
|
@@ -347,7 +270,7 @@ function transcriptHasContextPressureMarker(transcriptPath, fs) {
|
|
|
347
270
|
}
|
|
348
271
|
}
|
|
349
272
|
function isStopInput(value) {
|
|
350
|
-
return
|
|
273
|
+
return isRecord2(value) && isStopHookEventName(value["hook_event_name"]) && typeof value["session_id"] === "string" && typeof value["turn_id"] === "string" && typeof value["transcript_path"] === "string" && typeof value["cwd"] === "string" && typeof value["model"] === "string" && typeof value["permission_mode"] === "string" && typeof value["stop_hook_active"] === "boolean" && optionalString(value["last_assistant_message"]);
|
|
351
274
|
}
|
|
352
275
|
function isStopHookEventName(value) {
|
|
353
276
|
return value === "Stop" || value === "SubagentStop";
|
|
@@ -355,14 +278,14 @@ function isStopHookEventName(value) {
|
|
|
355
278
|
function optionalString(value) {
|
|
356
279
|
return value === undefined || typeof value === "string";
|
|
357
280
|
}
|
|
358
|
-
function
|
|
281
|
+
function isRecord2(value) {
|
|
359
282
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
360
283
|
}
|
|
361
284
|
|
|
362
285
|
// components/start-work-continuation/src/cli.ts
|
|
363
286
|
var nodeFileSystem = {
|
|
364
287
|
readFileSync(path, encoding) {
|
|
365
|
-
return
|
|
288
|
+
return readFileSync3(path, encoding);
|
|
366
289
|
}
|
|
367
290
|
};
|
|
368
291
|
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": "
|
|
10
|
+
"statusMessage": "(OmO) 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": "
|
|
22
|
+
"statusMessage": "(OmO) Checking Start-Work Continuation"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/codex-start-work-continuation",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.12.0",
|
|
4
4
|
"description": "Codex Stop hook continuation injector for omo-codex start-work plans.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@11.12.1",
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
"NOTICE"
|
|
43
43
|
],
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@oh-my-opencode/boulder-state": "file:../../../../boulder-state",
|
|
46
45
|
"@biomejs/biome": "2.4.16",
|
|
47
46
|
"@types/node": "^25.9.3",
|
|
48
47
|
"typescript": "^6.0.3",
|