oh-my-opencode 4.11.0 → 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.
Files changed (177) hide show
  1. package/.agents/skills/codex-qa/scripts/lib/app-server-client.mjs +132 -82
  2. package/.agents/skills/codex-qa/scripts/lib/app-server-client.test.js +48 -0
  3. package/.agents/skills/opencode-qa/scripts/serve-wake-split-probe.sh +361 -84
  4. package/.agents/skills/work-with-pr/SKILL.md +19 -5
  5. package/.opencode/skills/work-with-pr/SKILL.md +19 -5
  6. package/dist/agents/atlas/agent.d.ts +3 -2
  7. package/dist/agents/sisyphus/glm-5-2.d.ts +4 -0
  8. package/dist/agents/sisyphus/index.d.ts +1 -0
  9. package/dist/agents/sisyphus-agent-config.d.ts +1 -0
  10. package/dist/agents/sisyphus-junior/agent.d.ts +1 -1
  11. package/dist/agents/sisyphus-junior/glm-5-2.d.ts +1 -0
  12. package/dist/agents/sisyphus-junior/index.d.ts +1 -0
  13. package/dist/agents/types.d.ts +7 -6
  14. package/dist/cli/index.js +449 -318
  15. package/dist/cli-node/index.js +449 -318
  16. package/dist/features/background-agent/parent-wake-flush-runner.d.ts +1 -0
  17. package/dist/features/background-agent/parent-wake-history-state.d.ts +1 -0
  18. package/dist/features/background-agent/parent-wake-session-history.d.ts +1 -47
  19. package/dist/features/background-agent/parent-wake-session-message.d.ts +47 -0
  20. package/dist/hooks/keyword-detector/ultrawork/glm.d.ts +2 -0
  21. package/dist/hooks/keyword-detector/ultrawork/index.d.ts +4 -2
  22. package/dist/hooks/keyword-detector/ultrawork/source-detector.d.ts +5 -4
  23. package/dist/index.js +1185 -87
  24. package/dist/plugin/tool-execute-before.d.ts +2 -0
  25. package/dist/plugin-handlers/plugin-components-loader.d.ts +1 -0
  26. package/dist/skills/init-deep/SKILL.md +21 -26
  27. package/dist/skills/programming/SKILL.md +25 -121
  28. package/dist/skills/programming/references/code-smells.md +390 -0
  29. package/dist/skills/ulw-plan/SKILL.md +2 -1
  30. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  31. package/dist/skills/ulw-plan/references/intent-clear.md +2 -2
  32. package/dist/tools/delegate-task/sync-session-poller.d.ts +1 -2
  33. package/dist/tools/delegate-task/sync-session-turns.d.ts +3 -0
  34. package/dist/tui.js +16 -7
  35. package/package.json +13 -13
  36. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +22 -2
  37. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +51 -21
  38. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  39. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +18 -7
  41. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +9 -2
  42. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +8 -7
  44. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +7 -2
  45. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +2 -1
  46. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +20 -19
  47. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +94 -0
  48. package/packages/omo-codex/plugin/components/codegraph/test/serve-node-support.test.ts +33 -0
  49. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  50. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/comment-checker/test/package-smoke.test.ts +2 -71
  52. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  53. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  54. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  55. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  56. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  57. package/packages/omo-codex/plugin/components/lsp/package.json +2 -2
  58. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +26 -104
  59. package/packages/omo-codex/plugin/components/rules/dist/cli.js +2 -2
  60. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  61. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  62. package/packages/omo-codex/plugin/components/rules/src/sparkshell-awareness.ts +2 -2
  63. package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +12 -92
  64. package/packages/omo-codex/plugin/components/rules/test/sparkshell-awareness.test.ts +3 -0
  65. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +157 -234
  66. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  67. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -2
  68. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +227 -15
  69. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +62 -2
  70. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +34 -3
  71. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +139 -0
  72. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +111 -0
  73. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +250 -0
  74. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +220 -0
  75. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  76. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  77. package/packages/omo-codex/plugin/components/test-support/package-smoke-fixture.ts +158 -0
  78. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  79. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  80. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -1
  81. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/agents/openai.yaml +1 -1
  82. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
  83. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +2 -2
  84. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +76 -49
  85. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  86. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  87. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/agents/openai.yaml +1 -1
  88. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +1 -1
  89. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +17 -0
  90. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +17 -0
  91. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +17 -0
  92. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +17 -0
  93. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +17 -0
  94. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +17 -0
  95. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +17 -0
  96. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +17 -0
  97. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +17 -0
  98. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +17 -0
  99. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +16 -0
  100. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +16 -0
  101. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +16 -0
  102. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +16 -0
  103. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +16 -0
  104. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +17 -0
  105. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +16 -0
  106. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +16 -0
  107. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +16 -0
  108. package/packages/omo-codex/plugin/package-lock.json +12 -22
  109. package/packages/omo-codex/plugin/package.json +1 -1
  110. package/packages/omo-codex/plugin/scripts/auto-update.mjs +1 -1
  111. package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +14 -11
  112. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +15 -4
  113. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +37 -0
  114. package/packages/omo-codex/plugin/skills/ast-grep/agents/openai.yaml +2 -0
  115. package/packages/omo-codex/plugin/skills/comment-checker/agents/openai.yaml +2 -0
  116. package/packages/omo-codex/plugin/skills/debugging/agents/openai.yaml +2 -0
  117. package/packages/omo-codex/plugin/skills/frontend/agents/openai.yaml +2 -0
  118. package/packages/omo-codex/plugin/skills/git-master/agents/openai.yaml +1 -1
  119. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +21 -26
  120. package/packages/omo-codex/plugin/skills/init-deep/agents/openai.yaml +2 -0
  121. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  122. package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
  123. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  124. package/packages/omo-codex/plugin/skills/lsp/agents/openai.yaml +2 -0
  125. package/packages/omo-codex/plugin/skills/lsp-setup/agents/openai.yaml +2 -0
  126. package/packages/omo-codex/plugin/skills/programming/SKILL.md +25 -121
  127. package/packages/omo-codex/plugin/skills/programming/agents/openai.yaml +2 -0
  128. package/packages/omo-codex/plugin/skills/programming/references/code-smells.md +390 -0
  129. package/packages/omo-codex/plugin/skills/refactor/agents/openai.yaml +2 -0
  130. package/packages/omo-codex/plugin/skills/remove-ai-slops/agents/openai.yaml +2 -0
  131. package/packages/omo-codex/plugin/skills/review-work/agents/openai.yaml +2 -0
  132. package/packages/omo-codex/plugin/skills/rules/agents/openai.yaml +2 -0
  133. package/packages/omo-codex/plugin/skills/start-work/agents/openai.yaml +2 -0
  134. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +139 -0
  135. package/packages/omo-codex/plugin/skills/teammode/agents/openai.yaml +2 -0
  136. package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +111 -0
  137. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +250 -0
  138. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +220 -0
  139. package/packages/omo-codex/plugin/skills/ultraresearch/agents/openai.yaml +2 -0
  140. package/packages/omo-codex/plugin/skills/ulw-loop/agents/openai.yaml +1 -1
  141. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -1
  142. package/packages/omo-codex/plugin/skills/ulw-plan/agents/openai.yaml +1 -1
  143. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
  144. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +2 -2
  145. package/packages/omo-codex/plugin/skills/visual-qa/agents/openai.yaml +2 -0
  146. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +34 -33
  147. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +5 -2
  148. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +13 -0
  149. package/packages/omo-codex/plugin/test/auto-update.test.mjs +1 -1
  150. package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +1 -1
  151. package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +14 -6
  152. package/packages/omo-codex/plugin/test/bootstrap-ps-guard.test.mjs +7 -3
  153. package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +31 -0
  154. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +3 -3
  155. package/packages/omo-codex/plugin/test/display-metadata.test.mjs +83 -0
  156. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +30 -19
  157. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +3 -3
  158. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
  159. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -0
  160. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +9 -4
  161. package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +88 -0
  162. package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +240 -0
  163. package/packages/omo-codex/scripts/install-dist/install-local.mjs +412 -216
  164. package/packages/omo-codex/scripts/install-git-bash-mcp-env.test.mjs +24 -0
  165. package/packages/omo-codex/scripts/install-hook-targets.test.mjs +93 -0
  166. package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +2 -2
  167. package/packages/omo-codex/scripts/install-local-bun-global-update.test.mjs +63 -0
  168. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +109 -1
  169. package/packages/omo-codex/scripts/install-local.mjs +1 -0
  170. package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +36 -0
  171. package/packages/shared-skills/skills/init-deep/SKILL.md +21 -26
  172. package/packages/shared-skills/skills/programming/SKILL.md +25 -121
  173. package/packages/shared-skills/skills/programming/references/code-smells.md +390 -0
  174. package/packages/shared-skills/skills/ulw-plan/SKILL.md +2 -1
  175. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  176. package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +2 -2
  177. 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 readFileSync4 } from "node:fs";
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
- var CHECKBOX_PATTERN = /^- \[[ xX]\] /;
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, "utf-8"));
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(hasCountedSectionHeading);
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
- total += 1;
51
- if (!UNCHECKED_PATTERN.test(line)) {
59
+ if (!isCountedSection)
52
60
  continue;
53
- }
54
- remaining += 1;
55
- if (nextTaskLabel === null) {
56
- nextTaskLabel = line.slice("- [ ] ".length);
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 hasCountedSectionHeading(line) {
67
- const heading = parseLevelTwoHeading(line);
68
- return heading !== null && isCountedHeading(heading);
69
- }
70
- function parseLevelTwoHeading(line) {
71
- if (!line.startsWith("## ")) {
72
- return null;
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 parseIsoToMs(value) {
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 parsed = Date.parse(value);
123
- return Number.isNaN(parsed) ? null : parsed;
96
+ const mirrorWork = parseBoulderWork(value);
97
+ if (works.length === 0 && mirrorWork === null)
98
+ return null;
99
+ return { works, mirrorWork, hasWorksMap };
124
100
  }
125
- function buildWorkFromMirror(state) {
126
- const planName = state.plan_name ?? state.active_plan;
127
- const workId = `${planName}-legacy`;
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
- work_id: workId,
130
- active_plan: state.active_plan,
131
- plan_name: planName,
132
- status: state.status,
133
- started_at: state.started_at,
134
- ended_at: state.ended_at,
135
- elapsed_ms: state.elapsed_ms,
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 projectWorkToMirror(state, work) {
145
- state.active_plan = work.active_plan;
146
- state.plan_name = work.plan_name;
147
- state.status = work.status;
148
- state.started_at = work.started_at;
149
- state.ended_at = work.ended_at;
150
- state.elapsed_ms = work.elapsed_ms;
151
- state.updated_at = work.updated_at;
152
- state.session_ids = [...work.session_ids];
153
- state.session_origins = work.session_origins ? { ...work.session_origins } : {};
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
- const sorted = [...works].sort((left, right) => {
170
- const leftMs = parseIsoToMs(left.updated_at ?? left.started_at) ?? 0;
171
- const rightMs = parseIsoToMs(right.updated_at ?? right.started_at) ?? 0;
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 normalizeState(state) {
202
- normalizeSessionFields(state);
203
- const sessionIds = Array.isArray(state.session_ids) ? state.session_ids : [];
204
- const sessionOrigins = state.session_origins && typeof state.session_origins === "object" && !Array.isArray(state.session_origins) ? state.session_origins : {};
205
- state.session_origins = sessionOrigins;
206
- if (sessionIds.length === 1) {
207
- const soleSessionId = sessionIds[0];
208
- if (typeof soleSessionId === "string" && sessionOrigins[soleSessionId] !== "appended" && sessionOrigins[soleSessionId] !== "direct") {
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
- normalizeWorkSessionFields(state.works);
164
+ return null;
216
165
  }
217
- function normalizeSessionFields(target) {
218
- const sessionIds = Array.isArray(target.session_ids) ? target.session_ids.filter((sessionId) => typeof sessionId === "string").map((sessionId) => normalizeSessionId(sessionId)) : [];
219
- target.session_ids = sessionIds;
220
- const sessionOrigins = target.session_origins && typeof target.session_origins === "object" && !Array.isArray(target.session_origins) ? normalizeSessionOrigins(target.session_origins) : {};
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 normalizeSessionOrigins(sessionOrigins) {
224
- return Object.fromEntries(Object.entries(sessionOrigins).map(([sessionId, origin]) => [normalizeSessionId(sessionId), origin]));
171
+ function isCountedHeading(heading) {
172
+ return heading === TODO_HEADING || heading === FINAL_VERIFICATION_HEADING;
225
173
  }
226
- function normalizeWorkSessionFields(works) {
227
- if (!works || typeof works !== "object" || Array.isArray(works)) {
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 getBoulderWorks(state) {
237
- if (state.works && typeof state.works === "object") {
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 [buildWorkFromMirror(state)];
187
+ return sessionIds;
244
188
  }
245
- function getWorkForSession(directory, sessionId) {
246
- const state = readBoulderState(directory);
247
- if (!state) {
248
- return null;
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
- // components/start-work-continuation/src/boulder-reader.ts
269
- function readContinuationState(cwd, sessionId) {
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
- return {
278
- planName: work.plan_name,
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 readFileSync3 } from "node:fs";
292
- var START_WORK_CONTINUATION_DIRECTIVE = readFileSync3(new URL("../directive.md", import.meta.url), "utf8");
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 isRecord(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"]);
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 isRecord(value) {
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 readFileSync4(path, encoding);
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": "LazyCodex(4.11.0): Checking Start-Work Continuation"
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": "LazyCodex(4.11.0): Checking Start-Work Continuation"
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.11.0",
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",