oh-my-opencode 4.19.2 → 4.19.4

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 (241) hide show
  1. package/.agents/command/get-unpublished-changes.md +2 -0
  2. package/.agents/command/omomomo.md +1 -1
  3. package/.agents/command/publish.md +102 -28
  4. package/.agents/skills/get-unpublished-changes/SKILL.md +2 -0
  5. package/.agents/skills/hyperplan/SKILL.md +3 -3
  6. package/.agents/skills/omomomo/SKILL.md +1 -1
  7. package/.agents/skills/publish/SKILL.md +27 -6
  8. package/.opencode/command/get-unpublished-changes.md +2 -0
  9. package/.opencode/command/omomomo.md +1 -1
  10. package/.opencode/command/publish.md +102 -28
  11. package/.opencode/skills/hyperplan/SKILL.md +3 -3
  12. package/THIRD-PARTY-NOTICES.md +7 -7
  13. package/dist/agents/sisyphus-junior/agent.d.ts +1 -1
  14. package/dist/agents/sisyphus-runtime-prompt-reconciler.d.ts +1 -1
  15. package/dist/cli/config-migrate.d.ts +8 -0
  16. package/dist/cli/doctor/checks/deprecated-reasoning-keys.d.ts +2 -0
  17. package/dist/cli/doctor/checks/legacy-config-leftovers.d.ts +10 -0
  18. package/dist/cli/doctor/checks/model-resolution-config.d.ts +1 -1
  19. package/dist/cli/doctor/checks/tools-lsp.d.ts +1 -1
  20. package/dist/cli/doctor/framework/constants.d.ts +1 -1
  21. package/dist/cli/index.js +95684 -92814
  22. package/dist/cli-node/index.js +95650 -92780
  23. package/dist/config/schema/agent-overrides.d.ts +800 -0
  24. package/dist/config/schema/categories.d.ts +132 -0
  25. package/dist/config/schema/fallback-models.d.ts +50 -0
  26. package/dist/config/schema/oh-my-opencode-config.d.ts +818 -2
  27. package/dist/config/validate.d.ts +2 -1
  28. package/dist/config-migration/deep-diff.d.ts +1 -0
  29. package/dist/config-migration/discovery-paths.d.ts +11 -0
  30. package/dist/config-migration/discovery-roots.d.ts +7 -0
  31. package/dist/config-migration/discovery.d.ts +4 -0
  32. package/dist/config-migration/index.d.ts +10 -0
  33. package/dist/config-migration/legacy-history.d.ts +3 -0
  34. package/dist/config-migration/migration-executor.d.ts +4 -0
  35. package/dist/config-migration/migration-plans.d.ts +15 -0
  36. package/dist/config-migration/reasoning-unification.d.ts +3 -0
  37. package/dist/config-migration/record-values.d.ts +4 -0
  38. package/dist/config-migration/schema-url.d.ts +1 -0
  39. package/dist/config-migration/transform-config-jsonc.d.ts +2 -0
  40. package/dist/config-migration/transform-opencode.d.ts +2 -0
  41. package/dist/config-migration/transform-types.d.ts +24 -0
  42. package/dist/config-migration/types.d.ts +39 -0
  43. package/dist/features/builtin-commands/templates/hyperplan.d.ts +1 -1
  44. package/dist/features/team-mode/tools/lifecycle-test-fixture.d.ts +2 -0
  45. package/dist/hooks/auto-update-checker/constants.d.ts +3 -3
  46. package/dist/hooks/codegraph-bootstrap/command-runner.d.ts +1 -0
  47. package/dist/hooks/model-fallback/next-fallback.d.ts +1 -0
  48. package/dist/hooks/runtime-fallback/constants.d.ts +1 -1
  49. package/dist/index.js +12368 -9788
  50. package/dist/oh-my-opencode.schema.json +1672 -8
  51. package/dist/plugin-config/omo-config-chain.d.ts +14 -0
  52. package/dist/plugin-config/unknown-key-diagnostics.d.ts +5 -0
  53. package/dist/plugin-config.d.ts +2 -2
  54. package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +1 -0
  55. package/dist/shared/agent-display-names.d.ts +1 -1
  56. package/dist/shared/agent-variant.d.ts +11 -0
  57. package/dist/shared/jsonc-parser.d.ts +1 -1
  58. package/dist/shared/migration.d.ts +5 -1
  59. package/dist/shared/opencode-config-dir-types.d.ts +0 -1
  60. package/dist/shared/plugin-identity.d.ts +6 -8
  61. package/dist/shared/project-discovery-dirs.d.ts +0 -1
  62. package/dist/shared/session-prompt-params-helpers.d.ts +6 -1
  63. package/dist/skills/coding-agent-sessions/SKILL.md +4 -3
  64. package/dist/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  65. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
  66. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
  67. package/dist/skills/data-scientist/SKILL.md +243 -0
  68. package/dist/skills/data-scientist/references/common-scenarios.md +176 -0
  69. package/dist/skills/data-scientist/references/execution-templates.md +197 -0
  70. package/dist/skills/data-scientist/references/integration-patterns.md +153 -0
  71. package/dist/skills/data-scientist/references/performance-benchmarks.md +37 -0
  72. package/dist/skills/data-scientist/references/uv-setup.md +78 -0
  73. package/dist/skills/data-scientist/scripts/quick-query.py +111 -0
  74. package/dist/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
  75. package/dist/skills/data-scientist/scripts/setup-uv.sh +60 -0
  76. package/dist/skills/debugging/SKILL.md +1 -1
  77. package/dist/skills/frontend/ATTRIBUTION.md +7 -0
  78. package/dist/skills/frontend/SKILL.md +4 -1
  79. package/dist/skills/frontend/references/design/README.md +8 -0
  80. package/dist/skills/frontend/references/design/_INDEX.md +14 -1
  81. package/dist/skills/frontend/references/design/interaction-skill.md +144 -0
  82. package/dist/skills/programming/SKILL.md +1 -2
  83. package/dist/skills/ulw-plan/SKILL.md +1 -1
  84. package/dist/skills/ulw-research/SKILL.md +122 -11
  85. package/dist/startup-migration.d.ts +28 -0
  86. package/dist/testing/create-plugin-module.d.ts +4 -0
  87. package/dist/tools/delegate-task/builtin-categories.d.ts +1 -0
  88. package/dist/tools/delegate-task/builtin-category-definition.d.ts +1 -0
  89. package/dist/tools/delegate-task/constants.d.ts +1 -1
  90. package/dist/tui.js +6040 -5443
  91. package/package.json +13 -13
  92. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +21 -5
  93. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +11 -2
  94. package/packages/omo-codex/THIRD-PARTY-NOTICES.md +2 -2
  95. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  96. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +21 -3
  97. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  98. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  99. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +11 -5
  100. package/packages/omo-codex/plugin/components/codegraph/NOTICE +1 -1
  101. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +10373 -2570
  102. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +7502 -574
  103. package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
  104. package/packages/omo-codex/plugin/components/codegraph/src/hook-input.ts +33 -0
  105. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +36 -10
  106. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +164 -159
  107. package/packages/omo-codex/plugin/components/codegraph/src/post-tool-use-hook.ts +34 -0
  108. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +20 -2
  109. package/packages/omo-codex/plugin/components/codegraph/src/session-start-command.ts +106 -0
  110. package/packages/omo-codex/plugin/components/codegraph/src/session-start-cooldown.ts +145 -0
  111. package/packages/omo-codex/plugin/components/codegraph/src/session-start-hook-runtime.ts +21 -0
  112. package/packages/omo-codex/plugin/components/codegraph/src/session-start-lock.ts +139 -0
  113. package/packages/omo-codex/plugin/components/codegraph/src/session-start-outcome.ts +15 -0
  114. package/packages/omo-codex/plugin/components/codegraph/src/session-start-paths.ts +32 -0
  115. package/packages/omo-codex/plugin/components/codegraph/src/session-start-project.ts +109 -0
  116. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker-result.ts +148 -0
  117. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +126 -175
  118. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +4 -4
  119. package/packages/omo-codex/plugin/components/codegraph/test/hook-session-start-guard.test.ts +160 -0
  120. package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +12 -138
  121. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +34 -24
  122. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +3 -3
  123. package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +2 -2
  124. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +5 -5
  125. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -3
  126. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +1 -1
  127. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +45 -0
  128. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +2 -2
  129. package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +70 -13
  130. package/packages/omo-codex/plugin/components/codegraph/test/session-start-project.test.ts +58 -0
  131. package/packages/omo-codex/plugin/components/codegraph/test/session-start-state.test.ts +104 -0
  132. package/packages/omo-codex/plugin/components/codegraph/test/session-start-trust-boundary.test.ts +4 -2
  133. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-cooldown.test.ts +116 -0
  134. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +58 -57
  135. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  136. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  137. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  138. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  139. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  140. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  141. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  142. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  143. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  144. package/packages/omo-codex/plugin/components/rules/dist/cli.js +32 -15
  145. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  146. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  147. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +1 -1
  148. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  149. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  150. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  151. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  152. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  153. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  154. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  155. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  156. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +1 -1
  157. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  158. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  159. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +17 -0
  160. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  161. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  162. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  163. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  164. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  165. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  166. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  167. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  168. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  169. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  170. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  171. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  172. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  173. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  174. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  175. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  176. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  177. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  178. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  179. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  180. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  181. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  182. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  183. package/packages/omo-codex/plugin/package-lock.json +42 -41
  184. package/packages/omo-codex/plugin/package.json +1 -1
  185. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +6 -0
  186. package/packages/omo-codex/plugin/shared/src/config-loader.ts +165 -16
  187. package/packages/omo-codex/plugin/shared/src/config-migration.ts +162 -0
  188. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +186 -9
  189. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +4 -3
  190. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  191. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
  192. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
  193. package/packages/omo-codex/plugin/skills/data-scientist/SKILL.md +243 -0
  194. package/packages/omo-codex/plugin/skills/data-scientist/agents/openai.yaml +2 -0
  195. package/packages/omo-codex/plugin/skills/data-scientist/references/common-scenarios.md +176 -0
  196. package/packages/omo-codex/plugin/skills/data-scientist/references/execution-templates.md +197 -0
  197. package/packages/omo-codex/plugin/skills/data-scientist/references/integration-patterns.md +153 -0
  198. package/packages/omo-codex/plugin/skills/data-scientist/references/performance-benchmarks.md +37 -0
  199. package/packages/omo-codex/plugin/skills/data-scientist/references/uv-setup.md +78 -0
  200. package/packages/omo-codex/plugin/skills/data-scientist/scripts/quick-query.py +111 -0
  201. package/packages/omo-codex/plugin/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
  202. package/packages/omo-codex/plugin/skills/data-scientist/scripts/setup-uv.sh +60 -0
  203. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +1 -1
  204. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +7 -0
  205. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +4 -1
  206. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +8 -0
  207. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +14 -1
  208. package/packages/omo-codex/plugin/skills/frontend/references/design/interaction-skill.md +144 -0
  209. package/packages/omo-codex/plugin/skills/programming/SKILL.md +1 -2
  210. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +17 -0
  211. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +1 -1
  212. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +121 -11
  213. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +7 -0
  214. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +12 -0
  215. package/packages/omo-codex/scripts/install-dist/install-local.mjs +24 -5
  216. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +4 -3
  217. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  218. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
  219. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
  220. package/packages/shared-skills/skills/data-scientist/SKILL.md +243 -0
  221. package/packages/shared-skills/skills/data-scientist/references/common-scenarios.md +176 -0
  222. package/packages/shared-skills/skills/data-scientist/references/execution-templates.md +197 -0
  223. package/packages/shared-skills/skills/data-scientist/references/integration-patterns.md +153 -0
  224. package/packages/shared-skills/skills/data-scientist/references/performance-benchmarks.md +37 -0
  225. package/packages/shared-skills/skills/data-scientist/references/uv-setup.md +78 -0
  226. package/packages/shared-skills/skills/data-scientist/scripts/quick-query.py +111 -0
  227. package/packages/shared-skills/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
  228. package/packages/shared-skills/skills/data-scientist/scripts/setup-uv.sh +60 -0
  229. package/packages/shared-skills/skills/debugging/SKILL.md +1 -1
  230. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +7 -0
  231. package/packages/shared-skills/skills/frontend/SKILL.md +4 -1
  232. package/packages/shared-skills/skills/frontend/references/design/README.md +8 -0
  233. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +14 -1
  234. package/packages/shared-skills/skills/frontend/references/design/interaction-skill.md +144 -0
  235. package/packages/shared-skills/skills/programming/SKILL.md +1 -2
  236. package/packages/shared-skills/skills/ulw-plan/SKILL.md +1 -1
  237. package/packages/shared-skills/skills/ulw-research/SKILL.md +122 -11
  238. package/dist/plugin-config/layered-config-loader.d.ts +0 -2
  239. package/dist/plugin-config/single-config-loader.d.ts +0 -4
  240. package/dist/shared/migrate-legacy-config-file.d.ts +0 -1
  241. package/dist/shared/migration/config-migration.d.ts +0 -1
