oh-my-opencode 5.0.0-beta.34 → 5.0.0-beta.35

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 (217) hide show
  1. package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +1 -3
  2. package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +1 -1
  3. package/.agents/skills/tech-debt-audit/SKILL.md +6 -76
  4. package/README.ja.md +1 -1
  5. package/README.ko.md +1 -1
  6. package/README.md +1 -1
  7. package/README.ru.md +1 -1
  8. package/README.zh-cn.md +1 -1
  9. package/THIRD-PARTY-NOTICES.md +27 -99
  10. package/dist/cli/index.js +48342 -48539
  11. package/dist/cli-node/index.js +49017 -49214
  12. package/dist/config/index.d.ts +1 -1
  13. package/dist/config/schema/hooks.d.ts +0 -1
  14. package/dist/config/schema/oh-my-opencode-config.d.ts +0 -10
  15. package/dist/config/schema.d.ts +0 -1
  16. package/dist/create-hooks.d.ts +0 -1
  17. package/dist/hooks/index.d.ts +0 -1
  18. package/dist/index.js +11675 -13715
  19. package/dist/mcp/index.d.ts +0 -4
  20. package/dist/mcp/types.d.ts +0 -1
  21. package/dist/oh-my-opencode.schema.json +1 -45
  22. package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
  23. package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
  24. package/dist/shared/omo-process-sweep.d.ts +2 -4
  25. package/dist/skills/init-deep/SKILL.md +13 -13
  26. package/dist/skills/ulw-plan/SKILL.md +0 -1
  27. package/dist/tui.js +44809 -44883
  28. package/package.json +16 -18
  29. package/packages/omo-codex/THIRD-PARTY-NOTICES.md +0 -17
  30. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -3
  31. package/packages/omo-codex/plugin/.mcp.json +0 -6
  32. package/packages/omo-codex/plugin/AGENTS.md +5 -5
  33. package/packages/omo-codex/plugin/README.md +1 -1
  34. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +1 -15
  35. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  36. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  38. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  40. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  41. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  42. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/lcx/skills/lcx-doctor/SKILL.md +1 -1
  44. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  45. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  46. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  48. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  50. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +1 -1
  52. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  53. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  54. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +2 -2
  55. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  56. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +0 -1
  58. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  59. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
  60. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  61. package/packages/omo-codex/plugin/components/ulw-loop/README.md +11 -1
  62. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +2 -2
  63. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +12 -0
  64. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +45 -0
  65. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.d.ts +1 -0
  66. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.js +5 -0
  67. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.js +10 -3
  68. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +35 -38
  69. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +278 -193
  70. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.d.ts +5 -0
  71. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.js +7 -51
  72. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-artifacts.js +39 -22
  73. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-fields.d.ts +13 -1
  74. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-fields.js +64 -8
  75. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +5 -1
  76. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +37 -18
  77. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  78. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  79. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -1
  80. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +81 -0
  81. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-reconciliation.ts +6 -0
  82. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-template.ts +10 -3
  83. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +32 -63
  84. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-aggregate.ts +7 -53
  85. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-artifacts.ts +50 -31
  86. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-fields.ts +67 -14
  87. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +6 -1
  88. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +36 -17
  89. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-final.test.ts +26 -0
  90. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-template.test.ts +47 -2
  91. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-aggregate-basics.test.ts +258 -0
  92. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-cap-and-dedupe.test.ts +22 -0
  93. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-poisoning-cascades.test.ts +97 -0
  94. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-single-report.test.ts +241 -0
  95. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  97. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  99. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  106. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  107. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  108. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  109. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  110. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  111. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
  112. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  113. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  114. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  115. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  116. package/packages/omo-codex/plugin/package-lock.json +12 -146
  117. package/packages/omo-codex/plugin/package.json +1 -2
  118. package/packages/omo-codex/plugin/scripts/AGENTS.md +0 -1
  119. package/packages/omo-codex/plugin/scripts/auto-update-plan.mjs +0 -1
  120. package/packages/omo-codex/plugin/scripts/auto-update.mjs +0 -9
  121. package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +0 -1
  122. package/packages/omo-codex/plugin/shared/src/config-loader.ts +2 -102
  123. package/packages/omo-codex/plugin/shared/src/config-migration.ts +0 -1
  124. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +41 -130
  125. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +13 -13
  126. package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +1 -1
  127. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +2 -2
  128. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -1
  129. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +0 -1
  130. package/packages/omo-codex/plugin/test/AGENTS.md +2 -2
  131. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +2 -21
  132. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -2
  133. package/packages/omo-codex/plugin/test/aggregate-mcp.test.mjs +2 -7
  134. package/packages/omo-codex/plugin/test/auto-update.test.mjs +0 -25
  135. package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +0 -31
  136. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +0 -50
  137. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +0 -1
  138. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -3
  139. package/packages/omo-codex/scripts/install-config-git-bash.test.mjs +0 -23
  140. package/packages/omo-codex/scripts/install-dist/install-local.mjs +227 -435
  141. package/packages/omo-codex/scripts/install-git-bash-mcp-env.test.mjs +1 -25
  142. package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +0 -36
  143. package/packages/shared-skills/skills/init-deep/SKILL.md +13 -13
  144. package/packages/shared-skills/skills/ulw-plan/SKILL.md +0 -1
  145. package/dist/config/schema/codegraph.d.ts +0 -12
  146. package/dist/hooks/codegraph-bootstrap/command-runner.d.ts +0 -17
  147. package/dist/hooks/codegraph-bootstrap/hook.d.ts +0 -35
  148. package/dist/hooks/codegraph-bootstrap/index.d.ts +0 -1
  149. package/dist/hooks/codegraph-bootstrap/project-root.d.ts +0 -1
  150. package/dist/hooks/codegraph-bootstrap/status.d.ts +0 -11
  151. package/dist/mcp/codegraph.d.ts +0 -16
  152. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +0 -58
  153. package/packages/omo-codex/plugin/components/codegraph/LICENSE +0 -21
  154. package/packages/omo-codex/plugin/components/codegraph/NODE-RUNTIME-LICENSES.md +0 -2951
  155. package/packages/omo-codex/plugin/components/codegraph/NOTICE +0 -16
  156. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +0 -12326
  157. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +0 -9908
  158. package/packages/omo-codex/plugin/components/codegraph/package.json +0 -33
  159. package/packages/omo-codex/plugin/components/codegraph/src/cache-gc.ts +0 -29
  160. package/packages/omo-codex/plugin/components/codegraph/src/cli.ts +0 -79
  161. package/packages/omo-codex/plugin/components/codegraph/src/hook-input.ts +0 -33
  162. package/packages/omo-codex/plugin/components/codegraph/src/hook-sweep.ts +0 -25
  163. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +0 -131
  164. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +0 -258
  165. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +0 -310
  166. package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +0 -92
  167. package/packages/omo-codex/plugin/components/codegraph/src/post-tool-use-hook.ts +0 -34
  168. package/packages/omo-codex/plugin/components/codegraph/src/serve-invocation.ts +0 -29
  169. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +0 -263
  170. package/packages/omo-codex/plugin/components/codegraph/src/session-start-command.ts +0 -106
  171. package/packages/omo-codex/plugin/components/codegraph/src/session-start-cooldown.ts +0 -145
  172. package/packages/omo-codex/plugin/components/codegraph/src/session-start-hook-runtime.ts +0 -22
  173. package/packages/omo-codex/plugin/components/codegraph/src/session-start-lock.ts +0 -139
  174. package/packages/omo-codex/plugin/components/codegraph/src/session-start-outcome.ts +0 -15
  175. package/packages/omo-codex/plugin/components/codegraph/src/session-start-paths.ts +0 -32
  176. package/packages/omo-codex/plugin/components/codegraph/src/session-start-project.ts +0 -109
  177. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker-result.ts +0 -148
  178. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +0 -175
  179. package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +0 -81
  180. package/packages/omo-codex/plugin/components/codegraph/test/cache-gc.test.ts +0 -34
  181. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +0 -112
  182. package/packages/omo-codex/plugin/components/codegraph/test/hook-registration.test.ts +0 -26
  183. package/packages/omo-codex/plugin/components/codegraph/test/hook-session-start-guard.test.ts +0 -160
  184. package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +0 -50
  185. package/packages/omo-codex/plugin/components/codegraph/test/hook-sweep.test.ts +0 -29
  186. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +0 -320
  187. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +0 -172
  188. package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +0 -91
  189. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +0 -100
  190. package/packages/omo-codex/plugin/components/codegraph/test/serve-built-wrapper.test.ts +0 -76
  191. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +0 -133
  192. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +0 -245
  193. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +0 -69
  194. package/packages/omo-codex/plugin/components/codegraph/test/serve-node-support.test.ts +0 -43
  195. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +0 -147
  196. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable-idle.test.ts +0 -47
  197. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +0 -175
  198. package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +0 -385
  199. package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +0 -206
  200. package/packages/omo-codex/plugin/components/codegraph/test/session-start-project.test.ts +0 -58
  201. package/packages/omo-codex/plugin/components/codegraph/test/session-start-state.test.ts +0 -104
  202. package/packages/omo-codex/plugin/components/codegraph/test/session-start-trust-boundary.test.ts +0 -65
  203. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-availability.test.ts +0 -106
  204. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-cooldown.test.ts +0 -116
  205. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +0 -258
  206. package/packages/omo-codex/plugin/components/codegraph/test/sweep-cli.test.ts +0 -56
  207. package/packages/omo-codex/plugin/components/codegraph/tsconfig.build.json +0 -13
  208. package/packages/omo-codex/plugin/components/codegraph/tsconfig.json +0 -25
  209. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-aggregate.test.ts +0 -101
  210. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +0 -18
  211. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +0 -17
  212. package/packages/omo-codex/plugin/scripts/migrate-omo-sot/editor.mjs +0 -125
  213. package/packages/omo-codex/plugin/scripts/migrate-omo-sot/jsonc.mjs +0 -43
  214. package/packages/omo-codex/plugin/scripts/migrate-omo-sot/scaffold.mjs +0 -29
  215. package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +0 -121
  216. package/packages/omo-codex/plugin/test/component-codegraph-mcp-smoke.test.mjs +0 -76
  217. package/packages/omo-codex/plugin/test/migrate-omo-sot.test.mjs +0 -179
