@skein-code/cli 0.3.19 → 0.3.21
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/README.md +13 -6
- package/dist/cli.js +640 -389
- package/dist/cli.js.map +1 -1
- package/docs/NEXT_STEPS.md +21 -8
- package/package.json +7 -2
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
5
5
|
import { stdin as input, stdout as output } from "node:process";
|
|
6
6
|
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
7
|
-
import { basename as basename13, resolve as
|
|
7
|
+
import { basename as basename13, resolve as resolve25 } from "node:path";
|
|
8
8
|
import { Command, Option } from "commander";
|
|
9
9
|
import chalk4 from "chalk";
|
|
10
10
|
|
|
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
|
|
|
220
220
|
// package.json
|
|
221
221
|
var package_default = {
|
|
222
222
|
name: "@skein-code/cli",
|
|
223
|
-
version: "0.3.
|
|
223
|
+
version: "0.3.21",
|
|
224
224
|
description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
|
|
225
225
|
type: "module",
|
|
226
226
|
license: "MIT",
|
|
@@ -236,6 +236,10 @@ var package_default = {
|
|
|
236
236
|
},
|
|
237
237
|
skein: {
|
|
238
238
|
releaseNotes: [
|
|
239
|
+
"Failed configured verification output yields bounded current-run diagnostic ranking without persisting paths",
|
|
240
|
+
"Diagnostic hints cannot create zero-relevance hits and are cleared on success or the next agent run",
|
|
241
|
+
"Python absolute and relative module imports now contribute deterministic offline graph adjacency",
|
|
242
|
+
"Context provenance exposes the diagnostic score alongside graph and isolated Git recency signals",
|
|
239
243
|
"Local index v3 records TypeScript AST definitions, calls, and relative import adjacency for graph-assisted retrieval",
|
|
240
244
|
"Context hits expose generation, content hash, matched terms, and bm25/path/symbol/phrase/graph score provenance",
|
|
241
245
|
"Context benchmark v2 locks multilingual recall, MRR, useful-token, stale-hit, incremental-index, and warm-latency gates",
|
|
@@ -256,7 +260,8 @@ var package_default = {
|
|
|
256
260
|
"Prompt and timeline diagnostics expose the repository-first reuse ladder without blocking legitimate writes",
|
|
257
261
|
"Known tool changes refresh only their affected local-index paths before the next model turn",
|
|
258
262
|
"File ctime closes same-size and restored-mtime zero-hit freshness gaps without full-content scans",
|
|
259
|
-
"Repeated empty or unchanged search calls stop through the existing recovery circuit"
|
|
263
|
+
"Repeated empty or unchanged search calls stop through the existing recovery circuit",
|
|
264
|
+
"Git recency is an isolated, bounded tie-break with exact HEAD-bound cache invalidation and non-Git degradation"
|
|
260
265
|
]
|
|
261
266
|
},
|
|
262
267
|
bin: {
|
|
@@ -2611,9 +2616,9 @@ function countExplicitPaths(value) {
|
|
|
2611
2616
|
}
|
|
2612
2617
|
|
|
2613
2618
|
// src/context/local-index.ts
|
|
2614
|
-
import { createHash as
|
|
2615
|
-
import { lstat as
|
|
2616
|
-
import { basename as basename5, dirname as dirname7, extname as extname2, isAbsolute as
|
|
2619
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
2620
|
+
import { lstat as lstat9, readFile as readFile3, stat as stat3 } from "node:fs/promises";
|
|
2621
|
+
import { basename as basename5, dirname as dirname7, extname as extname2, isAbsolute as isAbsolute4, join as join8, posix, relative as relative5, resolve as resolve10, sep as sep4 } from "node:path";
|
|
2617
2622
|
import fg from "fast-glob";
|
|
2618
2623
|
import ts from "typescript";
|
|
2619
2624
|
import { z as z3 } from "zod";
|
|
@@ -3046,6 +3051,371 @@ function hash(value) {
|
|
|
3046
3051
|
return createHash5("sha256").update(value).digest("hex");
|
|
3047
3052
|
}
|
|
3048
3053
|
|
|
3054
|
+
// src/context/git-recency.ts
|
|
3055
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
3056
|
+
import { resolve as resolve9 } from "node:path";
|
|
3057
|
+
|
|
3058
|
+
// src/utils/process.ts
|
|
3059
|
+
import { spawn } from "node:child_process";
|
|
3060
|
+
import { constants as constants3 } from "node:fs";
|
|
3061
|
+
import { access as access2, lstat as lstat8, realpath as realpath6 } from "node:fs/promises";
|
|
3062
|
+
import { delimiter, isAbsolute as isAbsolute3, join as join7, resolve as resolve8 } from "node:path";
|
|
3063
|
+
import { StringDecoder } from "node:string_decoder";
|
|
3064
|
+
async function resolveExecutableRuntime(command2, cwd, excludedRoots = []) {
|
|
3065
|
+
const realRoots = await Promise.all(excludedRoots.map(async (root) => {
|
|
3066
|
+
try {
|
|
3067
|
+
return await realpath6(root);
|
|
3068
|
+
} catch {
|
|
3069
|
+
return resolve8(root);
|
|
3070
|
+
}
|
|
3071
|
+
}));
|
|
3072
|
+
const pathEntries = (process.env.PATH ?? process.env.Path ?? "").split(delimiter).filter(Boolean);
|
|
3073
|
+
const safeDirectories2 = [];
|
|
3074
|
+
let executable2;
|
|
3075
|
+
const explicit = isAbsolute3(command2) || command2.includes("/") || command2.includes("\\");
|
|
3076
|
+
const explicitPath = explicit ? resolve8(cwd, command2) : void 0;
|
|
3077
|
+
for (const entry of pathEntries) {
|
|
3078
|
+
let directory;
|
|
3079
|
+
try {
|
|
3080
|
+
directory = await realpath6(resolve8(cwd, entry));
|
|
3081
|
+
if (!(await lstat8(directory)).isDirectory()) continue;
|
|
3082
|
+
} catch {
|
|
3083
|
+
continue;
|
|
3084
|
+
}
|
|
3085
|
+
if (realRoots.some((root) => isInside(root, directory))) continue;
|
|
3086
|
+
let contaminated = false;
|
|
3087
|
+
if (!explicit) {
|
|
3088
|
+
for (const name of executableNames(command2)) {
|
|
3089
|
+
const candidate = join7(directory, name);
|
|
3090
|
+
const resolvedCandidate = await usableExecutable(candidate);
|
|
3091
|
+
if (!resolvedCandidate) continue;
|
|
3092
|
+
if (realRoots.some((root) => isInside(root, resolvedCandidate))) {
|
|
3093
|
+
contaminated = true;
|
|
3094
|
+
continue;
|
|
3095
|
+
}
|
|
3096
|
+
executable2 ??= resolvedCandidate;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
if (!contaminated && !safeDirectories2.includes(directory)) safeDirectories2.push(directory);
|
|
3100
|
+
}
|
|
3101
|
+
if (explicitPath) executable2 = await usableExecutable(explicitPath);
|
|
3102
|
+
if (!executable2) return void 0;
|
|
3103
|
+
return { executable: executable2, path: safeDirectories2.join(delimiter) };
|
|
3104
|
+
}
|
|
3105
|
+
async function usableExecutable(candidate) {
|
|
3106
|
+
try {
|
|
3107
|
+
await access2(candidate, process.platform === "win32" ? constants3.F_OK : constants3.X_OK);
|
|
3108
|
+
const resolvedCandidate = await realpath6(candidate);
|
|
3109
|
+
return (await lstat8(resolvedCandidate)).isFile() ? resolvedCandidate : void 0;
|
|
3110
|
+
} catch {
|
|
3111
|
+
return void 0;
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
function executableNames(command2) {
|
|
3115
|
+
if (process.platform !== "win32") return [command2];
|
|
3116
|
+
const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
|
|
3117
|
+
return [command2, ...extensions.map((extension) => `${command2}${extension}`)];
|
|
3118
|
+
}
|
|
3119
|
+
function runProcess(command2, args, options) {
|
|
3120
|
+
return new Promise((resolve26, reject) => {
|
|
3121
|
+
const started = Date.now();
|
|
3122
|
+
const environment = options.inheritEnv === false ? {} : { ...process.env };
|
|
3123
|
+
for (const name of options.unsetEnv ?? []) delete environment[name];
|
|
3124
|
+
for (const name of Object.keys(environment)) {
|
|
3125
|
+
if (options.unsetEnvPrefixes?.some((prefix) => name.startsWith(prefix))) {
|
|
3126
|
+
delete environment[name];
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
Object.assign(environment, options.env);
|
|
3130
|
+
const child = spawn(command2, args, {
|
|
3131
|
+
cwd: options.cwd,
|
|
3132
|
+
env: environment,
|
|
3133
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
3134
|
+
signal: options.signal
|
|
3135
|
+
});
|
|
3136
|
+
const maxBytes = options.maxOutputBytes ?? 1e6;
|
|
3137
|
+
let stdout = "";
|
|
3138
|
+
let stderr = "";
|
|
3139
|
+
let stdoutBytes = 0;
|
|
3140
|
+
let stderrBytes = 0;
|
|
3141
|
+
let stdoutObservedBytes = 0;
|
|
3142
|
+
let stderrObservedBytes = 0;
|
|
3143
|
+
let timedOut = false;
|
|
3144
|
+
let outputLimitReached = false;
|
|
3145
|
+
let callbackError;
|
|
3146
|
+
const stdoutDecoder = new StringDecoder("utf8");
|
|
3147
|
+
const stderrDecoder = new StringDecoder("utf8");
|
|
3148
|
+
const stdoutCallbackDecoder = new StringDecoder("utf8");
|
|
3149
|
+
const stderrCallbackDecoder = new StringDecoder("utf8");
|
|
3150
|
+
const append = (decoder, chunk, usedBytes) => {
|
|
3151
|
+
if (usedBytes >= maxBytes) return { text: "", usedBytes };
|
|
3152
|
+
const selected = chunk.subarray(0, maxBytes - usedBytes);
|
|
3153
|
+
return { text: decoder.write(selected), usedBytes: usedBytes + selected.length };
|
|
3154
|
+
};
|
|
3155
|
+
const notify = (callback, decoder, chunk) => {
|
|
3156
|
+
if (!callback || callbackError) return;
|
|
3157
|
+
try {
|
|
3158
|
+
const decoded = decoder.write(chunk);
|
|
3159
|
+
if (decoded) callback(decoded);
|
|
3160
|
+
} catch (error) {
|
|
3161
|
+
callbackError = error;
|
|
3162
|
+
child.kill("SIGTERM");
|
|
3163
|
+
}
|
|
3164
|
+
};
|
|
3165
|
+
child.stdout.on("data", (chunk) => {
|
|
3166
|
+
stdoutObservedBytes += chunk.length;
|
|
3167
|
+
if (options.stopOnOutputLimit && stdoutObservedBytes > maxBytes && !outputLimitReached) {
|
|
3168
|
+
outputLimitReached = true;
|
|
3169
|
+
child.kill("SIGTERM");
|
|
3170
|
+
}
|
|
3171
|
+
const appended = append(stdoutDecoder, chunk, stdoutBytes);
|
|
3172
|
+
stdout += appended.text;
|
|
3173
|
+
stdoutBytes = appended.usedBytes;
|
|
3174
|
+
notify(options.onStdout, stdoutCallbackDecoder, chunk);
|
|
3175
|
+
});
|
|
3176
|
+
child.stderr.on("data", (chunk) => {
|
|
3177
|
+
stderrObservedBytes += chunk.length;
|
|
3178
|
+
if (options.stopOnOutputLimit && stderrObservedBytes > maxBytes && !outputLimitReached) {
|
|
3179
|
+
outputLimitReached = true;
|
|
3180
|
+
child.kill("SIGTERM");
|
|
3181
|
+
}
|
|
3182
|
+
const appended = append(stderrDecoder, chunk, stderrBytes);
|
|
3183
|
+
stderr += appended.text;
|
|
3184
|
+
stderrBytes = appended.usedBytes;
|
|
3185
|
+
notify(options.onStderr, stderrCallbackDecoder, chunk);
|
|
3186
|
+
});
|
|
3187
|
+
child.on("error", reject);
|
|
3188
|
+
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
3189
|
+
const timeout = timeoutMs > 0 ? setTimeout(() => {
|
|
3190
|
+
timedOut = true;
|
|
3191
|
+
child.kill("SIGTERM");
|
|
3192
|
+
setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
|
|
3193
|
+
}, timeoutMs) : void 0;
|
|
3194
|
+
child.on("close", (code) => {
|
|
3195
|
+
if (timeout) clearTimeout(timeout);
|
|
3196
|
+
const stdoutTail = stdoutDecoder.end();
|
|
3197
|
+
const stderrTail = stderrDecoder.end();
|
|
3198
|
+
if (Buffer.byteLength(stdout) + Buffer.byteLength(stdoutTail) <= maxBytes) stdout += stdoutTail;
|
|
3199
|
+
if (Buffer.byteLength(stderr) + Buffer.byteLength(stderrTail) <= maxBytes) stderr += stderrTail;
|
|
3200
|
+
try {
|
|
3201
|
+
const stdoutCallbackTail = stdoutCallbackDecoder.end();
|
|
3202
|
+
const stderrCallbackTail = stderrCallbackDecoder.end();
|
|
3203
|
+
if (stdoutCallbackTail && options.onStdout && !callbackError) options.onStdout(stdoutCallbackTail);
|
|
3204
|
+
if (stderrCallbackTail && options.onStderr && !callbackError) options.onStderr(stderrCallbackTail);
|
|
3205
|
+
} catch (error) {
|
|
3206
|
+
callbackError = error;
|
|
3207
|
+
}
|
|
3208
|
+
if (callbackError) {
|
|
3209
|
+
reject(callbackError);
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
resolve26({
|
|
3213
|
+
command: [command2, ...args].join(" "),
|
|
3214
|
+
exitCode: code ?? (timedOut ? 124 : 1),
|
|
3215
|
+
stdout,
|
|
3216
|
+
stderr,
|
|
3217
|
+
timedOut,
|
|
3218
|
+
durationMs: Date.now() - started,
|
|
3219
|
+
stdoutBytes: stdoutObservedBytes,
|
|
3220
|
+
stderrBytes: stderrObservedBytes,
|
|
3221
|
+
stdoutTruncated: stdoutObservedBytes > stdoutBytes,
|
|
3222
|
+
stderrTruncated: stderrObservedBytes > stderrBytes
|
|
3223
|
+
});
|
|
3224
|
+
});
|
|
3225
|
+
if (options.stdin) child.stdin.end(options.stdin);
|
|
3226
|
+
else child.stdin.end();
|
|
3227
|
+
});
|
|
3228
|
+
}
|
|
3229
|
+
function runShell(command2, cwd, options = {}) {
|
|
3230
|
+
const shell = process.platform === "win32" ? process.env.COMSPEC ?? "cmd.exe" : process.env.SHELL ?? "/bin/sh";
|
|
3231
|
+
const args = process.platform === "win32" ? ["/d", "/s", "/c", command2] : ["-lc", command2];
|
|
3232
|
+
return runProcess(shell, args, { ...options, cwd });
|
|
3233
|
+
}
|
|
3234
|
+
|
|
3235
|
+
// src/utils/git.ts
|
|
3236
|
+
var unsafeInheritedGitEnvironment = [
|
|
3237
|
+
"GIT_DIR",
|
|
3238
|
+
"GIT_WORK_TREE",
|
|
3239
|
+
"GIT_COMMON_DIR",
|
|
3240
|
+
"GIT_INDEX_FILE",
|
|
3241
|
+
"GIT_OBJECT_DIRECTORY",
|
|
3242
|
+
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
3243
|
+
"GIT_NAMESPACE",
|
|
3244
|
+
"GIT_CEILING_DIRECTORIES",
|
|
3245
|
+
"GIT_DISCOVERY_ACROSS_FILESYSTEM",
|
|
3246
|
+
"GIT_EXTERNAL_DIFF",
|
|
3247
|
+
"GIT_DIFF_OPTS",
|
|
3248
|
+
"GIT_SSH",
|
|
3249
|
+
"GIT_SSH_COMMAND",
|
|
3250
|
+
"GIT_PROXY_COMMAND",
|
|
3251
|
+
"GIT_ASKPASS",
|
|
3252
|
+
"SSH_ASKPASS",
|
|
3253
|
+
"GIT_EXEC_PATH",
|
|
3254
|
+
"GIT_TEMPLATE_DIR",
|
|
3255
|
+
"GIT_CONFIG",
|
|
3256
|
+
"GIT_CONFIG_PARAMETERS",
|
|
3257
|
+
"GIT_QUARANTINE_PATH",
|
|
3258
|
+
"Path"
|
|
3259
|
+
];
|
|
3260
|
+
var isolatedGitEnvironment = {
|
|
3261
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
3262
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
3263
|
+
GIT_CONFIG_GLOBAL: "/dev/null",
|
|
3264
|
+
GIT_CONFIG_SYSTEM: "/dev/null",
|
|
3265
|
+
GIT_NO_REPLACE_OBJECTS: "1",
|
|
3266
|
+
GIT_PAGER: "cat",
|
|
3267
|
+
GIT_EDITOR: "true",
|
|
3268
|
+
GIT_SEQUENCE_EDITOR: "true",
|
|
3269
|
+
GIT_CONFIG_COUNT: "4",
|
|
3270
|
+
GIT_CONFIG_KEY_0: "core.hooksPath",
|
|
3271
|
+
GIT_CONFIG_VALUE_0: "/dev/null",
|
|
3272
|
+
GIT_CONFIG_KEY_1: "core.fsmonitor",
|
|
3273
|
+
GIT_CONFIG_VALUE_1: "false",
|
|
3274
|
+
GIT_CONFIG_KEY_2: "credential.helper",
|
|
3275
|
+
GIT_CONFIG_VALUE_2: "",
|
|
3276
|
+
GIT_CONFIG_KEY_3: "protocol.ext.allow",
|
|
3277
|
+
GIT_CONFIG_VALUE_3: "never"
|
|
3278
|
+
};
|
|
3279
|
+
function runIsolatedGit(runtime, args, cwd, options = {}) {
|
|
3280
|
+
return runProcess(runtime.executable, args, {
|
|
3281
|
+
cwd,
|
|
3282
|
+
timeoutMs: options.timeoutMs ?? 15e3,
|
|
3283
|
+
maxOutputBytes: options.maxOutputBytes ?? 2e6,
|
|
3284
|
+
...options.stopOnOutputLimit !== void 0 ? { stopOnOutputLimit: options.stopOnOutputLimit } : {},
|
|
3285
|
+
env: { ...isolatedGitEnvironment, PATH: runtime.path },
|
|
3286
|
+
unsetEnv: unsafeInheritedGitEnvironment,
|
|
3287
|
+
unsetEnvPrefixes: ["GIT_"],
|
|
3288
|
+
...options.stdin !== void 0 ? { stdin: options.stdin } : {},
|
|
3289
|
+
...options.signal ? { signal: options.signal } : {}
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
3292
|
+
|
|
3293
|
+
// src/context/git-recency.ts
|
|
3294
|
+
var MAX_COMMITS = 128;
|
|
3295
|
+
var MAX_OUTPUT_BYTES = 1e6;
|
|
3296
|
+
var TIMEOUT_MS = 2e3;
|
|
3297
|
+
var MAX_RECENCY_SCORE = 1e-3;
|
|
3298
|
+
var commitHashPattern = /^(?:[a-f\d]{40}|[a-f\d]{64})$/u;
|
|
3299
|
+
var GitRecencyCollector = class {
|
|
3300
|
+
roots;
|
|
3301
|
+
states = /* @__PURE__ */ new Map();
|
|
3302
|
+
constructor(roots) {
|
|
3303
|
+
this.roots = roots.map((root) => resolve9(root));
|
|
3304
|
+
}
|
|
3305
|
+
async collect() {
|
|
3306
|
+
const snapshots = await Promise.all(this.roots.map((root) => this.collectRoot(root)));
|
|
3307
|
+
const scores = /* @__PURE__ */ new Map();
|
|
3308
|
+
for (const snapshot of snapshots) {
|
|
3309
|
+
for (const [path, score] of snapshot.scores) {
|
|
3310
|
+
scores.set(path, Math.max(score, scores.get(path) ?? 0));
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
return {
|
|
3314
|
+
generation: digest(snapshots.map(({ root, generation }) => `${root}\0${generation}`).join("\n")),
|
|
3315
|
+
scores
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
async collectRoot(root) {
|
|
3319
|
+
const state = this.states.get(root) ?? {
|
|
3320
|
+
runtime: void 0,
|
|
3321
|
+
runtimeResolved: false,
|
|
3322
|
+
head: void 0,
|
|
3323
|
+
snapshot: void 0
|
|
3324
|
+
};
|
|
3325
|
+
this.states.set(root, state);
|
|
3326
|
+
try {
|
|
3327
|
+
if (!state.runtimeResolved) {
|
|
3328
|
+
state.runtime = await resolveExecutableRuntime("git", root, this.roots);
|
|
3329
|
+
state.runtimeResolved = true;
|
|
3330
|
+
}
|
|
3331
|
+
if (!state.runtime) return unavailableSnapshot(root, "missing");
|
|
3332
|
+
const headResult = await runIsolatedGit(
|
|
3333
|
+
state.runtime,
|
|
3334
|
+
["rev-parse", "--verify", "HEAD"],
|
|
3335
|
+
root,
|
|
3336
|
+
{ timeoutMs: TIMEOUT_MS, maxOutputBytes: 256, stopOnOutputLimit: true }
|
|
3337
|
+
);
|
|
3338
|
+
const head = headResult.stdout.trim();
|
|
3339
|
+
if (headResult.exitCode !== 0 || headResult.timedOut || headResult.stdoutTruncated || headResult.stderrTruncated || !commitHashPattern.test(head)) {
|
|
3340
|
+
state.head = void 0;
|
|
3341
|
+
state.snapshot = unavailableSnapshot(root, "no-head");
|
|
3342
|
+
return state.snapshot;
|
|
3343
|
+
}
|
|
3344
|
+
if (state.head === head && state.snapshot) return state.snapshot;
|
|
3345
|
+
state.head = head;
|
|
3346
|
+
state.snapshot = await collectRootGitLog(root, state.runtime);
|
|
3347
|
+
return state.snapshot;
|
|
3348
|
+
} catch {
|
|
3349
|
+
state.head = void 0;
|
|
3350
|
+
state.snapshot = unavailableSnapshot(root, "error");
|
|
3351
|
+
return state.snapshot;
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
};
|
|
3355
|
+
async function collectRootGitLog(root, runtime) {
|
|
3356
|
+
try {
|
|
3357
|
+
const result = await runIsolatedGit(runtime, [
|
|
3358
|
+
"--no-pager",
|
|
3359
|
+
"log",
|
|
3360
|
+
"--topo-order",
|
|
3361
|
+
`--max-count=${MAX_COMMITS}`,
|
|
3362
|
+
"--format=%x00%H",
|
|
3363
|
+
"--name-only",
|
|
3364
|
+
"-z",
|
|
3365
|
+
"--no-renames",
|
|
3366
|
+
"--no-ext-diff",
|
|
3367
|
+
"--no-textconv",
|
|
3368
|
+
"--relative",
|
|
3369
|
+
"--",
|
|
3370
|
+
"."
|
|
3371
|
+
], root, {
|
|
3372
|
+
timeoutMs: TIMEOUT_MS,
|
|
3373
|
+
maxOutputBytes: MAX_OUTPUT_BYTES,
|
|
3374
|
+
stopOnOutputLimit: true
|
|
3375
|
+
});
|
|
3376
|
+
if (result.exitCode !== 0 || result.timedOut || result.stdoutTruncated || result.stderrTruncated) {
|
|
3377
|
+
const reason = result.timedOut ? "timeout" : result.stdoutTruncated || result.stderrTruncated ? "output-limit" : `exit-${result.exitCode}`;
|
|
3378
|
+
return unavailableSnapshot(root, reason);
|
|
3379
|
+
}
|
|
3380
|
+
return {
|
|
3381
|
+
root,
|
|
3382
|
+
generation: digest(result.stdout),
|
|
3383
|
+
scores: parseGitLog(root, result.stdout)
|
|
3384
|
+
};
|
|
3385
|
+
} catch {
|
|
3386
|
+
return unavailableSnapshot(root, "error");
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
function parseGitLog(root, output2) {
|
|
3390
|
+
const scores = /* @__PURE__ */ new Map();
|
|
3391
|
+
const records = output2.split("\0");
|
|
3392
|
+
let commitRank = -1;
|
|
3393
|
+
for (let index = 0; index < records.length; index += 1) {
|
|
3394
|
+
const record = records[index] ?? "";
|
|
3395
|
+
if (!record) {
|
|
3396
|
+
const commit = records[index + 1]?.trim() ?? "";
|
|
3397
|
+
if (commitHashPattern.test(commit)) {
|
|
3398
|
+
commitRank += 1;
|
|
3399
|
+
index += 1;
|
|
3400
|
+
}
|
|
3401
|
+
continue;
|
|
3402
|
+
}
|
|
3403
|
+
if (commitRank < 0) continue;
|
|
3404
|
+
const path = record.startsWith("\n") ? record.slice(1) : record;
|
|
3405
|
+
if (!path) continue;
|
|
3406
|
+
const absolutePath = resolve9(root, path);
|
|
3407
|
+
if (!isInside(root, absolutePath) || scores.has(absolutePath)) continue;
|
|
3408
|
+
scores.set(absolutePath, MAX_RECENCY_SCORE / (commitRank + 1));
|
|
3409
|
+
}
|
|
3410
|
+
return scores;
|
|
3411
|
+
}
|
|
3412
|
+
function unavailableSnapshot(root, reason) {
|
|
3413
|
+
return { root, generation: digest(`unavailable\0${reason}`), scores: /* @__PURE__ */ new Map() };
|
|
3414
|
+
}
|
|
3415
|
+
function digest(value) {
|
|
3416
|
+
return createHash6("sha256").update(value, "utf8").digest("hex").slice(0, 16);
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3049
3419
|
// src/context/local-index.ts
|
|
3050
3420
|
var contentHashSchema = z3.string().regex(/^[a-f\d]{64}$/u);
|
|
3051
3421
|
var indexedChunkSchema = z3.object({
|
|
@@ -3108,17 +3478,23 @@ var MAX_QUERY_CACHE_ENTRIES = 64;
|
|
|
3108
3478
|
var CHUNK_LINES = 100;
|
|
3109
3479
|
var CHUNK_OVERLAP = 15;
|
|
3110
3480
|
var MIN_STRUCTURAL_CHUNK_LINES = 12;
|
|
3481
|
+
var MAX_DIAGNOSTIC_SCORE = 0.05;
|
|
3482
|
+
var MAX_DIAGNOSTIC_HINTS_PER_COMMAND = 16;
|
|
3111
3483
|
var LocalContextIndex = class {
|
|
3112
3484
|
constructor(roots) {
|
|
3113
3485
|
this.roots = roots;
|
|
3114
|
-
this.roots = roots.map((root) =>
|
|
3486
|
+
this.roots = roots.map((root) => resolve10(root));
|
|
3115
3487
|
this.workspace = new WorkspaceAccess(this.roots);
|
|
3116
|
-
this.
|
|
3488
|
+
this.gitRecency = new GitRecencyCollector(this.roots);
|
|
3489
|
+
this.indexPath = join8(resolveProjectNamespaceSync(this.roots[0] ?? process.cwd()).active, "index.json");
|
|
3117
3490
|
}
|
|
3118
3491
|
roots;
|
|
3119
3492
|
index;
|
|
3120
3493
|
workspace;
|
|
3121
3494
|
queryCache = /* @__PURE__ */ new Map();
|
|
3495
|
+
gitRecency;
|
|
3496
|
+
diagnosticsByCommand = /* @__PURE__ */ new Map();
|
|
3497
|
+
diagnosticScores = /* @__PURE__ */ new Map();
|
|
3122
3498
|
queryCacheHits = 0;
|
|
3123
3499
|
queryCacheMisses = 0;
|
|
3124
3500
|
fingerprintCache;
|
|
@@ -3132,13 +3508,13 @@ var LocalContextIndex = class {
|
|
|
3132
3508
|
this.roots[0] ?? process.cwd(),
|
|
3133
3509
|
dirname7(this.indexPath)
|
|
3134
3510
|
);
|
|
3135
|
-
const info = await
|
|
3511
|
+
const info = await lstat9(this.indexPath);
|
|
3136
3512
|
if (!info.isFile() || info.isSymbolicLink()) return false;
|
|
3137
3513
|
const parsed = localIndexSchema.parse(JSON.parse(await readFile3(this.indexPath, "utf8")));
|
|
3138
3514
|
const files = [];
|
|
3139
3515
|
for (const file of parsed.files) {
|
|
3140
3516
|
try {
|
|
3141
|
-
if (!this.roots.includes(file.root) ||
|
|
3517
|
+
if (!this.roots.includes(file.root) || resolve10(file.root, file.path) !== file.absolutePath) continue;
|
|
3142
3518
|
const safe = await this.workspace.resolvePath(file.absolutePath, { expect: "file" });
|
|
3143
3519
|
if (safe !== file.absolutePath) continue;
|
|
3144
3520
|
const { ctimeMs, ...persistedFile } = file;
|
|
@@ -3215,19 +3591,21 @@ var LocalContextIndex = class {
|
|
|
3215
3591
|
await this.flushDirty();
|
|
3216
3592
|
await this.ensureCurrentIndex();
|
|
3217
3593
|
const limit = Math.max(1, Math.floor(topK));
|
|
3218
|
-
|
|
3219
|
-
|
|
3594
|
+
let recency = await this.gitRecency.collect();
|
|
3595
|
+
const cached = this.getCachedHits(query, limit, recency.generation);
|
|
3596
|
+
let hits = cached ?? this.rank(query, limit, recency.scores);
|
|
3220
3597
|
if (!await this.hitsAreCurrent(hits)) {
|
|
3221
3598
|
await this.buildWithOptions(void 0, true);
|
|
3222
|
-
|
|
3599
|
+
recency = await this.gitRecency.collect();
|
|
3600
|
+
hits = this.rank(query, limit, recency.scores);
|
|
3223
3601
|
if (!await this.hitsAreCurrent(hits)) return [];
|
|
3224
3602
|
}
|
|
3225
|
-
this.cacheHits(query, limit, hits);
|
|
3603
|
+
this.cacheHits(query, limit, recency.generation, hits);
|
|
3226
3604
|
return cloneHits(hits);
|
|
3227
3605
|
}
|
|
3228
3606
|
invalidate(paths) {
|
|
3229
3607
|
for (const path of paths) {
|
|
3230
|
-
const absolutePath =
|
|
3608
|
+
const absolutePath = isAbsolute4(path) ? resolve10(path) : resolve10(this.roots[0] ?? process.cwd(), path);
|
|
3231
3609
|
if (this.roots.some((root) => isWithinRoot(root, absolutePath))) {
|
|
3232
3610
|
this.dirtyPaths.add(absolutePath);
|
|
3233
3611
|
}
|
|
@@ -3235,6 +3613,23 @@ var LocalContextIndex = class {
|
|
|
3235
3613
|
if (paths.length) this.queryCache.clear();
|
|
3236
3614
|
if (this.dirtyPaths.size) this.refreshState = "dirty";
|
|
3237
3615
|
}
|
|
3616
|
+
recordDiagnostics(update) {
|
|
3617
|
+
const paths = /* @__PURE__ */ new Set();
|
|
3618
|
+
for (const candidate of update.paths.slice(0, MAX_DIAGNOSTIC_HINTS_PER_COMMAND)) {
|
|
3619
|
+
const path = this.resolveDiagnosticPath(candidate);
|
|
3620
|
+
if (path) paths.add(path);
|
|
3621
|
+
}
|
|
3622
|
+
if (paths.size) this.diagnosticsByCommand.set(update.commandKey, paths);
|
|
3623
|
+
else this.diagnosticsByCommand.delete(update.commandKey);
|
|
3624
|
+
this.refreshDiagnosticScores();
|
|
3625
|
+
this.queryCache.clear();
|
|
3626
|
+
}
|
|
3627
|
+
resetDiagnostics() {
|
|
3628
|
+
if (!this.diagnosticsByCommand.size) return;
|
|
3629
|
+
this.diagnosticsByCommand.clear();
|
|
3630
|
+
this.diagnosticScores.clear();
|
|
3631
|
+
this.queryCache.clear();
|
|
3632
|
+
}
|
|
3238
3633
|
async flushDirty() {
|
|
3239
3634
|
if (!this.dirtyPaths.size) {
|
|
3240
3635
|
return {
|
|
@@ -3320,6 +3715,7 @@ var LocalContextIndex = class {
|
|
|
3320
3715
|
queryCacheEntries: this.queryCache.size,
|
|
3321
3716
|
queryCacheHits: this.queryCacheHits,
|
|
3322
3717
|
queryCacheMisses: this.queryCacheMisses,
|
|
3718
|
+
diagnosticHints: this.diagnosticScores.size,
|
|
3323
3719
|
refreshState: this.refreshState,
|
|
3324
3720
|
dirtyPaths: this.dirtyPaths.size,
|
|
3325
3721
|
...this.refreshError ? { refreshError: this.refreshError } : {},
|
|
@@ -3424,6 +3820,7 @@ var LocalContextIndex = class {
|
|
|
3424
3820
|
);
|
|
3425
3821
|
await atomicWrite(this.indexPath, `${JSON.stringify(this.index)}
|
|
3426
3822
|
`, 384);
|
|
3823
|
+
await this.gitRecency.collect();
|
|
3427
3824
|
onProgress?.({ phase: "done", completed: files.length, total: files.length });
|
|
3428
3825
|
return {
|
|
3429
3826
|
files: files.length,
|
|
@@ -3517,7 +3914,7 @@ var LocalContextIndex = class {
|
|
|
3517
3914
|
followSymbolicLinks: false,
|
|
3518
3915
|
ignore: ignorePatterns
|
|
3519
3916
|
});
|
|
3520
|
-
for (const path of paths) discovered.push({ root, path, absolutePath:
|
|
3917
|
+
for (const path of paths) discovered.push({ root, path, absolutePath: resolve10(root, path) });
|
|
3521
3918
|
}
|
|
3522
3919
|
discovered.sort((left, right) => left.absolutePath.localeCompare(right.absolutePath));
|
|
3523
3920
|
return discovered;
|
|
@@ -3537,7 +3934,7 @@ var LocalContextIndex = class {
|
|
|
3537
3934
|
});
|
|
3538
3935
|
return matches.length === 1 ? { root, path, absolutePath } : void 0;
|
|
3539
3936
|
}
|
|
3540
|
-
rank(query, topK) {
|
|
3937
|
+
rank(query, topK, recencyScores) {
|
|
3541
3938
|
const index = this.index;
|
|
3542
3939
|
const files = index?.files ?? [];
|
|
3543
3940
|
const chunks = files.flatMap((file) => file.chunks);
|
|
@@ -3567,11 +3964,13 @@ var LocalContextIndex = class {
|
|
|
3567
3964
|
averageLength
|
|
3568
3965
|
);
|
|
3569
3966
|
const graph = graphBoosts.get(chunk.absolutePath)?.score ?? 0;
|
|
3570
|
-
const
|
|
3571
|
-
|
|
3572
|
-
|
|
3967
|
+
const recency = recencyScores.get(chunk.absolutePath) ?? 0;
|
|
3968
|
+
const diagnostic = this.diagnosticScores.get(chunk.absolutePath) ?? 0;
|
|
3969
|
+
const score = lexical.bm25 + lexical.path + lexical.symbol + lexical.phrase + graph + recency + diagnostic;
|
|
3970
|
+
return { chunk, lexical, graph, recency, diagnostic, score };
|
|
3971
|
+
}).filter(({ lexical, graph }) => lexical.bm25 + lexical.path + lexical.symbol + lexical.phrase + graph > 0).sort((left, right) => right.score - left.score || left.chunk.absolutePath.localeCompare(right.chunk.absolutePath));
|
|
3573
3972
|
const covered = scored.filter(({ lexical, graph }) => graph > 0 || hasSufficientQueryCoverage(lexical.matchedTerms, coverageTerms));
|
|
3574
|
-
return (covered.length ? covered : scored).slice(0, topK).map(({ chunk, lexical, graph, score }) => {
|
|
3973
|
+
return (covered.length ? covered : scored).slice(0, topK).map(({ chunk, lexical, graph, recency, diagnostic, score }) => {
|
|
3575
3974
|
const file = filesByPath.get(chunk.absolutePath);
|
|
3576
3975
|
const breakdown = {
|
|
3577
3976
|
bm25: roundScore(lexical.bm25),
|
|
@@ -3579,6 +3978,8 @@ var LocalContextIndex = class {
|
|
|
3579
3978
|
symbol: roundScore(lexical.symbol),
|
|
3580
3979
|
phrase: roundScore(lexical.phrase),
|
|
3581
3980
|
graph: roundScore(graph),
|
|
3981
|
+
recency: roundScore(recency),
|
|
3982
|
+
diagnostic: roundScore(diagnostic),
|
|
3582
3983
|
total: roundScore(score)
|
|
3583
3984
|
};
|
|
3584
3985
|
const provenance = {
|
|
@@ -3594,7 +3995,7 @@ var LocalContextIndex = class {
|
|
|
3594
3995
|
endLine: chunk.endLine,
|
|
3595
3996
|
content: chunk.content,
|
|
3596
3997
|
score,
|
|
3597
|
-
source: "local-bm25+path+symbol+graph",
|
|
3998
|
+
source: "local-bm25+path+symbol+graph+recency+diagnostic",
|
|
3598
3999
|
...chunk.symbol ? { symbol: chunk.symbol } : {},
|
|
3599
4000
|
provenance
|
|
3600
4001
|
};
|
|
@@ -3620,13 +4021,13 @@ var LocalContextIndex = class {
|
|
|
3620
4021
|
}
|
|
3621
4022
|
return true;
|
|
3622
4023
|
}
|
|
3623
|
-
getCachedHits(query, topK) {
|
|
4024
|
+
getCachedHits(query, topK, rankingGeneration) {
|
|
3624
4025
|
const generation = this.index?.generation;
|
|
3625
4026
|
if (!generation) {
|
|
3626
4027
|
this.queryCacheMisses += 1;
|
|
3627
4028
|
return void 0;
|
|
3628
4029
|
}
|
|
3629
|
-
const key = `${generation}\0${topK}\0${query}`;
|
|
4030
|
+
const key = `${generation}\0${rankingGeneration}\0${topK}\0${query}`;
|
|
3630
4031
|
const cached = this.queryCache.get(key);
|
|
3631
4032
|
if (!cached) {
|
|
3632
4033
|
this.queryCacheMisses += 1;
|
|
@@ -3637,11 +4038,11 @@ var LocalContextIndex = class {
|
|
|
3637
4038
|
this.queryCache.set(key, cached);
|
|
3638
4039
|
return cloneHits(cached);
|
|
3639
4040
|
}
|
|
3640
|
-
cacheHits(query, topK, hits) {
|
|
4041
|
+
cacheHits(query, topK, rankingGeneration, hits) {
|
|
3641
4042
|
if (!hits.length) return;
|
|
3642
4043
|
const generation = this.index?.generation;
|
|
3643
4044
|
if (!generation) return;
|
|
3644
|
-
const key = `${generation}\0${topK}\0${query}`;
|
|
4045
|
+
const key = `${generation}\0${rankingGeneration}\0${topK}\0${query}`;
|
|
3645
4046
|
this.queryCache.delete(key);
|
|
3646
4047
|
this.queryCache.set(key, cloneHits(hits));
|
|
3647
4048
|
while (this.queryCache.size > MAX_QUERY_CACHE_ENTRIES) {
|
|
@@ -3650,10 +4051,26 @@ var LocalContextIndex = class {
|
|
|
3650
4051
|
this.queryCache.delete(oldest);
|
|
3651
4052
|
}
|
|
3652
4053
|
}
|
|
4054
|
+
resolveDiagnosticPath(candidate) {
|
|
4055
|
+
if (!candidate || candidate.includes("\0")) return void 0;
|
|
4056
|
+
const absolute = isAbsolute4(candidate) ? resolve10(candidate) : void 0;
|
|
4057
|
+
if (absolute && this.roots.some((root) => isWithinRoot(root, absolute))) return absolute;
|
|
4058
|
+
for (const root of this.roots) {
|
|
4059
|
+
const path = resolve10(root, candidate);
|
|
4060
|
+
if (isWithinRoot(root, path)) return path;
|
|
4061
|
+
}
|
|
4062
|
+
return void 0;
|
|
4063
|
+
}
|
|
4064
|
+
refreshDiagnosticScores() {
|
|
4065
|
+
this.diagnosticScores.clear();
|
|
4066
|
+
for (const paths of this.diagnosticsByCommand.values()) {
|
|
4067
|
+
for (const path of paths) this.diagnosticScores.set(path, MAX_DIAGNOSTIC_SCORE);
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
3653
4070
|
};
|
|
3654
4071
|
function isWithinRoot(root, path) {
|
|
3655
4072
|
const offset = relative5(root, path);
|
|
3656
|
-
return offset === "" || !offset.startsWith("..") && !
|
|
4073
|
+
return offset === "" || !offset.startsWith("..") && !isAbsolute4(offset);
|
|
3657
4074
|
}
|
|
3658
4075
|
function isIncludedSourcePath(path) {
|
|
3659
4076
|
const name = basename5(path);
|
|
@@ -3785,7 +4202,7 @@ function packContextHits(hits, roots, maxTokens, engine) {
|
|
|
3785
4202
|
const text = selected.map((hit) => {
|
|
3786
4203
|
const shownPath = workspaceAliasPath(hit.path, roots);
|
|
3787
4204
|
const symbol = hit.symbol ? ` symbol="${escapeAttribute(hit.symbol)}"` : "";
|
|
3788
|
-
const provenance = hit.provenance ? ` source="${escapeAttribute(hit.source)}" hash="${hit.provenance.contentHash.slice(0, 12)}" graph-score="${hit.provenance.score.graph.toFixed(3)}"` : "";
|
|
4205
|
+
const provenance = hit.provenance ? ` source="${escapeAttribute(hit.source)}" hash="${hit.provenance.contentHash.slice(0, 12)}" graph-score="${hit.provenance.score.graph.toFixed(3)}" recency-score="${hit.provenance.score.recency.toFixed(6)}" diagnostic-score="${hit.provenance.score.diagnostic.toFixed(3)}"` : "";
|
|
3789
4206
|
return `<code path="${escapeAttribute(shownPath)}" lines="${hit.startLine}-${hit.endLine}" score="${hit.score.toFixed(3)}"${symbol}${provenance}>
|
|
3790
4207
|
${hit.content}
|
|
3791
4208
|
</code>`;
|
|
@@ -3833,10 +4250,10 @@ function reconstructIndexedContent(chunks) {
|
|
|
3833
4250
|
return lines.map((line) => line ?? "").join("\n");
|
|
3834
4251
|
}
|
|
3835
4252
|
function hashContent(content) {
|
|
3836
|
-
return
|
|
4253
|
+
return createHash7("sha256").update(content, "utf8").digest("hex");
|
|
3837
4254
|
}
|
|
3838
4255
|
function createGeneration(files) {
|
|
3839
|
-
return
|
|
4256
|
+
return createHash7("sha256").update(files.slice().sort((left, right) => left.absolutePath.localeCompare(right.absolutePath)).map((file) => `${file.absolutePath}\0${file.contentHash}`).join("\n"), "utf8").digest("hex").slice(0, 16);
|
|
3840
4257
|
}
|
|
3841
4258
|
function hasSubstantialOverlap(left, right) {
|
|
3842
4259
|
if (left.path !== right.path) return false;
|
|
@@ -4112,18 +4529,37 @@ function definitionMatchesQuery(definitionTerms, queryTerms) {
|
|
|
4112
4529
|
return shared.length >= required;
|
|
4113
4530
|
}
|
|
4114
4531
|
function resolveImportTargets(importer, specifier, files) {
|
|
4115
|
-
|
|
4116
|
-
const
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
base,
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4532
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
4533
|
+
const extension = extname2(importer.path).toLocaleLowerCase();
|
|
4534
|
+
if (specifier.startsWith(".")) {
|
|
4535
|
+
const base = posix.normalize(posix.join(posix.dirname(importer.path), specifier));
|
|
4536
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
4537
|
+
const importedExtension = posix.extname(base);
|
|
4538
|
+
const extensionless = extensions.includes(importedExtension) ? base.slice(0, -importedExtension.length) : base;
|
|
4539
|
+
candidates.add(base);
|
|
4540
|
+
for (const candidateExtension of extensions) {
|
|
4541
|
+
candidates.add(`${extensionless}${candidateExtension}`);
|
|
4542
|
+
candidates.add(posix.join(extensionless, `index${candidateExtension}`));
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
if (extension === ".py") {
|
|
4546
|
+
const modulePath = resolvePythonModulePath(importer.path, specifier);
|
|
4547
|
+
if (modulePath) {
|
|
4548
|
+
candidates.add(`${modulePath}.py`);
|
|
4549
|
+
candidates.add(posix.join(modulePath, "__init__.py"));
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4125
4552
|
return files.filter((file) => file.root === importer.root && candidates.has(file.path)).map((file) => file.absolutePath);
|
|
4126
4553
|
}
|
|
4554
|
+
function resolvePythonModulePath(importerPath, specifier) {
|
|
4555
|
+
const match = specifier.match(/^(\.+)(.*)$/u);
|
|
4556
|
+
if (!match) return specifier.split(".").filter(Boolean).join("/");
|
|
4557
|
+
const [, dots, remainder] = match;
|
|
4558
|
+
if (!dots || !remainder) return void 0;
|
|
4559
|
+
let base = posix.dirname(importerPath);
|
|
4560
|
+
for (let level = 1; level < dots.length; level += 1) base = posix.dirname(base);
|
|
4561
|
+
return posix.normalize(posix.join(base, remainder.split(".").filter(Boolean).join("/")));
|
|
4562
|
+
}
|
|
4127
4563
|
function scoreChunk(chunk, terms, rawQuery, documentFrequency, documentCount, averageLength) {
|
|
4128
4564
|
const frequencies = /* @__PURE__ */ new Map();
|
|
4129
4565
|
for (const token of chunk.tokens) frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
@@ -4214,6 +4650,12 @@ var ContextEngine = class {
|
|
|
4214
4650
|
invalidate(paths) {
|
|
4215
4651
|
this.local.invalidate(paths);
|
|
4216
4652
|
}
|
|
4653
|
+
recordDiagnostics(update) {
|
|
4654
|
+
this.local.recordDiagnostics(update);
|
|
4655
|
+
}
|
|
4656
|
+
resetDiagnostics() {
|
|
4657
|
+
this.local.resetDiagnostics();
|
|
4658
|
+
}
|
|
4217
4659
|
async flushDirty() {
|
|
4218
4660
|
try {
|
|
4219
4661
|
const result = await this.local.flushDirty();
|
|
@@ -4260,7 +4702,7 @@ function formatContextHits(hits, roots) {
|
|
|
4260
4702
|
const path = workspaceAliasPath(hit.path, roots);
|
|
4261
4703
|
const symbol = hit.symbol ? ` ${hit.symbol}` : "";
|
|
4262
4704
|
const score = hit.provenance?.score;
|
|
4263
|
-
const breakdown = score ? ` bm25=${score.bm25.toFixed(2)} path=${score.path.toFixed(2)} symbol=${score.symbol.toFixed(2)} graph=${score.graph.toFixed(2)}` : "";
|
|
4705
|
+
const breakdown = score ? ` bm25=${score.bm25.toFixed(2)} path=${score.path.toFixed(2)} symbol=${score.symbol.toFixed(2)} graph=${score.graph.toFixed(2)} recency=${score.recency.toFixed(6)} diagnostic=${score.diagnostic.toFixed(3)}` : "";
|
|
4264
4706
|
const hash4 = hit.provenance?.contentHash.slice(0, 12);
|
|
4265
4707
|
return `[${hit.source} ${hit.score.toFixed(3)}${breakdown}${hash4 ? ` hash=${hash4}` : ""}]${symbol} ${path}:${hit.startLine}-${hit.endLine}`;
|
|
4266
4708
|
}).join("\n");
|
|
@@ -4501,7 +4943,7 @@ function toolTokens(messages) {
|
|
|
4501
4943
|
|
|
4502
4944
|
// src/context/mentions.ts
|
|
4503
4945
|
import { readFile as readFile4, stat as stat4 } from "node:fs/promises";
|
|
4504
|
-
import { isAbsolute as
|
|
4946
|
+
import { isAbsolute as isAbsolute5, resolve as resolve11 } from "node:path";
|
|
4505
4947
|
import fg2 from "fast-glob";
|
|
4506
4948
|
var defaultMentionIgnores = [
|
|
4507
4949
|
"**/.git/**",
|
|
@@ -4572,7 +5014,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
|
|
|
4572
5014
|
ignore: ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/.mosaic/**", "**/.skein/**", "**/.skein.migrating-*/**", "**/.skein.rollback-*/**"]
|
|
4573
5015
|
});
|
|
4574
5016
|
matched.push(...await safeMentionPaths(
|
|
4575
|
-
children.slice(0, 25).map((path) =>
|
|
5017
|
+
children.slice(0, 25).map((path) => resolve11(aliased, path)),
|
|
4576
5018
|
workspace
|
|
4577
5019
|
));
|
|
4578
5020
|
}
|
|
@@ -4580,7 +5022,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
|
|
|
4580
5022
|
}
|
|
4581
5023
|
}
|
|
4582
5024
|
for (const root of matched.length ? [] : roots) {
|
|
4583
|
-
const direct =
|
|
5025
|
+
const direct = resolve11(root, clean2);
|
|
4584
5026
|
try {
|
|
4585
5027
|
const safeDirect = await workspace.resolvePath(direct);
|
|
4586
5028
|
const info = await stat4(safeDirect);
|
|
@@ -4594,7 +5036,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
|
|
|
4594
5036
|
ignore: ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/.mosaic/**", "**/.skein/**", "**/.skein.migrating-*/**", "**/.skein.rollback-*/**"]
|
|
4595
5037
|
});
|
|
4596
5038
|
matched.push(...await safeMentionPaths(
|
|
4597
|
-
children.slice(0, 25).map((path) =>
|
|
5039
|
+
children.slice(0, 25).map((path) => resolve11(safeDirect, path)),
|
|
4598
5040
|
workspace
|
|
4599
5041
|
));
|
|
4600
5042
|
}
|
|
@@ -4610,7 +5052,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
|
|
|
4610
5052
|
ignore: ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/.mosaic/**", "**/.skein/**", "**/.skein.migrating-*/**", "**/.skein.rollback-*/**"]
|
|
4611
5053
|
});
|
|
4612
5054
|
matched.push(...await safeMentionPaths(
|
|
4613
|
-
globbed.slice(0, 25).map((path) =>
|
|
5055
|
+
globbed.slice(0, 25).map((path) => resolve11(root, path)),
|
|
4614
5056
|
workspace
|
|
4615
5057
|
));
|
|
4616
5058
|
}
|
|
@@ -4676,7 +5118,7 @@ async function buildMentionPathIndex(roots, options = {}) {
|
|
|
4676
5118
|
})
|
|
4677
5119
|
})));
|
|
4678
5120
|
const paths = filesByRoot.flatMap(({ root, files }) => files.map(
|
|
4679
|
-
(path) => workspaceAliasPath(
|
|
5121
|
+
(path) => workspaceAliasPath(resolve11(root, path), normalizedRoots)
|
|
4680
5122
|
));
|
|
4681
5123
|
return new MentionPathIndex(normalizedRoots, paths);
|
|
4682
5124
|
}
|
|
@@ -4710,22 +5152,22 @@ function mapMentionCandidatePath(path, roots) {
|
|
|
4710
5152
|
const primary = normalizedRoots[0];
|
|
4711
5153
|
if (!primary || !path || path.includes("\0")) return void 0;
|
|
4712
5154
|
let candidate;
|
|
4713
|
-
if (
|
|
4714
|
-
candidate =
|
|
5155
|
+
if (isAbsolute5(path)) {
|
|
5156
|
+
candidate = resolve11(path);
|
|
4715
5157
|
} else {
|
|
4716
5158
|
const normalized = path.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
4717
5159
|
const [first = "", ...rest] = normalized.split("/");
|
|
4718
5160
|
const alias = first.toLocaleLowerCase();
|
|
4719
5161
|
const workspace = alias.match(/^workspace(\d+)$/);
|
|
4720
5162
|
if (alias === "main") {
|
|
4721
|
-
candidate =
|
|
5163
|
+
candidate = resolve11(primary, ...rest);
|
|
4722
5164
|
} else if (workspace) {
|
|
4723
5165
|
const index = Number(workspace[1]) - 1;
|
|
4724
5166
|
const root = Number.isSafeInteger(index) && index >= 1 ? normalizedRoots[index] : void 0;
|
|
4725
5167
|
if (!root) return void 0;
|
|
4726
|
-
candidate =
|
|
5168
|
+
candidate = resolve11(root, ...rest);
|
|
4727
5169
|
} else {
|
|
4728
|
-
candidate =
|
|
5170
|
+
candidate = resolve11(primary, normalized);
|
|
4729
5171
|
}
|
|
4730
5172
|
}
|
|
4731
5173
|
if (!normalizedRoots.some((root) => isInside(root, candidate))) return void 0;
|
|
@@ -4755,7 +5197,7 @@ function rankMention(path, needle) {
|
|
|
4755
5197
|
}
|
|
4756
5198
|
function normalizeRoots(roots) {
|
|
4757
5199
|
const values = typeof roots === "string" ? [roots] : roots;
|
|
4758
|
-
return [...new Set(values.map((root) =>
|
|
5200
|
+
return [...new Set(values.map((root) => resolve11(root)))];
|
|
4759
5201
|
}
|
|
4760
5202
|
function normalizeMentionCandidate(path) {
|
|
4761
5203
|
const normalized = path.replaceAll("\\", "/").normalize("NFC");
|
|
@@ -5421,9 +5863,9 @@ function createProvider(config) {
|
|
|
5421
5863
|
}
|
|
5422
5864
|
|
|
5423
5865
|
// src/checkpoint/store.ts
|
|
5424
|
-
import { createHash as
|
|
5425
|
-
import { lstat as
|
|
5426
|
-
import { basename as basename6, dirname as dirname8, join as
|
|
5866
|
+
import { createHash as createHash8, randomUUID as randomUUID7 } from "node:crypto";
|
|
5867
|
+
import { lstat as lstat10, mkdir as mkdir7, readFile as readFile5, readdir as readdir2, unlink as unlink2 } from "node:fs/promises";
|
|
5868
|
+
import { basename as basename6, dirname as dirname8, join as join9, relative as relative6, resolve as resolve12 } from "node:path";
|
|
5427
5869
|
import { z as z4 } from "zod";
|
|
5428
5870
|
var entrySchema = z4.object({
|
|
5429
5871
|
path: z4.string(),
|
|
@@ -5448,7 +5890,7 @@ var CheckpointStore = class {
|
|
|
5448
5890
|
constructor(workspace, directory) {
|
|
5449
5891
|
this.workspace = typeof workspace === "string" ? new WorkspaceAccess([workspace]) : workspace;
|
|
5450
5892
|
this.managedDirectory = directory === void 0;
|
|
5451
|
-
this.directory = directory ?
|
|
5893
|
+
this.directory = directory ? resolve12(directory) : join9(resolveProjectNamespaceSync(this.workspace.primaryRoot).active, "checkpoints");
|
|
5452
5894
|
}
|
|
5453
5895
|
async capture(sessionId, paths, options = {}) {
|
|
5454
5896
|
validateIdentifier(sessionId, "session");
|
|
@@ -5458,8 +5900,8 @@ var CheckpointStore = class {
|
|
|
5458
5900
|
}
|
|
5459
5901
|
async captureUnlocked(sessionId, unique3, options) {
|
|
5460
5902
|
const id = `${Date.now().toString(36)}-${randomUUID7()}`;
|
|
5461
|
-
const target =
|
|
5462
|
-
const blobDirectory =
|
|
5903
|
+
const target = join9(this.directory, sessionId, id);
|
|
5904
|
+
const blobDirectory = join9(target, "blobs");
|
|
5463
5905
|
await this.ensureDirectory();
|
|
5464
5906
|
await this.assertManagedPath(target);
|
|
5465
5907
|
await mkdir7(blobDirectory, { recursive: true });
|
|
@@ -5468,14 +5910,14 @@ var CheckpointStore = class {
|
|
|
5468
5910
|
for (const input2 of unique3) {
|
|
5469
5911
|
const path = await this.workspace.resolvePath(input2, { allowMissing: true });
|
|
5470
5912
|
try {
|
|
5471
|
-
const info = await
|
|
5913
|
+
const info = await lstat10(path);
|
|
5472
5914
|
if (info.isSymbolicLink()) throw new Error(`Cannot checkpoint a symbolic link: ${input2}`);
|
|
5473
5915
|
if (!info.isFile()) throw new Error(`Cannot checkpoint a non-file path: ${input2}`);
|
|
5474
5916
|
if (info.size > 25e6) {
|
|
5475
5917
|
throw new Error(`File is too large to checkpoint (${info.size} bytes): ${input2}`);
|
|
5476
5918
|
}
|
|
5477
|
-
const blob = `${
|
|
5478
|
-
await atomicWrite(
|
|
5919
|
+
const blob = `${createHash8("sha256").update(path).digest("hex")}.bin`;
|
|
5920
|
+
await atomicWrite(join9(blobDirectory, blob), await readFile5(path), 384);
|
|
5479
5921
|
entries.push({
|
|
5480
5922
|
path,
|
|
5481
5923
|
relativePath: relative6(this.workspace.primaryRoot, path),
|
|
@@ -5502,7 +5944,7 @@ var CheckpointStore = class {
|
|
|
5502
5944
|
entries
|
|
5503
5945
|
};
|
|
5504
5946
|
await atomicWrite(
|
|
5505
|
-
|
|
5947
|
+
join9(target, "manifest.json"),
|
|
5506
5948
|
`${JSON.stringify(manifest, null, 2)}
|
|
5507
5949
|
`,
|
|
5508
5950
|
384
|
|
@@ -5515,9 +5957,9 @@ var CheckpointStore = class {
|
|
|
5515
5957
|
if (!await this.directoryAvailable()) {
|
|
5516
5958
|
throw new Error(`Checkpoint not found: ${checkpointId}`);
|
|
5517
5959
|
}
|
|
5518
|
-
const checkpointDirectory =
|
|
5960
|
+
const checkpointDirectory = join9(this.directory, sessionId, checkpointId);
|
|
5519
5961
|
await this.assertManagedPath(checkpointDirectory);
|
|
5520
|
-
const manifestPath =
|
|
5962
|
+
const manifestPath = join9(checkpointDirectory, "manifest.json");
|
|
5521
5963
|
await this.assertManagedFile(manifestPath);
|
|
5522
5964
|
const raw = await readFile5(manifestPath, "utf8");
|
|
5523
5965
|
const manifest = manifestSchema.parse(JSON.parse(raw));
|
|
@@ -5528,7 +5970,7 @@ var CheckpointStore = class {
|
|
|
5528
5970
|
}
|
|
5529
5971
|
async restore(sessionId, checkpointId) {
|
|
5530
5972
|
const manifest = await this.load(sessionId, checkpointId);
|
|
5531
|
-
const blobDirectory =
|
|
5973
|
+
const blobDirectory = join9(this.directory, sessionId, checkpointId, "blobs");
|
|
5532
5974
|
await this.assertManagedPath(blobDirectory);
|
|
5533
5975
|
const actions = [];
|
|
5534
5976
|
for (const entry of manifest.entries) {
|
|
@@ -5543,8 +5985,8 @@ var CheckpointStore = class {
|
|
|
5543
5985
|
if (!/^[a-f0-9]{64}\.bin$/.test(entry.blob) || basename6(entry.blob) !== entry.blob) {
|
|
5544
5986
|
throw new Error(`Checkpoint blob name is invalid for ${entry.relativePath}.`);
|
|
5545
5987
|
}
|
|
5546
|
-
const blobPath =
|
|
5547
|
-
const blobInfo = await
|
|
5988
|
+
const blobPath = join9(blobDirectory, entry.blob);
|
|
5989
|
+
const blobInfo = await lstat10(blobPath);
|
|
5548
5990
|
if (!blobInfo.isFile() || blobInfo.isSymbolicLink()) {
|
|
5549
5991
|
throw new Error(`Checkpoint blob is not a regular file for ${entry.relativePath}.`);
|
|
5550
5992
|
}
|
|
@@ -5585,7 +6027,7 @@ var CheckpointStore = class {
|
|
|
5585
6027
|
async list(sessionId) {
|
|
5586
6028
|
validateIdentifier(sessionId, "session");
|
|
5587
6029
|
if (!await this.directoryAvailable()) return [];
|
|
5588
|
-
const directory =
|
|
6030
|
+
const directory = join9(this.directory, sessionId);
|
|
5589
6031
|
let names;
|
|
5590
6032
|
try {
|
|
5591
6033
|
names = await readdir2(directory);
|
|
@@ -5624,7 +6066,7 @@ var CheckpointStore = class {
|
|
|
5624
6066
|
await assertNoSymlinkPath(this.workspace.primaryRoot, this.directory);
|
|
5625
6067
|
}
|
|
5626
6068
|
try {
|
|
5627
|
-
const info = await
|
|
6069
|
+
const info = await lstat10(this.directory);
|
|
5628
6070
|
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
5629
6071
|
throw new Error(`Checkpoint storage is not a regular directory: ${this.directory}`);
|
|
5630
6072
|
}
|
|
@@ -5643,7 +6085,7 @@ var CheckpointStore = class {
|
|
|
5643
6085
|
await this.assertManagedPath(dirname8(path));
|
|
5644
6086
|
if (!this.managedDirectory) return;
|
|
5645
6087
|
try {
|
|
5646
|
-
if ((await
|
|
6088
|
+
if ((await lstat10(path)).isSymbolicLink()) {
|
|
5647
6089
|
throw new Error(`Checkpoint path cannot contain a symbolic link: ${path}`);
|
|
5648
6090
|
}
|
|
5649
6091
|
} catch (error) {
|
|
@@ -5653,7 +6095,7 @@ var CheckpointStore = class {
|
|
|
5653
6095
|
};
|
|
5654
6096
|
async function readSnapshot(path) {
|
|
5655
6097
|
try {
|
|
5656
|
-
const info = await
|
|
6098
|
+
const info = await lstat10(path);
|
|
5657
6099
|
if (info.isSymbolicLink()) throw new Error(`Cannot restore over a symbolic link: ${path}`);
|
|
5658
6100
|
if (!info.isFile()) throw new Error(`Cannot restore over a non-file path: ${path}`);
|
|
5659
6101
|
return { content: await readFile5(path), mode: info.mode };
|
|
@@ -5678,174 +6120,6 @@ function validateIdentifier(value, kind) {
|
|
|
5678
6120
|
}
|
|
5679
6121
|
}
|
|
5680
6122
|
|
|
5681
|
-
// src/utils/process.ts
|
|
5682
|
-
import { spawn } from "node:child_process";
|
|
5683
|
-
import { constants as constants3 } from "node:fs";
|
|
5684
|
-
import { access as access2, lstat as lstat10, realpath as realpath6 } from "node:fs/promises";
|
|
5685
|
-
import { delimiter, isAbsolute as isAbsolute5, join as join9, resolve as resolve11 } from "node:path";
|
|
5686
|
-
import { StringDecoder } from "node:string_decoder";
|
|
5687
|
-
async function resolveExecutableRuntime(command2, cwd, excludedRoots = []) {
|
|
5688
|
-
const realRoots = await Promise.all(excludedRoots.map(async (root) => {
|
|
5689
|
-
try {
|
|
5690
|
-
return await realpath6(root);
|
|
5691
|
-
} catch {
|
|
5692
|
-
return resolve11(root);
|
|
5693
|
-
}
|
|
5694
|
-
}));
|
|
5695
|
-
const pathEntries = (process.env.PATH ?? process.env.Path ?? "").split(delimiter).filter(Boolean);
|
|
5696
|
-
const safeDirectories2 = [];
|
|
5697
|
-
let executable2;
|
|
5698
|
-
const explicit = isAbsolute5(command2) || command2.includes("/") || command2.includes("\\");
|
|
5699
|
-
const explicitPath = explicit ? resolve11(cwd, command2) : void 0;
|
|
5700
|
-
for (const entry of pathEntries) {
|
|
5701
|
-
let directory;
|
|
5702
|
-
try {
|
|
5703
|
-
directory = await realpath6(resolve11(cwd, entry));
|
|
5704
|
-
if (!(await lstat10(directory)).isDirectory()) continue;
|
|
5705
|
-
} catch {
|
|
5706
|
-
continue;
|
|
5707
|
-
}
|
|
5708
|
-
if (realRoots.some((root) => isInside(root, directory))) continue;
|
|
5709
|
-
let contaminated = false;
|
|
5710
|
-
if (!explicit) {
|
|
5711
|
-
for (const name of executableNames(command2)) {
|
|
5712
|
-
const candidate = join9(directory, name);
|
|
5713
|
-
const resolvedCandidate = await usableExecutable(candidate);
|
|
5714
|
-
if (!resolvedCandidate) continue;
|
|
5715
|
-
if (realRoots.some((root) => isInside(root, resolvedCandidate))) {
|
|
5716
|
-
contaminated = true;
|
|
5717
|
-
continue;
|
|
5718
|
-
}
|
|
5719
|
-
executable2 ??= resolvedCandidate;
|
|
5720
|
-
}
|
|
5721
|
-
}
|
|
5722
|
-
if (!contaminated && !safeDirectories2.includes(directory)) safeDirectories2.push(directory);
|
|
5723
|
-
}
|
|
5724
|
-
if (explicitPath) executable2 = await usableExecutable(explicitPath);
|
|
5725
|
-
if (!executable2) return void 0;
|
|
5726
|
-
return { executable: executable2, path: safeDirectories2.join(delimiter) };
|
|
5727
|
-
}
|
|
5728
|
-
async function usableExecutable(candidate) {
|
|
5729
|
-
try {
|
|
5730
|
-
await access2(candidate, process.platform === "win32" ? constants3.F_OK : constants3.X_OK);
|
|
5731
|
-
const resolvedCandidate = await realpath6(candidate);
|
|
5732
|
-
return (await lstat10(resolvedCandidate)).isFile() ? resolvedCandidate : void 0;
|
|
5733
|
-
} catch {
|
|
5734
|
-
return void 0;
|
|
5735
|
-
}
|
|
5736
|
-
}
|
|
5737
|
-
function executableNames(command2) {
|
|
5738
|
-
if (process.platform !== "win32") return [command2];
|
|
5739
|
-
const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
|
|
5740
|
-
return [command2, ...extensions.map((extension) => `${command2}${extension}`)];
|
|
5741
|
-
}
|
|
5742
|
-
function runProcess(command2, args, options) {
|
|
5743
|
-
return new Promise((resolve24, reject) => {
|
|
5744
|
-
const started = Date.now();
|
|
5745
|
-
const environment = options.inheritEnv === false ? {} : { ...process.env };
|
|
5746
|
-
for (const name of options.unsetEnv ?? []) delete environment[name];
|
|
5747
|
-
for (const name of Object.keys(environment)) {
|
|
5748
|
-
if (options.unsetEnvPrefixes?.some((prefix) => name.startsWith(prefix))) {
|
|
5749
|
-
delete environment[name];
|
|
5750
|
-
}
|
|
5751
|
-
}
|
|
5752
|
-
Object.assign(environment, options.env);
|
|
5753
|
-
const child = spawn(command2, args, {
|
|
5754
|
-
cwd: options.cwd,
|
|
5755
|
-
env: environment,
|
|
5756
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
5757
|
-
signal: options.signal
|
|
5758
|
-
});
|
|
5759
|
-
const maxBytes = options.maxOutputBytes ?? 1e6;
|
|
5760
|
-
let stdout = "";
|
|
5761
|
-
let stderr = "";
|
|
5762
|
-
let stdoutBytes = 0;
|
|
5763
|
-
let stderrBytes = 0;
|
|
5764
|
-
let stdoutObservedBytes = 0;
|
|
5765
|
-
let stderrObservedBytes = 0;
|
|
5766
|
-
let timedOut = false;
|
|
5767
|
-
let callbackError;
|
|
5768
|
-
const stdoutDecoder = new StringDecoder("utf8");
|
|
5769
|
-
const stderrDecoder = new StringDecoder("utf8");
|
|
5770
|
-
const stdoutCallbackDecoder = new StringDecoder("utf8");
|
|
5771
|
-
const stderrCallbackDecoder = new StringDecoder("utf8");
|
|
5772
|
-
const append = (decoder, chunk, usedBytes) => {
|
|
5773
|
-
if (usedBytes >= maxBytes) return { text: "", usedBytes };
|
|
5774
|
-
const selected = chunk.subarray(0, maxBytes - usedBytes);
|
|
5775
|
-
return { text: decoder.write(selected), usedBytes: usedBytes + selected.length };
|
|
5776
|
-
};
|
|
5777
|
-
const notify = (callback, decoder, chunk) => {
|
|
5778
|
-
if (!callback || callbackError) return;
|
|
5779
|
-
try {
|
|
5780
|
-
const decoded = decoder.write(chunk);
|
|
5781
|
-
if (decoded) callback(decoded);
|
|
5782
|
-
} catch (error) {
|
|
5783
|
-
callbackError = error;
|
|
5784
|
-
child.kill("SIGTERM");
|
|
5785
|
-
}
|
|
5786
|
-
};
|
|
5787
|
-
child.stdout.on("data", (chunk) => {
|
|
5788
|
-
stdoutObservedBytes += chunk.length;
|
|
5789
|
-
const appended = append(stdoutDecoder, chunk, stdoutBytes);
|
|
5790
|
-
stdout += appended.text;
|
|
5791
|
-
stdoutBytes = appended.usedBytes;
|
|
5792
|
-
notify(options.onStdout, stdoutCallbackDecoder, chunk);
|
|
5793
|
-
});
|
|
5794
|
-
child.stderr.on("data", (chunk) => {
|
|
5795
|
-
stderrObservedBytes += chunk.length;
|
|
5796
|
-
const appended = append(stderrDecoder, chunk, stderrBytes);
|
|
5797
|
-
stderr += appended.text;
|
|
5798
|
-
stderrBytes = appended.usedBytes;
|
|
5799
|
-
notify(options.onStderr, stderrCallbackDecoder, chunk);
|
|
5800
|
-
});
|
|
5801
|
-
child.on("error", reject);
|
|
5802
|
-
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
5803
|
-
const timeout = timeoutMs > 0 ? setTimeout(() => {
|
|
5804
|
-
timedOut = true;
|
|
5805
|
-
child.kill("SIGTERM");
|
|
5806
|
-
setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
|
|
5807
|
-
}, timeoutMs) : void 0;
|
|
5808
|
-
child.on("close", (code) => {
|
|
5809
|
-
if (timeout) clearTimeout(timeout);
|
|
5810
|
-
const stdoutTail = stdoutDecoder.end();
|
|
5811
|
-
const stderrTail = stderrDecoder.end();
|
|
5812
|
-
if (Buffer.byteLength(stdout) + Buffer.byteLength(stdoutTail) <= maxBytes) stdout += stdoutTail;
|
|
5813
|
-
if (Buffer.byteLength(stderr) + Buffer.byteLength(stderrTail) <= maxBytes) stderr += stderrTail;
|
|
5814
|
-
try {
|
|
5815
|
-
const stdoutCallbackTail = stdoutCallbackDecoder.end();
|
|
5816
|
-
const stderrCallbackTail = stderrCallbackDecoder.end();
|
|
5817
|
-
if (stdoutCallbackTail && options.onStdout && !callbackError) options.onStdout(stdoutCallbackTail);
|
|
5818
|
-
if (stderrCallbackTail && options.onStderr && !callbackError) options.onStderr(stderrCallbackTail);
|
|
5819
|
-
} catch (error) {
|
|
5820
|
-
callbackError = error;
|
|
5821
|
-
}
|
|
5822
|
-
if (callbackError) {
|
|
5823
|
-
reject(callbackError);
|
|
5824
|
-
return;
|
|
5825
|
-
}
|
|
5826
|
-
resolve24({
|
|
5827
|
-
command: [command2, ...args].join(" "),
|
|
5828
|
-
exitCode: code ?? (timedOut ? 124 : 1),
|
|
5829
|
-
stdout,
|
|
5830
|
-
stderr,
|
|
5831
|
-
timedOut,
|
|
5832
|
-
durationMs: Date.now() - started,
|
|
5833
|
-
stdoutBytes: stdoutObservedBytes,
|
|
5834
|
-
stderrBytes: stderrObservedBytes,
|
|
5835
|
-
stdoutTruncated: stdoutObservedBytes > stdoutBytes,
|
|
5836
|
-
stderrTruncated: stderrObservedBytes > stderrBytes
|
|
5837
|
-
});
|
|
5838
|
-
});
|
|
5839
|
-
if (options.stdin) child.stdin.end(options.stdin);
|
|
5840
|
-
else child.stdin.end();
|
|
5841
|
-
});
|
|
5842
|
-
}
|
|
5843
|
-
function runShell(command2, cwd, options = {}) {
|
|
5844
|
-
const shell = process.platform === "win32" ? process.env.COMSPEC ?? "cmd.exe" : process.env.SHELL ?? "/bin/sh";
|
|
5845
|
-
const args = process.platform === "win32" ? ["/d", "/s", "/c", command2] : ["-lc", command2];
|
|
5846
|
-
return runProcess(shell, args, { ...options, cwd });
|
|
5847
|
-
}
|
|
5848
|
-
|
|
5849
6123
|
// src/hooks/runner.ts
|
|
5850
6124
|
var HookError = class extends Error {
|
|
5851
6125
|
constructor(message2, result) {
|
|
@@ -5915,7 +6189,7 @@ import {
|
|
|
5915
6189
|
stat as stat5,
|
|
5916
6190
|
unlink as unlink3
|
|
5917
6191
|
} from "node:fs/promises";
|
|
5918
|
-
import { basename as basename7, dirname as dirname9, join as join10, resolve as
|
|
6192
|
+
import { basename as basename7, dirname as dirname9, join as join10, resolve as resolve13 } from "node:path";
|
|
5919
6193
|
import { z as z5 } from "zod";
|
|
5920
6194
|
var sessionIdSchema = z5.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/);
|
|
5921
6195
|
var toolCallSchema = z5.object({
|
|
@@ -6174,9 +6448,9 @@ var SessionStore = class {
|
|
|
6174
6448
|
managedDirectory;
|
|
6175
6449
|
writes = Promise.resolve();
|
|
6176
6450
|
constructor(workspace, directory) {
|
|
6177
|
-
this.workspace =
|
|
6451
|
+
this.workspace = resolve13(workspace);
|
|
6178
6452
|
this.managedDirectory = directory === void 0;
|
|
6179
|
-
this.directory = directory ?
|
|
6453
|
+
this.directory = directory ? resolve13(directory) : join10(resolveProjectNamespaceSync(this.workspace).active, "sessions");
|
|
6180
6454
|
}
|
|
6181
6455
|
async create(options) {
|
|
6182
6456
|
const session = createSession({ ...options, workspace: this.workspace });
|
|
@@ -6185,7 +6459,7 @@ var SessionStore = class {
|
|
|
6185
6459
|
}
|
|
6186
6460
|
async save(session) {
|
|
6187
6461
|
validateId(session.id);
|
|
6188
|
-
if (
|
|
6462
|
+
if (resolve13(session.workspace) !== this.workspace) {
|
|
6189
6463
|
throw new Error("Session workspace does not match this store.");
|
|
6190
6464
|
}
|
|
6191
6465
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6224,7 +6498,7 @@ var SessionStore = class {
|
|
|
6224
6498
|
const summaries = await Promise.all(
|
|
6225
6499
|
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => {
|
|
6226
6500
|
const session = await tryReadSession(join10(this.directory, entry.name));
|
|
6227
|
-
if (!session ||
|
|
6501
|
+
if (!session || resolve13(session.workspace) !== this.workspace) return;
|
|
6228
6502
|
return toSummary(session);
|
|
6229
6503
|
})
|
|
6230
6504
|
);
|
|
@@ -6295,7 +6569,7 @@ var SessionStore = class {
|
|
|
6295
6569
|
for (const candidate of candidates) {
|
|
6296
6570
|
await this.assertManagedFile(candidate.path);
|
|
6297
6571
|
const session = await tryReadSession(candidate.path);
|
|
6298
|
-
if (session?.id === id &&
|
|
6572
|
+
if (session?.id === id && resolve13(session.workspace) === this.workspace) return session;
|
|
6299
6573
|
}
|
|
6300
6574
|
return void 0;
|
|
6301
6575
|
}
|
|
@@ -6309,7 +6583,7 @@ var SessionStore = class {
|
|
|
6309
6583
|
}
|
|
6310
6584
|
}
|
|
6311
6585
|
assertWorkspace(session) {
|
|
6312
|
-
if (
|
|
6586
|
+
if (resolve13(session.workspace) !== this.workspace) {
|
|
6313
6587
|
throw new Error("Stored session belongs to a different workspace.");
|
|
6314
6588
|
}
|
|
6315
6589
|
return session;
|
|
@@ -6368,7 +6642,7 @@ function createSession(options) {
|
|
|
6368
6642
|
return {
|
|
6369
6643
|
id,
|
|
6370
6644
|
title: cleanTitle(options.title ?? "New session"),
|
|
6371
|
-
workspace:
|
|
6645
|
+
workspace: resolve13(options.workspace),
|
|
6372
6646
|
createdAt: now,
|
|
6373
6647
|
updatedAt: now,
|
|
6374
6648
|
model: options.model,
|
|
@@ -6423,9 +6697,9 @@ async function exists2(path) {
|
|
|
6423
6697
|
}
|
|
6424
6698
|
|
|
6425
6699
|
// src/session/tool-artifacts.ts
|
|
6426
|
-
import { createHash as
|
|
6700
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
6427
6701
|
import { lstat as lstat12, readFile as readFile7, readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
6428
|
-
import { join as join11, resolve as
|
|
6702
|
+
import { join as join11, resolve as resolve14 } from "node:path";
|
|
6429
6703
|
import { z as z6 } from "zod";
|
|
6430
6704
|
var MAX_ARTIFACT_BYTES = 5 * 1024 * 1024;
|
|
6431
6705
|
var MAX_TOTAL_BYTES = 20 * 1024 * 1024;
|
|
@@ -6455,9 +6729,9 @@ var ToolArtifactStore = class {
|
|
|
6455
6729
|
retentionMs;
|
|
6456
6730
|
writes = Promise.resolve();
|
|
6457
6731
|
constructor(workspace, options = {}) {
|
|
6458
|
-
this.workspace =
|
|
6732
|
+
this.workspace = resolve14(workspace);
|
|
6459
6733
|
this.managedDirectory = options.directory === void 0;
|
|
6460
|
-
this.directory = options.directory ?
|
|
6734
|
+
this.directory = options.directory ? resolve14(options.directory) : join11(resolveProjectNamespaceSync(this.workspace).active, "tool-artifacts");
|
|
6461
6735
|
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
6462
6736
|
this.maxArtifactBytes = options.maxArtifactBytes ?? MAX_ARTIFACT_BYTES;
|
|
6463
6737
|
this.maxTotalBytes = options.maxTotalBytes ?? MAX_TOTAL_BYTES;
|
|
@@ -6514,7 +6788,7 @@ var ToolArtifactStore = class {
|
|
|
6514
6788
|
}
|
|
6515
6789
|
if (retainedBytes + storageBytes > this.maxTotalBytes) return { stored: false, reason: "total_limit" };
|
|
6516
6790
|
const path = this.pathFor(sessionId, toolCallId);
|
|
6517
|
-
await ensureWorkspaceStorageDirectory(this.workspace,
|
|
6791
|
+
await ensureWorkspaceStorageDirectory(this.workspace, resolve14(path, ".."), {
|
|
6518
6792
|
requireActiveNamespace: this.managedDirectory
|
|
6519
6793
|
});
|
|
6520
6794
|
await atomicWrite(path, `${JSON.stringify(artifact)}
|
|
@@ -6665,7 +6939,7 @@ var ToolArtifactStore = class {
|
|
|
6665
6939
|
}
|
|
6666
6940
|
}
|
|
6667
6941
|
async assertRegularFile(path) {
|
|
6668
|
-
await assertNoSymlinkPath(this.workspace,
|
|
6942
|
+
await assertNoSymlinkPath(this.workspace, resolve14(path, ".."));
|
|
6669
6943
|
try {
|
|
6670
6944
|
const info = await lstat12(path);
|
|
6671
6945
|
if (info.isSymbolicLink() || !info.isFile()) {
|
|
@@ -6702,7 +6976,7 @@ function reference(artifact) {
|
|
|
6702
6976
|
};
|
|
6703
6977
|
}
|
|
6704
6978
|
function hash2(value) {
|
|
6705
|
-
return
|
|
6979
|
+
return createHash9("sha256").update(value).digest("hex");
|
|
6706
6980
|
}
|
|
6707
6981
|
function storedBytes(artifact) {
|
|
6708
6982
|
return Buffer.byteLength(JSON.stringify(artifact)) + 1;
|
|
@@ -7249,48 +7523,6 @@ var knownCommands = /* @__PURE__ */ new Set([
|
|
|
7249
7523
|
...networkCommands,
|
|
7250
7524
|
...workspaceMutationCommands
|
|
7251
7525
|
]);
|
|
7252
|
-
var unsafeInheritedGitEnvironment = [
|
|
7253
|
-
"GIT_DIR",
|
|
7254
|
-
"GIT_WORK_TREE",
|
|
7255
|
-
"GIT_COMMON_DIR",
|
|
7256
|
-
"GIT_INDEX_FILE",
|
|
7257
|
-
"GIT_OBJECT_DIRECTORY",
|
|
7258
|
-
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
7259
|
-
"GIT_NAMESPACE",
|
|
7260
|
-
"GIT_CEILING_DIRECTORIES",
|
|
7261
|
-
"GIT_DISCOVERY_ACROSS_FILESYSTEM",
|
|
7262
|
-
"GIT_EXTERNAL_DIFF",
|
|
7263
|
-
"GIT_DIFF_OPTS",
|
|
7264
|
-
"GIT_SSH",
|
|
7265
|
-
"GIT_SSH_COMMAND",
|
|
7266
|
-
"GIT_PROXY_COMMAND",
|
|
7267
|
-
"GIT_ASKPASS",
|
|
7268
|
-
"SSH_ASKPASS",
|
|
7269
|
-
"GIT_EXEC_PATH",
|
|
7270
|
-
"GIT_TEMPLATE_DIR",
|
|
7271
|
-
"GIT_CONFIG",
|
|
7272
|
-
"GIT_CONFIG_PARAMETERS",
|
|
7273
|
-
"GIT_QUARANTINE_PATH",
|
|
7274
|
-
"Path"
|
|
7275
|
-
];
|
|
7276
|
-
var isolatedGitEnvironment = {
|
|
7277
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
7278
|
-
GIT_CONFIG_NOSYSTEM: "1",
|
|
7279
|
-
GIT_CONFIG_GLOBAL: "/dev/null",
|
|
7280
|
-
GIT_CONFIG_SYSTEM: "/dev/null",
|
|
7281
|
-
GIT_PAGER: "cat",
|
|
7282
|
-
GIT_EDITOR: "true",
|
|
7283
|
-
GIT_SEQUENCE_EDITOR: "true",
|
|
7284
|
-
GIT_CONFIG_COUNT: "4",
|
|
7285
|
-
GIT_CONFIG_KEY_0: "core.hooksPath",
|
|
7286
|
-
GIT_CONFIG_VALUE_0: "/dev/null",
|
|
7287
|
-
GIT_CONFIG_KEY_1: "core.fsmonitor",
|
|
7288
|
-
GIT_CONFIG_VALUE_1: "false",
|
|
7289
|
-
GIT_CONFIG_KEY_2: "credential.helper",
|
|
7290
|
-
GIT_CONFIG_VALUE_2: "",
|
|
7291
|
-
GIT_CONFIG_KEY_3: "protocol.ext.allow",
|
|
7292
|
-
GIT_CONFIG_VALUE_3: "never"
|
|
7293
|
-
};
|
|
7294
7526
|
var diffCommands = /* @__PURE__ */ new Set(["diff", "log", "show", "whatchanged"]);
|
|
7295
7527
|
var gitTool = {
|
|
7296
7528
|
definition: {
|
|
@@ -7338,13 +7570,9 @@ var gitTool = {
|
|
|
7338
7570
|
}
|
|
7339
7571
|
const runtime = await resolveExecutableRuntime("git", cwd, context.workspace.roots);
|
|
7340
7572
|
if (!runtime) throw new Error("Git executable was not found outside the configured workspace roots.");
|
|
7341
|
-
const status = await
|
|
7342
|
-
cwd,
|
|
7573
|
+
const status = await runIsolatedGit(runtime, ["status", "--porcelain=v1", "-z", "--untracked-files=all"], cwd, {
|
|
7343
7574
|
timeoutMs: 15e3,
|
|
7344
|
-
maxOutputBytes: 2e6
|
|
7345
|
-
env: { ...isolatedGitEnvironment, PATH: runtime.path },
|
|
7346
|
-
unsetEnv: unsafeInheritedGitEnvironment,
|
|
7347
|
-
unsetEnvPrefixes: ["GIT_"]
|
|
7575
|
+
maxOutputBytes: 2e6
|
|
7348
7576
|
});
|
|
7349
7577
|
if (status.exitCode === 0) {
|
|
7350
7578
|
for (const record of status.stdout.split("\0")) {
|
|
@@ -7371,15 +7599,11 @@ var gitTool = {
|
|
|
7371
7599
|
const runtime = await resolveExecutableRuntime("git", cwd, context.workspace.roots);
|
|
7372
7600
|
if (!runtime) throw new Error("Git executable was not found outside the configured workspace roots.");
|
|
7373
7601
|
const before = worktreeTrackingCommands.has(command2) ? await captureGitState(runtime, cwd) : void 0;
|
|
7374
|
-
const result = await
|
|
7375
|
-
cwd,
|
|
7602
|
+
const result = await runIsolatedGit(runtime, protectedGitArguments(input2.args, command2), cwd, {
|
|
7376
7603
|
timeoutMs: input2.timeout_ms ?? 12e4,
|
|
7377
7604
|
maxOutputBytes: 2e6,
|
|
7378
7605
|
...input2.stdin !== void 0 ? { stdin: input2.stdin } : {},
|
|
7379
|
-
...context.signal ? { signal: context.signal } : {}
|
|
7380
|
-
env: { ...isolatedGitEnvironment, PATH: runtime.path },
|
|
7381
|
-
unsetEnv: unsafeInheritedGitEnvironment,
|
|
7382
|
-
unsetEnvPrefixes: ["GIT_"]
|
|
7606
|
+
...context.signal ? { signal: context.signal } : {}
|
|
7383
7607
|
});
|
|
7384
7608
|
const changedFiles = before ? await collectGitChanges(before, await captureGitState(runtime, cwd), runtime, cwd, context) : [];
|
|
7385
7609
|
return {
|
|
@@ -7457,18 +7681,6 @@ function parsePorcelainStatus(output2) {
|
|
|
7457
7681
|
}
|
|
7458
7682
|
return status;
|
|
7459
7683
|
}
|
|
7460
|
-
function runIsolatedGit(runtime, args, cwd, options = {}) {
|
|
7461
|
-
return runProcess(runtime.executable, args, {
|
|
7462
|
-
cwd,
|
|
7463
|
-
timeoutMs: options.timeoutMs ?? 15e3,
|
|
7464
|
-
maxOutputBytes: options.maxOutputBytes ?? 2e6,
|
|
7465
|
-
env: { ...isolatedGitEnvironment, PATH: runtime.path },
|
|
7466
|
-
unsetEnv: unsafeInheritedGitEnvironment,
|
|
7467
|
-
unsetEnvPrefixes: ["GIT_"],
|
|
7468
|
-
...options.stdin !== void 0 ? { stdin: options.stdin } : {},
|
|
7469
|
-
...options.signal ? { signal: options.signal } : {}
|
|
7470
|
-
});
|
|
7471
|
-
}
|
|
7472
7684
|
function validateGitArguments(args) {
|
|
7473
7685
|
for (const argument of args) {
|
|
7474
7686
|
if (argument.includes("\0") || argument.includes("\n") || argument.includes("\r")) {
|
|
@@ -7604,7 +7816,7 @@ function positionalArguments(args, command2) {
|
|
|
7604
7816
|
|
|
7605
7817
|
// src/tools/list.ts
|
|
7606
7818
|
import { lstat as lstat14 } from "node:fs/promises";
|
|
7607
|
-
import { relative as relative7, resolve as
|
|
7819
|
+
import { relative as relative7, resolve as resolve15 } from "node:path";
|
|
7608
7820
|
import fg3 from "fast-glob";
|
|
7609
7821
|
import { z as z9 } from "zod";
|
|
7610
7822
|
var inputSchema4 = z9.object({
|
|
@@ -7662,7 +7874,7 @@ var listFilesTool = {
|
|
|
7662
7874
|
for (const path of selected) {
|
|
7663
7875
|
let safePath;
|
|
7664
7876
|
try {
|
|
7665
|
-
safePath = await context.workspace.resolvePath(
|
|
7877
|
+
safePath = await context.workspace.resolvePath(resolve15(directory, path), { expect: "any" });
|
|
7666
7878
|
} catch {
|
|
7667
7879
|
continue;
|
|
7668
7880
|
}
|
|
@@ -7872,7 +8084,7 @@ function assertToolName(name) {
|
|
|
7872
8084
|
|
|
7873
8085
|
// src/tools/search.ts
|
|
7874
8086
|
import { readFile as readFile10, stat as stat8 } from "node:fs/promises";
|
|
7875
|
-
import { resolve as
|
|
8087
|
+
import { resolve as resolve16 } from "node:path";
|
|
7876
8088
|
import fg4 from "fast-glob";
|
|
7877
8089
|
import { z as z12 } from "zod";
|
|
7878
8090
|
var inputSchema7 = z12.object({
|
|
@@ -7942,7 +8154,7 @@ var searchCodeTool = {
|
|
|
7942
8154
|
if (results.length >= maxResults) break;
|
|
7943
8155
|
let path;
|
|
7944
8156
|
try {
|
|
7945
|
-
path = await context.workspace.resolvePath(
|
|
8157
|
+
path = await context.workspace.resolvePath(resolve16(directory, file), { expect: "file" });
|
|
7946
8158
|
} catch {
|
|
7947
8159
|
skipped += 1;
|
|
7948
8160
|
continue;
|
|
@@ -8024,7 +8236,7 @@ ${hit.content}`
|
|
|
8024
8236
|
}
|
|
8025
8237
|
|
|
8026
8238
|
// src/tools/shell.ts
|
|
8027
|
-
import { createHash as
|
|
8239
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
8028
8240
|
import { lstat as lstat15, readFile as readFile11, readdir as readdir5 } from "node:fs/promises";
|
|
8029
8241
|
import { join as join13 } from "node:path";
|
|
8030
8242
|
import { z as z13 } from "zod";
|
|
@@ -8238,7 +8450,7 @@ async function captureWorkspaceSnapshot(roots) {
|
|
|
8238
8450
|
files.set(path, {
|
|
8239
8451
|
size: info.size,
|
|
8240
8452
|
mtimeMs: info.mtimeMs,
|
|
8241
|
-
hash:
|
|
8453
|
+
hash: createHash10("sha256").update(content).digest("hex")
|
|
8242
8454
|
});
|
|
8243
8455
|
} catch (error) {
|
|
8244
8456
|
if (error.code !== "ENOENT") complete = false;
|
|
@@ -8441,15 +8653,16 @@ function compact(value, limit) {
|
|
|
8441
8653
|
}
|
|
8442
8654
|
|
|
8443
8655
|
// src/agent/completion-gate.ts
|
|
8444
|
-
import { createHash as
|
|
8656
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
8657
|
+
import { isAbsolute as isAbsolute6, resolve as resolve17 } from "node:path";
|
|
8445
8658
|
|
|
8446
8659
|
// src/tools/permissions.ts
|
|
8447
|
-
import { createHash as
|
|
8660
|
+
import { createHash as createHash11, createHmac, randomBytes } from "node:crypto";
|
|
8448
8661
|
var permissionScopeSecret = randomBytes(32);
|
|
8449
8662
|
function permissionKey(call, category) {
|
|
8450
8663
|
const categoryScope = category === "network" ? `\0network-request:${scopeFingerprint(normalizeRequestState(call.arguments))}` : "";
|
|
8451
|
-
const
|
|
8452
|
-
return `${category}:${call.name}:${
|
|
8664
|
+
const digest2 = createHash11("sha256").update(`${category}\0${call.name}\0${permissionTarget(call)}${categoryScope}`).digest("hex").slice(0, 24);
|
|
8665
|
+
return `${category}:${call.name}:${digest2}`;
|
|
8453
8666
|
}
|
|
8454
8667
|
function permissionTarget(call) {
|
|
8455
8668
|
const command2 = commandForCall(call);
|
|
@@ -8642,9 +8855,40 @@ function captureVerification(call, result, changeSequence, configuredCommands) {
|
|
|
8642
8855
|
kind,
|
|
8643
8856
|
ok: result.ok,
|
|
8644
8857
|
changeSequence,
|
|
8645
|
-
commandKey:
|
|
8858
|
+
commandKey: createHash12("sha256").update(normalized).digest("hex")
|
|
8646
8859
|
};
|
|
8647
8860
|
}
|
|
8861
|
+
function verificationDiagnosticPaths(result, workspaceRoots) {
|
|
8862
|
+
if (result.ok || result.metadata?.sourceTruncated === true || typeof result.metadata?.exitCode !== "number" || result.metadata.exitCode === 0) return [];
|
|
8863
|
+
const roots = workspaceRoots.map((root) => resolve17(root));
|
|
8864
|
+
const cwd = typeof result.metadata.cwd === "string" ? resolve17(result.metadata.cwd) : void 0;
|
|
8865
|
+
const bases = cwd ? [cwd, ...roots] : roots;
|
|
8866
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
8867
|
+
const pathPattern = String.raw`((?:[./][^\s():]+|[^\s():]+)\.[A-Za-z0-9]{1,10})`;
|
|
8868
|
+
const patterns = [
|
|
8869
|
+
new RegExp(`${pathPattern}\\(\\d+,\\d+\\):\\s*(?:error|warning|fatal)\\b`, "gimu"),
|
|
8870
|
+
new RegExp(`${pathPattern}:\\d+:\\d+:\\s*(?:error|warning|fatal)\\b`, "gimu"),
|
|
8871
|
+
new RegExp(`^\\s*(?:FAIL|ERROR)\\s+${pathPattern}`, "gimu")
|
|
8872
|
+
];
|
|
8873
|
+
const output2 = result.content.slice(0, 2e5).split(/\r?\n/u).filter((line) => !/^\s*Command:/u.test(line)).join("\n");
|
|
8874
|
+
for (const pattern of patterns) {
|
|
8875
|
+
for (const match of output2.matchAll(pattern)) {
|
|
8876
|
+
const candidate = match[1];
|
|
8877
|
+
if (!candidate) continue;
|
|
8878
|
+
const path = resolveDiagnosticPath(candidate, bases, roots);
|
|
8879
|
+
if (path) candidates.add(path);
|
|
8880
|
+
if (candidates.size >= 16) return [...candidates];
|
|
8881
|
+
}
|
|
8882
|
+
}
|
|
8883
|
+
return [...candidates];
|
|
8884
|
+
}
|
|
8885
|
+
function resolveDiagnosticPath(candidate, bases, roots) {
|
|
8886
|
+
for (const base of bases) {
|
|
8887
|
+
const path = isAbsolute6(candidate) ? resolve17(candidate) : resolve17(base, candidate);
|
|
8888
|
+
if (roots.some((root) => isInside(root, path))) return path;
|
|
8889
|
+
}
|
|
8890
|
+
return void 0;
|
|
8891
|
+
}
|
|
8648
8892
|
function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutationTracking = "complete", taskContract, audit = [], duplication) {
|
|
8649
8893
|
const files = [...new Set(changedFiles)];
|
|
8650
8894
|
const acceptance = taskContract ? buildAcceptance(taskContract, audit, evidence, currentChangeSequence) : void 0;
|
|
@@ -8826,7 +9070,7 @@ function verificationRequirementMet(requirement, checks) {
|
|
|
8826
9070
|
const normalized = normalizeCommand2(requirement);
|
|
8827
9071
|
const broad = normalized.match(/^Record at least one successful (.+) after the final mutation\.?$/iu);
|
|
8828
9072
|
if (broad) return checks.length > 0;
|
|
8829
|
-
const commandKey =
|
|
9073
|
+
const commandKey = createHash12("sha256").update(normalized).digest("hex");
|
|
8830
9074
|
return checks.some((item) => item.commandKey === commandKey);
|
|
8831
9075
|
}
|
|
8832
9076
|
function acceptanceUnresolved(acceptance) {
|
|
@@ -9420,7 +9664,7 @@ function escapeAttribute3(value) {
|
|
|
9420
9664
|
}
|
|
9421
9665
|
|
|
9422
9666
|
// src/agent/tool-recovery.ts
|
|
9423
|
-
import { createHash as
|
|
9667
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
9424
9668
|
var RETRY_BUDGET = {
|
|
9425
9669
|
schema_input: 3,
|
|
9426
9670
|
unknown_tool: 2,
|
|
@@ -9492,7 +9736,7 @@ var ToolRecoveryController = class {
|
|
|
9492
9736
|
recordEvidence(call, result) {
|
|
9493
9737
|
if (call.name !== "search_code" || !result.ok) return void 0;
|
|
9494
9738
|
const callKey = callSignature(call);
|
|
9495
|
-
const fingerprint =
|
|
9739
|
+
const fingerprint = createHash13("sha256").update(`${result.content}\0${stableJson(result.metadata ?? {})}`).digest("hex");
|
|
9496
9740
|
const count = typeof result.metadata?.count === "number" ? result.metadata.count : void 0;
|
|
9497
9741
|
const current = this.evidence.get(callKey);
|
|
9498
9742
|
const repeated = current?.fingerprint === fingerprint;
|
|
@@ -9502,7 +9746,7 @@ var ToolRecoveryController = class {
|
|
|
9502
9746
|
status: count === 0 ? "empty" : repeated ? "repeated" : "new",
|
|
9503
9747
|
repeatCount: repeats,
|
|
9504
9748
|
stop: repeats >= 2,
|
|
9505
|
-
signature:
|
|
9749
|
+
signature: createHash13("sha256").update(`${callKey}\0${fingerprint}`).digest("hex")
|
|
9506
9750
|
};
|
|
9507
9751
|
}
|
|
9508
9752
|
receipt(call, failureClass, attempt, circuitOpen) {
|
|
@@ -9538,10 +9782,10 @@ function isRetryable(failureClass) {
|
|
|
9538
9782
|
return RETRY_BUDGET[failureClass] > 0;
|
|
9539
9783
|
}
|
|
9540
9784
|
function callSignature(call) {
|
|
9541
|
-
return
|
|
9785
|
+
return createHash13("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}`).digest("hex");
|
|
9542
9786
|
}
|
|
9543
9787
|
function failureSignature(call, failureClass) {
|
|
9544
|
-
return
|
|
9788
|
+
return createHash13("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}\0${failureClass}`).digest("hex");
|
|
9545
9789
|
}
|
|
9546
9790
|
function redact(value, key = "") {
|
|
9547
9791
|
if (/api[_-]?key|authorization|cookie|password|secret|token/i.test(key)) return "[redacted]";
|
|
@@ -9889,7 +10133,7 @@ function escapeAttribute5(value) {
|
|
|
9889
10133
|
}
|
|
9890
10134
|
|
|
9891
10135
|
// src/agent/reuse-gate.ts
|
|
9892
|
-
import { createHash as
|
|
10136
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
9893
10137
|
import { readFile as readFile14, stat as stat10 } from "node:fs/promises";
|
|
9894
10138
|
import { basename as basename8, extname as extname3 } from "node:path";
|
|
9895
10139
|
var MAX_CANDIDATES = 5;
|
|
@@ -10098,14 +10342,14 @@ function unresolvedReceipt(input2, queryHash, targetPaths, trigger, detail) {
|
|
|
10098
10342
|
};
|
|
10099
10343
|
}
|
|
10100
10344
|
function hash3(value) {
|
|
10101
|
-
return
|
|
10345
|
+
return createHash14("sha256").update(value).digest("hex");
|
|
10102
10346
|
}
|
|
10103
10347
|
function roundScore2(value) {
|
|
10104
10348
|
return Number.isFinite(value) ? Math.round(value * 1e3) / 1e3 : 0;
|
|
10105
10349
|
}
|
|
10106
10350
|
|
|
10107
10351
|
// src/agent/duplication-audit.ts
|
|
10108
|
-
import { createHash as
|
|
10352
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
10109
10353
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
10110
10354
|
var DEFAULT_NEAR_CLONE_THRESHOLD = 0.55;
|
|
10111
10355
|
var MAX_MATCHES = 8;
|
|
@@ -10280,7 +10524,7 @@ function round(value) {
|
|
|
10280
10524
|
return Math.round(value * 1e3) / 1e3;
|
|
10281
10525
|
}
|
|
10282
10526
|
function matchId(generation, changeSequence, changed, candidate, similarity) {
|
|
10283
|
-
return
|
|
10527
|
+
return createHash15("sha256").update([
|
|
10284
10528
|
generation,
|
|
10285
10529
|
String(changeSequence),
|
|
10286
10530
|
changed.path,
|
|
@@ -10357,6 +10601,7 @@ var AgentRunner = class {
|
|
|
10357
10601
|
throw new Error("User input is too large; pass a focused request or attach files with @path.");
|
|
10358
10602
|
}
|
|
10359
10603
|
this.running = true;
|
|
10604
|
+
this.contextEngine.resetDiagnostics?.();
|
|
10360
10605
|
const emit = async (event) => {
|
|
10361
10606
|
await options.onEvent?.(event);
|
|
10362
10607
|
};
|
|
@@ -10383,7 +10628,13 @@ var AgentRunner = class {
|
|
|
10383
10628
|
this.changeSequence,
|
|
10384
10629
|
this.config.agent.verifyCommands
|
|
10385
10630
|
);
|
|
10386
|
-
if (evidence)
|
|
10631
|
+
if (evidence) {
|
|
10632
|
+
verificationEvidence.push(evidence);
|
|
10633
|
+
this.contextEngine.recordDiagnostics?.({
|
|
10634
|
+
commandKey: evidence.commandKey,
|
|
10635
|
+
paths: verificationDiagnosticPaths(result, this.workspace.roots)
|
|
10636
|
+
});
|
|
10637
|
+
}
|
|
10387
10638
|
};
|
|
10388
10639
|
const completionReport = () => {
|
|
10389
10640
|
const completion = buildRunCompletion(
|
|
@@ -11698,9 +11949,9 @@ function numeric(...values) {
|
|
|
11698
11949
|
}
|
|
11699
11950
|
|
|
11700
11951
|
// src/agent/team-store.ts
|
|
11701
|
-
import { createHash as
|
|
11952
|
+
import { createHash as createHash16, randomUUID as randomUUID13 } from "node:crypto";
|
|
11702
11953
|
import { lstat as lstat18, readFile as readFile17, readdir as readdir7, rm as rm3 } from "node:fs/promises";
|
|
11703
|
-
import { join as join16, resolve as
|
|
11954
|
+
import { join as join16, resolve as resolve18 } from "node:path";
|
|
11704
11955
|
import { z as z18 } from "zod";
|
|
11705
11956
|
var runIdSchema = z18.string().uuid();
|
|
11706
11957
|
var hashSchema = z18.string().regex(/^[a-f0-9]{64}$/u);
|
|
@@ -11784,9 +12035,9 @@ var TeamRunStore = class {
|
|
|
11784
12035
|
managedDirectory;
|
|
11785
12036
|
writes = Promise.resolve();
|
|
11786
12037
|
constructor(workspace, directory) {
|
|
11787
|
-
this.workspace =
|
|
12038
|
+
this.workspace = resolve18(workspace);
|
|
11788
12039
|
this.managedDirectory = directory === void 0;
|
|
11789
|
-
this.directory = directory ?
|
|
12040
|
+
this.directory = directory ? resolve18(directory) : join16(resolveProjectNamespaceSync(this.workspace).active, "team-runs");
|
|
11790
12041
|
}
|
|
11791
12042
|
async create(input2) {
|
|
11792
12043
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -11873,7 +12124,7 @@ var TeamRunStore = class {
|
|
|
11873
12124
|
const path = join16(this.runDirectory(runId), "manifest.json");
|
|
11874
12125
|
await this.assertRegularFile(path);
|
|
11875
12126
|
const manifest = manifestSchema2.parse(JSON.parse(await readFile17(path, "utf8")));
|
|
11876
|
-
if (manifest.id !== runId ||
|
|
12127
|
+
if (manifest.id !== runId || resolve18(manifest.workspace) !== this.workspace) {
|
|
11877
12128
|
throw new Error("Team run manifest identity does not match its location.");
|
|
11878
12129
|
}
|
|
11879
12130
|
if (verify) {
|
|
@@ -11956,7 +12207,7 @@ var TeamRunStore = class {
|
|
|
11956
12207
|
async writeArtifact(runId, content, truncate = true) {
|
|
11957
12208
|
const data = boundedArtifactText(content, 5e5, truncate);
|
|
11958
12209
|
const bytes = Buffer.byteLength(data);
|
|
11959
|
-
const sha256 =
|
|
12210
|
+
const sha256 = createHash16("sha256").update(data).digest("hex");
|
|
11960
12211
|
const directory = join16(this.runDirectory(runId), "blobs");
|
|
11961
12212
|
await ensureWorkspaceStorageDirectory(this.workspace, directory, {
|
|
11962
12213
|
requireActiveNamespace: this.managedDirectory
|
|
@@ -11965,7 +12216,7 @@ var TeamRunStore = class {
|
|
|
11965
12216
|
try {
|
|
11966
12217
|
await this.assertRegularFile(path);
|
|
11967
12218
|
const existing = await readFile17(path);
|
|
11968
|
-
if (
|
|
12219
|
+
if (createHash16("sha256").update(existing).digest("hex") !== sha256) {
|
|
11969
12220
|
throw new Error(`Team artifact hash collision or corruption: ${sha256}`);
|
|
11970
12221
|
}
|
|
11971
12222
|
} catch (error) {
|
|
@@ -11986,7 +12237,7 @@ var TeamRunStore = class {
|
|
|
11986
12237
|
const path = this.artifactPath(runId, artifact.sha256);
|
|
11987
12238
|
await this.assertRegularFile(path);
|
|
11988
12239
|
const data = await readFile17(path);
|
|
11989
|
-
const hash4 =
|
|
12240
|
+
const hash4 = createHash16("sha256").update(data).digest("hex");
|
|
11990
12241
|
if (hash4 !== artifact.sha256 || data.byteLength !== artifact.bytes) {
|
|
11991
12242
|
throw new Error(`Team artifact integrity check failed: ${artifact.sha256}`);
|
|
11992
12243
|
}
|
|
@@ -12004,7 +12255,7 @@ var TeamRunStore = class {
|
|
|
12004
12255
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Team run storage is not a regular directory.");
|
|
12005
12256
|
}
|
|
12006
12257
|
async assertRegularFile(path) {
|
|
12007
|
-
await assertNoSymlinkPath(this.workspace,
|
|
12258
|
+
await assertNoSymlinkPath(this.workspace, resolve18(path, ".."));
|
|
12008
12259
|
const info = await lstat18(path);
|
|
12009
12260
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Team run file is not a regular file: ${path}`);
|
|
12010
12261
|
}
|
|
@@ -12049,10 +12300,10 @@ function resolveAgentModelRoute(team, parent, profile) {
|
|
|
12049
12300
|
}
|
|
12050
12301
|
|
|
12051
12302
|
// src/agent/writer-lane.ts
|
|
12052
|
-
import { createHash as
|
|
12303
|
+
import { createHash as createHash17 } from "node:crypto";
|
|
12053
12304
|
import { lstat as lstat19, mkdtemp, realpath as realpath7, rm as rm4 } from "node:fs/promises";
|
|
12054
12305
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
12055
|
-
import { isAbsolute as
|
|
12306
|
+
import { isAbsolute as isAbsolute7, join as join17, resolve as resolve19 } from "node:path";
|
|
12056
12307
|
var WriterLaneApplyError = class extends Error {
|
|
12057
12308
|
constructor(message2, attempted, cause) {
|
|
12058
12309
|
super(message2, cause === void 0 ? void 0 : { cause });
|
|
@@ -12065,8 +12316,8 @@ var WriterLane = class {
|
|
|
12065
12316
|
workspace;
|
|
12066
12317
|
constructor(workspace, workspaceRoots) {
|
|
12067
12318
|
this.workspace = new WorkspaceAccess([
|
|
12068
|
-
|
|
12069
|
-
...workspaceRoots.map((root) =>
|
|
12319
|
+
resolve19(workspace),
|
|
12320
|
+
...workspaceRoots.map((root) => resolve19(root))
|
|
12070
12321
|
]);
|
|
12071
12322
|
}
|
|
12072
12323
|
async createDraft(maxPatchBytes, operation, signal) {
|
|
@@ -12147,7 +12398,7 @@ var WriterLane = class {
|
|
|
12147
12398
|
return {
|
|
12148
12399
|
baseCommit,
|
|
12149
12400
|
patch,
|
|
12150
|
-
patchSha256:
|
|
12401
|
+
patchSha256: createHash17("sha256").update(patch).digest("hex"),
|
|
12151
12402
|
files,
|
|
12152
12403
|
worktreeCleaned,
|
|
12153
12404
|
value
|
|
@@ -12271,7 +12522,7 @@ var WriterLane = class {
|
|
|
12271
12522
|
const files = unique2(parseNumstatPaths(numstat.stdout));
|
|
12272
12523
|
if (!files.length) throw new Error("Writer patch contains no file changes.");
|
|
12273
12524
|
for (const file of files) {
|
|
12274
|
-
if (
|
|
12525
|
+
if (isAbsolute7(file) || file === ".git" || file.startsWith(".git/") || file.includes("\0")) {
|
|
12275
12526
|
throw new Error(`Writer patch contains an unsafe path: ${file}`);
|
|
12276
12527
|
}
|
|
12277
12528
|
await this.workspace.resolvePath(join17(repository.root, file), { allowMissing: true });
|
|
@@ -12289,7 +12540,7 @@ var WriterLane = class {
|
|
|
12289
12540
|
if (topLevel.exitCode !== 0 || !topLevel.stdout.trim()) {
|
|
12290
12541
|
throw new Error("The primary workspace must be a Git repository root for writer lanes.");
|
|
12291
12542
|
}
|
|
12292
|
-
const discoveredRoot =
|
|
12543
|
+
const discoveredRoot = resolve19(topLevel.stdout.trim());
|
|
12293
12544
|
if (await realpath7(discoveredRoot) !== await realpath7(root)) {
|
|
12294
12545
|
throw new Error("The primary workspace must be the Git repository root for writer lanes.");
|
|
12295
12546
|
}
|
|
@@ -12298,7 +12549,7 @@ var WriterLane = class {
|
|
|
12298
12549
|
if (common.exitCode !== 0 || !common.stdout.trim()) {
|
|
12299
12550
|
throw new Error(`Unable to resolve the Git common directory: ${processDetail(common)}`);
|
|
12300
12551
|
}
|
|
12301
|
-
const commonDirectory = await realpath7(
|
|
12552
|
+
const commonDirectory = await realpath7(isAbsolute7(common.stdout.trim()) ? common.stdout.trim() : resolve19(repositoryRoot, common.stdout.trim()));
|
|
12302
12553
|
return { root: repositoryRoot, commonDirectory, runtime };
|
|
12303
12554
|
}
|
|
12304
12555
|
async head(repository) {
|
|
@@ -12310,7 +12561,7 @@ var WriterLane = class {
|
|
|
12310
12561
|
return value;
|
|
12311
12562
|
}
|
|
12312
12563
|
async cleanupWorktree(repository, worktree, added) {
|
|
12313
|
-
const physicalWorktree = await realpath7(worktree).catch(() =>
|
|
12564
|
+
const physicalWorktree = await realpath7(worktree).catch(() => resolve19(worktree));
|
|
12314
12565
|
if (added) {
|
|
12315
12566
|
await runIsolatedGit(repository.runtime, [
|
|
12316
12567
|
"worktree",
|
|
@@ -14236,7 +14487,7 @@ function formatResultDetail(result, paint = chalk2, glyphs = resolveCliGlyphs())
|
|
|
14236
14487
|
}
|
|
14237
14488
|
|
|
14238
14489
|
// src/cli/namespace-leases.ts
|
|
14239
|
-
import { resolve as
|
|
14490
|
+
import { resolve as resolve20 } from "node:path";
|
|
14240
14491
|
function cliNamespaceLeaseScopes(actionCommand) {
|
|
14241
14492
|
const names = commandNames(actionCommand);
|
|
14242
14493
|
const topLevel = names[1];
|
|
@@ -14258,7 +14509,7 @@ async function acquireCliNamespaceLeases(actionCommand) {
|
|
|
14258
14509
|
const scopes = cliNamespaceLeaseScopes(actionCommand);
|
|
14259
14510
|
const localOptions = actionCommand.opts();
|
|
14260
14511
|
const globalOptions = actionCommand.optsWithGlobals();
|
|
14261
|
-
const workspace =
|
|
14512
|
+
const workspace = resolve20(localOptions.workspace ?? globalOptions.workspace ?? process.cwd());
|
|
14262
14513
|
const targets = scopes.map((scope) => scope === "project" ? projectNamespacePaths(workspace).canonical : homeNamespacePaths().canonical);
|
|
14263
14514
|
const leases = [];
|
|
14264
14515
|
try {
|
|
@@ -14510,7 +14761,7 @@ function graphemes(value) {
|
|
|
14510
14761
|
|
|
14511
14762
|
// src/ui/theme.ts
|
|
14512
14763
|
import { lstat as lstat20, readdir as readdir8, readFile as readFile18 } from "node:fs/promises";
|
|
14513
|
-
import { basename as basename10, join as join19, resolve as
|
|
14764
|
+
import { basename as basename10, join as join19, resolve as resolve21 } from "node:path";
|
|
14514
14765
|
import React, { createContext, useContext } from "react";
|
|
14515
14766
|
function defineTheme(seed) {
|
|
14516
14767
|
return {
|
|
@@ -14632,7 +14883,7 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
|
|
|
14632
14883
|
userThemeNames.clear();
|
|
14633
14884
|
const loaded = [];
|
|
14634
14885
|
const errors = [];
|
|
14635
|
-
const resolvedDirectory =
|
|
14886
|
+
const resolvedDirectory = resolve21(directory);
|
|
14636
14887
|
let entries;
|
|
14637
14888
|
try {
|
|
14638
14889
|
entries = await readdir8(resolvedDirectory, { encoding: "utf8" });
|
|
@@ -17096,7 +17347,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
17096
17347
|
return () => clearInterval(timer);
|
|
17097
17348
|
}, [busy]);
|
|
17098
17349
|
const requestPermission = useCallback((call, category) => {
|
|
17099
|
-
return new Promise((
|
|
17350
|
+
return new Promise((resolve26) => setPermission({ call, category, resolve: resolve26 }));
|
|
17100
17351
|
}, []);
|
|
17101
17352
|
const onEvent = useCallback((event) => {
|
|
17102
17353
|
switch (event.type) {
|
|
@@ -17932,8 +18183,8 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
17932
18183
|
}, [append]);
|
|
17933
18184
|
function settlePermission(grant, stop = false) {
|
|
17934
18185
|
if (!permission) return;
|
|
17935
|
-
const { call, category, resolve:
|
|
17936
|
-
|
|
18186
|
+
const { call, category, resolve: resolve26 } = permission;
|
|
18187
|
+
resolve26(grant);
|
|
17937
18188
|
setPermission(void 0);
|
|
17938
18189
|
if (grant === "session") {
|
|
17939
18190
|
append({
|
|
@@ -19268,7 +19519,7 @@ async function runFirstRunOnboarding(initialConfig, options = {}) {
|
|
|
19268
19519
|
}
|
|
19269
19520
|
|
|
19270
19521
|
// src/runtime/extensions.ts
|
|
19271
|
-
import { resolve as
|
|
19522
|
+
import { resolve as resolve24 } from "node:path";
|
|
19272
19523
|
|
|
19273
19524
|
// src/mcp/manager.ts
|
|
19274
19525
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -19277,7 +19528,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|
|
19277
19528
|
import stripAnsi5 from "strip-ansi";
|
|
19278
19529
|
|
|
19279
19530
|
// src/mcp/tool.ts
|
|
19280
|
-
import { createHash as
|
|
19531
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
19281
19532
|
import stripAnsi4 from "strip-ansi";
|
|
19282
19533
|
var MAX_ARGUMENT_BYTES = 256e3;
|
|
19283
19534
|
var MAX_DESCRIPTION_LENGTH = 4e3;
|
|
@@ -19448,7 +19699,7 @@ function fitToolName(name, identity2) {
|
|
|
19448
19699
|
return `${name.slice(0, 64 - suffix.length).replace(/_+$/g, "")}${suffix}`;
|
|
19449
19700
|
}
|
|
19450
19701
|
function shortHash(value) {
|
|
19451
|
-
return
|
|
19702
|
+
return createHash18("sha256").update(value).digest("hex").slice(0, 8);
|
|
19452
19703
|
}
|
|
19453
19704
|
function sanitizeOutputText(value) {
|
|
19454
19705
|
return stripAnsi4(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
|
|
@@ -19481,7 +19732,7 @@ function isRecord2(value) {
|
|
|
19481
19732
|
|
|
19482
19733
|
// src/mcp/validation.ts
|
|
19483
19734
|
import { stat as stat11, realpath as realpath8 } from "node:fs/promises";
|
|
19484
|
-
import { isAbsolute as
|
|
19735
|
+
import { isAbsolute as isAbsolute8, resolve as resolve22 } from "node:path";
|
|
19485
19736
|
var SERVER_NAME = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
19486
19737
|
var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
19487
19738
|
var HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
@@ -19553,9 +19804,9 @@ async function validateCwd(configured, options) {
|
|
|
19553
19804
|
if (configured !== void 0 && (configured.length > 4e3 || CONTROL_CHARACTERS.test(configured))) {
|
|
19554
19805
|
throw new Error("MCP working directory is invalid or too long");
|
|
19555
19806
|
}
|
|
19556
|
-
const defaultCwd =
|
|
19557
|
-
const candidate = configured ?
|
|
19558
|
-
const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) =>
|
|
19807
|
+
const defaultCwd = resolve22(options.cwd ?? process.cwd());
|
|
19808
|
+
const candidate = configured ? isAbsolute8(configured) ? resolve22(configured) : resolve22(defaultCwd, configured) : defaultCwd;
|
|
19809
|
+
const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) => resolve22(root)) : [defaultCwd];
|
|
19559
19810
|
const resolvedCandidate = await realpath8(candidate).catch(() => {
|
|
19560
19811
|
throw new Error(`MCP working directory does not exist: ${candidate}`);
|
|
19561
19812
|
});
|
|
@@ -20209,7 +20460,7 @@ function scopeKey(scope, context) {
|
|
|
20209
20460
|
// src/skills/catalog.ts
|
|
20210
20461
|
import { lstat as lstat21, readFile as readFile20, readdir as readdir9, realpath as realpath9 } from "node:fs/promises";
|
|
20211
20462
|
import { homedir as homedir4 } from "node:os";
|
|
20212
|
-
import { basename as basename12, join as join21, resolve as
|
|
20463
|
+
import { basename as basename12, join as join21, resolve as resolve23 } from "node:path";
|
|
20213
20464
|
import { parse as parseYaml3 } from "yaml";
|
|
20214
20465
|
var SkillCatalog = class {
|
|
20215
20466
|
constructor(workspace, config) {
|
|
@@ -20278,14 +20529,14 @@ ${skill.content}
|
|
|
20278
20529
|
}
|
|
20279
20530
|
function discoveryLocations(workspace, configured) {
|
|
20280
20531
|
const home = homedir4();
|
|
20281
|
-
const workspaceRoot =
|
|
20532
|
+
const workspaceRoot = resolve23(workspace);
|
|
20282
20533
|
return [
|
|
20283
20534
|
{ path: join21(home, ".agents", "skills"), scope: "user", trusted: true },
|
|
20284
20535
|
{ path: join21(home, ".claude", "skills"), scope: "user", trusted: true },
|
|
20285
20536
|
{ path: join21(home, ".augment", "skills"), scope: "user", trusted: true },
|
|
20286
20537
|
{ path: join21(resolveHomeNamespace(), "skills"), scope: "user", trusted: true },
|
|
20287
20538
|
...configured.map((path) => {
|
|
20288
|
-
const resolved =
|
|
20539
|
+
const resolved = resolve23(workspaceRoot, path);
|
|
20289
20540
|
return {
|
|
20290
20541
|
path: resolved,
|
|
20291
20542
|
scope: "configured",
|
|
@@ -20314,7 +20565,7 @@ async function readMetadata(path) {
|
|
|
20314
20565
|
const { frontmatter } = splitFrontmatter(raw);
|
|
20315
20566
|
if (!frontmatter) return void 0;
|
|
20316
20567
|
const parsed = parseYaml3(frontmatter);
|
|
20317
|
-
const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename12(
|
|
20568
|
+
const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename12(resolve23(path, ".."));
|
|
20318
20569
|
const description = typeof parsed?.description === "string" ? parsed.description.trim() : "";
|
|
20319
20570
|
if (!/^[a-z][a-z0-9_-]{0,63}$/.test(name) || !description || description.length > 1e3) {
|
|
20320
20571
|
return void 0;
|
|
@@ -20335,7 +20586,7 @@ async function safeRead(path, maxBytes) {
|
|
|
20335
20586
|
try {
|
|
20336
20587
|
const info = await lstat21(path);
|
|
20337
20588
|
if (!info.isFile() || info.isSymbolicLink() || info.size > maxBytes) return void 0;
|
|
20338
|
-
const parent =
|
|
20589
|
+
const parent = resolve23(path, "..");
|
|
20339
20590
|
const resolvedParent = await realpath9(parent);
|
|
20340
20591
|
const resolvedPath = await realpath9(path);
|
|
20341
20592
|
if (!resolvedPath.startsWith(`${resolvedParent}/`)) return void 0;
|
|
@@ -20520,7 +20771,7 @@ var ExtensionRuntime = class _ExtensionRuntime {
|
|
|
20520
20771
|
delegation;
|
|
20521
20772
|
initialized = false;
|
|
20522
20773
|
static async create(config, registry, options = {}) {
|
|
20523
|
-
const runtime = new _ExtensionRuntime(config,
|
|
20774
|
+
const runtime = new _ExtensionRuntime(config, resolve24(config.workspaceRoots[0] ?? process.cwd()), options);
|
|
20524
20775
|
try {
|
|
20525
20776
|
await runtime.initialize(registry, options.signal);
|
|
20526
20777
|
return runtime;
|
|
@@ -20725,15 +20976,15 @@ function upgradeCommandOverride(env = process.env) {
|
|
|
20725
20976
|
return trimmed ? trimmed : void 0;
|
|
20726
20977
|
}
|
|
20727
20978
|
function runUpgrade(plan) {
|
|
20728
|
-
return new Promise((
|
|
20979
|
+
return new Promise((resolve26) => {
|
|
20729
20980
|
const child = spawn2(plan.command, plan.args, {
|
|
20730
20981
|
stdio: "inherit",
|
|
20731
20982
|
shell: plan.shell ?? false
|
|
20732
20983
|
});
|
|
20733
|
-
child.on("error", () =>
|
|
20984
|
+
child.on("error", () => resolve26({ ok: false, exitCode: 127, display: plan.display }));
|
|
20734
20985
|
child.on("close", (code) => {
|
|
20735
20986
|
const exitCode = code ?? 1;
|
|
20736
|
-
|
|
20987
|
+
resolve26({ ok: exitCode === 0, exitCode, display: plan.display });
|
|
20737
20988
|
});
|
|
20738
20989
|
});
|
|
20739
20990
|
}
|
|
@@ -20995,7 +21246,7 @@ sessionCommand.command("export <id>").description("Export a session as Markdown"
|
|
|
20995
21246
|
const store = new SessionStore(workspaceOption(options.workspace));
|
|
20996
21247
|
const session = await requireSessionSelector(store, id);
|
|
20997
21248
|
const markdown = sessionMarkdown(session);
|
|
20998
|
-
if (options.output) await writeFile3(
|
|
21249
|
+
if (options.output) await writeFile3(resolve25(options.output), markdown, "utf8");
|
|
20999
21250
|
else process.stdout.write(markdown);
|
|
21000
21251
|
});
|
|
21001
21252
|
var checkpointCommand = program.command("checkpoint").description("Inspect and restore pre-mutation snapshots");
|
|
@@ -21372,7 +21623,7 @@ async function runChat(prompts, options) {
|
|
|
21372
21623
|
const stdinPrompt = !process.stdin.isTTY ? await readStdin() : "";
|
|
21373
21624
|
const firstPrompt = [...prompts, stdinPrompt].filter(Boolean).join("\n\n").trim();
|
|
21374
21625
|
if (shouldPrint && !firstPrompt) throw new Error("Provide a prompt argument or pipe input on stdin.");
|
|
21375
|
-
const workspace =
|
|
21626
|
+
const workspace = resolve25(options.workspace);
|
|
21376
21627
|
let config = await runtimeConfig(workspace, options);
|
|
21377
21628
|
let completedOnboarding = false;
|
|
21378
21629
|
if (!shouldPrint && needsFirstRunOnboarding(config)) {
|
|
@@ -21597,14 +21848,14 @@ async function runAgentSetup(options) {
|
|
|
21597
21848
|
`);
|
|
21598
21849
|
}
|
|
21599
21850
|
async function runtimeConfig(workspaceInput, options) {
|
|
21600
|
-
const workspace =
|
|
21851
|
+
const workspace = resolve25(workspaceInput);
|
|
21601
21852
|
const loaded = await loadConfig(workspace, options.config, {
|
|
21602
21853
|
trustProjectConfig: options.trustProjectConfig === true
|
|
21603
21854
|
});
|
|
21604
21855
|
const roots = [
|
|
21605
21856
|
workspace,
|
|
21606
21857
|
...loaded.workspaceRoots,
|
|
21607
|
-
...(options.addWorkspace ?? []).map((root) =>
|
|
21858
|
+
...(options.addWorkspace ?? []).map((root) => resolve25(workspace, root))
|
|
21608
21859
|
];
|
|
21609
21860
|
const provider = options.provider ? validateProvider(options.provider) : loaded.model.provider;
|
|
21610
21861
|
return {
|
|
@@ -21760,7 +22011,7 @@ function collect(value, previous) {
|
|
|
21760
22011
|
}
|
|
21761
22012
|
function workspaceOption(value) {
|
|
21762
22013
|
const rootOptions = program.opts();
|
|
21763
|
-
return
|
|
22014
|
+
return resolve25(value ?? rootOptions.workspace ?? process.cwd());
|
|
21764
22015
|
}
|
|
21765
22016
|
function runtimeOptions(options) {
|
|
21766
22017
|
const root = program.opts();
|