@skein-code/cli 0.3.18 → 0.3.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.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 resolve23 } from "node:path";
7
+ import { basename as basename13, resolve as resolve24 } 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.18",
223
+ version: "0.3.20",
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,9 @@ var package_default = {
236
236
  },
237
237
  skein: {
238
238
  releaseNotes: [
239
+ "Local index v3 records TypeScript AST definitions, calls, and relative import adjacency for graph-assisted retrieval",
240
+ "Context hits expose generation, content hash, matched terms, and bm25/path/symbol/phrase/graph score provenance",
241
+ "Context benchmark v2 locks multilingual recall, MRR, useful-token, stale-hit, incremental-index, and warm-latency gates",
239
242
  "Large MCP catalogs now disclose at most eight request-relevant schemas per run while preserving network permissions",
240
243
  "Token Ledger receipts report deferred progressive schemas and selected definitions remain stable across tool turns",
241
244
  "A deterministic Token Economy replay benchmark guards evidence, output-firewall, and no-progress invariants",
@@ -253,7 +256,8 @@ var package_default = {
253
256
  "Prompt and timeline diagnostics expose the repository-first reuse ladder without blocking legitimate writes",
254
257
  "Known tool changes refresh only their affected local-index paths before the next model turn",
255
258
  "File ctime closes same-size and restored-mtime zero-hit freshness gaps without full-content scans",
256
- "Repeated empty or unchanged search calls stop through the existing recovery circuit"
259
+ "Repeated empty or unchanged search calls stop through the existing recovery circuit",
260
+ "Git recency is an isolated, bounded tie-break with exact HEAD-bound cache invalidation and non-Git degradation"
257
261
  ]
258
262
  },
259
263
  bin: {
@@ -301,6 +305,7 @@ var package_default = {
301
305
  react: "^19.2.3",
302
306
  "string-width": "^8.2.2",
303
307
  "strip-ansi": "^7.2.0",
308
+ typescript: "^5.9.3",
304
309
  yaml: "^2.8.2",
305
310
  zod: "^4.4.3"
306
311
  },
@@ -310,7 +315,6 @@ var package_default = {
310
315
  "@types/react": "^19.2.7",
311
316
  tsup: "^8.5.1",
312
317
  tsx: "^4.21.0",
313
- typescript: "^5.9.3",
314
318
  vitest: "^4.0.18"
315
319
  },
316
320
  overrides: {
@@ -2608,10 +2612,11 @@ function countExplicitPaths(value) {
2608
2612
  }
2609
2613
 
2610
2614
  // src/context/local-index.ts
2611
- import { createHash as createHash6 } from "node:crypto";
2612
- import { lstat as lstat8, readFile as readFile3, stat as stat3 } from "node:fs/promises";
2613
- import { basename as basename5, dirname as dirname7, extname as extname2, isAbsolute as isAbsolute3, join as join7, relative as relative5, resolve as resolve8, sep as sep4 } from "node:path";
2615
+ import { createHash as createHash7 } from "node:crypto";
2616
+ import { lstat as lstat9, readFile as readFile3, stat as stat3 } from "node:fs/promises";
2617
+ 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";
2614
2618
  import fg from "fast-glob";
2619
+ import ts from "typescript";
2615
2620
  import { z as z3 } from "zod";
2616
2621
 
2617
2622
  // src/tools/workspace.ts
@@ -3042,6 +3047,371 @@ function hash(value) {
3042
3047
  return createHash5("sha256").update(value).digest("hex");
3043
3048
  }
3044
3049
 
3050
+ // src/context/git-recency.ts
3051
+ import { createHash as createHash6 } from "node:crypto";
3052
+ import { resolve as resolve9 } from "node:path";
3053
+
3054
+ // src/utils/process.ts
3055
+ import { spawn } from "node:child_process";
3056
+ import { constants as constants3 } from "node:fs";
3057
+ import { access as access2, lstat as lstat8, realpath as realpath6 } from "node:fs/promises";
3058
+ import { delimiter, isAbsolute as isAbsolute3, join as join7, resolve as resolve8 } from "node:path";
3059
+ import { StringDecoder } from "node:string_decoder";
3060
+ async function resolveExecutableRuntime(command2, cwd, excludedRoots = []) {
3061
+ const realRoots = await Promise.all(excludedRoots.map(async (root) => {
3062
+ try {
3063
+ return await realpath6(root);
3064
+ } catch {
3065
+ return resolve8(root);
3066
+ }
3067
+ }));
3068
+ const pathEntries = (process.env.PATH ?? process.env.Path ?? "").split(delimiter).filter(Boolean);
3069
+ const safeDirectories2 = [];
3070
+ let executable2;
3071
+ const explicit = isAbsolute3(command2) || command2.includes("/") || command2.includes("\\");
3072
+ const explicitPath = explicit ? resolve8(cwd, command2) : void 0;
3073
+ for (const entry of pathEntries) {
3074
+ let directory;
3075
+ try {
3076
+ directory = await realpath6(resolve8(cwd, entry));
3077
+ if (!(await lstat8(directory)).isDirectory()) continue;
3078
+ } catch {
3079
+ continue;
3080
+ }
3081
+ if (realRoots.some((root) => isInside(root, directory))) continue;
3082
+ let contaminated = false;
3083
+ if (!explicit) {
3084
+ for (const name of executableNames(command2)) {
3085
+ const candidate = join7(directory, name);
3086
+ const resolvedCandidate = await usableExecutable(candidate);
3087
+ if (!resolvedCandidate) continue;
3088
+ if (realRoots.some((root) => isInside(root, resolvedCandidate))) {
3089
+ contaminated = true;
3090
+ continue;
3091
+ }
3092
+ executable2 ??= resolvedCandidate;
3093
+ }
3094
+ }
3095
+ if (!contaminated && !safeDirectories2.includes(directory)) safeDirectories2.push(directory);
3096
+ }
3097
+ if (explicitPath) executable2 = await usableExecutable(explicitPath);
3098
+ if (!executable2) return void 0;
3099
+ return { executable: executable2, path: safeDirectories2.join(delimiter) };
3100
+ }
3101
+ async function usableExecutable(candidate) {
3102
+ try {
3103
+ await access2(candidate, process.platform === "win32" ? constants3.F_OK : constants3.X_OK);
3104
+ const resolvedCandidate = await realpath6(candidate);
3105
+ return (await lstat8(resolvedCandidate)).isFile() ? resolvedCandidate : void 0;
3106
+ } catch {
3107
+ return void 0;
3108
+ }
3109
+ }
3110
+ function executableNames(command2) {
3111
+ if (process.platform !== "win32") return [command2];
3112
+ const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
3113
+ return [command2, ...extensions.map((extension) => `${command2}${extension}`)];
3114
+ }
3115
+ function runProcess(command2, args, options) {
3116
+ return new Promise((resolve25, reject) => {
3117
+ const started = Date.now();
3118
+ const environment = options.inheritEnv === false ? {} : { ...process.env };
3119
+ for (const name of options.unsetEnv ?? []) delete environment[name];
3120
+ for (const name of Object.keys(environment)) {
3121
+ if (options.unsetEnvPrefixes?.some((prefix) => name.startsWith(prefix))) {
3122
+ delete environment[name];
3123
+ }
3124
+ }
3125
+ Object.assign(environment, options.env);
3126
+ const child = spawn(command2, args, {
3127
+ cwd: options.cwd,
3128
+ env: environment,
3129
+ stdio: ["pipe", "pipe", "pipe"],
3130
+ signal: options.signal
3131
+ });
3132
+ const maxBytes = options.maxOutputBytes ?? 1e6;
3133
+ let stdout = "";
3134
+ let stderr = "";
3135
+ let stdoutBytes = 0;
3136
+ let stderrBytes = 0;
3137
+ let stdoutObservedBytes = 0;
3138
+ let stderrObservedBytes = 0;
3139
+ let timedOut = false;
3140
+ let outputLimitReached = false;
3141
+ let callbackError;
3142
+ const stdoutDecoder = new StringDecoder("utf8");
3143
+ const stderrDecoder = new StringDecoder("utf8");
3144
+ const stdoutCallbackDecoder = new StringDecoder("utf8");
3145
+ const stderrCallbackDecoder = new StringDecoder("utf8");
3146
+ const append = (decoder, chunk, usedBytes) => {
3147
+ if (usedBytes >= maxBytes) return { text: "", usedBytes };
3148
+ const selected = chunk.subarray(0, maxBytes - usedBytes);
3149
+ return { text: decoder.write(selected), usedBytes: usedBytes + selected.length };
3150
+ };
3151
+ const notify = (callback, decoder, chunk) => {
3152
+ if (!callback || callbackError) return;
3153
+ try {
3154
+ const decoded = decoder.write(chunk);
3155
+ if (decoded) callback(decoded);
3156
+ } catch (error) {
3157
+ callbackError = error;
3158
+ child.kill("SIGTERM");
3159
+ }
3160
+ };
3161
+ child.stdout.on("data", (chunk) => {
3162
+ stdoutObservedBytes += chunk.length;
3163
+ if (options.stopOnOutputLimit && stdoutObservedBytes > maxBytes && !outputLimitReached) {
3164
+ outputLimitReached = true;
3165
+ child.kill("SIGTERM");
3166
+ }
3167
+ const appended = append(stdoutDecoder, chunk, stdoutBytes);
3168
+ stdout += appended.text;
3169
+ stdoutBytes = appended.usedBytes;
3170
+ notify(options.onStdout, stdoutCallbackDecoder, chunk);
3171
+ });
3172
+ child.stderr.on("data", (chunk) => {
3173
+ stderrObservedBytes += chunk.length;
3174
+ if (options.stopOnOutputLimit && stderrObservedBytes > maxBytes && !outputLimitReached) {
3175
+ outputLimitReached = true;
3176
+ child.kill("SIGTERM");
3177
+ }
3178
+ const appended = append(stderrDecoder, chunk, stderrBytes);
3179
+ stderr += appended.text;
3180
+ stderrBytes = appended.usedBytes;
3181
+ notify(options.onStderr, stderrCallbackDecoder, chunk);
3182
+ });
3183
+ child.on("error", reject);
3184
+ const timeoutMs = options.timeoutMs ?? 12e4;
3185
+ const timeout = timeoutMs > 0 ? setTimeout(() => {
3186
+ timedOut = true;
3187
+ child.kill("SIGTERM");
3188
+ setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
3189
+ }, timeoutMs) : void 0;
3190
+ child.on("close", (code) => {
3191
+ if (timeout) clearTimeout(timeout);
3192
+ const stdoutTail = stdoutDecoder.end();
3193
+ const stderrTail = stderrDecoder.end();
3194
+ if (Buffer.byteLength(stdout) + Buffer.byteLength(stdoutTail) <= maxBytes) stdout += stdoutTail;
3195
+ if (Buffer.byteLength(stderr) + Buffer.byteLength(stderrTail) <= maxBytes) stderr += stderrTail;
3196
+ try {
3197
+ const stdoutCallbackTail = stdoutCallbackDecoder.end();
3198
+ const stderrCallbackTail = stderrCallbackDecoder.end();
3199
+ if (stdoutCallbackTail && options.onStdout && !callbackError) options.onStdout(stdoutCallbackTail);
3200
+ if (stderrCallbackTail && options.onStderr && !callbackError) options.onStderr(stderrCallbackTail);
3201
+ } catch (error) {
3202
+ callbackError = error;
3203
+ }
3204
+ if (callbackError) {
3205
+ reject(callbackError);
3206
+ return;
3207
+ }
3208
+ resolve25({
3209
+ command: [command2, ...args].join(" "),
3210
+ exitCode: code ?? (timedOut ? 124 : 1),
3211
+ stdout,
3212
+ stderr,
3213
+ timedOut,
3214
+ durationMs: Date.now() - started,
3215
+ stdoutBytes: stdoutObservedBytes,
3216
+ stderrBytes: stderrObservedBytes,
3217
+ stdoutTruncated: stdoutObservedBytes > stdoutBytes,
3218
+ stderrTruncated: stderrObservedBytes > stderrBytes
3219
+ });
3220
+ });
3221
+ if (options.stdin) child.stdin.end(options.stdin);
3222
+ else child.stdin.end();
3223
+ });
3224
+ }
3225
+ function runShell(command2, cwd, options = {}) {
3226
+ const shell = process.platform === "win32" ? process.env.COMSPEC ?? "cmd.exe" : process.env.SHELL ?? "/bin/sh";
3227
+ const args = process.platform === "win32" ? ["/d", "/s", "/c", command2] : ["-lc", command2];
3228
+ return runProcess(shell, args, { ...options, cwd });
3229
+ }
3230
+
3231
+ // src/utils/git.ts
3232
+ var unsafeInheritedGitEnvironment = [
3233
+ "GIT_DIR",
3234
+ "GIT_WORK_TREE",
3235
+ "GIT_COMMON_DIR",
3236
+ "GIT_INDEX_FILE",
3237
+ "GIT_OBJECT_DIRECTORY",
3238
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
3239
+ "GIT_NAMESPACE",
3240
+ "GIT_CEILING_DIRECTORIES",
3241
+ "GIT_DISCOVERY_ACROSS_FILESYSTEM",
3242
+ "GIT_EXTERNAL_DIFF",
3243
+ "GIT_DIFF_OPTS",
3244
+ "GIT_SSH",
3245
+ "GIT_SSH_COMMAND",
3246
+ "GIT_PROXY_COMMAND",
3247
+ "GIT_ASKPASS",
3248
+ "SSH_ASKPASS",
3249
+ "GIT_EXEC_PATH",
3250
+ "GIT_TEMPLATE_DIR",
3251
+ "GIT_CONFIG",
3252
+ "GIT_CONFIG_PARAMETERS",
3253
+ "GIT_QUARANTINE_PATH",
3254
+ "Path"
3255
+ ];
3256
+ var isolatedGitEnvironment = {
3257
+ GIT_TERMINAL_PROMPT: "0",
3258
+ GIT_CONFIG_NOSYSTEM: "1",
3259
+ GIT_CONFIG_GLOBAL: "/dev/null",
3260
+ GIT_CONFIG_SYSTEM: "/dev/null",
3261
+ GIT_NO_REPLACE_OBJECTS: "1",
3262
+ GIT_PAGER: "cat",
3263
+ GIT_EDITOR: "true",
3264
+ GIT_SEQUENCE_EDITOR: "true",
3265
+ GIT_CONFIG_COUNT: "4",
3266
+ GIT_CONFIG_KEY_0: "core.hooksPath",
3267
+ GIT_CONFIG_VALUE_0: "/dev/null",
3268
+ GIT_CONFIG_KEY_1: "core.fsmonitor",
3269
+ GIT_CONFIG_VALUE_1: "false",
3270
+ GIT_CONFIG_KEY_2: "credential.helper",
3271
+ GIT_CONFIG_VALUE_2: "",
3272
+ GIT_CONFIG_KEY_3: "protocol.ext.allow",
3273
+ GIT_CONFIG_VALUE_3: "never"
3274
+ };
3275
+ function runIsolatedGit(runtime, args, cwd, options = {}) {
3276
+ return runProcess(runtime.executable, args, {
3277
+ cwd,
3278
+ timeoutMs: options.timeoutMs ?? 15e3,
3279
+ maxOutputBytes: options.maxOutputBytes ?? 2e6,
3280
+ ...options.stopOnOutputLimit !== void 0 ? { stopOnOutputLimit: options.stopOnOutputLimit } : {},
3281
+ env: { ...isolatedGitEnvironment, PATH: runtime.path },
3282
+ unsetEnv: unsafeInheritedGitEnvironment,
3283
+ unsetEnvPrefixes: ["GIT_"],
3284
+ ...options.stdin !== void 0 ? { stdin: options.stdin } : {},
3285
+ ...options.signal ? { signal: options.signal } : {}
3286
+ });
3287
+ }
3288
+
3289
+ // src/context/git-recency.ts
3290
+ var MAX_COMMITS = 128;
3291
+ var MAX_OUTPUT_BYTES = 1e6;
3292
+ var TIMEOUT_MS = 2e3;
3293
+ var MAX_RECENCY_SCORE = 1e-3;
3294
+ var commitHashPattern = /^(?:[a-f\d]{40}|[a-f\d]{64})$/u;
3295
+ var GitRecencyCollector = class {
3296
+ roots;
3297
+ states = /* @__PURE__ */ new Map();
3298
+ constructor(roots) {
3299
+ this.roots = roots.map((root) => resolve9(root));
3300
+ }
3301
+ async collect() {
3302
+ const snapshots = await Promise.all(this.roots.map((root) => this.collectRoot(root)));
3303
+ const scores = /* @__PURE__ */ new Map();
3304
+ for (const snapshot of snapshots) {
3305
+ for (const [path, score] of snapshot.scores) {
3306
+ scores.set(path, Math.max(score, scores.get(path) ?? 0));
3307
+ }
3308
+ }
3309
+ return {
3310
+ generation: digest(snapshots.map(({ root, generation }) => `${root}\0${generation}`).join("\n")),
3311
+ scores
3312
+ };
3313
+ }
3314
+ async collectRoot(root) {
3315
+ const state = this.states.get(root) ?? {
3316
+ runtime: void 0,
3317
+ runtimeResolved: false,
3318
+ head: void 0,
3319
+ snapshot: void 0
3320
+ };
3321
+ this.states.set(root, state);
3322
+ try {
3323
+ if (!state.runtimeResolved) {
3324
+ state.runtime = await resolveExecutableRuntime("git", root, this.roots);
3325
+ state.runtimeResolved = true;
3326
+ }
3327
+ if (!state.runtime) return unavailableSnapshot(root, "missing");
3328
+ const headResult = await runIsolatedGit(
3329
+ state.runtime,
3330
+ ["rev-parse", "--verify", "HEAD"],
3331
+ root,
3332
+ { timeoutMs: TIMEOUT_MS, maxOutputBytes: 256, stopOnOutputLimit: true }
3333
+ );
3334
+ const head = headResult.stdout.trim();
3335
+ if (headResult.exitCode !== 0 || headResult.timedOut || headResult.stdoutTruncated || headResult.stderrTruncated || !commitHashPattern.test(head)) {
3336
+ state.head = void 0;
3337
+ state.snapshot = unavailableSnapshot(root, "no-head");
3338
+ return state.snapshot;
3339
+ }
3340
+ if (state.head === head && state.snapshot) return state.snapshot;
3341
+ state.head = head;
3342
+ state.snapshot = await collectRootGitLog(root, state.runtime);
3343
+ return state.snapshot;
3344
+ } catch {
3345
+ state.head = void 0;
3346
+ state.snapshot = unavailableSnapshot(root, "error");
3347
+ return state.snapshot;
3348
+ }
3349
+ }
3350
+ };
3351
+ async function collectRootGitLog(root, runtime) {
3352
+ try {
3353
+ const result = await runIsolatedGit(runtime, [
3354
+ "--no-pager",
3355
+ "log",
3356
+ "--topo-order",
3357
+ `--max-count=${MAX_COMMITS}`,
3358
+ "--format=%x00%H",
3359
+ "--name-only",
3360
+ "-z",
3361
+ "--no-renames",
3362
+ "--no-ext-diff",
3363
+ "--no-textconv",
3364
+ "--relative",
3365
+ "--",
3366
+ "."
3367
+ ], root, {
3368
+ timeoutMs: TIMEOUT_MS,
3369
+ maxOutputBytes: MAX_OUTPUT_BYTES,
3370
+ stopOnOutputLimit: true
3371
+ });
3372
+ if (result.exitCode !== 0 || result.timedOut || result.stdoutTruncated || result.stderrTruncated) {
3373
+ const reason = result.timedOut ? "timeout" : result.stdoutTruncated || result.stderrTruncated ? "output-limit" : `exit-${result.exitCode}`;
3374
+ return unavailableSnapshot(root, reason);
3375
+ }
3376
+ return {
3377
+ root,
3378
+ generation: digest(result.stdout),
3379
+ scores: parseGitLog(root, result.stdout)
3380
+ };
3381
+ } catch {
3382
+ return unavailableSnapshot(root, "error");
3383
+ }
3384
+ }
3385
+ function parseGitLog(root, output2) {
3386
+ const scores = /* @__PURE__ */ new Map();
3387
+ const records = output2.split("\0");
3388
+ let commitRank = -1;
3389
+ for (let index = 0; index < records.length; index += 1) {
3390
+ const record = records[index] ?? "";
3391
+ if (!record) {
3392
+ const commit = records[index + 1]?.trim() ?? "";
3393
+ if (commitHashPattern.test(commit)) {
3394
+ commitRank += 1;
3395
+ index += 1;
3396
+ }
3397
+ continue;
3398
+ }
3399
+ if (commitRank < 0) continue;
3400
+ const path = record.startsWith("\n") ? record.slice(1) : record;
3401
+ if (!path) continue;
3402
+ const absolutePath = resolve9(root, path);
3403
+ if (!isInside(root, absolutePath) || scores.has(absolutePath)) continue;
3404
+ scores.set(absolutePath, MAX_RECENCY_SCORE / (commitRank + 1));
3405
+ }
3406
+ return scores;
3407
+ }
3408
+ function unavailableSnapshot(root, reason) {
3409
+ return { root, generation: digest(`unavailable\0${reason}`), scores: /* @__PURE__ */ new Map() };
3410
+ }
3411
+ function digest(value) {
3412
+ return createHash6("sha256").update(value, "utf8").digest("hex").slice(0, 16);
3413
+ }
3414
+
3045
3415
  // src/context/local-index.ts
3046
3416
  var contentHashSchema = z3.string().regex(/^[a-f\d]{64}$/u);
3047
3417
  var indexedChunkSchema = z3.object({
@@ -3063,10 +3433,13 @@ var indexedFileSchema = z3.object({
3063
3433
  ctimeMs: z3.number().optional(),
3064
3434
  size: z3.number().nonnegative(),
3065
3435
  contentHash: contentHashSchema,
3066
- chunks: z3.array(indexedChunkSchema)
3436
+ chunks: z3.array(indexedChunkSchema),
3437
+ definitions: z3.array(z3.string()),
3438
+ references: z3.array(z3.string()),
3439
+ imports: z3.array(z3.string())
3067
3440
  }).strict();
3068
3441
  var localIndexSchema = z3.object({
3069
- version: z3.literal(2),
3442
+ version: z3.literal(3),
3070
3443
  createdAt: z3.string(),
3071
3444
  generation: z3.string().min(1),
3072
3445
  roots: z3.array(z3.string()),
@@ -3104,14 +3477,18 @@ var MIN_STRUCTURAL_CHUNK_LINES = 12;
3104
3477
  var LocalContextIndex = class {
3105
3478
  constructor(roots) {
3106
3479
  this.roots = roots;
3107
- this.roots = roots.map((root) => resolve8(root));
3480
+ this.roots = roots.map((root) => resolve10(root));
3108
3481
  this.workspace = new WorkspaceAccess(this.roots);
3109
- this.indexPath = join7(resolveProjectNamespaceSync(this.roots[0] ?? process.cwd()).active, "index.json");
3482
+ this.gitRecency = new GitRecencyCollector(this.roots);
3483
+ this.indexPath = join8(resolveProjectNamespaceSync(this.roots[0] ?? process.cwd()).active, "index.json");
3110
3484
  }
3111
3485
  roots;
3112
3486
  index;
3113
3487
  workspace;
3114
3488
  queryCache = /* @__PURE__ */ new Map();
3489
+ gitRecency;
3490
+ queryCacheHits = 0;
3491
+ queryCacheMisses = 0;
3115
3492
  fingerprintCache;
3116
3493
  dirtyPaths = /* @__PURE__ */ new Set();
3117
3494
  refreshState = "current";
@@ -3123,13 +3500,13 @@ var LocalContextIndex = class {
3123
3500
  this.roots[0] ?? process.cwd(),
3124
3501
  dirname7(this.indexPath)
3125
3502
  );
3126
- const info = await lstat8(this.indexPath);
3503
+ const info = await lstat9(this.indexPath);
3127
3504
  if (!info.isFile() || info.isSymbolicLink()) return false;
3128
3505
  const parsed = localIndexSchema.parse(JSON.parse(await readFile3(this.indexPath, "utf8")));
3129
3506
  const files = [];
3130
3507
  for (const file of parsed.files) {
3131
3508
  try {
3132
- if (!this.roots.includes(file.root) || resolve8(file.root, file.path) !== file.absolutePath) continue;
3509
+ if (!this.roots.includes(file.root) || resolve10(file.root, file.path) !== file.absolutePath) continue;
3133
3510
  const safe = await this.workspace.resolvePath(file.absolutePath, { expect: "file" });
3134
3511
  if (safe !== file.absolutePath) continue;
3135
3512
  const { ctimeMs, ...persistedFile } = file;
@@ -3206,19 +3583,21 @@ var LocalContextIndex = class {
3206
3583
  await this.flushDirty();
3207
3584
  await this.ensureCurrentIndex();
3208
3585
  const limit = Math.max(1, Math.floor(topK));
3209
- const cached = this.getCachedHits(query, limit);
3210
- let hits = cached ?? this.rank(query, limit);
3586
+ let recency = await this.gitRecency.collect();
3587
+ const cached = this.getCachedHits(query, limit, recency.generation);
3588
+ let hits = cached ?? this.rank(query, limit, recency.scores);
3211
3589
  if (!await this.hitsAreCurrent(hits)) {
3212
3590
  await this.buildWithOptions(void 0, true);
3213
- hits = this.rank(query, limit);
3591
+ recency = await this.gitRecency.collect();
3592
+ hits = this.rank(query, limit, recency.scores);
3214
3593
  if (!await this.hitsAreCurrent(hits)) return [];
3215
3594
  }
3216
- this.cacheHits(query, limit, hits);
3595
+ this.cacheHits(query, limit, recency.generation, hits);
3217
3596
  return cloneHits(hits);
3218
3597
  }
3219
3598
  invalidate(paths) {
3220
3599
  for (const path of paths) {
3221
- const absolutePath = isAbsolute3(path) ? resolve8(path) : resolve8(this.roots[0] ?? process.cwd(), path);
3600
+ const absolutePath = isAbsolute4(path) ? resolve10(path) : resolve10(this.roots[0] ?? process.cwd(), path);
3222
3601
  if (this.roots.some((root) => isWithinRoot(root, absolutePath))) {
3223
3602
  this.dirtyPaths.add(absolutePath);
3224
3603
  }
@@ -3257,7 +3636,7 @@ var LocalContextIndex = class {
3257
3636
  const nextFiles = [...files.values()].sort((left, right) => left.absolutePath.localeCompare(right.absolutePath));
3258
3637
  const generation = createGeneration(nextFiles);
3259
3638
  this.index = {
3260
- version: 2,
3639
+ version: 3,
3261
3640
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3262
3641
  generation,
3263
3642
  roots: this.roots,
@@ -3309,6 +3688,8 @@ var LocalContextIndex = class {
3309
3688
  files: this.index?.files.length ?? 0,
3310
3689
  chunks: this.index?.files.reduce((total, file) => total + file.chunks.length, 0) ?? 0,
3311
3690
  queryCacheEntries: this.queryCache.size,
3691
+ queryCacheHits: this.queryCacheHits,
3692
+ queryCacheMisses: this.queryCacheMisses,
3312
3693
  refreshState: this.refreshState,
3313
3694
  dirtyPaths: this.dirtyPaths.size,
3314
3695
  ...this.refreshError ? { refreshError: this.refreshError } : {},
@@ -3391,12 +3772,13 @@ var LocalContextIndex = class {
3391
3772
  ctimeMs: info.ctimeMs,
3392
3773
  size: info.size,
3393
3774
  contentHash,
3394
- chunks: chunkFile(safeItem, content)
3775
+ chunks: chunkFile(safeItem, content),
3776
+ ...extractSourceFacts(safeItem.path, content)
3395
3777
  });
3396
3778
  }
3397
3779
  const generation = createGeneration(files);
3398
3780
  this.index = {
3399
- version: 2,
3781
+ version: 3,
3400
3782
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3401
3783
  generation,
3402
3784
  roots: this.roots,
@@ -3412,6 +3794,7 @@ var LocalContextIndex = class {
3412
3794
  );
3413
3795
  await atomicWrite(this.indexPath, `${JSON.stringify(this.index)}
3414
3796
  `, 384);
3797
+ await this.gitRecency.collect();
3415
3798
  onProgress?.({ phase: "done", completed: files.length, total: files.length });
3416
3799
  return {
3417
3800
  files: files.length,
@@ -3461,7 +3844,8 @@ var LocalContextIndex = class {
3461
3844
  ctimeMs: info.ctimeMs,
3462
3845
  size: info.size,
3463
3846
  contentHash: hashContent(content),
3464
- chunks: chunkFile(safeItem, content)
3847
+ chunks: chunkFile(safeItem, content),
3848
+ ...extractSourceFacts(safeItem.path, content)
3465
3849
  };
3466
3850
  } catch {
3467
3851
  return void 0;
@@ -3484,7 +3868,8 @@ var LocalContextIndex = class {
3484
3868
  path: file.path,
3485
3869
  absolutePath: file.absolutePath
3486
3870
  }, content);
3487
- if (!chunksMatch(expectedChunks, file.chunks)) return false;
3871
+ const expectedFacts = extractSourceFacts(file.path, content);
3872
+ if (!chunksMatch(expectedChunks, file.chunks) || !sameStrings(expectedFacts.definitions, file.definitions) || !sameStrings(expectedFacts.references, file.references) || !sameStrings(expectedFacts.imports, file.imports)) return false;
3488
3873
  } catch {
3489
3874
  return false;
3490
3875
  }
@@ -3503,7 +3888,7 @@ var LocalContextIndex = class {
3503
3888
  followSymbolicLinks: false,
3504
3889
  ignore: ignorePatterns
3505
3890
  });
3506
- for (const path of paths) discovered.push({ root, path, absolutePath: resolve8(root, path) });
3891
+ for (const path of paths) discovered.push({ root, path, absolutePath: resolve10(root, path) });
3507
3892
  }
3508
3893
  discovered.sort((left, right) => left.absolutePath.localeCompare(right.absolutePath));
3509
3894
  return discovered;
@@ -3523,11 +3908,15 @@ var LocalContextIndex = class {
3523
3908
  });
3524
3909
  return matches.length === 1 ? { root, path, absolutePath } : void 0;
3525
3910
  }
3526
- rank(query, topK) {
3527
- const chunks = (this.index?.files ?? []).flatMap((file) => file.chunks);
3911
+ rank(query, topK, recencyScores) {
3912
+ const index = this.index;
3913
+ const files = index?.files ?? [];
3914
+ const chunks = files.flatMap((file) => file.chunks);
3528
3915
  if (!chunks.length) return [];
3529
- const terms = [...new Set(tokenize(query))];
3530
- if (!terms.length) return [];
3916
+ const directTerms = [...new Set(tokenize(query))];
3917
+ if (!directTerms.length || !index) return [];
3918
+ const expandedTerms = expandQueryTerms(files, directTerms);
3919
+ const terms = [.../* @__PURE__ */ new Set([...directTerms, ...expandedTerms])];
3531
3920
  const queryTerms = new Set(terms);
3532
3921
  const documentFrequency = new Map([...queryTerms].map((term) => [term, 0]));
3533
3922
  for (const chunk of chunks) {
@@ -3536,22 +3925,53 @@ var LocalContextIndex = class {
3536
3925
  }
3537
3926
  }
3538
3927
  const averageLength = chunks.reduce((sum, chunk) => sum + chunk.tokens.length, 0) / Math.max(chunks.length, 1);
3539
- return chunks.map((chunk) => ({ chunk, score: scoreChunk(
3540
- chunk,
3541
- terms,
3542
- query,
3543
- documentFrequency,
3544
- chunks.length,
3545
- averageLength
3546
- ) })).filter(({ score }) => score > 0).sort((left, right) => right.score - left.score || left.chunk.absolutePath.localeCompare(right.chunk.absolutePath)).slice(0, topK).map(({ chunk, score }) => ({
3547
- path: chunk.absolutePath,
3548
- startLine: chunk.startLine,
3549
- endLine: chunk.endLine,
3550
- content: chunk.content,
3551
- score,
3552
- source: "local-bm25+path+symbol",
3553
- ...chunk.symbol ? { symbol: chunk.symbol } : {}
3554
- }));
3928
+ const filesByPath = new Map(files.map((file) => [file.absolutePath, file]));
3929
+ const graphBoosts = buildGraphBoosts(files, directTerms);
3930
+ const coverageTerms = directTerms.filter((term) => !retrievalStopWords.has(term));
3931
+ const scored = chunks.map((chunk) => {
3932
+ const lexical = scoreChunk(
3933
+ chunk,
3934
+ terms,
3935
+ query,
3936
+ documentFrequency,
3937
+ chunks.length,
3938
+ averageLength
3939
+ );
3940
+ const graph = graphBoosts.get(chunk.absolutePath)?.score ?? 0;
3941
+ const recency = recencyScores.get(chunk.absolutePath) ?? 0;
3942
+ const score = lexical.bm25 + lexical.path + lexical.symbol + lexical.phrase + graph + recency;
3943
+ return { chunk, lexical, graph, recency, score };
3944
+ }).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));
3945
+ const covered = scored.filter(({ lexical, graph }) => graph > 0 || hasSufficientQueryCoverage(lexical.matchedTerms, coverageTerms));
3946
+ return (covered.length ? covered : scored).slice(0, topK).map(({ chunk, lexical, graph, recency, score }) => {
3947
+ const file = filesByPath.get(chunk.absolutePath);
3948
+ const breakdown = {
3949
+ bm25: roundScore(lexical.bm25),
3950
+ path: roundScore(lexical.path),
3951
+ symbol: roundScore(lexical.symbol),
3952
+ phrase: roundScore(lexical.phrase),
3953
+ graph: roundScore(graph),
3954
+ recency: roundScore(recency),
3955
+ total: roundScore(score)
3956
+ };
3957
+ const provenance = {
3958
+ generation: index.generation,
3959
+ contentHash: file?.contentHash ?? hashContent(chunk.content),
3960
+ matchedTerms: lexical.matchedTerms.slice(0, 16),
3961
+ expandedTerms: expandedTerms.slice(0, 16),
3962
+ score: breakdown
3963
+ };
3964
+ return {
3965
+ path: chunk.absolutePath,
3966
+ startLine: chunk.startLine,
3967
+ endLine: chunk.endLine,
3968
+ content: chunk.content,
3969
+ score,
3970
+ source: "local-bm25+path+symbol+graph+recency",
3971
+ ...chunk.symbol ? { symbol: chunk.symbol } : {},
3972
+ provenance
3973
+ };
3974
+ });
3555
3975
  }
3556
3976
  async hitsAreCurrent(hits) {
3557
3977
  const files = new Map((this.index?.files ?? []).map((file) => [file.absolutePath, file]));
@@ -3573,21 +3993,28 @@ var LocalContextIndex = class {
3573
3993
  }
3574
3994
  return true;
3575
3995
  }
3576
- getCachedHits(query, topK) {
3996
+ getCachedHits(query, topK, rankingGeneration) {
3577
3997
  const generation = this.index?.generation;
3578
- if (!generation) return void 0;
3579
- const key = `${generation}\0${topK}\0${query}`;
3998
+ if (!generation) {
3999
+ this.queryCacheMisses += 1;
4000
+ return void 0;
4001
+ }
4002
+ const key = `${generation}\0${rankingGeneration}\0${topK}\0${query}`;
3580
4003
  const cached = this.queryCache.get(key);
3581
- if (!cached) return void 0;
4004
+ if (!cached) {
4005
+ this.queryCacheMisses += 1;
4006
+ return void 0;
4007
+ }
4008
+ this.queryCacheHits += 1;
3582
4009
  this.queryCache.delete(key);
3583
4010
  this.queryCache.set(key, cached);
3584
4011
  return cloneHits(cached);
3585
4012
  }
3586
- cacheHits(query, topK, hits) {
4013
+ cacheHits(query, topK, rankingGeneration, hits) {
3587
4014
  if (!hits.length) return;
3588
4015
  const generation = this.index?.generation;
3589
4016
  if (!generation) return;
3590
- const key = `${generation}\0${topK}\0${query}`;
4017
+ const key = `${generation}\0${rankingGeneration}\0${topK}\0${query}`;
3591
4018
  this.queryCache.delete(key);
3592
4019
  this.queryCache.set(key, cloneHits(hits));
3593
4020
  while (this.queryCache.size > MAX_QUERY_CACHE_ENTRIES) {
@@ -3599,7 +4026,7 @@ var LocalContextIndex = class {
3599
4026
  };
3600
4027
  function isWithinRoot(root, path) {
3601
4028
  const offset = relative5(root, path);
3602
- return offset === "" || !offset.startsWith("..") && !isAbsolute3(offset);
4029
+ return offset === "" || !offset.startsWith("..") && !isAbsolute4(offset);
3603
4030
  }
3604
4031
  function isIncludedSourcePath(path) {
3605
4032
  const name = basename5(path);
@@ -3674,6 +4101,9 @@ function chunksMatch(expected, actual) {
3674
4101
  return Boolean(candidate) && chunk.id === candidate?.id && chunk.root === candidate.root && chunk.path === candidate.path && chunk.absolutePath === candidate.absolutePath && chunk.startLine === candidate.startLine && chunk.endLine === candidate.endLine && chunk.content === candidate.content && chunk.symbol === candidate.symbol && chunk.tokens.length === candidate.tokens.length && chunk.tokens.every((token, tokenIndex) => token === candidate.tokens[tokenIndex]);
3675
4102
  });
3676
4103
  }
4104
+ function sameStrings(expected, actual) {
4105
+ return expected.length === actual.length && expected.every((value, index) => value === actual[index]);
4106
+ }
3677
4107
  function existingBuildResult(status, started) {
3678
4108
  if (!status.available || !status.generation) {
3679
4109
  throw new Error("The existing local context index is unavailable.");
@@ -3728,7 +4158,8 @@ function packContextHits(hits, roots, maxTokens, engine) {
3728
4158
  const text = selected.map((hit) => {
3729
4159
  const shownPath = workspaceAliasPath(hit.path, roots);
3730
4160
  const symbol = hit.symbol ? ` symbol="${escapeAttribute(hit.symbol)}"` : "";
3731
- return `<code path="${escapeAttribute(shownPath)}" lines="${hit.startLine}-${hit.endLine}" score="${hit.score.toFixed(3)}"${symbol}>
4161
+ 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)}"` : "";
4162
+ return `<code path="${escapeAttribute(shownPath)}" lines="${hit.startLine}-${hit.endLine}" score="${hit.score.toFixed(3)}"${symbol}${provenance}>
3732
4163
  ${hit.content}
3733
4164
  </code>`;
3734
4165
  }).join("\n\n");
@@ -3744,7 +4175,17 @@ ${hit.content}
3744
4175
  };
3745
4176
  }
3746
4177
  function cloneHits(hits) {
3747
- return hits.map((hit) => ({ ...hit }));
4178
+ return hits.map((hit) => ({
4179
+ ...hit,
4180
+ ...hit.provenance ? {
4181
+ provenance: {
4182
+ ...hit.provenance,
4183
+ matchedTerms: [...hit.provenance.matchedTerms],
4184
+ expandedTerms: [...hit.provenance.expandedTerms],
4185
+ score: { ...hit.provenance.score }
4186
+ }
4187
+ } : {}
4188
+ }));
3748
4189
  }
3749
4190
  function cloneDuplicationBaseline(baseline) {
3750
4191
  return {
@@ -3765,10 +4206,10 @@ function reconstructIndexedContent(chunks) {
3765
4206
  return lines.map((line) => line ?? "").join("\n");
3766
4207
  }
3767
4208
  function hashContent(content) {
3768
- return createHash6("sha256").update(content, "utf8").digest("hex");
4209
+ return createHash7("sha256").update(content, "utf8").digest("hex");
3769
4210
  }
3770
4211
  function createGeneration(files) {
3771
- return createHash6("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);
4212
+ 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);
3772
4213
  }
3773
4214
  function hasSubstantialOverlap(left, right) {
3774
4215
  if (left.path !== right.path) return false;
@@ -3844,7 +4285,110 @@ function detectSymbol(lines) {
3844
4285
  if (match?.[1]) return match[1];
3845
4286
  }
3846
4287
  }
3847
- return void 0;
4288
+ return void 0;
4289
+ }
4290
+ function extractSourceFacts(path, content) {
4291
+ const extension = extname2(path).toLocaleLowerCase();
4292
+ if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(extension)) {
4293
+ return extractTypeScriptFacts(path, content);
4294
+ }
4295
+ if (extension === ".py") return extractPythonFacts(content);
4296
+ if (extension === ".sql") return extractSqlFacts(content);
4297
+ return { definitions: [], references: [], imports: [] };
4298
+ }
4299
+ function extractTypeScriptFacts(path, content) {
4300
+ const definitions = /* @__PURE__ */ new Set();
4301
+ const references = /* @__PURE__ */ new Set();
4302
+ const imports = /* @__PURE__ */ new Set();
4303
+ const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, false, scriptKind(path));
4304
+ const addName = (name) => {
4305
+ if (!name) return;
4306
+ if (ts.isIdentifier(name)) definitions.add(name.text);
4307
+ else if (ts.isStringLiteralLike(name) || ts.isNumericLiteral(name)) definitions.add(name.text);
4308
+ else if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
4309
+ for (const element of name.elements) if (ts.isBindingElement(element)) addName(element.name);
4310
+ }
4311
+ };
4312
+ const addCallReference = (expression) => {
4313
+ if (ts.isIdentifier(expression)) references.add(expression.text);
4314
+ else if (ts.isPropertyAccessExpression(expression)) references.add(expression.name.text);
4315
+ else if (ts.isElementAccessExpression(expression) && ts.isIdentifier(expression.expression)) {
4316
+ references.add(expression.expression.text);
4317
+ }
4318
+ };
4319
+ const visit = (node) => {
4320
+ if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
4321
+ if (node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) imports.add(node.moduleSpecifier.text);
4322
+ } else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && node.moduleReference.expression && ts.isStringLiteral(node.moduleReference.expression)) {
4323
+ imports.add(node.moduleReference.expression.text);
4324
+ } else if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
4325
+ addName(node.name);
4326
+ } else if (ts.isVariableDeclaration(node) || ts.isBindingElement(node) || ts.isMethodDeclaration(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node)) {
4327
+ addName(node.name);
4328
+ } else if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
4329
+ addCallReference(node.expression);
4330
+ }
4331
+ ts.forEachChild(node, visit);
4332
+ };
4333
+ visit(source);
4334
+ return {
4335
+ definitions: sortedValues(definitions),
4336
+ references: sortedValues(references),
4337
+ imports: sortedValues(imports)
4338
+ };
4339
+ }
4340
+ function scriptKind(path) {
4341
+ switch (extname2(path).toLocaleLowerCase()) {
4342
+ case ".tsx":
4343
+ return ts.ScriptKind.TSX;
4344
+ case ".jsx":
4345
+ return ts.ScriptKind.JSX;
4346
+ case ".js":
4347
+ case ".mjs":
4348
+ case ".cjs":
4349
+ return ts.ScriptKind.JS;
4350
+ default:
4351
+ return ts.ScriptKind.TS;
4352
+ }
4353
+ }
4354
+ function extractPythonFacts(content) {
4355
+ const definitions = /* @__PURE__ */ new Set();
4356
+ const references = /* @__PURE__ */ new Set();
4357
+ const imports = /* @__PURE__ */ new Set();
4358
+ for (const line of content.split("\n")) {
4359
+ const definition = line.match(/^\s*(?:async\s+)?(?:def|class)\s+([\p{L}_][\p{L}\p{N}_]*)/u);
4360
+ if (definition?.[1]) definitions.add(definition[1]);
4361
+ const imported = line.match(/^\s*(?:from\s+([\w.]+)\s+import|import\s+([\w.]+))/u);
4362
+ if (imported?.[1] || imported?.[2]) imports.add(imported[1] ?? imported[2] ?? "");
4363
+ for (const call of line.matchAll(/([\p{L}_][\p{L}\p{N}_]*)\s*\(/gu)) {
4364
+ if (call[1] && !["def", "class", "if", "for", "while"].includes(call[1])) references.add(call[1]);
4365
+ }
4366
+ }
4367
+ for (const definition of definitions) references.delete(definition);
4368
+ return {
4369
+ definitions: sortedValues(definitions),
4370
+ references: sortedValues(references),
4371
+ imports: sortedValues(imports)
4372
+ };
4373
+ }
4374
+ function extractSqlFacts(content) {
4375
+ const definitions = /* @__PURE__ */ new Set();
4376
+ const references = /* @__PURE__ */ new Set();
4377
+ const imports = /* @__PURE__ */ new Set();
4378
+ for (const definition of content.matchAll(/\bcreate\s+(?:table|view|function|procedure)\s+(?:if\s+not\s+exists\s+)?([\w.]+)/giu)) {
4379
+ if (definition[1]) definitions.add(definition[1]);
4380
+ }
4381
+ for (const reference2 of content.matchAll(/\b(?:from|join|update|into)\s+([\w.]+)/giu)) {
4382
+ if (reference2[1]) references.add(reference2[1]);
4383
+ }
4384
+ return {
4385
+ definitions: sortedValues(definitions),
4386
+ references: sortedValues(references),
4387
+ imports: sortedValues(imports)
4388
+ };
4389
+ }
4390
+ function sortedValues(values) {
4391
+ return [...values].filter(Boolean).sort((left, right) => left.localeCompare(right));
3848
4392
  }
3849
4393
  function tokenize(input2) {
3850
4394
  const normalized = input2.replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([\p{Ll}])([\p{Lu}])/gu, "$1 $2").toLocaleLowerCase();
@@ -3861,24 +4405,123 @@ function tokenize(input2) {
3861
4405
  }
3862
4406
  return output2;
3863
4407
  }
4408
+ var retrievalStopWords = /* @__PURE__ */ new Set([
4409
+ "add",
4410
+ "audit",
4411
+ "change",
4412
+ "code",
4413
+ "create",
4414
+ "explain",
4415
+ "find",
4416
+ "fix",
4417
+ "implement",
4418
+ "inspect",
4419
+ "review",
4420
+ "show",
4421
+ "test",
4422
+ "the",
4423
+ "update",
4424
+ "where",
4425
+ "with",
4426
+ "\u4FEE\u6539",
4427
+ "\u4FEE\u590D",
4428
+ "\u5B9E\u73B0",
4429
+ "\u5BA1\u67E5",
4430
+ "\u67E5\u627E",
4431
+ "\u89E3\u91CA",
4432
+ "\u6D4B\u8BD5",
4433
+ "\u66F4\u65B0"
4434
+ ]);
4435
+ function hasSufficientQueryCoverage(matchedTerms, coverageTerms) {
4436
+ if (!coverageTerms.length) return matchedTerms.length > 0;
4437
+ const matched = new Set(matchedTerms);
4438
+ const covered = [...new Set(coverageTerms)].filter((term) => matched.has(term)).length;
4439
+ return covered >= Math.min(2, new Set(coverageTerms).size);
4440
+ }
4441
+ function expandQueryTerms(files, directTerms) {
4442
+ const expanded = /* @__PURE__ */ new Set();
4443
+ for (const file of files) {
4444
+ for (const definition of file.definitions) {
4445
+ const definitionTerms = [...new Set(tokenize(definition))];
4446
+ if (!definitionMatchesQuery(definitionTerms, directTerms)) continue;
4447
+ for (const term of definitionTerms) if (!directTerms.includes(term)) expanded.add(term);
4448
+ }
4449
+ }
4450
+ return [...expanded].sort((left, right) => left.localeCompare(right)).slice(0, 24);
4451
+ }
4452
+ function buildGraphBoosts(files, directTerms) {
4453
+ const anchors = /* @__PURE__ */ new Map();
4454
+ for (const file of files) {
4455
+ for (const definition of file.definitions) {
4456
+ if (!definitionMatchesQuery(tokenize(definition), directTerms)) continue;
4457
+ const values = anchors.get(file.absolutePath) ?? /* @__PURE__ */ new Set();
4458
+ values.add(definition.toLocaleLowerCase());
4459
+ anchors.set(file.absolutePath, values);
4460
+ }
4461
+ }
4462
+ if (!anchors.size) return /* @__PURE__ */ new Map();
4463
+ const boosts = /* @__PURE__ */ new Map();
4464
+ const addBoost = (path, score) => {
4465
+ boosts.set(path, { score: (boosts.get(path)?.score ?? 0) + score });
4466
+ };
4467
+ const anchorSymbols = new Set([...anchors.values()].flatMap((symbols) => [...symbols]));
4468
+ for (const file of files) {
4469
+ if (file.references.some((reference2) => anchorSymbols.has(reference2.toLocaleLowerCase()))) {
4470
+ addBoost(file.absolutePath, 1.5);
4471
+ }
4472
+ for (const specifier of file.imports) {
4473
+ for (const target of resolveImportTargets(file, specifier, files)) {
4474
+ if (anchors.has(target)) addBoost(file.absolutePath, 1.25);
4475
+ if (anchors.has(file.absolutePath)) addBoost(target, 0.75);
4476
+ }
4477
+ }
4478
+ }
4479
+ return boosts;
4480
+ }
4481
+ function definitionMatchesQuery(definitionTerms, queryTerms) {
4482
+ const definitions = new Set(definitionTerms);
4483
+ const shared = [...new Set(queryTerms)].filter((term) => definitions.has(term));
4484
+ const required = Math.min(2, Math.max(1, Math.min(definitions.size, queryTerms.length)));
4485
+ return shared.length >= required;
4486
+ }
4487
+ function resolveImportTargets(importer, specifier, files) {
4488
+ if (!specifier.startsWith(".")) return [];
4489
+ const base = posix.normalize(posix.join(posix.dirname(importer.path), specifier));
4490
+ const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
4491
+ const importedExtension = posix.extname(base);
4492
+ const extensionless = extensions.includes(importedExtension) ? base.slice(0, -importedExtension.length) : base;
4493
+ const candidates = /* @__PURE__ */ new Set([
4494
+ base,
4495
+ ...extensions.map((extension) => `${extensionless}${extension}`),
4496
+ ...extensions.map((extension) => posix.join(extensionless, `index${extension}`))
4497
+ ]);
4498
+ return files.filter((file) => file.root === importer.root && candidates.has(file.path)).map((file) => file.absolutePath);
4499
+ }
3864
4500
  function scoreChunk(chunk, terms, rawQuery, documentFrequency, documentCount, averageLength) {
3865
4501
  const frequencies = /* @__PURE__ */ new Map();
3866
4502
  for (const token of chunk.tokens) frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
3867
4503
  const k1 = 1.2;
3868
4504
  const b = 0.75;
3869
- let score = 0;
4505
+ let bm25 = 0;
4506
+ let path = 0;
4507
+ let symbol = 0;
4508
+ const matchedTerms = [];
3870
4509
  for (const term of terms) {
3871
4510
  const frequency = frequencies.get(term) ?? 0;
3872
4511
  if (!frequency) continue;
4512
+ matchedTerms.push(term);
3873
4513
  const df = documentFrequency.get(term) ?? 0;
3874
4514
  const idf = Math.log(1 + (documentCount - df + 0.5) / (df + 0.5));
3875
- score += idf * (frequency * (k1 + 1) / (frequency + k1 * (1 - b + b * chunk.tokens.length / averageLength)));
3876
- if (chunk.path.toLocaleLowerCase().includes(term)) score += 1.5;
3877
- if (chunk.symbol?.toLocaleLowerCase().includes(term)) score += 2.5;
4515
+ bm25 += idf * (frequency * (k1 + 1) / (frequency + k1 * (1 - b + b * chunk.tokens.length / averageLength)));
4516
+ if (chunk.path.toLocaleLowerCase().includes(term)) path += 1.5;
4517
+ if (chunk.symbol?.toLocaleLowerCase().includes(term)) symbol += 2.5;
3878
4518
  }
3879
4519
  const phrase = rawQuery.trim().toLocaleLowerCase();
3880
- if (phrase.length > 3 && chunk.content.toLocaleLowerCase().includes(phrase)) score += 3;
3881
- return score;
4520
+ const phraseScore = phrase.length > 3 && chunk.content.toLocaleLowerCase().includes(phrase) ? 3 : 0;
4521
+ return { bm25, path, symbol, phrase: phraseScore, matchedTerms };
4522
+ }
4523
+ function roundScore(value) {
4524
+ return Number(value.toFixed(6));
3882
4525
  }
3883
4526
 
3884
4527
  // src/context/context-engine.ts
@@ -3989,7 +4632,10 @@ function formatContextHits(hits, roots) {
3989
4632
  return hits.map((hit) => {
3990
4633
  const path = workspaceAliasPath(hit.path, roots);
3991
4634
  const symbol = hit.symbol ? ` ${hit.symbol}` : "";
3992
- return `[${hit.source} ${hit.score.toFixed(3)}]${symbol} ${path}:${hit.startLine}-${hit.endLine}`;
4635
+ const score = hit.provenance?.score;
4636
+ 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)}` : "";
4637
+ const hash4 = hit.provenance?.contentHash.slice(0, 12);
4638
+ return `[${hit.source} ${hit.score.toFixed(3)}${breakdown}${hash4 ? ` hash=${hash4}` : ""}]${symbol} ${path}:${hit.startLine}-${hit.endLine}`;
3993
4639
  }).join("\n");
3994
4640
  }
3995
4641
 
@@ -4228,7 +4874,7 @@ function toolTokens(messages) {
4228
4874
 
4229
4875
  // src/context/mentions.ts
4230
4876
  import { readFile as readFile4, stat as stat4 } from "node:fs/promises";
4231
- import { isAbsolute as isAbsolute4, resolve as resolve9 } from "node:path";
4877
+ import { isAbsolute as isAbsolute5, resolve as resolve11 } from "node:path";
4232
4878
  import fg2 from "fast-glob";
4233
4879
  var defaultMentionIgnores = [
4234
4880
  "**/.git/**",
@@ -4299,7 +4945,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
4299
4945
  ignore: ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/.mosaic/**", "**/.skein/**", "**/.skein.migrating-*/**", "**/.skein.rollback-*/**"]
4300
4946
  });
4301
4947
  matched.push(...await safeMentionPaths(
4302
- children.slice(0, 25).map((path) => resolve9(aliased, path)),
4948
+ children.slice(0, 25).map((path) => resolve11(aliased, path)),
4303
4949
  workspace
4304
4950
  ));
4305
4951
  }
@@ -4307,7 +4953,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
4307
4953
  }
4308
4954
  }
4309
4955
  for (const root of matched.length ? [] : roots) {
4310
- const direct = resolve9(root, clean2);
4956
+ const direct = resolve11(root, clean2);
4311
4957
  try {
4312
4958
  const safeDirect = await workspace.resolvePath(direct);
4313
4959
  const info = await stat4(safeDirect);
@@ -4321,7 +4967,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
4321
4967
  ignore: ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/.mosaic/**", "**/.skein/**", "**/.skein.migrating-*/**", "**/.skein.rollback-*/**"]
4322
4968
  });
4323
4969
  matched.push(...await safeMentionPaths(
4324
- children.slice(0, 25).map((path) => resolve9(safeDirect, path)),
4970
+ children.slice(0, 25).map((path) => resolve11(safeDirect, path)),
4325
4971
  workspace
4326
4972
  ));
4327
4973
  }
@@ -4337,7 +4983,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
4337
4983
  ignore: ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/.mosaic/**", "**/.skein/**", "**/.skein.migrating-*/**", "**/.skein.rollback-*/**"]
4338
4984
  });
4339
4985
  matched.push(...await safeMentionPaths(
4340
- globbed.slice(0, 25).map((path) => resolve9(root, path)),
4986
+ globbed.slice(0, 25).map((path) => resolve11(root, path)),
4341
4987
  workspace
4342
4988
  ));
4343
4989
  }
@@ -4403,7 +5049,7 @@ async function buildMentionPathIndex(roots, options = {}) {
4403
5049
  })
4404
5050
  })));
4405
5051
  const paths = filesByRoot.flatMap(({ root, files }) => files.map(
4406
- (path) => workspaceAliasPath(resolve9(root, path), normalizedRoots)
5052
+ (path) => workspaceAliasPath(resolve11(root, path), normalizedRoots)
4407
5053
  ));
4408
5054
  return new MentionPathIndex(normalizedRoots, paths);
4409
5055
  }
@@ -4437,22 +5083,22 @@ function mapMentionCandidatePath(path, roots) {
4437
5083
  const primary = normalizedRoots[0];
4438
5084
  if (!primary || !path || path.includes("\0")) return void 0;
4439
5085
  let candidate;
4440
- if (isAbsolute4(path)) {
4441
- candidate = resolve9(path);
5086
+ if (isAbsolute5(path)) {
5087
+ candidate = resolve11(path);
4442
5088
  } else {
4443
5089
  const normalized = path.replaceAll("\\", "/").replace(/^\.\//, "");
4444
5090
  const [first = "", ...rest] = normalized.split("/");
4445
5091
  const alias = first.toLocaleLowerCase();
4446
5092
  const workspace = alias.match(/^workspace(\d+)$/);
4447
5093
  if (alias === "main") {
4448
- candidate = resolve9(primary, ...rest);
5094
+ candidate = resolve11(primary, ...rest);
4449
5095
  } else if (workspace) {
4450
5096
  const index = Number(workspace[1]) - 1;
4451
5097
  const root = Number.isSafeInteger(index) && index >= 1 ? normalizedRoots[index] : void 0;
4452
5098
  if (!root) return void 0;
4453
- candidate = resolve9(root, ...rest);
5099
+ candidate = resolve11(root, ...rest);
4454
5100
  } else {
4455
- candidate = resolve9(primary, normalized);
5101
+ candidate = resolve11(primary, normalized);
4456
5102
  }
4457
5103
  }
4458
5104
  if (!normalizedRoots.some((root) => isInside(root, candidate))) return void 0;
@@ -4482,7 +5128,7 @@ function rankMention(path, needle) {
4482
5128
  }
4483
5129
  function normalizeRoots(roots) {
4484
5130
  const values = typeof roots === "string" ? [roots] : roots;
4485
- return [...new Set(values.map((root) => resolve9(root)))];
5131
+ return [...new Set(values.map((root) => resolve11(root)))];
4486
5132
  }
4487
5133
  function normalizeMentionCandidate(path) {
4488
5134
  const normalized = path.replaceAll("\\", "/").normalize("NFC");
@@ -5148,9 +5794,9 @@ function createProvider(config) {
5148
5794
  }
5149
5795
 
5150
5796
  // src/checkpoint/store.ts
5151
- import { createHash as createHash7, randomUUID as randomUUID7 } from "node:crypto";
5152
- import { lstat as lstat9, mkdir as mkdir7, readFile as readFile5, readdir as readdir2, unlink as unlink2 } from "node:fs/promises";
5153
- import { basename as basename6, dirname as dirname8, join as join8, relative as relative6, resolve as resolve10 } from "node:path";
5797
+ import { createHash as createHash8, randomUUID as randomUUID7 } from "node:crypto";
5798
+ import { lstat as lstat10, mkdir as mkdir7, readFile as readFile5, readdir as readdir2, unlink as unlink2 } from "node:fs/promises";
5799
+ import { basename as basename6, dirname as dirname8, join as join9, relative as relative6, resolve as resolve12 } from "node:path";
5154
5800
  import { z as z4 } from "zod";
5155
5801
  var entrySchema = z4.object({
5156
5802
  path: z4.string(),
@@ -5175,7 +5821,7 @@ var CheckpointStore = class {
5175
5821
  constructor(workspace, directory) {
5176
5822
  this.workspace = typeof workspace === "string" ? new WorkspaceAccess([workspace]) : workspace;
5177
5823
  this.managedDirectory = directory === void 0;
5178
- this.directory = directory ? resolve10(directory) : join8(resolveProjectNamespaceSync(this.workspace.primaryRoot).active, "checkpoints");
5824
+ this.directory = directory ? resolve12(directory) : join9(resolveProjectNamespaceSync(this.workspace.primaryRoot).active, "checkpoints");
5179
5825
  }
5180
5826
  async capture(sessionId, paths, options = {}) {
5181
5827
  validateIdentifier(sessionId, "session");
@@ -5185,8 +5831,8 @@ var CheckpointStore = class {
5185
5831
  }
5186
5832
  async captureUnlocked(sessionId, unique3, options) {
5187
5833
  const id = `${Date.now().toString(36)}-${randomUUID7()}`;
5188
- const target = join8(this.directory, sessionId, id);
5189
- const blobDirectory = join8(target, "blobs");
5834
+ const target = join9(this.directory, sessionId, id);
5835
+ const blobDirectory = join9(target, "blobs");
5190
5836
  await this.ensureDirectory();
5191
5837
  await this.assertManagedPath(target);
5192
5838
  await mkdir7(blobDirectory, { recursive: true });
@@ -5195,14 +5841,14 @@ var CheckpointStore = class {
5195
5841
  for (const input2 of unique3) {
5196
5842
  const path = await this.workspace.resolvePath(input2, { allowMissing: true });
5197
5843
  try {
5198
- const info = await lstat9(path);
5844
+ const info = await lstat10(path);
5199
5845
  if (info.isSymbolicLink()) throw new Error(`Cannot checkpoint a symbolic link: ${input2}`);
5200
5846
  if (!info.isFile()) throw new Error(`Cannot checkpoint a non-file path: ${input2}`);
5201
5847
  if (info.size > 25e6) {
5202
5848
  throw new Error(`File is too large to checkpoint (${info.size} bytes): ${input2}`);
5203
5849
  }
5204
- const blob = `${createHash7("sha256").update(path).digest("hex")}.bin`;
5205
- await atomicWrite(join8(blobDirectory, blob), await readFile5(path), 384);
5850
+ const blob = `${createHash8("sha256").update(path).digest("hex")}.bin`;
5851
+ await atomicWrite(join9(blobDirectory, blob), await readFile5(path), 384);
5206
5852
  entries.push({
5207
5853
  path,
5208
5854
  relativePath: relative6(this.workspace.primaryRoot, path),
@@ -5229,7 +5875,7 @@ var CheckpointStore = class {
5229
5875
  entries
5230
5876
  };
5231
5877
  await atomicWrite(
5232
- join8(target, "manifest.json"),
5878
+ join9(target, "manifest.json"),
5233
5879
  `${JSON.stringify(manifest, null, 2)}
5234
5880
  `,
5235
5881
  384
@@ -5242,9 +5888,9 @@ var CheckpointStore = class {
5242
5888
  if (!await this.directoryAvailable()) {
5243
5889
  throw new Error(`Checkpoint not found: ${checkpointId}`);
5244
5890
  }
5245
- const checkpointDirectory = join8(this.directory, sessionId, checkpointId);
5891
+ const checkpointDirectory = join9(this.directory, sessionId, checkpointId);
5246
5892
  await this.assertManagedPath(checkpointDirectory);
5247
- const manifestPath = join8(checkpointDirectory, "manifest.json");
5893
+ const manifestPath = join9(checkpointDirectory, "manifest.json");
5248
5894
  await this.assertManagedFile(manifestPath);
5249
5895
  const raw = await readFile5(manifestPath, "utf8");
5250
5896
  const manifest = manifestSchema.parse(JSON.parse(raw));
@@ -5255,7 +5901,7 @@ var CheckpointStore = class {
5255
5901
  }
5256
5902
  async restore(sessionId, checkpointId) {
5257
5903
  const manifest = await this.load(sessionId, checkpointId);
5258
- const blobDirectory = join8(this.directory, sessionId, checkpointId, "blobs");
5904
+ const blobDirectory = join9(this.directory, sessionId, checkpointId, "blobs");
5259
5905
  await this.assertManagedPath(blobDirectory);
5260
5906
  const actions = [];
5261
5907
  for (const entry of manifest.entries) {
@@ -5270,8 +5916,8 @@ var CheckpointStore = class {
5270
5916
  if (!/^[a-f0-9]{64}\.bin$/.test(entry.blob) || basename6(entry.blob) !== entry.blob) {
5271
5917
  throw new Error(`Checkpoint blob name is invalid for ${entry.relativePath}.`);
5272
5918
  }
5273
- const blobPath = join8(blobDirectory, entry.blob);
5274
- const blobInfo = await lstat9(blobPath);
5919
+ const blobPath = join9(blobDirectory, entry.blob);
5920
+ const blobInfo = await lstat10(blobPath);
5275
5921
  if (!blobInfo.isFile() || blobInfo.isSymbolicLink()) {
5276
5922
  throw new Error(`Checkpoint blob is not a regular file for ${entry.relativePath}.`);
5277
5923
  }
@@ -5312,7 +5958,7 @@ var CheckpointStore = class {
5312
5958
  async list(sessionId) {
5313
5959
  validateIdentifier(sessionId, "session");
5314
5960
  if (!await this.directoryAvailable()) return [];
5315
- const directory = join8(this.directory, sessionId);
5961
+ const directory = join9(this.directory, sessionId);
5316
5962
  let names;
5317
5963
  try {
5318
5964
  names = await readdir2(directory);
@@ -5351,7 +5997,7 @@ var CheckpointStore = class {
5351
5997
  await assertNoSymlinkPath(this.workspace.primaryRoot, this.directory);
5352
5998
  }
5353
5999
  try {
5354
- const info = await lstat9(this.directory);
6000
+ const info = await lstat10(this.directory);
5355
6001
  if (info.isSymbolicLink() || !info.isDirectory()) {
5356
6002
  throw new Error(`Checkpoint storage is not a regular directory: ${this.directory}`);
5357
6003
  }
@@ -5370,7 +6016,7 @@ var CheckpointStore = class {
5370
6016
  await this.assertManagedPath(dirname8(path));
5371
6017
  if (!this.managedDirectory) return;
5372
6018
  try {
5373
- if ((await lstat9(path)).isSymbolicLink()) {
6019
+ if ((await lstat10(path)).isSymbolicLink()) {
5374
6020
  throw new Error(`Checkpoint path cannot contain a symbolic link: ${path}`);
5375
6021
  }
5376
6022
  } catch (error) {
@@ -5380,7 +6026,7 @@ var CheckpointStore = class {
5380
6026
  };
5381
6027
  async function readSnapshot(path) {
5382
6028
  try {
5383
- const info = await lstat9(path);
6029
+ const info = await lstat10(path);
5384
6030
  if (info.isSymbolicLink()) throw new Error(`Cannot restore over a symbolic link: ${path}`);
5385
6031
  if (!info.isFile()) throw new Error(`Cannot restore over a non-file path: ${path}`);
5386
6032
  return { content: await readFile5(path), mode: info.mode };
@@ -5405,174 +6051,6 @@ function validateIdentifier(value, kind) {
5405
6051
  }
5406
6052
  }
5407
6053
 
5408
- // src/utils/process.ts
5409
- import { spawn } from "node:child_process";
5410
- import { constants as constants3 } from "node:fs";
5411
- import { access as access2, lstat as lstat10, realpath as realpath6 } from "node:fs/promises";
5412
- import { delimiter, isAbsolute as isAbsolute5, join as join9, resolve as resolve11 } from "node:path";
5413
- import { StringDecoder } from "node:string_decoder";
5414
- async function resolveExecutableRuntime(command2, cwd, excludedRoots = []) {
5415
- const realRoots = await Promise.all(excludedRoots.map(async (root) => {
5416
- try {
5417
- return await realpath6(root);
5418
- } catch {
5419
- return resolve11(root);
5420
- }
5421
- }));
5422
- const pathEntries = (process.env.PATH ?? process.env.Path ?? "").split(delimiter).filter(Boolean);
5423
- const safeDirectories2 = [];
5424
- let executable2;
5425
- const explicit = isAbsolute5(command2) || command2.includes("/") || command2.includes("\\");
5426
- const explicitPath = explicit ? resolve11(cwd, command2) : void 0;
5427
- for (const entry of pathEntries) {
5428
- let directory;
5429
- try {
5430
- directory = await realpath6(resolve11(cwd, entry));
5431
- if (!(await lstat10(directory)).isDirectory()) continue;
5432
- } catch {
5433
- continue;
5434
- }
5435
- if (realRoots.some((root) => isInside(root, directory))) continue;
5436
- let contaminated = false;
5437
- if (!explicit) {
5438
- for (const name of executableNames(command2)) {
5439
- const candidate = join9(directory, name);
5440
- const resolvedCandidate = await usableExecutable(candidate);
5441
- if (!resolvedCandidate) continue;
5442
- if (realRoots.some((root) => isInside(root, resolvedCandidate))) {
5443
- contaminated = true;
5444
- continue;
5445
- }
5446
- executable2 ??= resolvedCandidate;
5447
- }
5448
- }
5449
- if (!contaminated && !safeDirectories2.includes(directory)) safeDirectories2.push(directory);
5450
- }
5451
- if (explicitPath) executable2 = await usableExecutable(explicitPath);
5452
- if (!executable2) return void 0;
5453
- return { executable: executable2, path: safeDirectories2.join(delimiter) };
5454
- }
5455
- async function usableExecutable(candidate) {
5456
- try {
5457
- await access2(candidate, process.platform === "win32" ? constants3.F_OK : constants3.X_OK);
5458
- const resolvedCandidate = await realpath6(candidate);
5459
- return (await lstat10(resolvedCandidate)).isFile() ? resolvedCandidate : void 0;
5460
- } catch {
5461
- return void 0;
5462
- }
5463
- }
5464
- function executableNames(command2) {
5465
- if (process.platform !== "win32") return [command2];
5466
- const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
5467
- return [command2, ...extensions.map((extension) => `${command2}${extension}`)];
5468
- }
5469
- function runProcess(command2, args, options) {
5470
- return new Promise((resolve24, reject) => {
5471
- const started = Date.now();
5472
- const environment = options.inheritEnv === false ? {} : { ...process.env };
5473
- for (const name of options.unsetEnv ?? []) delete environment[name];
5474
- for (const name of Object.keys(environment)) {
5475
- if (options.unsetEnvPrefixes?.some((prefix) => name.startsWith(prefix))) {
5476
- delete environment[name];
5477
- }
5478
- }
5479
- Object.assign(environment, options.env);
5480
- const child = spawn(command2, args, {
5481
- cwd: options.cwd,
5482
- env: environment,
5483
- stdio: ["pipe", "pipe", "pipe"],
5484
- signal: options.signal
5485
- });
5486
- const maxBytes = options.maxOutputBytes ?? 1e6;
5487
- let stdout = "";
5488
- let stderr = "";
5489
- let stdoutBytes = 0;
5490
- let stderrBytes = 0;
5491
- let stdoutObservedBytes = 0;
5492
- let stderrObservedBytes = 0;
5493
- let timedOut = false;
5494
- let callbackError;
5495
- const stdoutDecoder = new StringDecoder("utf8");
5496
- const stderrDecoder = new StringDecoder("utf8");
5497
- const stdoutCallbackDecoder = new StringDecoder("utf8");
5498
- const stderrCallbackDecoder = new StringDecoder("utf8");
5499
- const append = (decoder, chunk, usedBytes) => {
5500
- if (usedBytes >= maxBytes) return { text: "", usedBytes };
5501
- const selected = chunk.subarray(0, maxBytes - usedBytes);
5502
- return { text: decoder.write(selected), usedBytes: usedBytes + selected.length };
5503
- };
5504
- const notify = (callback, decoder, chunk) => {
5505
- if (!callback || callbackError) return;
5506
- try {
5507
- const decoded = decoder.write(chunk);
5508
- if (decoded) callback(decoded);
5509
- } catch (error) {
5510
- callbackError = error;
5511
- child.kill("SIGTERM");
5512
- }
5513
- };
5514
- child.stdout.on("data", (chunk) => {
5515
- stdoutObservedBytes += chunk.length;
5516
- const appended = append(stdoutDecoder, chunk, stdoutBytes);
5517
- stdout += appended.text;
5518
- stdoutBytes = appended.usedBytes;
5519
- notify(options.onStdout, stdoutCallbackDecoder, chunk);
5520
- });
5521
- child.stderr.on("data", (chunk) => {
5522
- stderrObservedBytes += chunk.length;
5523
- const appended = append(stderrDecoder, chunk, stderrBytes);
5524
- stderr += appended.text;
5525
- stderrBytes = appended.usedBytes;
5526
- notify(options.onStderr, stderrCallbackDecoder, chunk);
5527
- });
5528
- child.on("error", reject);
5529
- const timeoutMs = options.timeoutMs ?? 12e4;
5530
- const timeout = timeoutMs > 0 ? setTimeout(() => {
5531
- timedOut = true;
5532
- child.kill("SIGTERM");
5533
- setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
5534
- }, timeoutMs) : void 0;
5535
- child.on("close", (code) => {
5536
- if (timeout) clearTimeout(timeout);
5537
- const stdoutTail = stdoutDecoder.end();
5538
- const stderrTail = stderrDecoder.end();
5539
- if (Buffer.byteLength(stdout) + Buffer.byteLength(stdoutTail) <= maxBytes) stdout += stdoutTail;
5540
- if (Buffer.byteLength(stderr) + Buffer.byteLength(stderrTail) <= maxBytes) stderr += stderrTail;
5541
- try {
5542
- const stdoutCallbackTail = stdoutCallbackDecoder.end();
5543
- const stderrCallbackTail = stderrCallbackDecoder.end();
5544
- if (stdoutCallbackTail && options.onStdout && !callbackError) options.onStdout(stdoutCallbackTail);
5545
- if (stderrCallbackTail && options.onStderr && !callbackError) options.onStderr(stderrCallbackTail);
5546
- } catch (error) {
5547
- callbackError = error;
5548
- }
5549
- if (callbackError) {
5550
- reject(callbackError);
5551
- return;
5552
- }
5553
- resolve24({
5554
- command: [command2, ...args].join(" "),
5555
- exitCode: code ?? (timedOut ? 124 : 1),
5556
- stdout,
5557
- stderr,
5558
- timedOut,
5559
- durationMs: Date.now() - started,
5560
- stdoutBytes: stdoutObservedBytes,
5561
- stderrBytes: stderrObservedBytes,
5562
- stdoutTruncated: stdoutObservedBytes > stdoutBytes,
5563
- stderrTruncated: stderrObservedBytes > stderrBytes
5564
- });
5565
- });
5566
- if (options.stdin) child.stdin.end(options.stdin);
5567
- else child.stdin.end();
5568
- });
5569
- }
5570
- function runShell(command2, cwd, options = {}) {
5571
- const shell = process.platform === "win32" ? process.env.COMSPEC ?? "cmd.exe" : process.env.SHELL ?? "/bin/sh";
5572
- const args = process.platform === "win32" ? ["/d", "/s", "/c", command2] : ["-lc", command2];
5573
- return runProcess(shell, args, { ...options, cwd });
5574
- }
5575
-
5576
6054
  // src/hooks/runner.ts
5577
6055
  var HookError = class extends Error {
5578
6056
  constructor(message2, result) {
@@ -5642,7 +6120,7 @@ import {
5642
6120
  stat as stat5,
5643
6121
  unlink as unlink3
5644
6122
  } from "node:fs/promises";
5645
- import { basename as basename7, dirname as dirname9, join as join10, resolve as resolve12 } from "node:path";
6123
+ import { basename as basename7, dirname as dirname9, join as join10, resolve as resolve13 } from "node:path";
5646
6124
  import { z as z5 } from "zod";
5647
6125
  var sessionIdSchema = z5.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/);
5648
6126
  var toolCallSchema = z5.object({
@@ -5901,9 +6379,9 @@ var SessionStore = class {
5901
6379
  managedDirectory;
5902
6380
  writes = Promise.resolve();
5903
6381
  constructor(workspace, directory) {
5904
- this.workspace = resolve12(workspace);
6382
+ this.workspace = resolve13(workspace);
5905
6383
  this.managedDirectory = directory === void 0;
5906
- this.directory = directory ? resolve12(directory) : join10(resolveProjectNamespaceSync(this.workspace).active, "sessions");
6384
+ this.directory = directory ? resolve13(directory) : join10(resolveProjectNamespaceSync(this.workspace).active, "sessions");
5907
6385
  }
5908
6386
  async create(options) {
5909
6387
  const session = createSession({ ...options, workspace: this.workspace });
@@ -5912,7 +6390,7 @@ var SessionStore = class {
5912
6390
  }
5913
6391
  async save(session) {
5914
6392
  validateId(session.id);
5915
- if (resolve12(session.workspace) !== this.workspace) {
6393
+ if (resolve13(session.workspace) !== this.workspace) {
5916
6394
  throw new Error("Session workspace does not match this store.");
5917
6395
  }
5918
6396
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -5951,7 +6429,7 @@ var SessionStore = class {
5951
6429
  const summaries = await Promise.all(
5952
6430
  entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => {
5953
6431
  const session = await tryReadSession(join10(this.directory, entry.name));
5954
- if (!session || resolve12(session.workspace) !== this.workspace) return;
6432
+ if (!session || resolve13(session.workspace) !== this.workspace) return;
5955
6433
  return toSummary(session);
5956
6434
  })
5957
6435
  );
@@ -6022,7 +6500,7 @@ var SessionStore = class {
6022
6500
  for (const candidate of candidates) {
6023
6501
  await this.assertManagedFile(candidate.path);
6024
6502
  const session = await tryReadSession(candidate.path);
6025
- if (session?.id === id && resolve12(session.workspace) === this.workspace) return session;
6503
+ if (session?.id === id && resolve13(session.workspace) === this.workspace) return session;
6026
6504
  }
6027
6505
  return void 0;
6028
6506
  }
@@ -6036,7 +6514,7 @@ var SessionStore = class {
6036
6514
  }
6037
6515
  }
6038
6516
  assertWorkspace(session) {
6039
- if (resolve12(session.workspace) !== this.workspace) {
6517
+ if (resolve13(session.workspace) !== this.workspace) {
6040
6518
  throw new Error("Stored session belongs to a different workspace.");
6041
6519
  }
6042
6520
  return session;
@@ -6095,7 +6573,7 @@ function createSession(options) {
6095
6573
  return {
6096
6574
  id,
6097
6575
  title: cleanTitle(options.title ?? "New session"),
6098
- workspace: resolve12(options.workspace),
6576
+ workspace: resolve13(options.workspace),
6099
6577
  createdAt: now,
6100
6578
  updatedAt: now,
6101
6579
  model: options.model,
@@ -6150,9 +6628,9 @@ async function exists2(path) {
6150
6628
  }
6151
6629
 
6152
6630
  // src/session/tool-artifacts.ts
6153
- import { createHash as createHash8 } from "node:crypto";
6631
+ import { createHash as createHash9 } from "node:crypto";
6154
6632
  import { lstat as lstat12, readFile as readFile7, readdir as readdir4, rm as rm2 } from "node:fs/promises";
6155
- import { join as join11, resolve as resolve13 } from "node:path";
6633
+ import { join as join11, resolve as resolve14 } from "node:path";
6156
6634
  import { z as z6 } from "zod";
6157
6635
  var MAX_ARTIFACT_BYTES = 5 * 1024 * 1024;
6158
6636
  var MAX_TOTAL_BYTES = 20 * 1024 * 1024;
@@ -6182,9 +6660,9 @@ var ToolArtifactStore = class {
6182
6660
  retentionMs;
6183
6661
  writes = Promise.resolve();
6184
6662
  constructor(workspace, options = {}) {
6185
- this.workspace = resolve13(workspace);
6663
+ this.workspace = resolve14(workspace);
6186
6664
  this.managedDirectory = options.directory === void 0;
6187
- this.directory = options.directory ? resolve13(options.directory) : join11(resolveProjectNamespaceSync(this.workspace).active, "tool-artifacts");
6665
+ this.directory = options.directory ? resolve14(options.directory) : join11(resolveProjectNamespaceSync(this.workspace).active, "tool-artifacts");
6188
6666
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
6189
6667
  this.maxArtifactBytes = options.maxArtifactBytes ?? MAX_ARTIFACT_BYTES;
6190
6668
  this.maxTotalBytes = options.maxTotalBytes ?? MAX_TOTAL_BYTES;
@@ -6241,7 +6719,7 @@ var ToolArtifactStore = class {
6241
6719
  }
6242
6720
  if (retainedBytes + storageBytes > this.maxTotalBytes) return { stored: false, reason: "total_limit" };
6243
6721
  const path = this.pathFor(sessionId, toolCallId);
6244
- await ensureWorkspaceStorageDirectory(this.workspace, resolve13(path, ".."), {
6722
+ await ensureWorkspaceStorageDirectory(this.workspace, resolve14(path, ".."), {
6245
6723
  requireActiveNamespace: this.managedDirectory
6246
6724
  });
6247
6725
  await atomicWrite(path, `${JSON.stringify(artifact)}
@@ -6392,7 +6870,7 @@ var ToolArtifactStore = class {
6392
6870
  }
6393
6871
  }
6394
6872
  async assertRegularFile(path) {
6395
- await assertNoSymlinkPath(this.workspace, resolve13(path, ".."));
6873
+ await assertNoSymlinkPath(this.workspace, resolve14(path, ".."));
6396
6874
  try {
6397
6875
  const info = await lstat12(path);
6398
6876
  if (info.isSymbolicLink() || !info.isFile()) {
@@ -6429,7 +6907,7 @@ function reference(artifact) {
6429
6907
  };
6430
6908
  }
6431
6909
  function hash2(value) {
6432
- return createHash8("sha256").update(value).digest("hex");
6910
+ return createHash9("sha256").update(value).digest("hex");
6433
6911
  }
6434
6912
  function storedBytes(artifact) {
6435
6913
  return Buffer.byteLength(JSON.stringify(artifact)) + 1;
@@ -6976,48 +7454,6 @@ var knownCommands = /* @__PURE__ */ new Set([
6976
7454
  ...networkCommands,
6977
7455
  ...workspaceMutationCommands
6978
7456
  ]);
6979
- var unsafeInheritedGitEnvironment = [
6980
- "GIT_DIR",
6981
- "GIT_WORK_TREE",
6982
- "GIT_COMMON_DIR",
6983
- "GIT_INDEX_FILE",
6984
- "GIT_OBJECT_DIRECTORY",
6985
- "GIT_ALTERNATE_OBJECT_DIRECTORIES",
6986
- "GIT_NAMESPACE",
6987
- "GIT_CEILING_DIRECTORIES",
6988
- "GIT_DISCOVERY_ACROSS_FILESYSTEM",
6989
- "GIT_EXTERNAL_DIFF",
6990
- "GIT_DIFF_OPTS",
6991
- "GIT_SSH",
6992
- "GIT_SSH_COMMAND",
6993
- "GIT_PROXY_COMMAND",
6994
- "GIT_ASKPASS",
6995
- "SSH_ASKPASS",
6996
- "GIT_EXEC_PATH",
6997
- "GIT_TEMPLATE_DIR",
6998
- "GIT_CONFIG",
6999
- "GIT_CONFIG_PARAMETERS",
7000
- "GIT_QUARANTINE_PATH",
7001
- "Path"
7002
- ];
7003
- var isolatedGitEnvironment = {
7004
- GIT_TERMINAL_PROMPT: "0",
7005
- GIT_CONFIG_NOSYSTEM: "1",
7006
- GIT_CONFIG_GLOBAL: "/dev/null",
7007
- GIT_CONFIG_SYSTEM: "/dev/null",
7008
- GIT_PAGER: "cat",
7009
- GIT_EDITOR: "true",
7010
- GIT_SEQUENCE_EDITOR: "true",
7011
- GIT_CONFIG_COUNT: "4",
7012
- GIT_CONFIG_KEY_0: "core.hooksPath",
7013
- GIT_CONFIG_VALUE_0: "/dev/null",
7014
- GIT_CONFIG_KEY_1: "core.fsmonitor",
7015
- GIT_CONFIG_VALUE_1: "false",
7016
- GIT_CONFIG_KEY_2: "credential.helper",
7017
- GIT_CONFIG_VALUE_2: "",
7018
- GIT_CONFIG_KEY_3: "protocol.ext.allow",
7019
- GIT_CONFIG_VALUE_3: "never"
7020
- };
7021
7457
  var diffCommands = /* @__PURE__ */ new Set(["diff", "log", "show", "whatchanged"]);
7022
7458
  var gitTool = {
7023
7459
  definition: {
@@ -7065,13 +7501,9 @@ var gitTool = {
7065
7501
  }
7066
7502
  const runtime = await resolveExecutableRuntime("git", cwd, context.workspace.roots);
7067
7503
  if (!runtime) throw new Error("Git executable was not found outside the configured workspace roots.");
7068
- const status = await runProcess(runtime.executable, ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
7069
- cwd,
7504
+ const status = await runIsolatedGit(runtime, ["status", "--porcelain=v1", "-z", "--untracked-files=all"], cwd, {
7070
7505
  timeoutMs: 15e3,
7071
- maxOutputBytes: 2e6,
7072
- env: { ...isolatedGitEnvironment, PATH: runtime.path },
7073
- unsetEnv: unsafeInheritedGitEnvironment,
7074
- unsetEnvPrefixes: ["GIT_"]
7506
+ maxOutputBytes: 2e6
7075
7507
  });
7076
7508
  if (status.exitCode === 0) {
7077
7509
  for (const record of status.stdout.split("\0")) {
@@ -7098,15 +7530,11 @@ var gitTool = {
7098
7530
  const runtime = await resolveExecutableRuntime("git", cwd, context.workspace.roots);
7099
7531
  if (!runtime) throw new Error("Git executable was not found outside the configured workspace roots.");
7100
7532
  const before = worktreeTrackingCommands.has(command2) ? await captureGitState(runtime, cwd) : void 0;
7101
- const result = await runProcess(runtime.executable, protectedGitArguments(input2.args, command2), {
7102
- cwd,
7533
+ const result = await runIsolatedGit(runtime, protectedGitArguments(input2.args, command2), cwd, {
7103
7534
  timeoutMs: input2.timeout_ms ?? 12e4,
7104
7535
  maxOutputBytes: 2e6,
7105
7536
  ...input2.stdin !== void 0 ? { stdin: input2.stdin } : {},
7106
- ...context.signal ? { signal: context.signal } : {},
7107
- env: { ...isolatedGitEnvironment, PATH: runtime.path },
7108
- unsetEnv: unsafeInheritedGitEnvironment,
7109
- unsetEnvPrefixes: ["GIT_"]
7537
+ ...context.signal ? { signal: context.signal } : {}
7110
7538
  });
7111
7539
  const changedFiles = before ? await collectGitChanges(before, await captureGitState(runtime, cwd), runtime, cwd, context) : [];
7112
7540
  return {
@@ -7184,18 +7612,6 @@ function parsePorcelainStatus(output2) {
7184
7612
  }
7185
7613
  return status;
7186
7614
  }
7187
- function runIsolatedGit(runtime, args, cwd, options = {}) {
7188
- return runProcess(runtime.executable, args, {
7189
- cwd,
7190
- timeoutMs: options.timeoutMs ?? 15e3,
7191
- maxOutputBytes: options.maxOutputBytes ?? 2e6,
7192
- env: { ...isolatedGitEnvironment, PATH: runtime.path },
7193
- unsetEnv: unsafeInheritedGitEnvironment,
7194
- unsetEnvPrefixes: ["GIT_"],
7195
- ...options.stdin !== void 0 ? { stdin: options.stdin } : {},
7196
- ...options.signal ? { signal: options.signal } : {}
7197
- });
7198
- }
7199
7615
  function validateGitArguments(args) {
7200
7616
  for (const argument of args) {
7201
7617
  if (argument.includes("\0") || argument.includes("\n") || argument.includes("\r")) {
@@ -7331,7 +7747,7 @@ function positionalArguments(args, command2) {
7331
7747
 
7332
7748
  // src/tools/list.ts
7333
7749
  import { lstat as lstat14 } from "node:fs/promises";
7334
- import { relative as relative7, resolve as resolve14 } from "node:path";
7750
+ import { relative as relative7, resolve as resolve15 } from "node:path";
7335
7751
  import fg3 from "fast-glob";
7336
7752
  import { z as z9 } from "zod";
7337
7753
  var inputSchema4 = z9.object({
@@ -7389,7 +7805,7 @@ var listFilesTool = {
7389
7805
  for (const path of selected) {
7390
7806
  let safePath;
7391
7807
  try {
7392
- safePath = await context.workspace.resolvePath(resolve14(directory, path), { expect: "any" });
7808
+ safePath = await context.workspace.resolvePath(resolve15(directory, path), { expect: "any" });
7393
7809
  } catch {
7394
7810
  continue;
7395
7811
  }
@@ -7599,7 +8015,7 @@ function assertToolName(name) {
7599
8015
 
7600
8016
  // src/tools/search.ts
7601
8017
  import { readFile as readFile10, stat as stat8 } from "node:fs/promises";
7602
- import { resolve as resolve15 } from "node:path";
8018
+ import { resolve as resolve16 } from "node:path";
7603
8019
  import fg4 from "fast-glob";
7604
8020
  import { z as z12 } from "zod";
7605
8021
  var inputSchema7 = z12.object({
@@ -7669,7 +8085,7 @@ var searchCodeTool = {
7669
8085
  if (results.length >= maxResults) break;
7670
8086
  let path;
7671
8087
  try {
7672
- path = await context.workspace.resolvePath(resolve15(directory, file), { expect: "file" });
8088
+ path = await context.workspace.resolvePath(resolve16(directory, file), { expect: "file" });
7673
8089
  } catch {
7674
8090
  skipped += 1;
7675
8091
  continue;
@@ -7751,7 +8167,7 @@ ${hit.content}`
7751
8167
  }
7752
8168
 
7753
8169
  // src/tools/shell.ts
7754
- import { createHash as createHash9 } from "node:crypto";
8170
+ import { createHash as createHash10 } from "node:crypto";
7755
8171
  import { lstat as lstat15, readFile as readFile11, readdir as readdir5 } from "node:fs/promises";
7756
8172
  import { join as join13 } from "node:path";
7757
8173
  import { z as z13 } from "zod";
@@ -7965,7 +8381,7 @@ async function captureWorkspaceSnapshot(roots) {
7965
8381
  files.set(path, {
7966
8382
  size: info.size,
7967
8383
  mtimeMs: info.mtimeMs,
7968
- hash: createHash9("sha256").update(content).digest("hex")
8384
+ hash: createHash10("sha256").update(content).digest("hex")
7969
8385
  });
7970
8386
  } catch (error) {
7971
8387
  if (error.code !== "ENOENT") complete = false;
@@ -8168,15 +8584,15 @@ function compact(value, limit) {
8168
8584
  }
8169
8585
 
8170
8586
  // src/agent/completion-gate.ts
8171
- import { createHash as createHash11 } from "node:crypto";
8587
+ import { createHash as createHash12 } from "node:crypto";
8172
8588
 
8173
8589
  // src/tools/permissions.ts
8174
- import { createHash as createHash10, createHmac, randomBytes } from "node:crypto";
8590
+ import { createHash as createHash11, createHmac, randomBytes } from "node:crypto";
8175
8591
  var permissionScopeSecret = randomBytes(32);
8176
8592
  function permissionKey(call, category) {
8177
8593
  const categoryScope = category === "network" ? `\0network-request:${scopeFingerprint(normalizeRequestState(call.arguments))}` : "";
8178
- const digest = createHash10("sha256").update(`${category}\0${call.name}\0${permissionTarget(call)}${categoryScope}`).digest("hex").slice(0, 24);
8179
- return `${category}:${call.name}:${digest}`;
8594
+ const digest2 = createHash11("sha256").update(`${category}\0${call.name}\0${permissionTarget(call)}${categoryScope}`).digest("hex").slice(0, 24);
8595
+ return `${category}:${call.name}:${digest2}`;
8180
8596
  }
8181
8597
  function permissionTarget(call) {
8182
8598
  const command2 = commandForCall(call);
@@ -8369,7 +8785,7 @@ function captureVerification(call, result, changeSequence, configuredCommands) {
8369
8785
  kind,
8370
8786
  ok: result.ok,
8371
8787
  changeSequence,
8372
- commandKey: createHash11("sha256").update(normalized).digest("hex")
8788
+ commandKey: createHash12("sha256").update(normalized).digest("hex")
8373
8789
  };
8374
8790
  }
8375
8791
  function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutationTracking = "complete", taskContract, audit = [], duplication) {
@@ -8553,7 +8969,7 @@ function verificationRequirementMet(requirement, checks) {
8553
8969
  const normalized = normalizeCommand2(requirement);
8554
8970
  const broad = normalized.match(/^Record at least one successful (.+) after the final mutation\.?$/iu);
8555
8971
  if (broad) return checks.length > 0;
8556
- const commandKey = createHash11("sha256").update(normalized).digest("hex");
8972
+ const commandKey = createHash12("sha256").update(normalized).digest("hex");
8557
8973
  return checks.some((item) => item.commandKey === commandKey);
8558
8974
  }
8559
8975
  function acceptanceUnresolved(acceptance) {
@@ -9147,7 +9563,7 @@ function escapeAttribute3(value) {
9147
9563
  }
9148
9564
 
9149
9565
  // src/agent/tool-recovery.ts
9150
- import { createHash as createHash12 } from "node:crypto";
9566
+ import { createHash as createHash13 } from "node:crypto";
9151
9567
  var RETRY_BUDGET = {
9152
9568
  schema_input: 3,
9153
9569
  unknown_tool: 2,
@@ -9219,7 +9635,7 @@ var ToolRecoveryController = class {
9219
9635
  recordEvidence(call, result) {
9220
9636
  if (call.name !== "search_code" || !result.ok) return void 0;
9221
9637
  const callKey = callSignature(call);
9222
- const fingerprint = createHash12("sha256").update(`${result.content}\0${stableJson(result.metadata ?? {})}`).digest("hex");
9638
+ const fingerprint = createHash13("sha256").update(`${result.content}\0${stableJson(result.metadata ?? {})}`).digest("hex");
9223
9639
  const count = typeof result.metadata?.count === "number" ? result.metadata.count : void 0;
9224
9640
  const current = this.evidence.get(callKey);
9225
9641
  const repeated = current?.fingerprint === fingerprint;
@@ -9229,7 +9645,7 @@ var ToolRecoveryController = class {
9229
9645
  status: count === 0 ? "empty" : repeated ? "repeated" : "new",
9230
9646
  repeatCount: repeats,
9231
9647
  stop: repeats >= 2,
9232
- signature: createHash12("sha256").update(`${callKey}\0${fingerprint}`).digest("hex")
9648
+ signature: createHash13("sha256").update(`${callKey}\0${fingerprint}`).digest("hex")
9233
9649
  };
9234
9650
  }
9235
9651
  receipt(call, failureClass, attempt, circuitOpen) {
@@ -9265,10 +9681,10 @@ function isRetryable(failureClass) {
9265
9681
  return RETRY_BUDGET[failureClass] > 0;
9266
9682
  }
9267
9683
  function callSignature(call) {
9268
- return createHash12("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}`).digest("hex");
9684
+ return createHash13("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}`).digest("hex");
9269
9685
  }
9270
9686
  function failureSignature(call, failureClass) {
9271
- return createHash12("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}\0${failureClass}`).digest("hex");
9687
+ return createHash13("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}\0${failureClass}`).digest("hex");
9272
9688
  }
9273
9689
  function redact(value, key = "") {
9274
9690
  if (/api[_-]?key|authorization|cookie|password|secret|token/i.test(key)) return "[redacted]";
@@ -9616,7 +10032,7 @@ function escapeAttribute5(value) {
9616
10032
  }
9617
10033
 
9618
10034
  // src/agent/reuse-gate.ts
9619
- import { createHash as createHash13 } from "node:crypto";
10035
+ import { createHash as createHash14 } from "node:crypto";
9620
10036
  import { readFile as readFile14, stat as stat10 } from "node:fs/promises";
9621
10037
  import { basename as basename8, extname as extname3 } from "node:path";
9622
10038
  var MAX_CANDIDATES = 5;
@@ -9719,7 +10135,7 @@ async function evaluateReuseGate(input2) {
9719
10135
  candidates.push({
9720
10136
  path: hit.path,
9721
10137
  ...hit.symbol ? { symbol: hit.symbol.slice(0, 160) } : {},
9722
- score: roundScore(hit.score),
10138
+ score: roundScore2(hit.score),
9723
10139
  read
9724
10140
  });
9725
10141
  }
@@ -9825,14 +10241,14 @@ function unresolvedReceipt(input2, queryHash, targetPaths, trigger, detail) {
9825
10241
  };
9826
10242
  }
9827
10243
  function hash3(value) {
9828
- return createHash13("sha256").update(value).digest("hex");
10244
+ return createHash14("sha256").update(value).digest("hex");
9829
10245
  }
9830
- function roundScore(value) {
10246
+ function roundScore2(value) {
9831
10247
  return Number.isFinite(value) ? Math.round(value * 1e3) / 1e3 : 0;
9832
10248
  }
9833
10249
 
9834
10250
  // src/agent/duplication-audit.ts
9835
- import { createHash as createHash14 } from "node:crypto";
10251
+ import { createHash as createHash15 } from "node:crypto";
9836
10252
  import { readFile as readFile15 } from "node:fs/promises";
9837
10253
  var DEFAULT_NEAR_CLONE_THRESHOLD = 0.55;
9838
10254
  var MAX_MATCHES = 8;
@@ -10007,7 +10423,7 @@ function round(value) {
10007
10423
  return Math.round(value * 1e3) / 1e3;
10008
10424
  }
10009
10425
  function matchId(generation, changeSequence, changed, candidate, similarity) {
10010
- return createHash14("sha256").update([
10426
+ return createHash15("sha256").update([
10011
10427
  generation,
10012
10428
  String(changeSequence),
10013
10429
  changed.path,
@@ -11425,9 +11841,9 @@ function numeric(...values) {
11425
11841
  }
11426
11842
 
11427
11843
  // src/agent/team-store.ts
11428
- import { createHash as createHash15, randomUUID as randomUUID13 } from "node:crypto";
11844
+ import { createHash as createHash16, randomUUID as randomUUID13 } from "node:crypto";
11429
11845
  import { lstat as lstat18, readFile as readFile17, readdir as readdir7, rm as rm3 } from "node:fs/promises";
11430
- import { join as join16, resolve as resolve16 } from "node:path";
11846
+ import { join as join16, resolve as resolve17 } from "node:path";
11431
11847
  import { z as z18 } from "zod";
11432
11848
  var runIdSchema = z18.string().uuid();
11433
11849
  var hashSchema = z18.string().regex(/^[a-f0-9]{64}$/u);
@@ -11511,9 +11927,9 @@ var TeamRunStore = class {
11511
11927
  managedDirectory;
11512
11928
  writes = Promise.resolve();
11513
11929
  constructor(workspace, directory) {
11514
- this.workspace = resolve16(workspace);
11930
+ this.workspace = resolve17(workspace);
11515
11931
  this.managedDirectory = directory === void 0;
11516
- this.directory = directory ? resolve16(directory) : join16(resolveProjectNamespaceSync(this.workspace).active, "team-runs");
11932
+ this.directory = directory ? resolve17(directory) : join16(resolveProjectNamespaceSync(this.workspace).active, "team-runs");
11517
11933
  }
11518
11934
  async create(input2) {
11519
11935
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -11600,7 +12016,7 @@ var TeamRunStore = class {
11600
12016
  const path = join16(this.runDirectory(runId), "manifest.json");
11601
12017
  await this.assertRegularFile(path);
11602
12018
  const manifest = manifestSchema2.parse(JSON.parse(await readFile17(path, "utf8")));
11603
- if (manifest.id !== runId || resolve16(manifest.workspace) !== this.workspace) {
12019
+ if (manifest.id !== runId || resolve17(manifest.workspace) !== this.workspace) {
11604
12020
  throw new Error("Team run manifest identity does not match its location.");
11605
12021
  }
11606
12022
  if (verify) {
@@ -11683,7 +12099,7 @@ var TeamRunStore = class {
11683
12099
  async writeArtifact(runId, content, truncate = true) {
11684
12100
  const data = boundedArtifactText(content, 5e5, truncate);
11685
12101
  const bytes = Buffer.byteLength(data);
11686
- const sha256 = createHash15("sha256").update(data).digest("hex");
12102
+ const sha256 = createHash16("sha256").update(data).digest("hex");
11687
12103
  const directory = join16(this.runDirectory(runId), "blobs");
11688
12104
  await ensureWorkspaceStorageDirectory(this.workspace, directory, {
11689
12105
  requireActiveNamespace: this.managedDirectory
@@ -11692,7 +12108,7 @@ var TeamRunStore = class {
11692
12108
  try {
11693
12109
  await this.assertRegularFile(path);
11694
12110
  const existing = await readFile17(path);
11695
- if (createHash15("sha256").update(existing).digest("hex") !== sha256) {
12111
+ if (createHash16("sha256").update(existing).digest("hex") !== sha256) {
11696
12112
  throw new Error(`Team artifact hash collision or corruption: ${sha256}`);
11697
12113
  }
11698
12114
  } catch (error) {
@@ -11713,7 +12129,7 @@ var TeamRunStore = class {
11713
12129
  const path = this.artifactPath(runId, artifact.sha256);
11714
12130
  await this.assertRegularFile(path);
11715
12131
  const data = await readFile17(path);
11716
- const hash4 = createHash15("sha256").update(data).digest("hex");
12132
+ const hash4 = createHash16("sha256").update(data).digest("hex");
11717
12133
  if (hash4 !== artifact.sha256 || data.byteLength !== artifact.bytes) {
11718
12134
  throw new Error(`Team artifact integrity check failed: ${artifact.sha256}`);
11719
12135
  }
@@ -11731,7 +12147,7 @@ var TeamRunStore = class {
11731
12147
  if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Team run storage is not a regular directory.");
11732
12148
  }
11733
12149
  async assertRegularFile(path) {
11734
- await assertNoSymlinkPath(this.workspace, resolve16(path, ".."));
12150
+ await assertNoSymlinkPath(this.workspace, resolve17(path, ".."));
11735
12151
  const info = await lstat18(path);
11736
12152
  if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Team run file is not a regular file: ${path}`);
11737
12153
  }
@@ -11776,10 +12192,10 @@ function resolveAgentModelRoute(team, parent, profile) {
11776
12192
  }
11777
12193
 
11778
12194
  // src/agent/writer-lane.ts
11779
- import { createHash as createHash16 } from "node:crypto";
12195
+ import { createHash as createHash17 } from "node:crypto";
11780
12196
  import { lstat as lstat19, mkdtemp, realpath as realpath7, rm as rm4 } from "node:fs/promises";
11781
12197
  import { tmpdir as tmpdir2 } from "node:os";
11782
- import { isAbsolute as isAbsolute6, join as join17, resolve as resolve17 } from "node:path";
12198
+ import { isAbsolute as isAbsolute6, join as join17, resolve as resolve18 } from "node:path";
11783
12199
  var WriterLaneApplyError = class extends Error {
11784
12200
  constructor(message2, attempted, cause) {
11785
12201
  super(message2, cause === void 0 ? void 0 : { cause });
@@ -11792,8 +12208,8 @@ var WriterLane = class {
11792
12208
  workspace;
11793
12209
  constructor(workspace, workspaceRoots) {
11794
12210
  this.workspace = new WorkspaceAccess([
11795
- resolve17(workspace),
11796
- ...workspaceRoots.map((root) => resolve17(root))
12211
+ resolve18(workspace),
12212
+ ...workspaceRoots.map((root) => resolve18(root))
11797
12213
  ]);
11798
12214
  }
11799
12215
  async createDraft(maxPatchBytes, operation, signal) {
@@ -11874,7 +12290,7 @@ var WriterLane = class {
11874
12290
  return {
11875
12291
  baseCommit,
11876
12292
  patch,
11877
- patchSha256: createHash16("sha256").update(patch).digest("hex"),
12293
+ patchSha256: createHash17("sha256").update(patch).digest("hex"),
11878
12294
  files,
11879
12295
  worktreeCleaned,
11880
12296
  value
@@ -12016,7 +12432,7 @@ var WriterLane = class {
12016
12432
  if (topLevel.exitCode !== 0 || !topLevel.stdout.trim()) {
12017
12433
  throw new Error("The primary workspace must be a Git repository root for writer lanes.");
12018
12434
  }
12019
- const discoveredRoot = resolve17(topLevel.stdout.trim());
12435
+ const discoveredRoot = resolve18(topLevel.stdout.trim());
12020
12436
  if (await realpath7(discoveredRoot) !== await realpath7(root)) {
12021
12437
  throw new Error("The primary workspace must be the Git repository root for writer lanes.");
12022
12438
  }
@@ -12025,7 +12441,7 @@ var WriterLane = class {
12025
12441
  if (common.exitCode !== 0 || !common.stdout.trim()) {
12026
12442
  throw new Error(`Unable to resolve the Git common directory: ${processDetail(common)}`);
12027
12443
  }
12028
- const commonDirectory = await realpath7(isAbsolute6(common.stdout.trim()) ? common.stdout.trim() : resolve17(repositoryRoot, common.stdout.trim()));
12444
+ const commonDirectory = await realpath7(isAbsolute6(common.stdout.trim()) ? common.stdout.trim() : resolve18(repositoryRoot, common.stdout.trim()));
12029
12445
  return { root: repositoryRoot, commonDirectory, runtime };
12030
12446
  }
12031
12447
  async head(repository) {
@@ -12037,7 +12453,7 @@ var WriterLane = class {
12037
12453
  return value;
12038
12454
  }
12039
12455
  async cleanupWorktree(repository, worktree, added) {
12040
- const physicalWorktree = await realpath7(worktree).catch(() => resolve17(worktree));
12456
+ const physicalWorktree = await realpath7(worktree).catch(() => resolve18(worktree));
12041
12457
  if (added) {
12042
12458
  await runIsolatedGit(repository.runtime, [
12043
12459
  "worktree",
@@ -13963,7 +14379,7 @@ function formatResultDetail(result, paint = chalk2, glyphs = resolveCliGlyphs())
13963
14379
  }
13964
14380
 
13965
14381
  // src/cli/namespace-leases.ts
13966
- import { resolve as resolve18 } from "node:path";
14382
+ import { resolve as resolve19 } from "node:path";
13967
14383
  function cliNamespaceLeaseScopes(actionCommand) {
13968
14384
  const names = commandNames(actionCommand);
13969
14385
  const topLevel = names[1];
@@ -13985,7 +14401,7 @@ async function acquireCliNamespaceLeases(actionCommand) {
13985
14401
  const scopes = cliNamespaceLeaseScopes(actionCommand);
13986
14402
  const localOptions = actionCommand.opts();
13987
14403
  const globalOptions = actionCommand.optsWithGlobals();
13988
- const workspace = resolve18(localOptions.workspace ?? globalOptions.workspace ?? process.cwd());
14404
+ const workspace = resolve19(localOptions.workspace ?? globalOptions.workspace ?? process.cwd());
13989
14405
  const targets = scopes.map((scope) => scope === "project" ? projectNamespacePaths(workspace).canonical : homeNamespacePaths().canonical);
13990
14406
  const leases = [];
13991
14407
  try {
@@ -14237,7 +14653,7 @@ function graphemes(value) {
14237
14653
 
14238
14654
  // src/ui/theme.ts
14239
14655
  import { lstat as lstat20, readdir as readdir8, readFile as readFile18 } from "node:fs/promises";
14240
- import { basename as basename10, join as join19, resolve as resolve19 } from "node:path";
14656
+ import { basename as basename10, join as join19, resolve as resolve20 } from "node:path";
14241
14657
  import React, { createContext, useContext } from "react";
14242
14658
  function defineTheme(seed) {
14243
14659
  return {
@@ -14359,7 +14775,7 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
14359
14775
  userThemeNames.clear();
14360
14776
  const loaded = [];
14361
14777
  const errors = [];
14362
- const resolvedDirectory = resolve19(directory);
14778
+ const resolvedDirectory = resolve20(directory);
14363
14779
  let entries;
14364
14780
  try {
14365
14781
  entries = await readdir8(resolvedDirectory, { encoding: "utf8" });
@@ -16823,7 +17239,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
16823
17239
  return () => clearInterval(timer);
16824
17240
  }, [busy]);
16825
17241
  const requestPermission = useCallback((call, category) => {
16826
- return new Promise((resolve24) => setPermission({ call, category, resolve: resolve24 }));
17242
+ return new Promise((resolve25) => setPermission({ call, category, resolve: resolve25 }));
16827
17243
  }, []);
16828
17244
  const onEvent = useCallback((event) => {
16829
17245
  switch (event.type) {
@@ -17659,8 +18075,8 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
17659
18075
  }, [append]);
17660
18076
  function settlePermission(grant, stop = false) {
17661
18077
  if (!permission) return;
17662
- const { call, category, resolve: resolve24 } = permission;
17663
- resolve24(grant);
18078
+ const { call, category, resolve: resolve25 } = permission;
18079
+ resolve25(grant);
17664
18080
  setPermission(void 0);
17665
18081
  if (grant === "session") {
17666
18082
  append({
@@ -18995,7 +19411,7 @@ async function runFirstRunOnboarding(initialConfig, options = {}) {
18995
19411
  }
18996
19412
 
18997
19413
  // src/runtime/extensions.ts
18998
- import { resolve as resolve22 } from "node:path";
19414
+ import { resolve as resolve23 } from "node:path";
18999
19415
 
19000
19416
  // src/mcp/manager.ts
19001
19417
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -19004,7 +19420,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
19004
19420
  import stripAnsi5 from "strip-ansi";
19005
19421
 
19006
19422
  // src/mcp/tool.ts
19007
- import { createHash as createHash17 } from "node:crypto";
19423
+ import { createHash as createHash18 } from "node:crypto";
19008
19424
  import stripAnsi4 from "strip-ansi";
19009
19425
  var MAX_ARGUMENT_BYTES = 256e3;
19010
19426
  var MAX_DESCRIPTION_LENGTH = 4e3;
@@ -19175,7 +19591,7 @@ function fitToolName(name, identity2) {
19175
19591
  return `${name.slice(0, 64 - suffix.length).replace(/_+$/g, "")}${suffix}`;
19176
19592
  }
19177
19593
  function shortHash(value) {
19178
- return createHash17("sha256").update(value).digest("hex").slice(0, 8);
19594
+ return createHash18("sha256").update(value).digest("hex").slice(0, 8);
19179
19595
  }
19180
19596
  function sanitizeOutputText(value) {
19181
19597
  return stripAnsi4(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
@@ -19208,7 +19624,7 @@ function isRecord2(value) {
19208
19624
 
19209
19625
  // src/mcp/validation.ts
19210
19626
  import { stat as stat11, realpath as realpath8 } from "node:fs/promises";
19211
- import { isAbsolute as isAbsolute7, resolve as resolve20 } from "node:path";
19627
+ import { isAbsolute as isAbsolute7, resolve as resolve21 } from "node:path";
19212
19628
  var SERVER_NAME = /^[a-z][a-z0-9_-]{0,63}$/;
19213
19629
  var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
19214
19630
  var HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
@@ -19280,9 +19696,9 @@ async function validateCwd(configured, options) {
19280
19696
  if (configured !== void 0 && (configured.length > 4e3 || CONTROL_CHARACTERS.test(configured))) {
19281
19697
  throw new Error("MCP working directory is invalid or too long");
19282
19698
  }
19283
- const defaultCwd = resolve20(options.cwd ?? process.cwd());
19284
- const candidate = configured ? isAbsolute7(configured) ? resolve20(configured) : resolve20(defaultCwd, configured) : defaultCwd;
19285
- const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) => resolve20(root)) : [defaultCwd];
19699
+ const defaultCwd = resolve21(options.cwd ?? process.cwd());
19700
+ const candidate = configured ? isAbsolute7(configured) ? resolve21(configured) : resolve21(defaultCwd, configured) : defaultCwd;
19701
+ const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) => resolve21(root)) : [defaultCwd];
19286
19702
  const resolvedCandidate = await realpath8(candidate).catch(() => {
19287
19703
  throw new Error(`MCP working directory does not exist: ${candidate}`);
19288
19704
  });
@@ -19936,7 +20352,7 @@ function scopeKey(scope, context) {
19936
20352
  // src/skills/catalog.ts
19937
20353
  import { lstat as lstat21, readFile as readFile20, readdir as readdir9, realpath as realpath9 } from "node:fs/promises";
19938
20354
  import { homedir as homedir4 } from "node:os";
19939
- import { basename as basename12, join as join21, resolve as resolve21 } from "node:path";
20355
+ import { basename as basename12, join as join21, resolve as resolve22 } from "node:path";
19940
20356
  import { parse as parseYaml3 } from "yaml";
19941
20357
  var SkillCatalog = class {
19942
20358
  constructor(workspace, config) {
@@ -20005,14 +20421,14 @@ ${skill.content}
20005
20421
  }
20006
20422
  function discoveryLocations(workspace, configured) {
20007
20423
  const home = homedir4();
20008
- const workspaceRoot = resolve21(workspace);
20424
+ const workspaceRoot = resolve22(workspace);
20009
20425
  return [
20010
20426
  { path: join21(home, ".agents", "skills"), scope: "user", trusted: true },
20011
20427
  { path: join21(home, ".claude", "skills"), scope: "user", trusted: true },
20012
20428
  { path: join21(home, ".augment", "skills"), scope: "user", trusted: true },
20013
20429
  { path: join21(resolveHomeNamespace(), "skills"), scope: "user", trusted: true },
20014
20430
  ...configured.map((path) => {
20015
- const resolved = resolve21(workspaceRoot, path);
20431
+ const resolved = resolve22(workspaceRoot, path);
20016
20432
  return {
20017
20433
  path: resolved,
20018
20434
  scope: "configured",
@@ -20041,7 +20457,7 @@ async function readMetadata(path) {
20041
20457
  const { frontmatter } = splitFrontmatter(raw);
20042
20458
  if (!frontmatter) return void 0;
20043
20459
  const parsed = parseYaml3(frontmatter);
20044
- const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename12(resolve21(path, ".."));
20460
+ const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename12(resolve22(path, ".."));
20045
20461
  const description = typeof parsed?.description === "string" ? parsed.description.trim() : "";
20046
20462
  if (!/^[a-z][a-z0-9_-]{0,63}$/.test(name) || !description || description.length > 1e3) {
20047
20463
  return void 0;
@@ -20062,7 +20478,7 @@ async function safeRead(path, maxBytes) {
20062
20478
  try {
20063
20479
  const info = await lstat21(path);
20064
20480
  if (!info.isFile() || info.isSymbolicLink() || info.size > maxBytes) return void 0;
20065
- const parent = resolve21(path, "..");
20481
+ const parent = resolve22(path, "..");
20066
20482
  const resolvedParent = await realpath9(parent);
20067
20483
  const resolvedPath = await realpath9(path);
20068
20484
  if (!resolvedPath.startsWith(`${resolvedParent}/`)) return void 0;
@@ -20247,7 +20663,7 @@ var ExtensionRuntime = class _ExtensionRuntime {
20247
20663
  delegation;
20248
20664
  initialized = false;
20249
20665
  static async create(config, registry, options = {}) {
20250
- const runtime = new _ExtensionRuntime(config, resolve22(config.workspaceRoots[0] ?? process.cwd()), options);
20666
+ const runtime = new _ExtensionRuntime(config, resolve23(config.workspaceRoots[0] ?? process.cwd()), options);
20251
20667
  try {
20252
20668
  await runtime.initialize(registry, options.signal);
20253
20669
  return runtime;
@@ -20452,15 +20868,15 @@ function upgradeCommandOverride(env = process.env) {
20452
20868
  return trimmed ? trimmed : void 0;
20453
20869
  }
20454
20870
  function runUpgrade(plan) {
20455
- return new Promise((resolve24) => {
20871
+ return new Promise((resolve25) => {
20456
20872
  const child = spawn2(plan.command, plan.args, {
20457
20873
  stdio: "inherit",
20458
20874
  shell: plan.shell ?? false
20459
20875
  });
20460
- child.on("error", () => resolve24({ ok: false, exitCode: 127, display: plan.display }));
20876
+ child.on("error", () => resolve25({ ok: false, exitCode: 127, display: plan.display }));
20461
20877
  child.on("close", (code) => {
20462
20878
  const exitCode = code ?? 1;
20463
- resolve24({ ok: exitCode === 0, exitCode, display: plan.display });
20879
+ resolve25({ ok: exitCode === 0, exitCode, display: plan.display });
20464
20880
  });
20465
20881
  });
20466
20882
  }
@@ -20722,7 +21138,7 @@ sessionCommand.command("export <id>").description("Export a session as Markdown"
20722
21138
  const store = new SessionStore(workspaceOption(options.workspace));
20723
21139
  const session = await requireSessionSelector(store, id);
20724
21140
  const markdown = sessionMarkdown(session);
20725
- if (options.output) await writeFile3(resolve23(options.output), markdown, "utf8");
21141
+ if (options.output) await writeFile3(resolve24(options.output), markdown, "utf8");
20726
21142
  else process.stdout.write(markdown);
20727
21143
  });
20728
21144
  var checkpointCommand = program.command("checkpoint").description("Inspect and restore pre-mutation snapshots");
@@ -21099,7 +21515,7 @@ async function runChat(prompts, options) {
21099
21515
  const stdinPrompt = !process.stdin.isTTY ? await readStdin() : "";
21100
21516
  const firstPrompt = [...prompts, stdinPrompt].filter(Boolean).join("\n\n").trim();
21101
21517
  if (shouldPrint && !firstPrompt) throw new Error("Provide a prompt argument or pipe input on stdin.");
21102
- const workspace = resolve23(options.workspace);
21518
+ const workspace = resolve24(options.workspace);
21103
21519
  let config = await runtimeConfig(workspace, options);
21104
21520
  let completedOnboarding = false;
21105
21521
  if (!shouldPrint && needsFirstRunOnboarding(config)) {
@@ -21324,14 +21740,14 @@ async function runAgentSetup(options) {
21324
21740
  `);
21325
21741
  }
21326
21742
  async function runtimeConfig(workspaceInput, options) {
21327
- const workspace = resolve23(workspaceInput);
21743
+ const workspace = resolve24(workspaceInput);
21328
21744
  const loaded = await loadConfig(workspace, options.config, {
21329
21745
  trustProjectConfig: options.trustProjectConfig === true
21330
21746
  });
21331
21747
  const roots = [
21332
21748
  workspace,
21333
21749
  ...loaded.workspaceRoots,
21334
- ...(options.addWorkspace ?? []).map((root) => resolve23(workspace, root))
21750
+ ...(options.addWorkspace ?? []).map((root) => resolve24(workspace, root))
21335
21751
  ];
21336
21752
  const provider = options.provider ? validateProvider(options.provider) : loaded.model.provider;
21337
21753
  return {
@@ -21487,7 +21903,7 @@ function collect(value, previous) {
21487
21903
  }
21488
21904
  function workspaceOption(value) {
21489
21905
  const rootOptions = program.opts();
21490
- return resolve23(value ?? rootOptions.workspace ?? process.cwd());
21906
+ return resolve24(value ?? rootOptions.workspace ?? process.cwd());
21491
21907
  }
21492
21908
  function runtimeOptions(options) {
21493
21909
  const root = program.opts();