oh-my-opencode 5.0.0-beta.33 → 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.
- package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +1 -3
- package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +1 -1
- package/.agents/skills/tech-debt-audit/SKILL.md +6 -76
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.zh-cn.md +1 -1
- package/THIRD-PARTY-NOTICES.md +27 -99
- package/dist/cli/index.js +48350 -48559
- package/dist/cli-node/index.js +48480 -48689
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schema/hooks.d.ts +0 -1
- package/dist/config/schema/oh-my-opencode-config.d.ts +0 -10
- package/dist/config/schema.d.ts +0 -1
- package/dist/create-hooks.d.ts +0 -1
- package/dist/hooks/index.d.ts +0 -1
- package/dist/index.js +11675 -13742
- package/dist/mcp/index.d.ts +0 -4
- package/dist/mcp/types.d.ts +0 -1
- package/dist/oh-my-opencode.schema.json +1 -45
- package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
- package/dist/shared/omo-process-sweep.d.ts +2 -4
- package/dist/skills/init-deep/SKILL.md +13 -13
- package/dist/skills/ulw-plan/SKILL.md +0 -1
- package/dist/tui.js +44809 -44910
- package/package.json +16 -18
- package/packages/omo-codex/THIRD-PARTY-NOTICES.md +0 -17
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -3
- package/packages/omo-codex/plugin/.mcp.json +0 -6
- package/packages/omo-codex/plugin/AGENTS.md +5 -5
- package/packages/omo-codex/plugin/README.md +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +1 -15
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-doctor/SKILL.md +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +0 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +11 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +12 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +45 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.js +5 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.js +10 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +35 -38
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +278 -193
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.d.ts +5 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.js +7 -51
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-artifacts.js +39 -22
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-fields.d.ts +13 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-fields.js +64 -8
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +5 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +37 -18
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +81 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-reconciliation.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-template.ts +10 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +32 -63
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-aggregate.ts +7 -53
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-artifacts.ts +50 -31
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-fields.ts +67 -14
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +6 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +36 -17
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-final.test.ts +26 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-template.test.ts +47 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-aggregate-basics.test.ts +258 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-cap-and-dedupe.test.ts +22 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-poisoning-cascades.test.ts +97 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-single-report.test.ts +241 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -146
- package/packages/omo-codex/plugin/package.json +1 -2
- package/packages/omo-codex/plugin/scripts/AGENTS.md +0 -1
- package/packages/omo-codex/plugin/scripts/auto-update-plan.mjs +0 -1
- package/packages/omo-codex/plugin/scripts/auto-update.mjs +0 -9
- package/packages/omo-codex/plugin/scripts/canonical-ultrawork-directive.mjs +17 -0
- package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +0 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +2 -8
- package/packages/omo-codex/plugin/shared/src/config-loader.ts +2 -102
- package/packages/omo-codex/plugin/shared/src/config-migration.ts +0 -1
- package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +41 -130
- package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +13 -13
- package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +0 -1
- package/packages/omo-codex/plugin/test/AGENTS.md +2 -2
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +2 -21
- package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -2
- package/packages/omo-codex/plugin/test/aggregate-mcp.test.mjs +2 -7
- package/packages/omo-codex/plugin/test/auto-update.test.mjs +0 -25
- package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +0 -31
- package/packages/omo-codex/plugin/test/canonical-ultrawork-directive.test.mjs +53 -0
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +0 -50
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +0 -1
- package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -3
- package/packages/omo-codex/scripts/install-cache-copy.test.mjs +27 -1
- package/packages/omo-codex/scripts/install-config-git-bash.test.mjs +0 -23
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +238 -431
- package/packages/omo-codex/scripts/install-git-bash-mcp-env.test.mjs +1 -25
- package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +0 -36
- package/packages/shared-skills/skills/init-deep/SKILL.md +13 -13
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +0 -1
- package/dist/config/schema/codegraph.d.ts +0 -12
- package/dist/hooks/codegraph-bootstrap/command-runner.d.ts +0 -17
- package/dist/hooks/codegraph-bootstrap/hook.d.ts +0 -35
- package/dist/hooks/codegraph-bootstrap/index.d.ts +0 -1
- package/dist/hooks/codegraph-bootstrap/project-root.d.ts +0 -1
- package/dist/hooks/codegraph-bootstrap/status.d.ts +0 -11
- package/dist/mcp/codegraph.d.ts +0 -16
- package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +0 -58
- package/packages/omo-codex/plugin/components/codegraph/LICENSE +0 -21
- package/packages/omo-codex/plugin/components/codegraph/NODE-RUNTIME-LICENSES.md +0 -2951
- package/packages/omo-codex/plugin/components/codegraph/NOTICE +0 -16
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +0 -12326
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +0 -9908
- package/packages/omo-codex/plugin/components/codegraph/package.json +0 -33
- package/packages/omo-codex/plugin/components/codegraph/src/cache-gc.ts +0 -29
- package/packages/omo-codex/plugin/components/codegraph/src/cli.ts +0 -79
- package/packages/omo-codex/plugin/components/codegraph/src/hook-input.ts +0 -33
- package/packages/omo-codex/plugin/components/codegraph/src/hook-sweep.ts +0 -25
- package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +0 -131
- package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +0 -258
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +0 -310
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +0 -92
- package/packages/omo-codex/plugin/components/codegraph/src/post-tool-use-hook.ts +0 -34
- package/packages/omo-codex/plugin/components/codegraph/src/serve-invocation.ts +0 -29
- package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +0 -263
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-command.ts +0 -106
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-cooldown.ts +0 -145
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-hook-runtime.ts +0 -22
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-lock.ts +0 -139
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-outcome.ts +0 -15
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-paths.ts +0 -32
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-project.ts +0 -109
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker-result.ts +0 -148
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +0 -175
- package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +0 -81
- package/packages/omo-codex/plugin/components/codegraph/test/cache-gc.test.ts +0 -34
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +0 -112
- package/packages/omo-codex/plugin/components/codegraph/test/hook-registration.test.ts +0 -26
- package/packages/omo-codex/plugin/components/codegraph/test/hook-session-start-guard.test.ts +0 -160
- package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +0 -50
- package/packages/omo-codex/plugin/components/codegraph/test/hook-sweep.test.ts +0 -29
- package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +0 -320
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +0 -172
- package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +0 -91
- package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +0 -100
- package/packages/omo-codex/plugin/components/codegraph/test/serve-built-wrapper.test.ts +0 -76
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +0 -133
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +0 -245
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +0 -69
- package/packages/omo-codex/plugin/components/codegraph/test/serve-node-support.test.ts +0 -43
- package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +0 -147
- package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable-idle.test.ts +0 -47
- package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +0 -175
- package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +0 -385
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +0 -206
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-project.test.ts +0 -58
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-state.test.ts +0 -104
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-trust-boundary.test.ts +0 -65
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-availability.test.ts +0 -106
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-cooldown.test.ts +0 -116
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +0 -258
- package/packages/omo-codex/plugin/components/codegraph/test/sweep-cli.test.ts +0 -56
- package/packages/omo-codex/plugin/components/codegraph/tsconfig.build.json +0 -13
- package/packages/omo-codex/plugin/components/codegraph/tsconfig.json +0 -25
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-aggregate.test.ts +0 -101
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +0 -18
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +0 -17
- package/packages/omo-codex/plugin/scripts/migrate-omo-sot/editor.mjs +0 -125
- package/packages/omo-codex/plugin/scripts/migrate-omo-sot/jsonc.mjs +0 -43
- package/packages/omo-codex/plugin/scripts/migrate-omo-sot/scaffold.mjs +0 -29
- package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +0 -121
- package/packages/omo-codex/plugin/test/component-codegraph-mcp-smoke.test.mjs +0 -76
- 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
|
-
});
|