@@ -1,4 +1,6 @@
1
1
  import { describe, expect, it } from "bun:test";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
2
4
  import { join } from "node:path";
3
5
 
4
6
  import { runCodegraphServe } from "../src/serve.ts";
@@ -51,6 +53,49 @@ describe("runCodegraphServe provisioning", () => {
51
53
  ]);
52
54
  });
53
55
 
56
+ it("#given a bundled runtime and a stale managed install #when serving MCP #then it upgrades and uses the managed runtime", async () => {
57
+ // given
58
+ const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-serve-managed-upgrade-"));
59
+ const installDir = join(homeDir, ".omo", "codegraph");
60
+ const binPath = join(installDir, "bin", "codegraph");
61
+ mkdirSync(join(installDir, "bin"), { recursive: true });
62
+ mkdirSync(join(installDir, ".provisioned"), { recursive: true });
63
+ writeFileSync(binPath, "stale codegraph\n");
64
+ writeFileSync(
65
+ join(installDir, ".provisioned", "codegraph-1.4.1.json"),
66
+ `${JSON.stringify({ binPath, version: "1.4.1" })}\n`,
67
+ );
68
+ const provisionCalls: unknown[] = [];
69
+ const processCalls: Array<{ readonly args: readonly string[]; readonly command: string }> = [];
70
+
71
+ try {
72
+ // when
73
+ const exitCode = await runCodegraphServe({
74
+ config: { codegraph: { enabled: true }, sources: [], warnings: [] },
75
+ homeDir,
76
+ managedInstallExists: () => true,
77
+ nodeVersion: "22.14.0",
78
+ resolve: () => ({ argsPrefix: ["codegraph.js"], command: "/opt/node22/bin/node", exists: true, source: "bundled" }),
79
+ resolveManagedBin: () => null,
80
+ ensureProvisioned: (options) => {
81
+ provisionCalls.push(options);
82
+ return Promise.resolve({ binPath, provisioned: true });
83
+ },
84
+ runProcess: (command, args) => {
85
+ processCalls.push({ args, command });
86
+ return Promise.resolve(0);
87
+ },
88
+ });
89
+
90
+ // then
91
+ expect(exitCode).toBe(0);
92
+ expect(provisionCalls).toEqual([{ installDir, lockDir: join(installDir, ".locks"), version: "1.5.0" }]);
93
+ expect(processCalls).toEqual([{ args: ["serve", "--mcp"], command: binPath }]);
94
+ } finally {
95
+ rmSync(homeDir, { force: true, recursive: true });
96
+ }
97
+ });
98
+
54
99
  it("#given CodeGraph is unresolved and codegraph.daemon=false #when serving MCP #then provisions CodeGraph with daemon-off", async () => {
55
100
  // given
56
101
  const binPath = join("/tmp/home/.omo/codegraph", "bin", "codegraph");
@@ -97,8 +97,8 @@ describe("runCodegraphServe unavailable CodeGraph paths", () => {
97
97
  try {
98
98
  mkdirSync(join(homeDir, ".omo"), { recursive: true });
99
99
  mkdirSync(join(workspace, ".omo"), { recursive: true });
100
- writeFileSync(join(homeDir, ".omo", "config.jsonc"), '{ "codegraph": { "enabled": true } }\n');
101
- writeFileSync(join(workspace, ".omo", "config.jsonc"), '{ "[codex]": { "codegraph": { "enabled": false } } }\n');
100
+ writeFileSync(join(homeDir, ".omo", "omo.jsonc"), '{ "codegraph": { "enabled": true } }\n');
101
+ writeFileSync(join(workspace, ".omo", "omo.jsonc"), '{ "[codex]": { "codegraph": { "enabled": false } } }\n');
102
102
 
103
103
  // when
104
104
  const exitCode = await runCodegraphServe({
@@ -1,10 +1,15 @@
1
1
  import { describe, expect, it } from "bun:test";
2
- import { existsSync, mkdtempSync, rmSync } from "node:fs";
2
+ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
 
6
6
  import { runCodegraphSessionStartWorker } from "../src/hook.ts";
7
7
 
8
+ function writeProjectDatabase(workspace: string): void {
9
+ mkdirSync(join(workspace, ".codegraph"), { recursive: true });
10
+ writeFileSync(join(workspace, ".codegraph", "codegraph.db"), "fixture");
11
+ }
12
+
8
13
  describe("CodeGraph SessionStart worker Node support", () => {
9
14
  it("#given an unsupported local Node and a PATH CodeGraph command with auto provisioning disabled #when worker runs #then it skips without touching the workspace", async () => {
10
15
  // given
@@ -52,7 +57,7 @@ describe("CodeGraph SessionStart worker Node support", () => {
52
57
  const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-node-provision-home-"));
53
58
  const binPath = join(homeDir, ".omo", "codegraph", "bin", "codegraph");
54
59
  const calls: Array<{ readonly args: readonly string[]; readonly command: string }> = [];
55
- const provisionCalls: Array<{ readonly installDir?: string; readonly lockDir: string; readonly version: "1.4.1" }> = [];
60
+ const provisionCalls: Array<{ readonly installDir?: string; readonly lockDir: string; readonly version: "1.5.0" }> = [];
56
61
  const outcomes: unknown[] = [];
57
62
 
58
63
  try {
@@ -78,19 +83,17 @@ describe("CodeGraph SessionStart worker Node support", () => {
78
83
  resolveCommand: () => ({ argsPrefix: [], command: "/usr/local/bin/codegraph", exists: true, source: "path" }),
79
84
  runCommand: (_projectRoot, command, args) => {
80
85
  calls.push({ args, command });
81
- return Promise.resolve({ exitCode: 0, stdout: calls.length === 1 ? '{"initialized":false}' : "", timedOut: false });
86
+ writeProjectDatabase(workspace);
87
+ return Promise.resolve({ exitCode: 0, stdout: "", timedOut: false });
82
88
  },
83
89
  },
84
90
  });
85
91
 
86
92
  // then
87
93
  expect(result).toEqual({ action: "initialized" });
88
- expect(calls).toEqual([
89
- { args: ["status", "--json"], command: binPath },
90
- { args: ["init"], command: binPath },
91
- ]);
94
+ expect(calls).toEqual([{ args: ["init"], command: binPath }]);
92
95
  expect(provisionCalls).toEqual([
93
- { installDir: join(homeDir, ".omo", "codegraph"), lockDir: join(homeDir, ".omo", "codegraph", ".locks"), version: "1.4.1" },
96
+ { installDir: join(homeDir, ".omo", "codegraph"), lockDir: join(homeDir, ".omo", "codegraph", ".locks"), version: "1.5.0" },
94
97
  ]);
95
98
  expect(outcomes).toEqual([{ action: "initialized", exitCode: 0, projectRoot: workspace, source: "provisioned", timedOut: false }]);
96
99
  } finally {
@@ -99,6 +102,62 @@ describe("CodeGraph SessionStart worker Node support", () => {
99
102
  }
100
103
  });
101
104
 
105
+ it("#given bundled CodeGraph and a stale managed install #when the worker runs #then it upgrades and uses the managed runtime", async () => {
106
+ // given
107
+ const workspace = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-managed-upgrade-"));
108
+ const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-managed-upgrade-home-"));
109
+ const installDir = join(homeDir, ".omo", "codegraph");
110
+ const binPath = join(installDir, "bin", "codegraph");
111
+ mkdirSync(join(installDir, "bin"), { recursive: true });
112
+ mkdirSync(join(installDir, ".provisioned"), { recursive: true });
113
+ writeFileSync(binPath, "stale codegraph\n");
114
+ writeFileSync(
115
+ join(installDir, ".provisioned", "codegraph-1.4.1.json"),
116
+ `${JSON.stringify({ binPath, version: "1.4.1" })}\n`,
117
+ );
118
+ const provisionCalls: unknown[] = [];
119
+ const commandCalls: Array<{ readonly args: readonly string[]; readonly command: string }> = [];
120
+
121
+ try {
122
+ // when
123
+ const result = await runCodegraphSessionStartWorker({
124
+ cwd: workspace,
125
+ env: { HOME: homeDir },
126
+ nodeVersion: "22.14.0",
127
+ deps: {
128
+ ensureGitignored: () => true,
129
+ ensureProvisioned: (options) => {
130
+ provisionCalls.push(options);
131
+ return Promise.resolve({ binPath, provisioned: true });
132
+ },
133
+ managedInstallExists: () => true,
134
+ prepareWorkspace: () => ({
135
+ dataDir: join(homeDir, ".omo/codegraph/projects/test"),
136
+ dataRoot: join(homeDir, ".omo/codegraph"),
137
+ linked: true,
138
+ mode: "global-linked",
139
+ projectLink: join(workspace, ".codegraph"),
140
+ }),
141
+ resolveCommand: () => ({ argsPrefix: ["codegraph.js"], command: "/opt/node22/bin/node", exists: true, source: "bundled" }),
142
+ resolveManagedBin: () => null,
143
+ runCommand: (_projectRoot, command, args) => {
144
+ commandCalls.push({ args, command });
145
+ writeProjectDatabase(workspace);
146
+ return Promise.resolve({ exitCode: 0, stdout: "", timedOut: false });
147
+ },
148
+ },
149
+ });
150
+
151
+ // then
152
+ expect(result).toEqual({ action: "initialized" });
153
+ expect(provisionCalls).toEqual([{ installDir, lockDir: join(installDir, ".locks"), version: "1.5.0" }]);
154
+ expect(commandCalls).toEqual([{ args: ["init"], command: binPath }]);
155
+ } finally {
156
+ rmSync(workspace, { recursive: true, force: true });
157
+ rmSync(homeDir, { recursive: true, force: true });
158
+ }
159
+ });
160
+
102
161
  it("#given an unsupported local Node but bundled CodeGraph resolves through CODEGRAPH_NODE_BIN #when worker runs #then it bootstraps with the compatible runtime", async () => {
103
162
  // given
104
163
  const workspace = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-compatible-node-"));
@@ -129,17 +188,15 @@ describe("CodeGraph SessionStart worker Node support", () => {
129
188
  resolveCommand: () => ({ argsPrefix: ["codegraph.js"], command: nodeBin, exists: true, source: "bundled" }),
130
189
  runCommand: (_projectRoot, command, args) => {
131
190
  calls.push({ args, command });
132
- return Promise.resolve({ exitCode: 0, stdout: calls.length === 1 ? '{"initialized":false}' : "", timedOut: false });
191
+ writeProjectDatabase(workspace);
192
+ return Promise.resolve({ exitCode: 0, stdout: "", timedOut: false });
133
193
  },
134
194
  },
135
195
  });
136
196
 
137
197
  // then
138
198
  expect(result).toEqual({ action: "initialized" });
139
- expect(calls).toEqual([
140
- { args: ["codegraph.js", "status", "--json"], command: nodeBin },
141
- { args: ["codegraph.js", "init"], command: nodeBin },
142
- ]);
199
+ expect(calls).toEqual([{ args: ["codegraph.js", "init"], command: nodeBin }]);
143
200
  expect(outcomes).toEqual([{ action: "initialized", exitCode: 0, projectRoot: workspace, source: "bundled", timedOut: false }]);
144
201
  } finally {
145
202
  rmSync(workspace, { recursive: true, force: true });
@@ -0,0 +1,58 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ import { probeCodegraphProject } from "../src/session-start-project.ts";
7
+
8
+ describe("CodeGraph SessionStart project probe", () => {
9
+ it("#given the exact project directory has a CodeGraph database #when probing #then it is initialized", () => {
10
+ // given
11
+ const projectRoot = mkdtempSync(join(tmpdir(), "omo-codegraph-probe-exact-"));
12
+ try {
13
+ mkdirSync(join(projectRoot, ".codegraph"), { recursive: true });
14
+ writeFileSync(join(projectRoot, ".codegraph", "codegraph.db"), "fixture");
15
+
16
+ // when
17
+ const result = probeCodegraphProject(projectRoot);
18
+
19
+ // then
20
+ expect(result).toEqual({ kind: "initialized" });
21
+ } finally {
22
+ rmSync(projectRoot, { recursive: true, force: true });
23
+ }
24
+ });
25
+
26
+ it("#given an ancestor directory has a CodeGraph database #when probing a nested project #then the ancestor covers it", () => {
27
+ // given
28
+ const ancestorRoot = mkdtempSync(join(tmpdir(), "omo-codegraph-probe-ancestor-"));
29
+ const projectRoot = join(ancestorRoot, "apps", "server");
30
+ try {
31
+ mkdirSync(join(ancestorRoot, ".codegraph"), { recursive: true });
32
+ mkdirSync(projectRoot, { recursive: true });
33
+ writeFileSync(join(ancestorRoot, ".codegraph", "codegraph.db"), "fixture");
34
+
35
+ // when
36
+ const result = probeCodegraphProject(projectRoot);
37
+
38
+ // then
39
+ expect(result).toEqual({ ancestorRoot: realpathSync(ancestorRoot), kind: "nested-root" });
40
+ } finally {
41
+ rmSync(ancestorRoot, { recursive: true, force: true });
42
+ }
43
+ });
44
+
45
+ it("#given neither the project nor its ancestors have a CodeGraph database #when probing #then it is definitively uninitialized", () => {
46
+ // given
47
+ const projectRoot = mkdtempSync(join(tmpdir(), "omo-codegraph-probe-missing-"));
48
+ try {
49
+ // when
50
+ const result = probeCodegraphProject(projectRoot);
51
+
52
+ // then
53
+ expect(result).toEqual({ kind: "uninitialized" });
54
+ } finally {
55
+ rmSync(projectRoot, { recursive: true, force: true });
56
+ }
57
+ });
58
+ });
@@ -0,0 +1,104 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { mkdtempSync, rmSync, utimesSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ import {
7
+ clearSessionStartCooldown,
8
+ readSessionStartCooldown,
9
+ recordSessionStartFailure,
10
+ } from "../src/session-start-cooldown.ts";
11
+ import {
12
+ acquireSessionStartLock,
13
+ releaseSessionStartLock,
14
+ } from "../src/session-start-lock.ts";
15
+
16
+ describe("CodeGraph SessionStart lock", () => {
17
+ it("#given a live project lock #when a second hook acquires it #then the second hook is deduplicated", () => {
18
+ // given
19
+ const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-lock-home-"));
20
+ const projectRoot = mkdtempSync(join(tmpdir(), "omo-codegraph-lock-project-"));
21
+ try {
22
+ const first = acquireSessionStartLock({ homeDir, nowMs: 10_000, projectRoot, staleMs: 60_000 });
23
+ expect(first.kind).toBe("acquired");
24
+
25
+ // when
26
+ const second = acquireSessionStartLock({ homeDir, nowMs: 10_001, projectRoot, staleMs: 60_000 });
27
+
28
+ // then
29
+ expect(second).toEqual({ kind: "locked" });
30
+ if (first.kind === "acquired") releaseSessionStartLock(first.lock);
31
+ } finally {
32
+ rmSync(homeDir, { recursive: true, force: true });
33
+ rmSync(projectRoot, { recursive: true, force: true });
34
+ }
35
+ });
36
+
37
+ it("#given a project lock older than the stale threshold #when another hook acquires it #then the stale lock is recovered", () => {
38
+ // given
39
+ const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-stale-home-"));
40
+ const projectRoot = mkdtempSync(join(tmpdir(), "omo-codegraph-stale-project-"));
41
+ try {
42
+ const first = acquireSessionStartLock({ homeDir, nowMs: 1_000, projectRoot, staleMs: 500 });
43
+ expect(first.kind).toBe("acquired");
44
+ if (first.kind !== "acquired") return;
45
+ const staleDate = new Date(1_000);
46
+ utimesSync(first.lock.path, staleDate, staleDate);
47
+
48
+ // when
49
+ const second = acquireSessionStartLock({ homeDir, nowMs: 2_000, projectRoot, staleMs: 500 });
50
+
51
+ // then
52
+ expect(second.kind).toBe("acquired");
53
+ if (second.kind === "acquired") {
54
+ expect(second.recoveredStale).toBe(true);
55
+ releaseSessionStartLock(second.lock);
56
+ }
57
+ } finally {
58
+ rmSync(homeDir, { recursive: true, force: true });
59
+ rmSync(projectRoot, { recursive: true, force: true });
60
+ }
61
+ });
62
+ });
63
+
64
+ describe("CodeGraph SessionStart cooldown", () => {
65
+ it("#given a failed worker outcome #when the cooldown is checked #then retries are suppressed until expiry and the next failure backs off", () => {
66
+ // given
67
+ const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-cooldown-home-"));
68
+ const projectRoot = mkdtempSync(join(tmpdir(), "omo-codegraph-cooldown-project-"));
69
+ try {
70
+ const first = recordSessionStartFailure({
71
+ action: "failed",
72
+ baseCooldownMs: 1_000,
73
+ homeDir,
74
+ nowMs: 1_000,
75
+ projectRoot,
76
+ });
77
+
78
+ // when
79
+ const active = readSessionStartCooldown({ baseCooldownMs: 1_000, homeDir, nowMs: 1_999, projectRoot });
80
+ const expired = readSessionStartCooldown({ baseCooldownMs: 1_000, homeDir, nowMs: 2_000, projectRoot });
81
+ const second = recordSessionStartFailure({
82
+ action: "skipped-status",
83
+ baseCooldownMs: 1_000,
84
+ homeDir,
85
+ nowMs: 2_000,
86
+ projectRoot,
87
+ });
88
+
89
+ // then
90
+ expect(first).toMatchObject({ cooldownUntil: 2_000, failureCount: 1 });
91
+ expect(active).toMatchObject({ cooldownUntil: 2_000, failureCount: 1, kind: "active" });
92
+ expect(expired).toEqual({ failureCount: 1, kind: "inactive" });
93
+ expect(second).toMatchObject({ cooldownUntil: 4_000, failureCount: 2 });
94
+ clearSessionStartCooldown({ homeDir, projectRoot });
95
+ expect(readSessionStartCooldown({ baseCooldownMs: 1_000, homeDir, nowMs: 2_001, projectRoot })).toEqual({
96
+ failureCount: 0,
97
+ kind: "inactive",
98
+ });
99
+ } finally {
100
+ rmSync(homeDir, { recursive: true, force: true });
101
+ rmSync(projectRoot, { recursive: true, force: true });
102
+ }
103
+ });
104
+ });
@@ -15,7 +15,7 @@ describe("CodeGraph SessionStart trust boundary", () => {
15
15
  const calls: Array<{ readonly env: Record<string, string>; readonly installDir?: string; readonly lockDir?: string }> = [];
16
16
  try {
17
17
  mkdirSync(join(workspace, ".omo"), { recursive: true });
18
- writeFileSync(join(workspace, ".omo", "config.jsonc"), JSON.stringify({ codegraph: { enabled: true, install_dir: attackerInstallDir } }));
18
+ writeFileSync(join(workspace, ".omo", "omo.jsonc"), JSON.stringify({ codegraph: { enabled: true, install_dir: attackerInstallDir } }));
19
19
 
20
20
  // when
21
21
  const result = await runCodegraphSessionStartWorker({
@@ -45,7 +45,9 @@ describe("CodeGraph SessionStart trust boundary", () => {
45
45
  },
46
46
  runCommand: (_projectRoot, _command, _args, options) => {
47
47
  calls.push({ env: options.env });
48
- return Promise.resolve({ exitCode: 0, stdout: calls.length === 2 ? '{"initialized":false}' : "", timedOut: false });
48
+ mkdirSync(join(workspace, ".codegraph"), { recursive: true });
49
+ writeFileSync(join(workspace, ".codegraph", "codegraph.db"), "fixture");
50
+ return Promise.resolve({ exitCode: 0, stdout: "", timedOut: false });
49
51
  },
50
52
  },
51
53
  });
@@ -0,0 +1,116 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ import type { CodegraphSessionStartOutcome } from "../src/hook.ts";
7
+ import { runCodegraphSessionStartWorker } from "../src/hook.ts";
8
+ import { readSessionStartCooldown } from "../src/session-start-cooldown.ts";
9
+
10
+ describe("CodeGraph SessionStart worker cooldown", () => {
11
+ it("#given codegraph init times out #when the worker finishes #then it records a failed outcome and cooldown", async () => {
12
+ // given
13
+ const workspace = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-timeout-"));
14
+ const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-timeout-home-"));
15
+ const outcomes: CodegraphSessionStartOutcome[] = [];
16
+ try {
17
+ // when
18
+ const result = await runCodegraphSessionStartWorker({
19
+ config: { codegraph: { enabled: true, session_start_cooldown_ms: 60_000 }, sources: [], warnings: [] },
20
+ cwd: workspace,
21
+ env: { HOME: homeDir },
22
+ logOutcome: (outcome) => outcomes.push(outcome),
23
+ nodeVersion: "22.14.0",
24
+ nowMs: 1_000,
25
+ projectProbe: () => ({ kind: "uninitialized" }),
26
+ deps: {
27
+ ensureGitignored: () => true,
28
+ ensureProvisioned: () => Promise.resolve({ binPath: "/tmp/codegraph", provisioned: true }),
29
+ prepareWorkspace: () => ({
30
+ dataDir: join(homeDir, ".omo/codegraph/projects/test"),
31
+ dataRoot: join(homeDir, ".omo/codegraph"),
32
+ linked: true,
33
+ mode: "global-linked",
34
+ projectLink: join(workspace, ".codegraph"),
35
+ }),
36
+ resolveCommand: () => ({ argsPrefix: [], command: "/tmp/codegraph", exists: true, source: "path" }),
37
+ runCommand: () => Promise.resolve({ exitCode: 1, stdout: "", timedOut: true }),
38
+ },
39
+ });
40
+ const cooldown = readSessionStartCooldown({
41
+ baseCooldownMs: 60_000,
42
+ homeDir,
43
+ nowMs: 2_000,
44
+ projectRoot: workspace,
45
+ });
46
+
47
+ // then
48
+ expect(result).toEqual({ action: "failed" });
49
+ expect(outcomes).toEqual([{
50
+ action: "failed",
51
+ error: "codegraph init timed out",
52
+ exitCode: 1,
53
+ projectRoot: workspace,
54
+ source: "path",
55
+ timedOut: true,
56
+ }]);
57
+ expect(cooldown).toEqual({ cooldownUntil: 61_000, failureCount: 1, kind: "active" });
58
+ } finally {
59
+ rmSync(homeDir, { recursive: true, force: true });
60
+ rmSync(workspace, { recursive: true, force: true });
61
+ }
62
+ });
63
+
64
+ it("#given codegraph init exits zero without an exact database #when the worker verifies the result #then it records a failure cooldown", async () => {
65
+ // given
66
+ const workspace = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-no-db-"));
67
+ const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-no-db-home-"));
68
+ const outcomes: CodegraphSessionStartOutcome[] = [];
69
+ try {
70
+ // when
71
+ const result = await runCodegraphSessionStartWorker({
72
+ config: { codegraph: { enabled: true, session_start_cooldown_ms: 60_000 }, sources: [], warnings: [] },
73
+ cwd: workspace,
74
+ env: { HOME: homeDir },
75
+ logOutcome: (outcome) => outcomes.push(outcome),
76
+ nodeVersion: "22.14.0",
77
+ nowMs: 1_000,
78
+ projectProbe: () => ({ kind: "uninitialized" }),
79
+ deps: {
80
+ ensureGitignored: () => true,
81
+ ensureProvisioned: () => Promise.resolve({ binPath: "/tmp/codegraph", provisioned: true }),
82
+ prepareWorkspace: () => ({
83
+ dataDir: join(homeDir, ".omo/codegraph/projects/test"),
84
+ dataRoot: join(homeDir, ".omo/codegraph"),
85
+ linked: true,
86
+ mode: "global-linked",
87
+ projectLink: join(workspace, ".codegraph"),
88
+ }),
89
+ resolveCommand: () => ({ argsPrefix: [], command: "/tmp/codegraph", exists: true, source: "path" }),
90
+ runCommand: () => Promise.resolve({ exitCode: 0, stdout: "", timedOut: false }),
91
+ },
92
+ });
93
+ const cooldown = readSessionStartCooldown({
94
+ baseCooldownMs: 60_000,
95
+ homeDir,
96
+ nowMs: 2_000,
97
+ projectRoot: workspace,
98
+ });
99
+
100
+ // then
101
+ expect(result).toEqual({ action: "failed" });
102
+ expect(outcomes).toEqual([{
103
+ action: "failed",
104
+ error: "codegraph init completed without creating an exact project database",
105
+ exitCode: 0,
106
+ projectRoot: workspace,
107
+ source: "path",
108
+ timedOut: false,
109
+ }]);
110
+ expect(cooldown).toEqual({ cooldownUntil: 61_000, failureCount: 1, kind: "active" });
111
+ } finally {
112
+ rmSync(homeDir, { recursive: true, force: true });
113
+ rmSync(workspace, { recursive: true, force: true });
114
+ }
115
+ });
116
+ });