@@ -1,320 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { Readable } from "node:stream";
6
- import { fileURLToPath } from "node:url";
7
-
8
- import { runCodegraphCli } from "../src/cli.ts";
9
- import {
10
- executeCodegraphSessionStartHook,
11
- runCodegraphPostToolUseHook,
12
- type WorkerSpawnInvocation,
13
- } from "../src/hook.ts";
14
-
15
- const pluginRoot = resolve(fileURLToPath(new URL("../../..", import.meta.url)));
16
-
17
- function createAllowedWorkspace(prefix: string): string {
18
- return mkdtempSync(join(pluginRoot, `.tmp-${prefix}-`));
19
- }
20
-
21
- function expectOmoCodegraphProjectStoreGuidance(context: string): void {
22
- expect(context).toContain(".omo");
23
- expect(context).toContain("codegraph");
24
- expect(context).toContain("projects");
25
- expect(context).toContain("project-");
26
- }
27
-
28
- describe("CodeGraph SessionStart hook", () => {
29
- it("#given hook session-start cli args #when invoked with empty JSON input #then it emits valid JSON and exits zero", async () => {
30
- // given
31
- const stdout: string[] = [];
32
- const spawned: WorkerSpawnInvocation[] = [];
33
- const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-hook-home-"));
34
- try {
35
- // when
36
- const exitCode = await runCodegraphCli({
37
- argv: ["node", "cli.js", "hook", "session-start"],
38
- cwd: createAllowedWorkspace("codegraph-hook-workspace"),
39
- env: { HOME: homeDir },
40
- stdin: Readable.from(["{}"]),
41
- stdout: { write: (chunk) => stdout.push(chunk) },
42
- ancestorProbe: () => ({ kind: "uninitialized" }),
43
- spawnWorker: (invocation) => spawned.push(invocation),
44
- sweepZombies: () => undefined,
45
- });
46
-
47
- // then
48
- expect(exitCode).toBe(0);
49
- expect(spawned).toHaveLength(1);
50
- const parsed = JSON.parse(stdout.join(""));
51
- expect(parsed).toEqual({
52
- hookSpecificOutput: {
53
- hookEventName: "SessionStart",
54
- additionalContext: "LazyCodex CodeGraph bootstrap scheduled in background",
55
- },
56
- });
57
- } finally {
58
- rmSync(homeDir, { recursive: true, force: true });
59
- }
60
- });
61
-
62
- it("#given CodeGraph MCP reports an uninitialized project #when PostToolUse fires #then it emits OMO global-store init guidance", async () => {
63
- // given
64
- const output = runCodegraphPostToolUseHook(
65
- {
66
- cwd: "/Users/me/project",
67
- tool_name: "codegraph.codegraph_status",
68
- tool_response: {
69
- error: [
70
- "Tool execution failed: CodeGraph not initialized in /Users/me/project.",
71
- "Run 'codegraph init' in that project first.",
72
- ].join(" "),
73
- },
74
- },
75
- { homeDir: "/Users/me" },
76
- );
77
-
78
- // when
79
- const parsed = JSON.parse(output);
80
-
81
- // then
82
- expect(parsed.hookSpecificOutput.hookEventName).toBe("PostToolUse");
83
- expect(parsed.hookSpecificOutput.additionalContext).toContain('CodeGraph is not initialized for "/Users/me/project"');
84
- expectOmoCodegraphProjectStoreGuidance(parsed.hookSpecificOutput.additionalContext);
85
- expect(parsed.hookSpecificOutput.additionalContext).toContain('run `codegraph init` from "/Users/me/project"');
86
- });
87
-
88
- it("#given real CodeGraph status output has no MCP path phrase #when PostToolUse fires #then it emits OMO global-store init guidance", async () => {
89
- // given
90
- const output = runCodegraphPostToolUseHook(
91
- {
92
- cwd: "/Users/me/project",
93
- tool_name: "mcp__codegraph__codegraph_status",
94
- tool_response: ['Project: /Users/me/project', "Not initialized", 'Run "codegraph init" to initialize'].join("\n"),
95
- },
96
- { homeDir: "/Users/me" },
97
- );
98
-
99
- // when
100
- const parsed = JSON.parse(output);
101
-
102
- // then
103
- expect(parsed.hookSpecificOutput.additionalContext).toContain('CodeGraph is not initialized for "/Users/me/project"');
104
- expectOmoCodegraphProjectStoreGuidance(parsed.hookSpecificOutput.additionalContext);
105
- });
106
-
107
- it("#given CodeGraph is disabled by Codex SOT config #when SessionStart fires #then it skips without spawning", async () => {
108
- // given
109
- const stdout: string[] = [];
110
- const spawned: WorkerSpawnInvocation[] = [];
111
-
112
- // when
113
- const result = await executeCodegraphSessionStartHook({
114
- config: { codegraph: { enabled: false }, sources: [], warnings: [] },
115
- env: {},
116
- stdin: Readable.from(["{}"]),
117
- stdout: { write: (chunk) => stdout.push(chunk) },
118
- sweepZombies: () => undefined,
119
- spawnWorker: (invocation) => spawned.push(invocation),
120
- });
121
-
122
- // then
123
- expect(result).toEqual({ action: "skipped-disabled", exitCode: 0 });
124
- expect(spawned).toEqual([]);
125
- expect(stdout.join("")).toBe("");
126
- });
127
-
128
- it("#given HOME OMO config disables Codex CodeGraph #when SessionStart fires #then it skips without spawning", async () => {
129
- // given
130
- const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-sot-home-"));
131
- const workspace = mkdtempSync(join(tmpdir(), "omo-codegraph-sot-workspace-"));
132
- const stdout: string[] = [];
133
- const spawned: WorkerSpawnInvocation[] = [];
134
- try {
135
- mkdirSync(join(homeDir, ".omo"), { recursive: true });
136
- writeFileSync(
137
- join(homeDir, ".omo", "omo.jsonc"),
138
- '{ "codegraph": { "enabled": true }, "[codex]": { "codegraph": { "enabled": false } } }\n',
139
- );
140
-
141
- // when
142
- const result = await executeCodegraphSessionStartHook({
143
- cwd: workspace,
144
- env: { HOME: homeDir },
145
- stdin: Readable.from(["{}"]),
146
- stdout: { write: (chunk) => stdout.push(chunk) },
147
- sweepZombies: () => undefined,
148
- spawnWorker: (invocation) => spawned.push(invocation),
149
- });
150
-
151
- // then
152
- expect(result).toEqual({ action: "skipped-disabled", exitCode: 0 });
153
- expect(spawned).toEqual([]);
154
- expect(stdout.join("")).toBe("");
155
- } finally {
156
- rmSync(homeDir, { recursive: true, force: true });
157
- rmSync(workspace, { recursive: true, force: true });
158
- }
159
- });
160
-
161
- it("#given project Codex SOT disables global CodeGraph enablement #when SessionStart fires #then project config wins", async () => {
162
- // given
163
- const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-project-sot-home-"));
164
- const workspace = mkdtempSync(join(tmpdir(), "omo-codegraph-project-sot-workspace-"));
165
- const stdout: string[] = [];
166
- const spawned: WorkerSpawnInvocation[] = [];
167
- try {
168
- mkdirSync(join(homeDir, ".omo"), { recursive: true });
169
- mkdirSync(join(workspace, ".omo"), { recursive: true });
170
- writeFileSync(join(homeDir, ".omo", "omo.jsonc"), '{ "codegraph": { "enabled": true } }\n');
171
- writeFileSync(join(workspace, ".omo", "omo.jsonc"), '{ "[codex]": { "codegraph": { "enabled": false } } }\n');
172
-
173
- // when
174
- const result = await executeCodegraphSessionStartHook({
175
- cwd: workspace,
176
- env: { HOME: homeDir },
177
- stdin: Readable.from(["{}"]),
178
- stdout: { write: (chunk) => stdout.push(chunk) },
179
- sweepZombies: () => undefined,
180
- spawnWorker: (invocation) => spawned.push(invocation),
181
- });
182
-
183
- // then
184
- expect(result).toEqual({ action: "skipped-disabled", exitCode: 0 });
185
- expect(spawned).toEqual([]);
186
- expect(stdout.join("")).toBe("");
187
- } finally {
188
- rmSync(homeDir, { recursive: true, force: true });
189
- rmSync(workspace, { recursive: true, force: true });
190
- }
191
- });
192
-
193
- it("#given env disables CodeGraph over SOT enablement #when SessionStart fires #then it skips without spawning", async () => {
194
- // given
195
- const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-env-home-"));
196
- const stdout: string[] = [];
197
- const spawned: WorkerSpawnInvocation[] = [];
198
- try {
199
- mkdirSync(join(homeDir, ".omo"), { recursive: true });
200
- writeFileSync(join(homeDir, ".omo", "omo.jsonc"), '{ "codegraph": { "enabled": true } }\n');
201
-
202
- // when
203
- const result = await executeCodegraphSessionStartHook({
204
- env: { CODEX_CODEGRAPH_ENABLED: "0", HOME: homeDir },
205
- stdin: Readable.from(["{}"]),
206
- stdout: { write: (chunk) => stdout.push(chunk) },
207
- sweepZombies: () => undefined,
208
- spawnWorker: (invocation) => spawned.push(invocation),
209
- });
210
-
211
- // then
212
- expect(result).toEqual({ action: "skipped-disabled", exitCode: 0 });
213
- expect(spawned).toEqual([]);
214
- } finally {
215
- rmSync(homeDir, { recursive: true, force: true });
216
- }
217
- });
218
-
219
- it("#given CodeGraph is enabled #when SessionStart fires #then it detaches a background worker immediately", async () => {
220
- // given
221
- const stdout: string[] = [];
222
- const spawned: WorkerSpawnInvocation[] = [];
223
- const workspace = createAllowedWorkspace("codegraph-workspace");
224
- const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-spawn-home-"));
225
-
226
- try {
227
- // when
228
- const result = await executeCodegraphSessionStartHook({
229
- ancestorProbe: () => ({ kind: "uninitialized" }),
230
- config: { codegraph: { enabled: true }, sources: [], warnings: [] },
231
- cwd: workspace,
232
- env: { HOME: homeDir, KEEP: "1", OPENAI_API_KEY: "sk-test-secret" },
233
- stdin: Readable.from(["{}"]),
234
- stdout: { write: (chunk) => stdout.push(chunk) },
235
- spawnWorker: (invocation) => {
236
- spawned.push(invocation);
237
- },
238
- sweepZombies: () => undefined,
239
- workerCliPath: "/plugin/components/codegraph/dist/cli.js",
240
- });
241
-
242
- // then
243
- expect(result).toEqual({ action: "spawned", exitCode: 0 });
244
- expect(spawned).toHaveLength(1);
245
- expect(spawned[0]?.args).toEqual(["/plugin/components/codegraph/dist/cli.js", "hook", "session-start-worker"]);
246
- expect(spawned[0]?.command).toBe(process.execPath);
247
- expect(spawned[0]?.env["HOME"]).toBe(homeDir);
248
- expect(spawned[0]?.env["OMO_CODEGRAPH_SESSION_START_CWD"]).toBe(workspace);
249
- expect(spawned[0]?.env["OMO_CODEGRAPH_SESSION_START_LOCK_TOKEN"]).toMatch(/^[0-9a-f-]{36}$/);
250
- expect(spawned[0]?.env["OPENAI_API_KEY"]).toBeUndefined();
251
- expect(spawned[0]?.env["KEEP"]).toBeUndefined();
252
- expect(JSON.parse(stdout.join(""))).toEqual({
253
- hookSpecificOutput: {
254
- additionalContext: "LazyCodex CodeGraph bootstrap scheduled in background",
255
- hookEventName: "SessionStart",
256
- },
257
- });
258
- } finally {
259
- rmSync(homeDir, { recursive: true, force: true });
260
- rmSync(workspace, { recursive: true, force: true });
261
- }
262
- });
263
-
264
- it("#given CodeGraph is already initialized #when SessionStart fires #then it stays silent without spawning", async () => {
265
- // given
266
- const stdout: string[] = [];
267
- const spawned: WorkerSpawnInvocation[] = [];
268
- const workspace = createAllowedWorkspace("codegraph-initialized-workspace");
269
- const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-initialized-home-"));
270
-
271
- try {
272
- mkdirSync(join(workspace, ".codegraph"), { recursive: true });
273
- writeFileSync(join(workspace, ".codegraph", "codegraph.db"), "fixture");
274
-
275
- // when
276
- const result = await executeCodegraphSessionStartHook({
277
- ancestorProbe: () => {
278
- throw new Error("an initialized exact root must not invoke any fallback probe");
279
- },
280
- config: { codegraph: { enabled: true }, sources: [], warnings: [] },
281
- cwd: workspace,
282
- env: { HOME: homeDir, KEEP: "1" },
283
- stdin: Readable.from(["{}"]),
284
- stdout: { write: (chunk) => stdout.push(chunk) },
285
- sweepZombies: () => undefined,
286
- spawnWorker: (invocation) => spawned.push(invocation),
287
- });
288
-
289
- // then
290
- expect(result).toEqual({ action: "skipped-initialized", exitCode: 0 });
291
- expect(spawned).toEqual([]);
292
- expect(stdout.join("")).toBe("");
293
- } finally {
294
- rmSync(homeDir, { recursive: true, force: true });
295
- rmSync(workspace, { recursive: true, force: true });
296
- }
297
- });
298
-
299
- it("#given malformed hook input with CodeGraph disabled #when SessionStart fires #then it stays silent and exits zero", async () => {
300
- // given
301
- const stdout: string[] = [];
302
- const spawned: WorkerSpawnInvocation[] = [];
303
-
304
- // when
305
- const result = await executeCodegraphSessionStartHook({
306
- config: { codegraph: { enabled: false }, sources: [], warnings: [] },
307
- env: {},
308
- stdin: Readable.from(["{not-json"]),
309
- stdout: { write: (chunk) => stdout.push(chunk) },
310
- sweepZombies: () => undefined,
311
- spawnWorker: (invocation) => spawned.push(invocation),
312
- });
313
-
314
- // then
315
- expect(result.exitCode).toBe(0);
316
- expect(spawned).toEqual([]);
317
- expect(stdout.join("")).toBe("");
318
- });
319
-
320
- });
@@ -1,172 +0,0 @@
1
- import { chmodSync, writeFileSync } from "node:fs";
2
-
3
- export const CODEGRAPH_150_DEFAULT_TOOLS: readonly Record<string, unknown>[] = [
4
- {
5
- name: "codegraph_explore",
6
- description:
7
- "PRIMARY TOOL — call FIRST for almost any question OR before an edit: how does X work, architecture, a bug, where/what is X, surveying an area, or the symbols you are about to change. Returns the verbatim source of the relevant symbols grouped by file in ONE capped call (Read-equivalent — treat the shown source as already Read; do NOT re-open those files), plus the call path among them. Query can be a natural-language question OR a bag of symbol/file names. Usually the ONLY call you need — more accurate context, in far fewer tokens and round-trips than a search/Read/Grep loop.",
8
- inputSchema: {
9
- type: "object",
10
- properties: {
11
- query: {
12
- type: "string",
13
- description:
14
- 'Symbol names, file names, or short code terms to explore (e.g., "AuthService loginUser session-manager", "GraphTraverser BFS impact traversal.ts"). For a flow question, name the symbols spanning the flow (e.g. "mutateElement renderScene"). A natural-language question works too — no prior codegraph_search needed.',
15
- },
16
- maxFiles: {
17
- type: "number",
18
- description: "Maximum number of files to include source code from (default: 12)",
19
- default: 12,
20
- },
21
- projectPath: {
22
- type: "string",
23
- description:
24
- "Absolute path to the project to query (or any directory inside it) — codegraph uses the nearest .codegraph/ index at or above that path. Omit to use this session's default project. Pass it to query a second codebase, or when the server root has no index of its own (e.g. a monorepo where only sub-projects are indexed, so there is no default project).",
25
- },
26
- },
27
- required: ["query"],
28
- },
29
- },
30
- ];
31
-
32
- export function writeFakeNewlineCodegraph(filePath: string): void {
33
- writeFileSync(
34
- filePath,
35
- [
36
- "#!/usr/bin/env node",
37
- "const fs = require('node:fs');",
38
- "const readline = require('node:readline');",
39
- "fs.writeFileSync(process.env.CODEGRAPH_FAKE_LOG, `cwd=${process.cwd()}\\n`);",
40
- "const rl = readline.createInterface({ input: process.stdin });",
41
- "rl.on('line', (line) => {",
42
- " const request = JSON.parse(line);",
43
- " if (request.method === 'initialize') {",
44
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { capabilities: { tools: { listChanged: false } }, protocolVersion: request.params.protocolVersion, serverInfo: { name: 'codegraph', version: '1.5.0' } } }) + '\\n');",
45
- " }",
46
- " if (request.method === 'tools/list') {",
47
- ` process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { tools: ${JSON.stringify(CODEGRAPH_150_DEFAULT_TOOLS)} } }) + '\\n');`,
48
- " }",
49
- "});",
50
- ].join("\n"),
51
- );
52
- chmodSync(filePath, 0o755);
53
- }
54
-
55
- export function writeFakeContractCodegraph(filePath: string): void {
56
- writeFileSync(
57
- filePath,
58
- [
59
- "#!/usr/bin/env node",
60
- "const fs = require('node:fs');",
61
- "const readline = require('node:readline');",
62
- "fs.writeFileSync(process.env.CODEGRAPH_FAKE_LOG, `cwd=${process.cwd()}\\n`);",
63
- "const rl = readline.createInterface({ input: process.stdin });",
64
- "rl.on('line', (line) => {",
65
- " const request = JSON.parse(line);",
66
- " if (request.method === 'tools/list') {",
67
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { tools: [",
68
- " { name: 'codegraph_search', description: 'search stays unchanged' },",
69
- " { name: 'codegraph_node', description: 'ONE SYMBOL you can name - its location, signature, verbatim source (includeCode=true) and caller/callee trail in one call', inputSchema: { type: 'object', properties: { includeCode: { type: 'boolean', description: \"Symbol mode: include the symbol's full body (default: false).\" }, symbol: { type: 'string' } } } }",
70
- " ] } }) + '\\n');",
71
- " }",
72
- " if (request.method === 'tools/call') {",
73
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { content: [{ type: 'text', text: 'JsonlMCP\\nMembers (7)\\nStructural outline only. For full source, use the Read tool on this file or request a member.' }] } }) + '\\n');",
74
- " }",
75
- "});",
76
- ].join("\n"),
77
- );
78
- chmodSync(filePath, 0o755);
79
- }
80
-
81
- export function writeFakeHeldOpenCodegraph(filePath: string): void {
82
- writeFileSync(
83
- filePath,
84
- [
85
- "#!/usr/bin/env node",
86
- "const fs = require('node:fs');",
87
- "const readline = require('node:readline');",
88
- "fs.writeFileSync(process.env.CODEGRAPH_FAKE_LOG, String(process.pid));",
89
- "const rl = readline.createInterface({ input: process.stdin });",
90
- "rl.once('line', (line) => {",
91
- " const request = JSON.parse(line);",
92
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { ok: true } }) + '\\n');",
93
- "});",
94
- "setInterval(() => {}, 1000);",
95
- ].join("\n"),
96
- );
97
- chmodSync(filePath, 0o755);
98
- }
99
-
100
- export function writeFakeExitingCodegraph(filePath: string): void {
101
- writeFileSync(
102
- filePath,
103
- [
104
- "#!/usr/bin/env node",
105
- "const readline = require('node:readline');",
106
- "const rl = readline.createInterface({ input: process.stdin });",
107
- "rl.once('line', (line) => {",
108
- " const request = JSON.parse(line);",
109
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { ok: true } }) + '\\n', () => process.exit(0));",
110
- "});",
111
- ].join("\n"),
112
- );
113
- chmodSync(filePath, 0o755);
114
- }
115
-
116
- export function frameMcpRequest(request: {
117
- readonly id: number;
118
- readonly method: string;
119
- readonly params: Record<string, unknown>;
120
- }): string {
121
- const body = JSON.stringify({ jsonrpc: "2.0", ...request });
122
- return `Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`;
123
- }
124
-
125
- export function parseMcpBodies(transcript: string): readonly unknown[] {
126
- const bodies: unknown[] = [];
127
- let cursor = 0;
128
- while (cursor < transcript.length) {
129
- const headerEnd = transcript.indexOf("\r\n\r\n", cursor);
130
- if (headerEnd === -1) break;
131
- const header = transcript.slice(cursor, headerEnd);
132
- const match = /^Content-Length: (?<length>\d+)$/m.exec(header);
133
- if (match?.groups?.["length"] === undefined) break;
134
- const length = Number.parseInt(match.groups["length"], 10);
135
- const bodyStart = headerEnd + 4;
136
- const body = transcript.slice(bodyStart, bodyStart + length);
137
- bodies.push(JSON.parse(body));
138
- cursor = bodyStart + length;
139
- }
140
- return bodies;
141
- }
142
-
143
- export function findTool(tools: readonly unknown[], name: string): Record<string, unknown> {
144
- for (const tool of tools) {
145
- const record = recordValue(tool);
146
- if (record["name"] === name) return record;
147
- }
148
- throw new Error(`Missing tool ${name}`);
149
- }
150
-
151
- export function arrayProperty(record: Record<string, unknown>, key: string): readonly unknown[] {
152
- const value = record[key];
153
- if (!Array.isArray(value)) throw new Error(`Expected ${key} to be an array`);
154
- return value;
155
- }
156
-
157
- export function recordProperty(record: Record<string, unknown>, key: string): Record<string, unknown> {
158
- return recordValue(record[key]);
159
- }
160
-
161
- export function stringProperty(record: Record<string, unknown>, key: string): string {
162
- const value = record[key];
163
- if (typeof value !== "string") throw new Error(`Expected ${key} to be a string`);
164
- return value;
165
- }
166
-
167
- export function recordValue(value: unknown): Record<string, unknown> {
168
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
169
- throw new Error("Expected record value");
170
- }
171
- return value as Record<string, unknown>;
172
- }
@@ -1,91 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { dirname, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- type CodegraphPackageJson = {
7
- readonly files?: readonly string[];
8
- readonly optionalDependencies?: Readonly<Record<string, string>>;
9
- };
10
-
11
- const componentRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
12
- const repoRoot = resolve(componentRoot, "../../../../..");
13
-
14
- function readCodegraphPackageJson(): CodegraphPackageJson {
15
- const parsed: unknown = JSON.parse(readFileSync(resolve(componentRoot, "package.json"), "utf8"));
16
- if (!isCodegraphPackageJson(parsed)) throw new TypeError("Invalid CodeGraph package metadata");
17
- return parsed;
18
- }
19
-
20
- function isCodegraphPackageJson(value: unknown): value is CodegraphPackageJson {
21
- return isRecord(value) && maybeStringArray(value["files"]) && maybeStringRecord(value["optionalDependencies"]);
22
- }
23
-
24
- function maybeStringArray(value: unknown): value is readonly string[] | undefined {
25
- return value === undefined || (Array.isArray(value) && value.every((item) => typeof item === "string"));
26
- }
27
-
28
- function maybeStringRecord(value: unknown): value is Readonly<Record<string, string>> | undefined {
29
- return value === undefined || (isRecord(value) && Object.values(value).every((item) => typeof item === "string"));
30
- }
31
-
32
- function isRecord(value: unknown): value is Record<string, unknown> {
33
- return typeof value === "object" && value !== null && !Array.isArray(value);
34
- }
35
-
36
- describe("CodeGraph component runtime package metadata", () => {
37
- it("#given the CodeGraph component package #when validating runtime distribution metadata #then upstream CodeGraph installs optionally with shipped legal files", () => {
38
- // given
39
- const packageJson = readCodegraphPackageJson();
40
-
41
- // when
42
- const optionalDependencies = packageJson.optionalDependencies ?? {};
43
- const files = packageJson.files ?? [];
44
-
45
- // then
46
- expect(optionalDependencies["@colbymchenry/codegraph"]).toBe("1.5.0");
47
- expect(files).toContain("LICENSE");
48
- expect(files).toContain("NODE-RUNTIME-LICENSES.md");
49
- expect(files).toContain("NOTICE");
50
- expect(existsSync(resolve(componentRoot, "LICENSE"))).toBe(true);
51
- expect(existsSync(resolve(componentRoot, "NODE-RUNTIME-LICENSES.md"))).toBe(true);
52
- expect(existsSync(resolve(componentRoot, "NOTICE"))).toBe(true);
53
- expect(existsSync(resolve(componentRoot, "dist", "cli.d.ts"))).toBe(false);
54
- expect(existsSync(resolve(componentRoot, "dist", "serve.d.ts"))).toBe(false);
55
- });
56
-
57
- it("#given marketplace payload includes only the plugin tree #when reading CodeGraph NOTICE #then it is self-contained", () => {
58
- // given
59
- const notice = readFileSync(resolve(componentRoot, "NOTICE"), "utf8");
60
-
61
- expect(notice).toContain("@colbymchenry/codegraph@1.5.0");
62
- expect(notice).toContain("MIT license");
63
- expect(notice).toContain("Node.js v24.16.0 runtime");
64
- expect(notice).toContain("NODE-RUNTIME-LICENSES.md");
65
- expect(notice).not.toContain("packages/omo-codex/THIRD-PARTY-NOTICES.md");
66
- });
67
-
68
- it("#given CodeGraph platform bundles include Node #when reading runtime licenses #then Node license text ships with the component", () => {
69
- // given
70
- const licenseText = readFileSync(resolve(componentRoot, "NODE-RUNTIME-LICENSES.md"), "utf8");
71
-
72
- // then
73
- expect(licenseText).toContain("Node.js v24.16.0 LICENSE text");
74
- expect(licenseText).toContain("Copyright Node.js contributors");
75
- expect(licenseText).toContain("The externally maintained libraries used by Node.js are:");
76
- });
77
-
78
- it("#given third-party notices #when validating CodeGraph component notices #then aggregate Codex notices list CodeGraph as shipped", () => {
79
- // given
80
- const codexNotice = readFileSync(resolve(repoRoot, "packages/omo-codex/THIRD-PARTY-NOTICES.md"), "utf8");
81
-
82
- // when
83
- const listsCodegraphComponent = codexNotice.includes("@sisyphuslabs/codex-codegraph");
84
- const listsUpstreamRuntime = codexNotice.includes("@colbymchenry/codegraph");
85
-
86
- // then
87
- expect(listsCodegraphComponent).toBe(true);
88
- expect(listsUpstreamRuntime).toBe(true);
89
- expect(codexNotice).not.toContain("not currently an omo-codex plugin component");
90
- });
91
- });
@@ -1,100 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
-
6
- import { CODEGRAPH_PINNED_VERSION } from "../../../../../utils/src/codegraph/manifest.ts";
7
- import { runCodegraphSessionStartWorker } from "../src/hook.ts";
8
- import { runCodegraphServe } from "../src/serve.ts";
9
-
10
- describe("CodeGraph provisioned launcher Node guard", () => {
11
- it("#given provisioned CodeGraph binary #when serve runs under unsupported local Node #then it trusts the launcher", async () => {
12
- // given
13
- const commandPath = "/home/test/.omo/codegraph/bin/codegraph";
14
- const spawned: Array<{ readonly args: readonly string[]; readonly command: string }> = [];
15
-
16
- // when
17
- const exitCode = await runCodegraphServe({
18
- env: {},
19
- nodeVersion: "26.3.0",
20
- buildEnv: () => ({}),
21
- resolve: () => ({ argsPrefix: [], command: commandPath, exists: true, source: "provisioned" }),
22
- runProcess: (command, args) => {
23
- spawned.push({ args, command });
24
- return Promise.resolve(0);
25
- },
26
- stderr: { write: () => undefined },
27
- });
28
-
29
- // then
30
- expect(exitCode).toBe(0);
31
- expect(spawned).toEqual([{ args: ["serve", "--mcp"], command: commandPath }]);
32
- });
33
-
34
- it("#given provisioned CodeGraph exists #when SessionStart worker runs under unsupported local Node #then it bootstraps through the launcher", async () => {
35
- // given
36
- const workspace = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-node25-"));
37
- const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-node25-home-"));
38
- const installDir = mkdtempSync(join(tmpdir(), "omo-codegraph-worker-node25-install-"));
39
- const binPath = join(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
40
- const calls: Array<{ readonly args: readonly string[]; readonly command: string }> = [];
41
- const outcomes: unknown[] = [];
42
-
43
- try {
44
- mkdirSync(join(installDir, "bin"), { recursive: true });
45
- mkdirSync(join(installDir, ".provisioned"), { recursive: true });
46
- writeFileSync(binPath, "");
47
- writeFileSync(
48
- join(installDir, ".provisioned", `codegraph-${CODEGRAPH_PINNED_VERSION}.json`),
49
- `${JSON.stringify({ binPath, version: CODEGRAPH_PINNED_VERSION })}\n`,
50
- );
51
-
52
- // when
53
- const result = await runCodegraphSessionStartWorker({
54
- config: { codegraph: { enabled: true, install_dir: installDir }, sources: [], trustedCodegraphInstallDir: installDir, warnings: [] },
55
- nodeVersion: "26.3.0",
56
- cwd: workspace,
57
- env: { HOME: homeDir },
58
- logOutcome: (outcome) => outcomes.push(outcome),
59
- deps: {
60
- ensureGitignored: () => true,
61
- ensureProvisioned: () => {
62
- throw new Error("provisioning should not run when install_dir binary exists");
63
- },
64
- prepareWorkspace: () => ({
65
- dataDir: join(homeDir, ".omo/codegraph/projects/test"),
66
- dataRoot: join(homeDir, ".omo/codegraph"),
67
- linked: true,
68
- mode: "global-linked",
69
- projectLink: join(workspace, ".codegraph"),
70
- }),
71
- resolveCommand: (options) => {
72
- const provisioned = options?.provisioned?.() ?? null;
73
- return {
74
- argsPrefix: [],
75
- command: provisioned ?? "missing-codegraph",
76
- exists: provisioned !== null,
77
- source: provisioned === null ? "path" : "provisioned",
78
- };
79
- },
80
- resolveManagedBin: () => binPath,
81
- runCommand: (_projectRoot, command, args) => {
82
- calls.push({ args, command });
83
- mkdirSync(join(workspace, ".codegraph"), { recursive: true });
84
- writeFileSync(join(workspace, ".codegraph", "codegraph.db"), "fixture");
85
- return Promise.resolve({ exitCode: 0, stdout: "", timedOut: false });
86
- },
87
- },
88
- });
89
-
90
- // then
91
- expect(result).toEqual({ action: "initialized" });
92
- expect(calls).toEqual([{ args: ["init"], command: binPath }]);
93
- expect(outcomes).toEqual([{ action: "initialized", exitCode: 0, projectRoot: workspace, source: "provisioned", timedOut: false }]);
94
- } finally {
95
- rmSync(workspace, { recursive: true, force: true });
96
- rmSync(homeDir, { recursive: true, force: true });
97
- rmSync(installDir, { recursive: true, force: true });
98
- }
99
- });
100
- });