oh-my-opencode 4.12.0 → 4.12.1
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/dist/cli/index.js +36 -17
- package/dist/cli-node/index.js +36 -17
- package/dist/index.js +361 -254
- package/dist/skills/ultraresearch/SKILL.md +11 -2
- package/dist/tools/background-task/constants.d.ts +1 -1
- package/dist/tui.js +9 -3
- package/package.json +13 -13
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +3 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +327 -171
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +275 -31
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/cli.ts +11 -0
- package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +12 -0
- package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +34 -0
- package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +40 -3
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +6 -5
- package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +49 -87
- package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +48 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +1 -0
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +142 -0
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/dist/cli.js +94 -0
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +17 -0
- package/packages/omo-codex/plugin/components/teammode/package.json +26 -0
- package/packages/omo-codex/plugin/components/teammode/src/cli.ts +12 -0
- package/packages/omo-codex/plugin/components/teammode/src/codex-hook.ts +125 -0
- package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +116 -0
- package/packages/omo-codex/plugin/components/teammode/tsconfig.json +25 -0
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +17 -0
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +30 -12
- package/packages/omo-codex/plugin/package.json +2 -1
- package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +1 -0
- package/packages/omo-codex/plugin/skills/ultraresearch/SKILL.md +11 -2
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +36 -0
- package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +2 -1
- package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +1 -1
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/component-hook-contract-cases.mjs +24 -0
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/ultraresearch-skill-contract.test.mjs +48 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +14 -1
- package/packages/shared-skills/skills/ultraresearch/SKILL.md +11 -2
|
@@ -8,13 +8,205 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
8
8
|
|
|
9
9
|
// components/codegraph/src/hook.ts
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
11
|
-
import { homedir as
|
|
11
|
+
import { homedir as homedir8 } from "node:os";
|
|
12
12
|
import { cwd as processCwd2, env as processEnv2, stdin as processStdin, stdout as processStdout } from "node:process";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
|
|
15
|
-
// ../../utils/src/
|
|
16
|
-
import {
|
|
15
|
+
// ../../utils/src/codegraph/guidance.ts
|
|
16
|
+
import { homedir as homedir2 } from "node:os";
|
|
17
|
+
|
|
18
|
+
// ../../utils/src/codegraph/workspace.ts
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
20
|
+
import {
|
|
21
|
+
appendFileSync,
|
|
22
|
+
existsSync,
|
|
23
|
+
lstatSync,
|
|
24
|
+
mkdirSync,
|
|
25
|
+
readFileSync,
|
|
26
|
+
readdirSync,
|
|
27
|
+
realpathSync,
|
|
28
|
+
rmSync,
|
|
29
|
+
statSync,
|
|
30
|
+
symlinkSync
|
|
31
|
+
} from "node:fs";
|
|
17
32
|
import { homedir } from "node:os";
|
|
33
|
+
import { basename, join, resolve } from "node:path";
|
|
34
|
+
function sanitizeBase(value) {
|
|
35
|
+
const sanitized = value.replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-");
|
|
36
|
+
return sanitized.length > 0 ? sanitized : "workspace";
|
|
37
|
+
}
|
|
38
|
+
function codegraphDataRoot(homeDir) {
|
|
39
|
+
return join(homeDir, ".omo", "codegraph");
|
|
40
|
+
}
|
|
41
|
+
function workspaceStorageName(workspace) {
|
|
42
|
+
const resolved = resolve(workspace);
|
|
43
|
+
const hash = createHash("sha256").update(resolved).digest("hex").slice(0, 16);
|
|
44
|
+
return `${sanitizeBase(basename(resolved))}-${hash}`;
|
|
45
|
+
}
|
|
46
|
+
function resolveCodegraphWorkspacePaths(workspace, options = {}) {
|
|
47
|
+
const resolvedWorkspace = resolve(workspace);
|
|
48
|
+
const dataRoot = codegraphDataRoot(options.homeDir ?? homedir());
|
|
49
|
+
return {
|
|
50
|
+
dataDir: join(dataRoot, "projects", workspaceStorageName(resolvedWorkspace)),
|
|
51
|
+
dataRoot,
|
|
52
|
+
projectLink: join(resolvedWorkspace, ".codegraph")
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function fallbackResult(dataRoot, projectLink, reason) {
|
|
56
|
+
return { dataDir: projectLink, dataRoot, linked: false, mode: "in-place-fallback", projectLink, reason };
|
|
57
|
+
}
|
|
58
|
+
function isSameFilesystem(workspace, dataRoot, override) {
|
|
59
|
+
if (override !== undefined)
|
|
60
|
+
return override;
|
|
61
|
+
return statSync(workspace).dev === statSync(dataRoot).dev;
|
|
62
|
+
}
|
|
63
|
+
function ensureInPlaceFallback(projectLink) {
|
|
64
|
+
if (!existsSync(projectLink))
|
|
65
|
+
mkdirSync(projectLink, { recursive: true });
|
|
66
|
+
}
|
|
67
|
+
function prepareCodegraphWorkspace(workspace, options = {}) {
|
|
68
|
+
const resolvedWorkspace = resolve(workspace);
|
|
69
|
+
const { dataDir, dataRoot, projectLink } = resolveCodegraphWorkspacePaths(resolvedWorkspace, options);
|
|
70
|
+
try {
|
|
71
|
+
mkdirSync(dataDir, { recursive: true });
|
|
72
|
+
if (existsSync(projectLink)) {
|
|
73
|
+
const linkStat = lstatSync(projectLink);
|
|
74
|
+
if (!linkStat.isSymbolicLink()) {
|
|
75
|
+
return { dataDir: projectLink, dataRoot, linked: false, mode: "in-project", projectLink };
|
|
76
|
+
}
|
|
77
|
+
if (realpathSync(projectLink) === realpathSync(dataDir)) {
|
|
78
|
+
return { dataDir, dataRoot, linked: true, mode: "global-linked", projectLink };
|
|
79
|
+
}
|
|
80
|
+
return fallbackResult(dataRoot, projectLink, "existing .codegraph symlink points outside OMO store");
|
|
81
|
+
}
|
|
82
|
+
if (!isSameFilesystem(resolvedWorkspace, dataRoot, options.sameFilesystem)) {
|
|
83
|
+
ensureInPlaceFallback(projectLink);
|
|
84
|
+
return fallbackResult(dataRoot, projectLink, "workspace and OMO store are on different filesystems");
|
|
85
|
+
}
|
|
86
|
+
const symlink = options.symlink ?? symlinkSync;
|
|
87
|
+
symlink(dataDir, projectLink, (options.platform ?? process.platform) === "win32" ? "junction" : "dir");
|
|
88
|
+
return { dataDir, dataRoot, linked: true, mode: "global-linked", projectLink };
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
91
|
+
try {
|
|
92
|
+
ensureInPlaceFallback(projectLink);
|
|
93
|
+
} catch (fallbackError) {
|
|
94
|
+
return fallbackResult(dataRoot, projectLink, `${reason}; fallback failed: ${String(fallbackError)}`);
|
|
95
|
+
}
|
|
96
|
+
return fallbackResult(dataRoot, projectLink, reason);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function ensureCodegraphGitignored(workspace) {
|
|
100
|
+
const gitDir = join(workspace, ".git");
|
|
101
|
+
if (!existsSync(gitDir))
|
|
102
|
+
return false;
|
|
103
|
+
const excludePath = join(gitDir, "info", "exclude");
|
|
104
|
+
try {
|
|
105
|
+
mkdirSync(join(gitDir, "info"), { recursive: true });
|
|
106
|
+
const existing = existsSync(excludePath) ? readFileSync(excludePath, "utf8") : "";
|
|
107
|
+
if (existing.split(/\r?\n/).includes(".codegraph"))
|
|
108
|
+
return true;
|
|
109
|
+
appendFileSync(excludePath, `${existing.endsWith(`
|
|
110
|
+
`) || existing.length === 0 ? "" : `
|
|
111
|
+
`}.codegraph
|
|
112
|
+
`);
|
|
113
|
+
return true;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (error instanceof Error)
|
|
116
|
+
return false;
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ../../utils/src/codegraph/guidance.ts
|
|
122
|
+
var CODEGRAPH_UNINITIALIZED_PATTERN = /CodeGraph not initialized in ([\s\S]*?)\.\s*Run ['`]codegraph init['`] in that project first\./i;
|
|
123
|
+
var ANSI_ESCAPE_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
124
|
+
var CODEGRAPH_STATUS_PROJECT_PATTERN = /^.*?\bProject:\s*(.+?)\s*$/im;
|
|
125
|
+
var CODEGRAPH_STATUS_UNINITIALIZED_PATTERN = /^.*?\bNot initialized\s*$/im;
|
|
126
|
+
var CODEGRAPH_INIT_HINT_PATTERN = /Run\s+["'`]codegraph init["'`]\s+(?:in that project first|to initialize)\.?/i;
|
|
127
|
+
function getCodegraphUninitializedProject(input) {
|
|
128
|
+
const output = textFromUnknown(input.toolOutput);
|
|
129
|
+
if (!isCodegraphTool(input.toolName))
|
|
130
|
+
return null;
|
|
131
|
+
const projectPath = extractProjectPath(output);
|
|
132
|
+
if (projectPath !== null)
|
|
133
|
+
return projectPath;
|
|
134
|
+
if (!looksLikeCodegraphUninitializedOutput(output))
|
|
135
|
+
return null;
|
|
136
|
+
return typeof input.cwd === "string" && input.cwd.trim().length > 0 ? input.cwd.trim() : null;
|
|
137
|
+
}
|
|
138
|
+
function buildCodegraphInitGuidance(projectPath, options = {}) {
|
|
139
|
+
const { dataDir, dataRoot, projectLink } = resolveCodegraphWorkspacePaths(projectPath, {
|
|
140
|
+
homeDir: options.homeDir ?? homedir2()
|
|
141
|
+
});
|
|
142
|
+
const displayProjectPath = formatDisplayPath(projectPath);
|
|
143
|
+
const displayProjectLink = formatDisplayPath(projectLink);
|
|
144
|
+
const displayDataDir = formatDisplayPath(dataDir);
|
|
145
|
+
const displayDataRoot = formatDisplayPath(dataRoot);
|
|
146
|
+
return [
|
|
147
|
+
"OMO CodeGraph initialization guidance:",
|
|
148
|
+
"",
|
|
149
|
+
`CodeGraph is not initialized for ${displayProjectPath}. Initialize it through OMO's global local store instead of leaving a standalone project-local index.`,
|
|
150
|
+
"",
|
|
151
|
+
`- Link or create ${displayProjectLink} so it points at ${displayDataDir} under the OMO store ${displayDataRoot}.`,
|
|
152
|
+
`- Then run \`codegraph init\` from ${displayProjectPath} and retry the CodeGraph tool.`,
|
|
153
|
+
"- OMO's CodeGraph bootstrap does this automatically on session start; if bootstrap just ran, wait for it to finish and retry."
|
|
154
|
+
].join(`
|
|
155
|
+
`);
|
|
156
|
+
}
|
|
157
|
+
function buildCodegraphInitGuidanceForToolResult(input, options = {}) {
|
|
158
|
+
const projectPath = getCodegraphUninitializedProject(input);
|
|
159
|
+
return projectPath === null ? null : buildCodegraphInitGuidance(projectPath, options);
|
|
160
|
+
}
|
|
161
|
+
function extractProjectPath(output) {
|
|
162
|
+
const normalizedOutput = normalizeCodegraphOutput(output);
|
|
163
|
+
const uninitializedMatch = normalizedOutput.match(CODEGRAPH_UNINITIALIZED_PATTERN);
|
|
164
|
+
const uninitializedProject = uninitializedMatch?.[1]?.trim();
|
|
165
|
+
if (uninitializedProject && uninitializedProject.length > 0)
|
|
166
|
+
return uninitializedProject;
|
|
167
|
+
if (!looksLikeCodegraphUninitializedOutput(normalizedOutput))
|
|
168
|
+
return null;
|
|
169
|
+
const statusMatch = normalizedOutput.match(CODEGRAPH_STATUS_PROJECT_PATTERN);
|
|
170
|
+
const statusProject = statusMatch?.[1]?.trim();
|
|
171
|
+
return statusProject && statusProject.length > 0 ? statusProject : null;
|
|
172
|
+
}
|
|
173
|
+
function looksLikeCodegraphUninitializedOutput(output) {
|
|
174
|
+
const normalizedOutput = normalizeCodegraphOutput(output);
|
|
175
|
+
if (normalizedOutput.match(CODEGRAPH_UNINITIALIZED_PATTERN) !== null)
|
|
176
|
+
return true;
|
|
177
|
+
return CODEGRAPH_STATUS_UNINITIALIZED_PATTERN.test(normalizedOutput) && CODEGRAPH_INIT_HINT_PATTERN.test(normalizedOutput);
|
|
178
|
+
}
|
|
179
|
+
function normalizeCodegraphOutput(output) {
|
|
180
|
+
return output.replace(ANSI_ESCAPE_PATTERN, "");
|
|
181
|
+
}
|
|
182
|
+
function isCodegraphTool(toolName) {
|
|
183
|
+
if (typeof toolName !== "string")
|
|
184
|
+
return false;
|
|
185
|
+
return toolName.startsWith("codegraph.") || toolName.startsWith("codegraph_") || toolName.startsWith("mcp__codegraph__");
|
|
186
|
+
}
|
|
187
|
+
function formatDisplayPath(value) {
|
|
188
|
+
return JSON.stringify(value);
|
|
189
|
+
}
|
|
190
|
+
function textFromUnknown(value) {
|
|
191
|
+
if (typeof value === "string")
|
|
192
|
+
return value;
|
|
193
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint")
|
|
194
|
+
return String(value);
|
|
195
|
+
if (Array.isArray(value))
|
|
196
|
+
return value.map(textFromUnknown).filter(Boolean).join(`
|
|
197
|
+
`);
|
|
198
|
+
if (!isRecord(value))
|
|
199
|
+
return "";
|
|
200
|
+
return Object.entries(value).map(([key, nested]) => `${key}: ${textFromUnknown(nested)}`).filter((line) => line.trim().length > 0).join(`
|
|
201
|
+
`);
|
|
202
|
+
}
|
|
203
|
+
function isRecord(value) {
|
|
204
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ../../utils/src/omo-config/loader.ts
|
|
208
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
209
|
+
import { homedir as homedir3 } from "node:os";
|
|
18
210
|
|
|
19
211
|
// ../../utils/src/deep-merge.ts
|
|
20
212
|
var DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
@@ -970,22 +1162,22 @@ function buildEnvOverrides(harness, env, warnings, merge) {
|
|
|
970
1162
|
}
|
|
971
1163
|
|
|
972
1164
|
// ../../utils/src/omo-config/resolve.ts
|
|
973
|
-
import { existsSync } from "node:fs";
|
|
974
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
1165
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
1166
|
+
import { dirname, isAbsolute, join as join2, relative, resolve as resolve2 } from "node:path";
|
|
975
1167
|
function containsPath(parent, child) {
|
|
976
1168
|
const pathToChild = relative(parent, child);
|
|
977
1169
|
return pathToChild === "" || !pathToChild.startsWith("..") && !isAbsolute(pathToChild);
|
|
978
1170
|
}
|
|
979
1171
|
function findProjectConfigPathsNearestFirst(cwd, homeDir) {
|
|
980
|
-
const startDir =
|
|
981
|
-
const stopBeforeDir = containsPath(
|
|
1172
|
+
const startDir = resolve2(cwd);
|
|
1173
|
+
const stopBeforeDir = containsPath(resolve2(homeDir), startDir) ? resolve2(homeDir) : null;
|
|
982
1174
|
const paths = [];
|
|
983
1175
|
let currentDir = startDir;
|
|
984
1176
|
while (true) {
|
|
985
1177
|
if (stopBeforeDir !== null && currentDir === stopBeforeDir)
|
|
986
1178
|
break;
|
|
987
|
-
const configPath =
|
|
988
|
-
if (
|
|
1179
|
+
const configPath = join2(currentDir, ".omo", "config.jsonc");
|
|
1180
|
+
if (existsSync2(configPath)) {
|
|
989
1181
|
paths.push(configPath);
|
|
990
1182
|
}
|
|
991
1183
|
const parentDir = dirname(currentDir);
|
|
@@ -996,7 +1188,7 @@ function findProjectConfigPathsNearestFirst(cwd, homeDir) {
|
|
|
996
1188
|
return paths;
|
|
997
1189
|
}
|
|
998
1190
|
function resolveOmoConfigPaths(options) {
|
|
999
|
-
const globalPath =
|
|
1191
|
+
const globalPath = join2(resolve2(options.homeDir), ".omo", "config.jsonc");
|
|
1000
1192
|
const projectPathsFarthestFirst = findProjectConfigPathsNearestFirst(options.cwd, options.homeDir).reverse();
|
|
1001
1193
|
return [
|
|
1002
1194
|
{ path: globalPath, scope: "global" },
|
|
@@ -1028,7 +1220,7 @@ var CODEGRAPH_SETTING_KEYS = [
|
|
|
1028
1220
|
"telemetry",
|
|
1029
1221
|
"watch_debounce_ms"
|
|
1030
1222
|
];
|
|
1031
|
-
function
|
|
1223
|
+
function isRecord2(value) {
|
|
1032
1224
|
return isPlainObject(value);
|
|
1033
1225
|
}
|
|
1034
1226
|
function hasOwn(record, key) {
|
|
@@ -1056,7 +1248,7 @@ function mergeValues(base, override) {
|
|
|
1056
1248
|
}
|
|
1057
1249
|
function mergeCodegraphConfig(base, override) {
|
|
1058
1250
|
const merged = mergeValues(base, override);
|
|
1059
|
-
if (!
|
|
1251
|
+
if (!isRecord2(merged))
|
|
1060
1252
|
return;
|
|
1061
1253
|
const codegraph = {};
|
|
1062
1254
|
for (const key of CODEGRAPH_SETTING_KEYS) {
|
|
@@ -1111,7 +1303,7 @@ function setCodegraphSetting2(config, key, value) {
|
|
|
1111
1303
|
}
|
|
1112
1304
|
}
|
|
1113
1305
|
function normalizeCodegraphSection(section, pathPrefix, warnings) {
|
|
1114
|
-
if (!
|
|
1306
|
+
if (!isRecord2(section)) {
|
|
1115
1307
|
warnings.push(`${pathPrefix} must be an object`);
|
|
1116
1308
|
return {};
|
|
1117
1309
|
}
|
|
@@ -1131,7 +1323,7 @@ function normalizeCodegraphSection(section, pathPrefix, warnings) {
|
|
|
1131
1323
|
return codegraph;
|
|
1132
1324
|
}
|
|
1133
1325
|
function normalizeConfigBody(value, pathPrefix, warnings) {
|
|
1134
|
-
if (!
|
|
1326
|
+
if (!isRecord2(value)) {
|
|
1135
1327
|
warnings.push(`${pathPrefix} must be an object`);
|
|
1136
1328
|
return {};
|
|
1137
1329
|
}
|
|
@@ -1152,7 +1344,7 @@ function normalizeConfigBody(value, pathPrefix, warnings) {
|
|
|
1152
1344
|
return config;
|
|
1153
1345
|
}
|
|
1154
1346
|
function normalizeActiveHarnessBlock(value, harness, pathPrefix, warnings) {
|
|
1155
|
-
if (!
|
|
1347
|
+
if (!isRecord2(value))
|
|
1156
1348
|
return {};
|
|
1157
1349
|
const blockKey = `[${harness}]`;
|
|
1158
1350
|
if (!hasOwn(value, blockKey))
|
|
@@ -1161,7 +1353,7 @@ function normalizeActiveHarnessBlock(value, harness, pathPrefix, warnings) {
|
|
|
1161
1353
|
}
|
|
1162
1354
|
function loadConfigFile(path, harness) {
|
|
1163
1355
|
try {
|
|
1164
|
-
const content =
|
|
1356
|
+
const content = readFileSync2(path, "utf-8");
|
|
1165
1357
|
const parsed = parseJsoncSafe(content);
|
|
1166
1358
|
if (parsed.errors.length > 0) {
|
|
1167
1359
|
return {
|
|
@@ -1207,13 +1399,13 @@ function validateHarnessApplicability(config, harness) {
|
|
|
1207
1399
|
}
|
|
1208
1400
|
function loadOmoConfig(options) {
|
|
1209
1401
|
const cwd = options.cwd ?? process.cwd();
|
|
1210
|
-
const homeDir = options.homeDir ?? process.env["HOME"] ?? process.env["USERPROFILE"] ??
|
|
1402
|
+
const homeDir = options.homeDir ?? process.env["HOME"] ?? process.env["USERPROFILE"] ?? homedir3();
|
|
1211
1403
|
const env = options.env ?? process.env;
|
|
1212
1404
|
let config = BUILT_IN_DEFAULTS;
|
|
1213
1405
|
const sources = [];
|
|
1214
1406
|
const warnings = [];
|
|
1215
1407
|
for (const candidate of resolveOmoConfigPaths({ cwd, homeDir })) {
|
|
1216
|
-
if (!
|
|
1408
|
+
if (!existsSync3(candidate.path)) {
|
|
1217
1409
|
if (candidate.scope === "global") {
|
|
1218
1410
|
sources.push(toMissingSource(candidate));
|
|
1219
1411
|
}
|
|
@@ -1253,21 +1445,21 @@ function getCodexOmoConfig(options = {}) {
|
|
|
1253
1445
|
// components/codegraph/src/session-start-worker.ts
|
|
1254
1446
|
import { execFile as execFile2 } from "node:child_process";
|
|
1255
1447
|
import { appendFileSync as appendFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "node:fs";
|
|
1256
|
-
import { homedir as
|
|
1448
|
+
import { homedir as homedir7 } from "node:os";
|
|
1257
1449
|
import { extname, join as join7 } from "node:path";
|
|
1258
1450
|
import { cwd as processCwd, env as processEnv, stderr as processStderr } from "node:process";
|
|
1259
1451
|
|
|
1260
1452
|
// ../../utils/src/codegraph/env.ts
|
|
1261
|
-
import { homedir as
|
|
1262
|
-
import { join as
|
|
1453
|
+
import { homedir as homedir4 } from "node:os";
|
|
1454
|
+
import { join as join3 } from "node:path";
|
|
1263
1455
|
var CODEGRAPH_INSTALL_DIR_ENV = "CODEGRAPH_INSTALL_DIR";
|
|
1264
1456
|
var CODEGRAPH_NO_DOWNLOAD_ENV = "CODEGRAPH_NO_DOWNLOAD";
|
|
1265
1457
|
var CODEGRAPH_TELEMETRY_ENV = "CODEGRAPH_TELEMETRY";
|
|
1266
1458
|
var DO_NOT_TRACK_ENV = "DO_NOT_TRACK";
|
|
1267
1459
|
function buildCodegraphEnv(options = {}) {
|
|
1268
|
-
const homeDir = options.homeDir ??
|
|
1460
|
+
const homeDir = options.homeDir ?? homedir4();
|
|
1269
1461
|
return {
|
|
1270
|
-
[CODEGRAPH_INSTALL_DIR_ENV]:
|
|
1462
|
+
[CODEGRAPH_INSTALL_DIR_ENV]: join3(homeDir, ".omo", "codegraph"),
|
|
1271
1463
|
[CODEGRAPH_NO_DOWNLOAD_ENV]: "1",
|
|
1272
1464
|
[CODEGRAPH_TELEMETRY_ENV]: "0",
|
|
1273
1465
|
[DO_NOT_TRACK_ENV]: "1"
|
|
@@ -1304,12 +1496,12 @@ function parseNodeMajor(version) {
|
|
|
1304
1496
|
}
|
|
1305
1497
|
|
|
1306
1498
|
// ../../utils/src/codegraph/provision.ts
|
|
1307
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
1499
|
+
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
1308
1500
|
import { execFile } from "node:child_process";
|
|
1309
1501
|
import { chmod, mkdir, readdir, readFile, rename, rm, rmdir, stat, writeFile } from "node:fs/promises";
|
|
1310
|
-
import { existsSync as
|
|
1311
|
-
import { homedir as
|
|
1312
|
-
import { basename, join as
|
|
1502
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
1503
|
+
import { homedir as homedir5, hostname } from "node:os";
|
|
1504
|
+
import { basename as basename2, join as join4 } from "node:path";
|
|
1313
1505
|
import { promisify } from "node:util";
|
|
1314
1506
|
|
|
1315
1507
|
// ../../utils/src/codegraph/manifest.ts
|
|
@@ -1358,13 +1550,13 @@ function platformKey() {
|
|
|
1358
1550
|
return `${process.platform}-${process.arch}`;
|
|
1359
1551
|
}
|
|
1360
1552
|
function markerPath(installDir, version) {
|
|
1361
|
-
return
|
|
1553
|
+
return join4(installDir, ".provisioned", `codegraph-${version}.json`);
|
|
1362
1554
|
}
|
|
1363
1555
|
function defaultInstallDir() {
|
|
1364
|
-
return
|
|
1556
|
+
return join4(homedir5(), ".omo", "codegraph");
|
|
1365
1557
|
}
|
|
1366
1558
|
function sha256(bytes) {
|
|
1367
|
-
return
|
|
1559
|
+
return createHash2("sha256").update(bytes).digest("hex");
|
|
1368
1560
|
}
|
|
1369
1561
|
function isErrnoException(error) {
|
|
1370
1562
|
return error instanceof Error && "code" in error;
|
|
@@ -1381,7 +1573,7 @@ async function removeEmptyDirectory(path) {
|
|
|
1381
1573
|
}
|
|
1382
1574
|
}
|
|
1383
1575
|
function sleep(ms) {
|
|
1384
|
-
return new Promise((
|
|
1576
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
1385
1577
|
}
|
|
1386
1578
|
async function defaultDownloader(asset, timeoutMs = DEFAULT_DOWNLOAD_TIMEOUT_MS) {
|
|
1387
1579
|
const response = await fetch(asset.url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
@@ -1395,7 +1587,7 @@ function forcedBadChecksumOptions(options) {
|
|
|
1395
1587
|
const key = options.platformKey ?? platformKey();
|
|
1396
1588
|
return {
|
|
1397
1589
|
downloader: async () => new TextEncoder().encode("checksum mismatch"),
|
|
1398
|
-
installDir: options.installDir ??
|
|
1590
|
+
installDir: options.installDir ?? join4(options.lockDir, "codegraph-force-bad-checksum"),
|
|
1399
1591
|
manifest: {
|
|
1400
1592
|
assets: {
|
|
1401
1593
|
[key]: { executableName: process.platform === "win32" ? "codegraph.cmd" : "codegraph", sha256: "0000", url: "memory://bad" }
|
|
@@ -1406,13 +1598,13 @@ function forcedBadChecksumOptions(options) {
|
|
|
1406
1598
|
};
|
|
1407
1599
|
}
|
|
1408
1600
|
async function readMarker(path) {
|
|
1409
|
-
if (!
|
|
1601
|
+
if (!existsSync4(path))
|
|
1410
1602
|
return null;
|
|
1411
1603
|
try {
|
|
1412
1604
|
const raw = JSON.parse(await readFile(path, "utf8"));
|
|
1413
1605
|
if (typeof raw === "object" && raw !== null && "binPath" in raw) {
|
|
1414
1606
|
const value = raw.binPath;
|
|
1415
|
-
return typeof value === "string" &&
|
|
1607
|
+
return typeof value === "string" && existsSync4(value) ? value : null;
|
|
1416
1608
|
}
|
|
1417
1609
|
return null;
|
|
1418
1610
|
} catch (error) {
|
|
@@ -1423,7 +1615,7 @@ async function readMarker(path) {
|
|
|
1423
1615
|
}
|
|
1424
1616
|
async function acquireLock(lockPath, waitMs, staleMs) {
|
|
1425
1617
|
const startedAt = Date.now();
|
|
1426
|
-
await mkdir(
|
|
1618
|
+
await mkdir(join4(lockPath, ".."), { recursive: true });
|
|
1427
1619
|
while (Date.now() - startedAt <= waitMs) {
|
|
1428
1620
|
try {
|
|
1429
1621
|
await mkdir(lockPath);
|
|
@@ -1448,44 +1640,44 @@ async function installExtractedBundle(extractDir, installDir, executableName) {
|
|
|
1448
1640
|
const roots = await readdir(extractDir);
|
|
1449
1641
|
if (roots.length !== 1)
|
|
1450
1642
|
throw new Error(`CodeGraph archive should contain one root directory, found ${roots.length}`);
|
|
1451
|
-
const bundleDir =
|
|
1643
|
+
const bundleDir = join4(extractDir, roots[0] ?? "");
|
|
1452
1644
|
const bundleEntries = await readdir(bundleDir);
|
|
1453
1645
|
await mkdir(installDir, { recursive: true });
|
|
1454
1646
|
for (const entry of bundleEntries) {
|
|
1455
|
-
await rm(
|
|
1456
|
-
await rename(
|
|
1647
|
+
await rm(join4(installDir, entry), { force: true, recursive: true });
|
|
1648
|
+
await rename(join4(bundleDir, entry), join4(installDir, entry));
|
|
1457
1649
|
}
|
|
1458
|
-
const destination =
|
|
1459
|
-
if (!
|
|
1650
|
+
const destination = join4(installDir, "bin", executableName);
|
|
1651
|
+
if (!existsSync4(destination))
|
|
1460
1652
|
throw new Error(`CodeGraph archive did not contain bin/${executableName}`);
|
|
1461
1653
|
await chmod(destination, 493);
|
|
1462
1654
|
return destination;
|
|
1463
1655
|
}
|
|
1464
1656
|
async function installAsset(layout) {
|
|
1465
1657
|
const { asset, downloader, installDir, version } = layout;
|
|
1466
|
-
const stagingDir =
|
|
1467
|
-
const archivePath =
|
|
1468
|
-
const extractDir =
|
|
1658
|
+
const stagingDir = join4(installDir, ".staging", randomUUID());
|
|
1659
|
+
const archivePath = join4(stagingDir, basename2(asset.url));
|
|
1660
|
+
const extractDir = join4(stagingDir, "extract");
|
|
1469
1661
|
try {
|
|
1470
1662
|
await mkdir(extractDir, { recursive: true });
|
|
1471
1663
|
const bytes = await downloader(asset);
|
|
1472
1664
|
const actualChecksum = sha256(bytes);
|
|
1473
1665
|
if (actualChecksum !== asset.sha256) {
|
|
1474
|
-
throw new Error(`checksum mismatch for ${
|
|
1666
|
+
throw new Error(`checksum mismatch for ${basename2(asset.url)}: expected ${asset.sha256}, got ${actualChecksum}`);
|
|
1475
1667
|
}
|
|
1476
1668
|
if (!asset.url.endsWith(".tar.gz") && !asset.url.endsWith(".tgz")) {
|
|
1477
|
-
throw new Error(`unsupported CodeGraph archive type for ${
|
|
1669
|
+
throw new Error(`unsupported CodeGraph archive type for ${basename2(asset.url)}`);
|
|
1478
1670
|
}
|
|
1479
1671
|
await writeFile(archivePath, bytes);
|
|
1480
1672
|
await extractTarGz(archivePath, extractDir);
|
|
1481
1673
|
const destination = await installExtractedBundle(extractDir, installDir, asset.executableName);
|
|
1482
|
-
await mkdir(
|
|
1674
|
+
await mkdir(join4(installDir, ".provisioned"), { recursive: true });
|
|
1483
1675
|
await writeFile(markerPath(installDir, version), `${JSON.stringify({ binPath: destination, version })}
|
|
1484
1676
|
`);
|
|
1485
1677
|
return destination;
|
|
1486
1678
|
} finally {
|
|
1487
1679
|
await rm(stagingDir, { force: true, recursive: true });
|
|
1488
|
-
await removeEmptyDirectory(
|
|
1680
|
+
await removeEmptyDirectory(join4(installDir, ".staging"));
|
|
1489
1681
|
}
|
|
1490
1682
|
}
|
|
1491
1683
|
async function ensureCodegraphProvisioned(options) {
|
|
@@ -1498,7 +1690,7 @@ async function ensureCodegraphProvisioned(options) {
|
|
|
1498
1690
|
const existing = await readMarker(marker);
|
|
1499
1691
|
if (existing !== null)
|
|
1500
1692
|
return { binPath: existing, provisioned: true };
|
|
1501
|
-
const lockPath =
|
|
1693
|
+
const lockPath = join4(options.lockDir, `codegraph-${hostname()}.lock`);
|
|
1502
1694
|
const release = await acquireLock(lockPath, options.lockWaitMs ?? DEFAULT_LOCK_WAIT_MS, options.lockStaleMs ?? DEFAULT_LOCK_STALE_MS);
|
|
1503
1695
|
if (release === null)
|
|
1504
1696
|
return { error: "timed out waiting for codegraph provisioning lock", provisioned: false };
|
|
@@ -1523,15 +1715,15 @@ async function ensureCodegraphProvisioned(options) {
|
|
|
1523
1715
|
}
|
|
1524
1716
|
|
|
1525
1717
|
// ../../utils/src/codegraph/resolve.ts
|
|
1526
|
-
import { existsSync as
|
|
1527
|
-
import { homedir as
|
|
1718
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
1719
|
+
import { homedir as homedir6 } from "node:os";
|
|
1528
1720
|
import { spawnSync } from "node:child_process";
|
|
1529
|
-
import { basename as
|
|
1721
|
+
import { basename as basename3, dirname as dirname2, join as join6 } from "node:path";
|
|
1530
1722
|
import { createRequire } from "node:module";
|
|
1531
1723
|
|
|
1532
1724
|
// ../../utils/src/runtime/which.ts
|
|
1533
1725
|
import { accessSync, constants } from "node:fs";
|
|
1534
|
-
import { delimiter, join as
|
|
1726
|
+
import { delimiter, join as join5 } from "node:path";
|
|
1535
1727
|
var runtime = globalThis;
|
|
1536
1728
|
function isUnsafeCommandName(commandName) {
|
|
1537
1729
|
if (commandName.includes("/") || commandName.includes("\\"))
|
|
@@ -1586,7 +1778,7 @@ function bunWhich(commandName) {
|
|
|
1586
1778
|
return null;
|
|
1587
1779
|
for (const pathEntry of pathEntries) {
|
|
1588
1780
|
for (const candidateName of candidateNames) {
|
|
1589
|
-
const candidatePath =
|
|
1781
|
+
const candidatePath = join5(pathEntry, candidateName);
|
|
1590
1782
|
if (isExecutable(candidatePath))
|
|
1591
1783
|
return candidatePath;
|
|
1592
1784
|
}
|
|
@@ -1627,7 +1819,7 @@ ${result.stderr}`.trim().split(/\s+/)[0];
|
|
|
1627
1819
|
}
|
|
1628
1820
|
}
|
|
1629
1821
|
function isNodeExecutableName(filePath) {
|
|
1630
|
-
const executable =
|
|
1822
|
+
const executable = basename3(filePath).toLowerCase();
|
|
1631
1823
|
return executable === "node" || executable === "node.exe" || /^node\d+(\.exe)?$/.test(executable);
|
|
1632
1824
|
}
|
|
1633
1825
|
function looksLikePath(command) {
|
|
@@ -1667,8 +1859,8 @@ function defaultNodeRuntime(env, fileExists, which, nodeVersion) {
|
|
|
1667
1859
|
function defaultProvisionedBin(homeDir, fileExists) {
|
|
1668
1860
|
const binaryName = process.platform === "win32" ? "codegraph.cmd" : "codegraph";
|
|
1669
1861
|
const candidates = [
|
|
1670
|
-
|
|
1671
|
-
|
|
1862
|
+
join6(homeDir, ".omo", "codegraph", "bin", binaryName),
|
|
1863
|
+
join6(homeDir, ".omo", "codegraph", "node-servers", "node_modules", ".bin", binaryName)
|
|
1672
1864
|
];
|
|
1673
1865
|
return candidates.find((candidate) => fileExists(candidate)) ?? null;
|
|
1674
1866
|
}
|
|
@@ -1676,7 +1868,7 @@ function resolveBundledShim(requireResolve, fileExists) {
|
|
|
1676
1868
|
try {
|
|
1677
1869
|
const packageJson = requireResolve(`${CODEGRAPH_PACKAGE}/package.json`);
|
|
1678
1870
|
const packageRoot = dirname2(packageJson);
|
|
1679
|
-
const candidates = [
|
|
1871
|
+
const candidates = [join6(packageRoot, "bin", "codegraph.js"), join6(packageRoot, "npm-shim.js")];
|
|
1680
1872
|
return candidates.find((candidate) => fileExists(candidate)) ?? null;
|
|
1681
1873
|
} catch (error) {
|
|
1682
1874
|
if (error instanceof Error)
|
|
@@ -1692,7 +1884,7 @@ function resolveBundledShim(requireResolve, fileExists) {
|
|
|
1692
1884
|
}
|
|
1693
1885
|
function resolveCodegraphCommand(options = {}) {
|
|
1694
1886
|
const env = options.env ?? process.env;
|
|
1695
|
-
const fileExists = options.fileExists ??
|
|
1887
|
+
const fileExists = options.fileExists ?? existsSync5;
|
|
1696
1888
|
const configuredBin = env[CODEGRAPH_ENV_BIN]?.trim() || env[CODEGRAPH_LEGACY_ENV_BIN]?.trim();
|
|
1697
1889
|
if (configuredBin !== undefined && configuredBin.length > 0) {
|
|
1698
1890
|
return { argsPrefix: [], command: configuredBin, exists: fileExists(configuredBin), source: "env" };
|
|
@@ -1704,7 +1896,7 @@ function resolveCodegraphCommand(options = {}) {
|
|
|
1704
1896
|
if (bundled !== null && runtime2 !== null) {
|
|
1705
1897
|
return { argsPrefix: [bundled], command: runtime2, exists: true, source: "bundled" };
|
|
1706
1898
|
}
|
|
1707
|
-
const provisioned = options.provisioned?.() ?? defaultProvisionedBin(options.homeDir ??
|
|
1899
|
+
const provisioned = options.provisioned?.() ?? defaultProvisionedBin(options.homeDir ?? homedir6(), fileExists);
|
|
1708
1900
|
if (provisioned !== null && fileExists(provisioned)) {
|
|
1709
1901
|
return { argsPrefix: [], command: provisioned, exists: true, source: "provisioned" };
|
|
1710
1902
|
}
|
|
@@ -1717,102 +1909,6 @@ function resolveCodegraphCommand(options = {}) {
|
|
|
1717
1909
|
};
|
|
1718
1910
|
}
|
|
1719
1911
|
|
|
1720
|
-
// ../../utils/src/codegraph/workspace.ts
|
|
1721
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
1722
|
-
import {
|
|
1723
|
-
appendFileSync,
|
|
1724
|
-
existsSync as existsSync5,
|
|
1725
|
-
lstatSync,
|
|
1726
|
-
mkdirSync,
|
|
1727
|
-
readFileSync as readFileSync2,
|
|
1728
|
-
readdirSync,
|
|
1729
|
-
realpathSync,
|
|
1730
|
-
rmSync,
|
|
1731
|
-
statSync,
|
|
1732
|
-
symlinkSync
|
|
1733
|
-
} from "node:fs";
|
|
1734
|
-
import { homedir as homedir5 } from "node:os";
|
|
1735
|
-
import { basename as basename3, join as join6, resolve as resolve2 } from "node:path";
|
|
1736
|
-
function sanitizeBase(value) {
|
|
1737
|
-
const sanitized = value.replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-");
|
|
1738
|
-
return sanitized.length > 0 ? sanitized : "workspace";
|
|
1739
|
-
}
|
|
1740
|
-
function codegraphDataRoot(homeDir) {
|
|
1741
|
-
return join6(homeDir, ".omo", "codegraph");
|
|
1742
|
-
}
|
|
1743
|
-
function workspaceStorageName(workspace) {
|
|
1744
|
-
const resolved = resolve2(workspace);
|
|
1745
|
-
const hash = createHash2("sha256").update(resolved).digest("hex").slice(0, 16);
|
|
1746
|
-
return `${sanitizeBase(basename3(resolved))}-${hash}`;
|
|
1747
|
-
}
|
|
1748
|
-
function fallbackResult(dataRoot, projectLink, reason) {
|
|
1749
|
-
return { dataDir: projectLink, dataRoot, linked: false, mode: "in-place-fallback", projectLink, reason };
|
|
1750
|
-
}
|
|
1751
|
-
function isSameFilesystem(workspace, dataRoot, override) {
|
|
1752
|
-
if (override !== undefined)
|
|
1753
|
-
return override;
|
|
1754
|
-
return statSync(workspace).dev === statSync(dataRoot).dev;
|
|
1755
|
-
}
|
|
1756
|
-
function ensureInPlaceFallback(projectLink) {
|
|
1757
|
-
if (!existsSync5(projectLink))
|
|
1758
|
-
mkdirSync(projectLink, { recursive: true });
|
|
1759
|
-
}
|
|
1760
|
-
function prepareCodegraphWorkspace(workspace, options = {}) {
|
|
1761
|
-
const resolvedWorkspace = resolve2(workspace);
|
|
1762
|
-
const dataRoot = codegraphDataRoot(options.homeDir ?? homedir5());
|
|
1763
|
-
const dataDir = join6(dataRoot, "projects", workspaceStorageName(resolvedWorkspace));
|
|
1764
|
-
const projectLink = join6(resolvedWorkspace, ".codegraph");
|
|
1765
|
-
try {
|
|
1766
|
-
mkdirSync(dataDir, { recursive: true });
|
|
1767
|
-
if (existsSync5(projectLink)) {
|
|
1768
|
-
const linkStat = lstatSync(projectLink);
|
|
1769
|
-
if (!linkStat.isSymbolicLink()) {
|
|
1770
|
-
return { dataDir: projectLink, dataRoot, linked: false, mode: "in-project", projectLink };
|
|
1771
|
-
}
|
|
1772
|
-
if (realpathSync(projectLink) === realpathSync(dataDir)) {
|
|
1773
|
-
return { dataDir, dataRoot, linked: true, mode: "global-linked", projectLink };
|
|
1774
|
-
}
|
|
1775
|
-
return fallbackResult(dataRoot, projectLink, "existing .codegraph symlink points outside OMO store");
|
|
1776
|
-
}
|
|
1777
|
-
if (!isSameFilesystem(resolvedWorkspace, dataRoot, options.sameFilesystem)) {
|
|
1778
|
-
ensureInPlaceFallback(projectLink);
|
|
1779
|
-
return fallbackResult(dataRoot, projectLink, "workspace and OMO store are on different filesystems");
|
|
1780
|
-
}
|
|
1781
|
-
const symlink = options.symlink ?? symlinkSync;
|
|
1782
|
-
symlink(dataDir, projectLink, (options.platform ?? process.platform) === "win32" ? "junction" : "dir");
|
|
1783
|
-
return { dataDir, dataRoot, linked: true, mode: "global-linked", projectLink };
|
|
1784
|
-
} catch (error) {
|
|
1785
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
1786
|
-
try {
|
|
1787
|
-
ensureInPlaceFallback(projectLink);
|
|
1788
|
-
} catch (fallbackError) {
|
|
1789
|
-
return fallbackResult(dataRoot, projectLink, `${reason}; fallback failed: ${String(fallbackError)}`);
|
|
1790
|
-
}
|
|
1791
|
-
return fallbackResult(dataRoot, projectLink, reason);
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
function ensureCodegraphGitignored(workspace) {
|
|
1795
|
-
const gitDir = join6(workspace, ".git");
|
|
1796
|
-
if (!existsSync5(gitDir))
|
|
1797
|
-
return false;
|
|
1798
|
-
const excludePath = join6(gitDir, "info", "exclude");
|
|
1799
|
-
try {
|
|
1800
|
-
mkdirSync(join6(gitDir, "info"), { recursive: true });
|
|
1801
|
-
const existing = existsSync5(excludePath) ? readFileSync2(excludePath, "utf8") : "";
|
|
1802
|
-
if (existing.split(/\r?\n/).includes(".codegraph"))
|
|
1803
|
-
return true;
|
|
1804
|
-
appendFileSync(excludePath, `${existing.endsWith(`
|
|
1805
|
-
`) || existing.length === 0 ? "" : `
|
|
1806
|
-
`}.codegraph
|
|
1807
|
-
`);
|
|
1808
|
-
return true;
|
|
1809
|
-
} catch (error) {
|
|
1810
|
-
if (error instanceof Error)
|
|
1811
|
-
return false;
|
|
1812
|
-
throw error;
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
1912
|
// components/codegraph/src/session-start-worker.ts
|
|
1817
1913
|
var SESSION_START_CWD_ENV = "OMO_CODEGRAPH_SESSION_START_CWD";
|
|
1818
1914
|
var CODEGRAPH_VERSION = "1.0.1";
|
|
@@ -1866,13 +1962,10 @@ function finish(action, detail, logOutcome) {
|
|
|
1866
1962
|
}
|
|
1867
1963
|
async function resolveOrProvisionCommand(deps, config, env, homeDir, nodeSupport) {
|
|
1868
1964
|
const resolved = deps.resolveCommand({ env, homeDir, provisioned: () => provisionedBinFromInstallDir(config.install_dir) });
|
|
1869
|
-
if (resolved.exists) {
|
|
1870
|
-
if (codegraphCommandRequiresSupportedLocalNode(resolved) && !nodeSupport.supported) {
|
|
1871
|
-
return { kind: "unsupported-node" };
|
|
1872
|
-
}
|
|
1965
|
+
if (resolved.exists && canUseResolvedCommand(resolved, nodeSupport)) {
|
|
1873
1966
|
return { kind: "resolved", resolution: resolved };
|
|
1874
1967
|
}
|
|
1875
|
-
if (
|
|
1968
|
+
if (resolved.exists && config.auto_provision === false)
|
|
1876
1969
|
return { kind: "unsupported-node" };
|
|
1877
1970
|
if (config.auto_provision === false)
|
|
1878
1971
|
return { error: "codegraph binary unavailable and auto_provision is disabled", kind: "unavailable", source: resolved.source };
|
|
@@ -1883,6 +1976,9 @@ async function resolveOrProvisionCommand(deps, config, env, homeDir, nodeSupport
|
|
|
1883
1976
|
}
|
|
1884
1977
|
return { kind: "resolved", resolution: { argsPrefix: [], command: provisioned.binPath, exists: true, source: "provisioned" } };
|
|
1885
1978
|
}
|
|
1979
|
+
function canUseResolvedCommand(resolved, nodeSupport) {
|
|
1980
|
+
return !codegraphCommandRequiresSupportedLocalNode(resolved) || nodeSupport.supported;
|
|
1981
|
+
}
|
|
1886
1982
|
function decideStartupAction(status) {
|
|
1887
1983
|
if (status.timedOut)
|
|
1888
1984
|
return { kind: "skip", reason: "status timed out" };
|
|
@@ -1900,7 +1996,7 @@ ${status.stderr ?? ""}`.toLowerCase();
|
|
|
1900
1996
|
return { kind: "sync" };
|
|
1901
1997
|
}
|
|
1902
1998
|
function jsonSaysInitialized(value) {
|
|
1903
|
-
if (!
|
|
1999
|
+
if (!isRecord3(value))
|
|
1904
2000
|
return;
|
|
1905
2001
|
const initialized = value["initialized"] ?? value["isInitialized"] ?? value["ready"];
|
|
1906
2002
|
if (typeof initialized === "boolean")
|
|
@@ -1966,7 +2062,7 @@ function toOutputText(value) {
|
|
|
1966
2062
|
return Buffer.isBuffer(value) ? value.toString("utf8") : value;
|
|
1967
2063
|
}
|
|
1968
2064
|
function resolveHomeDir(env) {
|
|
1969
|
-
return env["HOME"] ?? env["USERPROFILE"] ??
|
|
2065
|
+
return env["HOME"] ?? env["USERPROFILE"] ?? homedir7();
|
|
1970
2066
|
}
|
|
1971
2067
|
function parseJson(text) {
|
|
1972
2068
|
try {
|
|
@@ -1977,7 +2073,7 @@ function parseJson(text) {
|
|
|
1977
2073
|
throw error;
|
|
1978
2074
|
}
|
|
1979
2075
|
}
|
|
1980
|
-
function
|
|
2076
|
+
function isRecord3(value) {
|
|
1981
2077
|
return typeof value === "object" && value !== null;
|
|
1982
2078
|
}
|
|
1983
2079
|
|
|
@@ -1986,6 +2082,9 @@ var CODEGRAPH_SESSION_START_NOTICE = "LazyCodex CodeGraph bootstrap scheduled in
|
|
|
1986
2082
|
async function runCodegraphSessionStartHook(options = {}) {
|
|
1987
2083
|
return (await executeCodegraphSessionStartHook(options)).exitCode;
|
|
1988
2084
|
}
|
|
2085
|
+
async function runCodegraphPostToolUseHookCli(options = {}) {
|
|
2086
|
+
return (await executeCodegraphPostToolUseHook(options)).exitCode;
|
|
2087
|
+
}
|
|
1989
2088
|
async function executeCodegraphSessionStartHook(options = {}) {
|
|
1990
2089
|
const env = options.env ?? processEnv2;
|
|
1991
2090
|
const input = await readHookInput(options.stdin ?? processStdin);
|
|
@@ -2003,6 +2102,30 @@ async function executeCodegraphSessionStartHook(options = {}) {
|
|
|
2003
2102
|
writeHookJson(options.stdout ?? processStdout);
|
|
2004
2103
|
return { action: "spawned", exitCode: 0 };
|
|
2005
2104
|
}
|
|
2105
|
+
async function executeCodegraphPostToolUseHook(options = {}) {
|
|
2106
|
+
const env = options.env ?? processEnv2;
|
|
2107
|
+
const input = await readHookInput(options.stdin ?? processStdin);
|
|
2108
|
+
const output = runCodegraphPostToolUseHook(input, { homeDir: resolveHomeDir2(env) });
|
|
2109
|
+
if (output.length === 0)
|
|
2110
|
+
return { action: "skipped", exitCode: 0 };
|
|
2111
|
+
(options.stdout ?? processStdout).write(output);
|
|
2112
|
+
return { action: "emitted-guidance", exitCode: 0 };
|
|
2113
|
+
}
|
|
2114
|
+
function runCodegraphPostToolUseHook(input, options = {}) {
|
|
2115
|
+
const toolName = isRecord4(input) ? input["tool_name"] : undefined;
|
|
2116
|
+
const cwd = isRecord4(input) ? input["cwd"] : undefined;
|
|
2117
|
+
const toolOutput = isRecord4(input) ? input["tool_response"] ?? input["tool_output"] ?? input["response"] : input;
|
|
2118
|
+
const guidance = buildCodegraphInitGuidanceForToolResult({ cwd, toolName, toolOutput }, options);
|
|
2119
|
+
if (guidance === null)
|
|
2120
|
+
return "";
|
|
2121
|
+
return `${JSON.stringify({
|
|
2122
|
+
hookSpecificOutput: {
|
|
2123
|
+
hookEventName: "PostToolUse",
|
|
2124
|
+
additionalContext: guidance
|
|
2125
|
+
}
|
|
2126
|
+
})}
|
|
2127
|
+
`;
|
|
2128
|
+
}
|
|
2006
2129
|
function writeHookJson(stdout) {
|
|
2007
2130
|
const output = {
|
|
2008
2131
|
hookSpecificOutput: {
|
|
@@ -2018,10 +2141,10 @@ function spawnDetachedWorker(invocation) {
|
|
|
2018
2141
|
child.unref();
|
|
2019
2142
|
}
|
|
2020
2143
|
function resolveHomeDir2(env) {
|
|
2021
|
-
return env["HOME"] ?? env["USERPROFILE"] ??
|
|
2144
|
+
return env["HOME"] ?? env["USERPROFILE"] ?? homedir8();
|
|
2022
2145
|
}
|
|
2023
2146
|
function resolveProjectRoot(input, fallback) {
|
|
2024
|
-
if (!
|
|
2147
|
+
if (!isRecord4(input))
|
|
2025
2148
|
return fallback;
|
|
2026
2149
|
const cwd = input["cwd"];
|
|
2027
2150
|
return typeof cwd === "string" && cwd.trim().length > 0 ? cwd : fallback;
|
|
@@ -2045,7 +2168,7 @@ function parseJson2(text) {
|
|
|
2045
2168
|
throw error;
|
|
2046
2169
|
}
|
|
2047
2170
|
}
|
|
2048
|
-
function
|
|
2171
|
+
function isRecord4(value) {
|
|
2049
2172
|
return typeof value === "object" && value !== null;
|
|
2050
2173
|
}
|
|
2051
2174
|
function defaultWorkerCliPath() {
|
|
@@ -2055,7 +2178,7 @@ function defaultWorkerCliPath() {
|
|
|
2055
2178
|
// components/codegraph/src/serve.ts
|
|
2056
2179
|
import { spawn as spawn2 } from "node:child_process";
|
|
2057
2180
|
import { existsSync as existsSync7, realpathSync as realpathSync2 } from "node:fs";
|
|
2058
|
-
import { homedir as
|
|
2181
|
+
import { homedir as homedir9 } from "node:os";
|
|
2059
2182
|
import { basename as basename4, extname as extname2, join as join8, resolve as resolve3 } from "node:path";
|
|
2060
2183
|
import {
|
|
2061
2184
|
cwd as processCwd3,
|
|
@@ -2068,11 +2191,12 @@ var CODEGRAPH_SKIP_HINT = `CodeGraph MCP skipped: codegraph binary not found. In
|
|
|
2068
2191
|
`;
|
|
2069
2192
|
var CODEGRAPH_DISABLED_HINT = `CodeGraph MCP skipped: disabled by OMO SOT config. Set [codex].codegraph.enabled=true to enable it.
|
|
2070
2193
|
`;
|
|
2194
|
+
var CODEGRAPH_VERSION2 = "1.0.1";
|
|
2071
2195
|
var WINDOWS_CMD_EXTENSIONS2 = new Set([".bat", ".cmd"]);
|
|
2072
2196
|
var WINDOWS_NODE_SCRIPT_EXTENSIONS = new Set([".cjs", ".js", ".mjs"]);
|
|
2073
2197
|
async function runCodegraphServe(options = {}) {
|
|
2074
2198
|
const env = options.env ?? processEnv3;
|
|
2075
|
-
const homeDir = options.homeDir ??
|
|
2199
|
+
const homeDir = options.homeDir ?? homedir9();
|
|
2076
2200
|
const config = options.config ?? getCodexOmoConfig({ cwd: options.cwd ?? processCwd3(), env, homeDir });
|
|
2077
2201
|
const codegraphConfig = config.codegraph ?? {};
|
|
2078
2202
|
if (codegraphConfig.enabled === false) {
|
|
@@ -2084,15 +2208,24 @@ async function runCodegraphServe(options = {}) {
|
|
|
2084
2208
|
homeDir,
|
|
2085
2209
|
provisioned: () => provisionedBinFromInstallDir2(codegraphConfig.install_dir)
|
|
2086
2210
|
};
|
|
2087
|
-
|
|
2211
|
+
let resolution = options.resolve?.(resolutionOptions) ?? resolveCodegraphCommand(resolutionOptions);
|
|
2088
2212
|
const nodeSupport = evaluateCodegraphNodeSupport({ env, nodeVersion: options.nodeVersion });
|
|
2089
2213
|
if (!resolution.exists || shouldSkipResolvedCommand(resolution, options.commandExists ?? existsSync7)) {
|
|
2090
2214
|
if (resolution.source === "path" && !nodeSupport.supported) {
|
|
2091
2215
|
(options.stderr ?? processStderr2).write(buildCodegraphNodeSkipHint(nodeSupport));
|
|
2092
2216
|
return 1;
|
|
2093
2217
|
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2218
|
+
const provisioned = await provisionMissingCodegraph({
|
|
2219
|
+
config: codegraphConfig,
|
|
2220
|
+
ensureProvisioned: options.ensureProvisioned ?? ensureCodegraphProvisioned,
|
|
2221
|
+
homeDir,
|
|
2222
|
+
resolution
|
|
2223
|
+
});
|
|
2224
|
+
if (provisioned === null) {
|
|
2225
|
+
(options.stderr ?? processStderr2).write(CODEGRAPH_SKIP_HINT);
|
|
2226
|
+
return 1;
|
|
2227
|
+
}
|
|
2228
|
+
resolution = provisioned;
|
|
2096
2229
|
}
|
|
2097
2230
|
if (codegraphCommandRequiresSupportedLocalNode(resolution) && !nodeSupport.supported) {
|
|
2098
2231
|
(options.stderr ?? processStderr2).write(buildCodegraphNodeSkipHint(nodeSupport));
|
|
@@ -2109,6 +2242,21 @@ async function runCodegraphServe(options = {}) {
|
|
|
2109
2242
|
stdio: "inherit"
|
|
2110
2243
|
});
|
|
2111
2244
|
}
|
|
2245
|
+
async function provisionMissingCodegraph(options) {
|
|
2246
|
+
if (options.resolution.source === "env")
|
|
2247
|
+
return null;
|
|
2248
|
+
if (options.config.auto_provision === false)
|
|
2249
|
+
return null;
|
|
2250
|
+
const installDir = options.config.install_dir ?? join8(options.homeDir, ".omo", "codegraph");
|
|
2251
|
+
const result = await options.ensureProvisioned({
|
|
2252
|
+
installDir,
|
|
2253
|
+
lockDir: join8(installDir, ".locks"),
|
|
2254
|
+
version: CODEGRAPH_VERSION2
|
|
2255
|
+
});
|
|
2256
|
+
if (!result.provisioned || result.binPath === undefined)
|
|
2257
|
+
return null;
|
|
2258
|
+
return { argsPrefix: [], command: result.binPath, exists: true, source: "provisioned" };
|
|
2259
|
+
}
|
|
2112
2260
|
function shouldSkipResolvedCommand(resolution, commandExists) {
|
|
2113
2261
|
if (resolution.source !== "env")
|
|
2114
2262
|
return false;
|
|
@@ -2183,6 +2331,14 @@ async function runCodegraphCli(options = {}) {
|
|
|
2183
2331
|
if (command === "hook" && subcommand === "session-start") {
|
|
2184
2332
|
return runCodegraphSessionStartHook(options);
|
|
2185
2333
|
}
|
|
2334
|
+
if (command === "hook" && subcommand === "post-tool-use") {
|
|
2335
|
+
const hookOptions = {
|
|
2336
|
+
...options.env === undefined ? {} : { env: options.env },
|
|
2337
|
+
...options.stdin === undefined ? {} : { stdin: options.stdin },
|
|
2338
|
+
...options.stdout === undefined ? {} : { stdout: options.stdout }
|
|
2339
|
+
};
|
|
2340
|
+
return runCodegraphPostToolUseHookCli(hookOptions);
|
|
2341
|
+
}
|
|
2186
2342
|
if (command === "hook" && subcommand === "session-start-worker") {
|
|
2187
2343
|
const workerOptions = {
|
|
2188
2344
|
...options.workerOptions,
|