@skein-code/cli 0.3.19 → 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.19",
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",
@@ -256,7 +256,8 @@ var package_default = {
256
256
  "Prompt and timeline diagnostics expose the repository-first reuse ladder without blocking legitimate writes",
257
257
  "Known tool changes refresh only their affected local-index paths before the next model turn",
258
258
  "File ctime closes same-size and restored-mtime zero-hit freshness gaps without full-content scans",
259
- "Repeated empty or unchanged search calls stop through the existing recovery circuit"
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"
260
261
  ]
261
262
  },
262
263
  bin: {
@@ -2611,9 +2612,9 @@ function countExplicitPaths(value) {
2611
2612
  }
2612
2613
 
2613
2614
  // src/context/local-index.ts
2614
- import { createHash as createHash6 } from "node:crypto";
2615
- import { lstat as lstat8, readFile as readFile3, stat as stat3 } from "node:fs/promises";
2616
- import { basename as basename5, dirname as dirname7, extname as extname2, isAbsolute as isAbsolute3, join as join7, posix, 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";
2617
2618
  import fg from "fast-glob";
2618
2619
  import ts from "typescript";
2619
2620
  import { z as z3 } from "zod";
@@ -3046,6 +3047,371 @@ function hash(value) {
3046
3047
  return createHash5("sha256").update(value).digest("hex");
3047
3048
  }
3048
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
+
3049
3415
  // src/context/local-index.ts
3050
3416
  var contentHashSchema = z3.string().regex(/^[a-f\d]{64}$/u);
3051
3417
  var indexedChunkSchema = z3.object({
@@ -3111,14 +3477,16 @@ var MIN_STRUCTURAL_CHUNK_LINES = 12;
3111
3477
  var LocalContextIndex = class {
3112
3478
  constructor(roots) {
3113
3479
  this.roots = roots;
3114
- this.roots = roots.map((root) => resolve8(root));
3480
+ this.roots = roots.map((root) => resolve10(root));
3115
3481
  this.workspace = new WorkspaceAccess(this.roots);
3116
- 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");
3117
3484
  }
3118
3485
  roots;
3119
3486
  index;
3120
3487
  workspace;
3121
3488
  queryCache = /* @__PURE__ */ new Map();
3489
+ gitRecency;
3122
3490
  queryCacheHits = 0;
3123
3491
  queryCacheMisses = 0;
3124
3492
  fingerprintCache;
@@ -3132,13 +3500,13 @@ var LocalContextIndex = class {
3132
3500
  this.roots[0] ?? process.cwd(),
3133
3501
  dirname7(this.indexPath)
3134
3502
  );
3135
- const info = await lstat8(this.indexPath);
3503
+ const info = await lstat9(this.indexPath);
3136
3504
  if (!info.isFile() || info.isSymbolicLink()) return false;
3137
3505
  const parsed = localIndexSchema.parse(JSON.parse(await readFile3(this.indexPath, "utf8")));
3138
3506
  const files = [];
3139
3507
  for (const file of parsed.files) {
3140
3508
  try {
3141
- 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;
3142
3510
  const safe = await this.workspace.resolvePath(file.absolutePath, { expect: "file" });
3143
3511
  if (safe !== file.absolutePath) continue;
3144
3512
  const { ctimeMs, ...persistedFile } = file;
@@ -3215,19 +3583,21 @@ var LocalContextIndex = class {
3215
3583
  await this.flushDirty();
3216
3584
  await this.ensureCurrentIndex();
3217
3585
  const limit = Math.max(1, Math.floor(topK));
3218
- const cached = this.getCachedHits(query, limit);
3219
- 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);
3220
3589
  if (!await this.hitsAreCurrent(hits)) {
3221
3590
  await this.buildWithOptions(void 0, true);
3222
- hits = this.rank(query, limit);
3591
+ recency = await this.gitRecency.collect();
3592
+ hits = this.rank(query, limit, recency.scores);
3223
3593
  if (!await this.hitsAreCurrent(hits)) return [];
3224
3594
  }
3225
- this.cacheHits(query, limit, hits);
3595
+ this.cacheHits(query, limit, recency.generation, hits);
3226
3596
  return cloneHits(hits);
3227
3597
  }
3228
3598
  invalidate(paths) {
3229
3599
  for (const path of paths) {
3230
- 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);
3231
3601
  if (this.roots.some((root) => isWithinRoot(root, absolutePath))) {
3232
3602
  this.dirtyPaths.add(absolutePath);
3233
3603
  }
@@ -3424,6 +3794,7 @@ var LocalContextIndex = class {
3424
3794
  );
3425
3795
  await atomicWrite(this.indexPath, `${JSON.stringify(this.index)}
3426
3796
  `, 384);
3797
+ await this.gitRecency.collect();
3427
3798
  onProgress?.({ phase: "done", completed: files.length, total: files.length });
3428
3799
  return {
3429
3800
  files: files.length,
@@ -3517,7 +3888,7 @@ var LocalContextIndex = class {
3517
3888
  followSymbolicLinks: false,
3518
3889
  ignore: ignorePatterns
3519
3890
  });
3520
- 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) });
3521
3892
  }
3522
3893
  discovered.sort((left, right) => left.absolutePath.localeCompare(right.absolutePath));
3523
3894
  return discovered;
@@ -3537,7 +3908,7 @@ var LocalContextIndex = class {
3537
3908
  });
3538
3909
  return matches.length === 1 ? { root, path, absolutePath } : void 0;
3539
3910
  }
3540
- rank(query, topK) {
3911
+ rank(query, topK, recencyScores) {
3541
3912
  const index = this.index;
3542
3913
  const files = index?.files ?? [];
3543
3914
  const chunks = files.flatMap((file) => file.chunks);
@@ -3567,11 +3938,12 @@ var LocalContextIndex = class {
3567
3938
  averageLength
3568
3939
  );
3569
3940
  const graph = graphBoosts.get(chunk.absolutePath)?.score ?? 0;
3570
- const score = lexical.bm25 + lexical.path + lexical.symbol + lexical.phrase + graph;
3571
- return { chunk, lexical, graph, score };
3572
- }).filter(({ score }) => score > 0).sort((left, right) => right.score - left.score || left.chunk.absolutePath.localeCompare(right.chunk.absolutePath));
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));
3573
3945
  const covered = scored.filter(({ lexical, graph }) => graph > 0 || hasSufficientQueryCoverage(lexical.matchedTerms, coverageTerms));
3574
- return (covered.length ? covered : scored).slice(0, topK).map(({ chunk, lexical, graph, score }) => {
3946
+ return (covered.length ? covered : scored).slice(0, topK).map(({ chunk, lexical, graph, recency, score }) => {
3575
3947
  const file = filesByPath.get(chunk.absolutePath);
3576
3948
  const breakdown = {
3577
3949
  bm25: roundScore(lexical.bm25),
@@ -3579,6 +3951,7 @@ var LocalContextIndex = class {
3579
3951
  symbol: roundScore(lexical.symbol),
3580
3952
  phrase: roundScore(lexical.phrase),
3581
3953
  graph: roundScore(graph),
3954
+ recency: roundScore(recency),
3582
3955
  total: roundScore(score)
3583
3956
  };
3584
3957
  const provenance = {
@@ -3594,7 +3967,7 @@ var LocalContextIndex = class {
3594
3967
  endLine: chunk.endLine,
3595
3968
  content: chunk.content,
3596
3969
  score,
3597
- source: "local-bm25+path+symbol+graph",
3970
+ source: "local-bm25+path+symbol+graph+recency",
3598
3971
  ...chunk.symbol ? { symbol: chunk.symbol } : {},
3599
3972
  provenance
3600
3973
  };
@@ -3620,13 +3993,13 @@ var LocalContextIndex = class {
3620
3993
  }
3621
3994
  return true;
3622
3995
  }
3623
- getCachedHits(query, topK) {
3996
+ getCachedHits(query, topK, rankingGeneration) {
3624
3997
  const generation = this.index?.generation;
3625
3998
  if (!generation) {
3626
3999
  this.queryCacheMisses += 1;
3627
4000
  return void 0;
3628
4001
  }
3629
- const key = `${generation}\0${topK}\0${query}`;
4002
+ const key = `${generation}\0${rankingGeneration}\0${topK}\0${query}`;
3630
4003
  const cached = this.queryCache.get(key);
3631
4004
  if (!cached) {
3632
4005
  this.queryCacheMisses += 1;
@@ -3637,11 +4010,11 @@ var LocalContextIndex = class {
3637
4010
  this.queryCache.set(key, cached);
3638
4011
  return cloneHits(cached);
3639
4012
  }
3640
- cacheHits(query, topK, hits) {
4013
+ cacheHits(query, topK, rankingGeneration, hits) {
3641
4014
  if (!hits.length) return;
3642
4015
  const generation = this.index?.generation;
3643
4016
  if (!generation) return;
3644
- const key = `${generation}\0${topK}\0${query}`;
4017
+ const key = `${generation}\0${rankingGeneration}\0${topK}\0${query}`;
3645
4018
  this.queryCache.delete(key);
3646
4019
  this.queryCache.set(key, cloneHits(hits));
3647
4020
  while (this.queryCache.size > MAX_QUERY_CACHE_ENTRIES) {
@@ -3653,7 +4026,7 @@ var LocalContextIndex = class {
3653
4026
  };
3654
4027
  function isWithinRoot(root, path) {
3655
4028
  const offset = relative5(root, path);
3656
- return offset === "" || !offset.startsWith("..") && !isAbsolute3(offset);
4029
+ return offset === "" || !offset.startsWith("..") && !isAbsolute4(offset);
3657
4030
  }
3658
4031
  function isIncludedSourcePath(path) {
3659
4032
  const name = basename5(path);
@@ -3785,7 +4158,7 @@ function packContextHits(hits, roots, maxTokens, engine) {
3785
4158
  const text = selected.map((hit) => {
3786
4159
  const shownPath = workspaceAliasPath(hit.path, roots);
3787
4160
  const symbol = hit.symbol ? ` symbol="${escapeAttribute(hit.symbol)}"` : "";
3788
- const provenance = hit.provenance ? ` source="${escapeAttribute(hit.source)}" hash="${hit.provenance.contentHash.slice(0, 12)}" graph-score="${hit.provenance.score.graph.toFixed(3)}"` : "";
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)}"` : "";
3789
4162
  return `<code path="${escapeAttribute(shownPath)}" lines="${hit.startLine}-${hit.endLine}" score="${hit.score.toFixed(3)}"${symbol}${provenance}>
3790
4163
  ${hit.content}
3791
4164
  </code>`;
@@ -3833,10 +4206,10 @@ function reconstructIndexedContent(chunks) {
3833
4206
  return lines.map((line) => line ?? "").join("\n");
3834
4207
  }
3835
4208
  function hashContent(content) {
3836
- return createHash6("sha256").update(content, "utf8").digest("hex");
4209
+ return createHash7("sha256").update(content, "utf8").digest("hex");
3837
4210
  }
3838
4211
  function createGeneration(files) {
3839
- 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);
3840
4213
  }
3841
4214
  function hasSubstantialOverlap(left, right) {
3842
4215
  if (left.path !== right.path) return false;
@@ -4260,7 +4633,7 @@ function formatContextHits(hits, roots) {
4260
4633
  const path = workspaceAliasPath(hit.path, roots);
4261
4634
  const symbol = hit.symbol ? ` ${hit.symbol}` : "";
4262
4635
  const score = hit.provenance?.score;
4263
- const breakdown = score ? ` bm25=${score.bm25.toFixed(2)} path=${score.path.toFixed(2)} symbol=${score.symbol.toFixed(2)} graph=${score.graph.toFixed(2)}` : "";
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)}` : "";
4264
4637
  const hash4 = hit.provenance?.contentHash.slice(0, 12);
4265
4638
  return `[${hit.source} ${hit.score.toFixed(3)}${breakdown}${hash4 ? ` hash=${hash4}` : ""}]${symbol} ${path}:${hit.startLine}-${hit.endLine}`;
4266
4639
  }).join("\n");
@@ -4501,7 +4874,7 @@ function toolTokens(messages) {
4501
4874
 
4502
4875
  // src/context/mentions.ts
4503
4876
  import { readFile as readFile4, stat as stat4 } from "node:fs/promises";
4504
- import { isAbsolute as isAbsolute4, resolve as resolve9 } from "node:path";
4877
+ import { isAbsolute as isAbsolute5, resolve as resolve11 } from "node:path";
4505
4878
  import fg2 from "fast-glob";
4506
4879
  var defaultMentionIgnores = [
4507
4880
  "**/.git/**",
@@ -4572,7 +4945,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
4572
4945
  ignore: ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/.mosaic/**", "**/.skein/**", "**/.skein.migrating-*/**", "**/.skein.rollback-*/**"]
4573
4946
  });
4574
4947
  matched.push(...await safeMentionPaths(
4575
- children.slice(0, 25).map((path) => resolve9(aliased, path)),
4948
+ children.slice(0, 25).map((path) => resolve11(aliased, path)),
4576
4949
  workspace
4577
4950
  ));
4578
4951
  }
@@ -4580,7 +4953,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
4580
4953
  }
4581
4954
  }
4582
4955
  for (const root of matched.length ? [] : roots) {
4583
- const direct = resolve9(root, clean2);
4956
+ const direct = resolve11(root, clean2);
4584
4957
  try {
4585
4958
  const safeDirect = await workspace.resolvePath(direct);
4586
4959
  const info = await stat4(safeDirect);
@@ -4594,7 +4967,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
4594
4967
  ignore: ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/.mosaic/**", "**/.skein/**", "**/.skein.migrating-*/**", "**/.skein.rollback-*/**"]
4595
4968
  });
4596
4969
  matched.push(...await safeMentionPaths(
4597
- children.slice(0, 25).map((path) => resolve9(safeDirect, path)),
4970
+ children.slice(0, 25).map((path) => resolve11(safeDirect, path)),
4598
4971
  workspace
4599
4972
  ));
4600
4973
  }
@@ -4610,7 +4983,7 @@ async function resolveMentions(input2, roots, maxChars = 8e4) {
4610
4983
  ignore: ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/.mosaic/**", "**/.skein/**", "**/.skein.migrating-*/**", "**/.skein.rollback-*/**"]
4611
4984
  });
4612
4985
  matched.push(...await safeMentionPaths(
4613
- globbed.slice(0, 25).map((path) => resolve9(root, path)),
4986
+ globbed.slice(0, 25).map((path) => resolve11(root, path)),
4614
4987
  workspace
4615
4988
  ));
4616
4989
  }
@@ -4676,7 +5049,7 @@ async function buildMentionPathIndex(roots, options = {}) {
4676
5049
  })
4677
5050
  })));
4678
5051
  const paths = filesByRoot.flatMap(({ root, files }) => files.map(
4679
- (path) => workspaceAliasPath(resolve9(root, path), normalizedRoots)
5052
+ (path) => workspaceAliasPath(resolve11(root, path), normalizedRoots)
4680
5053
  ));
4681
5054
  return new MentionPathIndex(normalizedRoots, paths);
4682
5055
  }
@@ -4710,22 +5083,22 @@ function mapMentionCandidatePath(path, roots) {
4710
5083
  const primary = normalizedRoots[0];
4711
5084
  if (!primary || !path || path.includes("\0")) return void 0;
4712
5085
  let candidate;
4713
- if (isAbsolute4(path)) {
4714
- candidate = resolve9(path);
5086
+ if (isAbsolute5(path)) {
5087
+ candidate = resolve11(path);
4715
5088
  } else {
4716
5089
  const normalized = path.replaceAll("\\", "/").replace(/^\.\//, "");
4717
5090
  const [first = "", ...rest] = normalized.split("/");
4718
5091
  const alias = first.toLocaleLowerCase();
4719
5092
  const workspace = alias.match(/^workspace(\d+)$/);
4720
5093
  if (alias === "main") {
4721
- candidate = resolve9(primary, ...rest);
5094
+ candidate = resolve11(primary, ...rest);
4722
5095
  } else if (workspace) {
4723
5096
  const index = Number(workspace[1]) - 1;
4724
5097
  const root = Number.isSafeInteger(index) && index >= 1 ? normalizedRoots[index] : void 0;
4725
5098
  if (!root) return void 0;
4726
- candidate = resolve9(root, ...rest);
5099
+ candidate = resolve11(root, ...rest);
4727
5100
  } else {
4728
- candidate = resolve9(primary, normalized);
5101
+ candidate = resolve11(primary, normalized);
4729
5102
  }
4730
5103
  }
4731
5104
  if (!normalizedRoots.some((root) => isInside(root, candidate))) return void 0;
@@ -4755,7 +5128,7 @@ function rankMention(path, needle) {
4755
5128
  }
4756
5129
  function normalizeRoots(roots) {
4757
5130
  const values = typeof roots === "string" ? [roots] : roots;
4758
- return [...new Set(values.map((root) => resolve9(root)))];
5131
+ return [...new Set(values.map((root) => resolve11(root)))];
4759
5132
  }
4760
5133
  function normalizeMentionCandidate(path) {
4761
5134
  const normalized = path.replaceAll("\\", "/").normalize("NFC");
@@ -5421,9 +5794,9 @@ function createProvider(config) {
5421
5794
  }
5422
5795
 
5423
5796
  // src/checkpoint/store.ts
5424
- import { createHash as createHash7, randomUUID as randomUUID7 } from "node:crypto";
5425
- import { lstat as lstat9, mkdir as mkdir7, readFile as readFile5, readdir as readdir2, unlink as unlink2 } from "node:fs/promises";
5426
- 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";
5427
5800
  import { z as z4 } from "zod";
5428
5801
  var entrySchema = z4.object({
5429
5802
  path: z4.string(),
@@ -5448,7 +5821,7 @@ var CheckpointStore = class {
5448
5821
  constructor(workspace, directory) {
5449
5822
  this.workspace = typeof workspace === "string" ? new WorkspaceAccess([workspace]) : workspace;
5450
5823
  this.managedDirectory = directory === void 0;
5451
- 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");
5452
5825
  }
5453
5826
  async capture(sessionId, paths, options = {}) {
5454
5827
  validateIdentifier(sessionId, "session");
@@ -5458,8 +5831,8 @@ var CheckpointStore = class {
5458
5831
  }
5459
5832
  async captureUnlocked(sessionId, unique3, options) {
5460
5833
  const id = `${Date.now().toString(36)}-${randomUUID7()}`;
5461
- const target = join8(this.directory, sessionId, id);
5462
- const blobDirectory = join8(target, "blobs");
5834
+ const target = join9(this.directory, sessionId, id);
5835
+ const blobDirectory = join9(target, "blobs");
5463
5836
  await this.ensureDirectory();
5464
5837
  await this.assertManagedPath(target);
5465
5838
  await mkdir7(blobDirectory, { recursive: true });
@@ -5468,14 +5841,14 @@ var CheckpointStore = class {
5468
5841
  for (const input2 of unique3) {
5469
5842
  const path = await this.workspace.resolvePath(input2, { allowMissing: true });
5470
5843
  try {
5471
- const info = await lstat9(path);
5844
+ const info = await lstat10(path);
5472
5845
  if (info.isSymbolicLink()) throw new Error(`Cannot checkpoint a symbolic link: ${input2}`);
5473
5846
  if (!info.isFile()) throw new Error(`Cannot checkpoint a non-file path: ${input2}`);
5474
5847
  if (info.size > 25e6) {
5475
5848
  throw new Error(`File is too large to checkpoint (${info.size} bytes): ${input2}`);
5476
5849
  }
5477
- const blob = `${createHash7("sha256").update(path).digest("hex")}.bin`;
5478
- 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);
5479
5852
  entries.push({
5480
5853
  path,
5481
5854
  relativePath: relative6(this.workspace.primaryRoot, path),
@@ -5502,7 +5875,7 @@ var CheckpointStore = class {
5502
5875
  entries
5503
5876
  };
5504
5877
  await atomicWrite(
5505
- join8(target, "manifest.json"),
5878
+ join9(target, "manifest.json"),
5506
5879
  `${JSON.stringify(manifest, null, 2)}
5507
5880
  `,
5508
5881
  384
@@ -5515,9 +5888,9 @@ var CheckpointStore = class {
5515
5888
  if (!await this.directoryAvailable()) {
5516
5889
  throw new Error(`Checkpoint not found: ${checkpointId}`);
5517
5890
  }
5518
- const checkpointDirectory = join8(this.directory, sessionId, checkpointId);
5891
+ const checkpointDirectory = join9(this.directory, sessionId, checkpointId);
5519
5892
  await this.assertManagedPath(checkpointDirectory);
5520
- const manifestPath = join8(checkpointDirectory, "manifest.json");
5893
+ const manifestPath = join9(checkpointDirectory, "manifest.json");
5521
5894
  await this.assertManagedFile(manifestPath);
5522
5895
  const raw = await readFile5(manifestPath, "utf8");
5523
5896
  const manifest = manifestSchema.parse(JSON.parse(raw));
@@ -5528,7 +5901,7 @@ var CheckpointStore = class {
5528
5901
  }
5529
5902
  async restore(sessionId, checkpointId) {
5530
5903
  const manifest = await this.load(sessionId, checkpointId);
5531
- const blobDirectory = join8(this.directory, sessionId, checkpointId, "blobs");
5904
+ const blobDirectory = join9(this.directory, sessionId, checkpointId, "blobs");
5532
5905
  await this.assertManagedPath(blobDirectory);
5533
5906
  const actions = [];
5534
5907
  for (const entry of manifest.entries) {
@@ -5543,8 +5916,8 @@ var CheckpointStore = class {
5543
5916
  if (!/^[a-f0-9]{64}\.bin$/.test(entry.blob) || basename6(entry.blob) !== entry.blob) {
5544
5917
  throw new Error(`Checkpoint blob name is invalid for ${entry.relativePath}.`);
5545
5918
  }
5546
- const blobPath = join8(blobDirectory, entry.blob);
5547
- const blobInfo = await lstat9(blobPath);
5919
+ const blobPath = join9(blobDirectory, entry.blob);
5920
+ const blobInfo = await lstat10(blobPath);
5548
5921
  if (!blobInfo.isFile() || blobInfo.isSymbolicLink()) {
5549
5922
  throw new Error(`Checkpoint blob is not a regular file for ${entry.relativePath}.`);
5550
5923
  }
@@ -5585,7 +5958,7 @@ var CheckpointStore = class {
5585
5958
  async list(sessionId) {
5586
5959
  validateIdentifier(sessionId, "session");
5587
5960
  if (!await this.directoryAvailable()) return [];
5588
- const directory = join8(this.directory, sessionId);
5961
+ const directory = join9(this.directory, sessionId);
5589
5962
  let names;
5590
5963
  try {
5591
5964
  names = await readdir2(directory);
@@ -5624,7 +5997,7 @@ var CheckpointStore = class {
5624
5997
  await assertNoSymlinkPath(this.workspace.primaryRoot, this.directory);
5625
5998
  }
5626
5999
  try {
5627
- const info = await lstat9(this.directory);
6000
+ const info = await lstat10(this.directory);
5628
6001
  if (info.isSymbolicLink() || !info.isDirectory()) {
5629
6002
  throw new Error(`Checkpoint storage is not a regular directory: ${this.directory}`);
5630
6003
  }
@@ -5643,7 +6016,7 @@ var CheckpointStore = class {
5643
6016
  await this.assertManagedPath(dirname8(path));
5644
6017
  if (!this.managedDirectory) return;
5645
6018
  try {
5646
- if ((await lstat9(path)).isSymbolicLink()) {
6019
+ if ((await lstat10(path)).isSymbolicLink()) {
5647
6020
  throw new Error(`Checkpoint path cannot contain a symbolic link: ${path}`);
5648
6021
  }
5649
6022
  } catch (error) {
@@ -5653,7 +6026,7 @@ var CheckpointStore = class {
5653
6026
  };
5654
6027
  async function readSnapshot(path) {
5655
6028
  try {
5656
- const info = await lstat9(path);
6029
+ const info = await lstat10(path);
5657
6030
  if (info.isSymbolicLink()) throw new Error(`Cannot restore over a symbolic link: ${path}`);
5658
6031
  if (!info.isFile()) throw new Error(`Cannot restore over a non-file path: ${path}`);
5659
6032
  return { content: await readFile5(path), mode: info.mode };
@@ -5678,174 +6051,6 @@ function validateIdentifier(value, kind) {
5678
6051
  }
5679
6052
  }
5680
6053
 
5681
- // src/utils/process.ts
5682
- import { spawn } from "node:child_process";
5683
- import { constants as constants3 } from "node:fs";
5684
- import { access as access2, lstat as lstat10, realpath as realpath6 } from "node:fs/promises";
5685
- import { delimiter, isAbsolute as isAbsolute5, join as join9, resolve as resolve11 } from "node:path";
5686
- import { StringDecoder } from "node:string_decoder";
5687
- async function resolveExecutableRuntime(command2, cwd, excludedRoots = []) {
5688
- const realRoots = await Promise.all(excludedRoots.map(async (root) => {
5689
- try {
5690
- return await realpath6(root);
5691
- } catch {
5692
- return resolve11(root);
5693
- }
5694
- }));
5695
- const pathEntries = (process.env.PATH ?? process.env.Path ?? "").split(delimiter).filter(Boolean);
5696
- const safeDirectories2 = [];
5697
- let executable2;
5698
- const explicit = isAbsolute5(command2) || command2.includes("/") || command2.includes("\\");
5699
- const explicitPath = explicit ? resolve11(cwd, command2) : void 0;
5700
- for (const entry of pathEntries) {
5701
- let directory;
5702
- try {
5703
- directory = await realpath6(resolve11(cwd, entry));
5704
- if (!(await lstat10(directory)).isDirectory()) continue;
5705
- } catch {
5706
- continue;
5707
- }
5708
- if (realRoots.some((root) => isInside(root, directory))) continue;
5709
- let contaminated = false;
5710
- if (!explicit) {
5711
- for (const name of executableNames(command2)) {
5712
- const candidate = join9(directory, name);
5713
- const resolvedCandidate = await usableExecutable(candidate);
5714
- if (!resolvedCandidate) continue;
5715
- if (realRoots.some((root) => isInside(root, resolvedCandidate))) {
5716
- contaminated = true;
5717
- continue;
5718
- }
5719
- executable2 ??= resolvedCandidate;
5720
- }
5721
- }
5722
- if (!contaminated && !safeDirectories2.includes(directory)) safeDirectories2.push(directory);
5723
- }
5724
- if (explicitPath) executable2 = await usableExecutable(explicitPath);
5725
- if (!executable2) return void 0;
5726
- return { executable: executable2, path: safeDirectories2.join(delimiter) };
5727
- }
5728
- async function usableExecutable(candidate) {
5729
- try {
5730
- await access2(candidate, process.platform === "win32" ? constants3.F_OK : constants3.X_OK);
5731
- const resolvedCandidate = await realpath6(candidate);
5732
- return (await lstat10(resolvedCandidate)).isFile() ? resolvedCandidate : void 0;
5733
- } catch {
5734
- return void 0;
5735
- }
5736
- }
5737
- function executableNames(command2) {
5738
- if (process.platform !== "win32") return [command2];
5739
- const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
5740
- return [command2, ...extensions.map((extension) => `${command2}${extension}`)];
5741
- }
5742
- function runProcess(command2, args, options) {
5743
- return new Promise((resolve24, reject) => {
5744
- const started = Date.now();
5745
- const environment = options.inheritEnv === false ? {} : { ...process.env };
5746
- for (const name of options.unsetEnv ?? []) delete environment[name];
5747
- for (const name of Object.keys(environment)) {
5748
- if (options.unsetEnvPrefixes?.some((prefix) => name.startsWith(prefix))) {
5749
- delete environment[name];
5750
- }
5751
- }
5752
- Object.assign(environment, options.env);
5753
- const child = spawn(command2, args, {
5754
- cwd: options.cwd,
5755
- env: environment,
5756
- stdio: ["pipe", "pipe", "pipe"],
5757
- signal: options.signal
5758
- });
5759
- const maxBytes = options.maxOutputBytes ?? 1e6;
5760
- let stdout = "";
5761
- let stderr = "";
5762
- let stdoutBytes = 0;
5763
- let stderrBytes = 0;
5764
- let stdoutObservedBytes = 0;
5765
- let stderrObservedBytes = 0;
5766
- let timedOut = false;
5767
- let callbackError;
5768
- const stdoutDecoder = new StringDecoder("utf8");
5769
- const stderrDecoder = new StringDecoder("utf8");
5770
- const stdoutCallbackDecoder = new StringDecoder("utf8");
5771
- const stderrCallbackDecoder = new StringDecoder("utf8");
5772
- const append = (decoder, chunk, usedBytes) => {
5773
- if (usedBytes >= maxBytes) return { text: "", usedBytes };
5774
- const selected = chunk.subarray(0, maxBytes - usedBytes);
5775
- return { text: decoder.write(selected), usedBytes: usedBytes + selected.length };
5776
- };
5777
- const notify = (callback, decoder, chunk) => {
5778
- if (!callback || callbackError) return;
5779
- try {
5780
- const decoded = decoder.write(chunk);
5781
- if (decoded) callback(decoded);
5782
- } catch (error) {
5783
- callbackError = error;
5784
- child.kill("SIGTERM");
5785
- }
5786
- };
5787
- child.stdout.on("data", (chunk) => {
5788
- stdoutObservedBytes += chunk.length;
5789
- const appended = append(stdoutDecoder, chunk, stdoutBytes);
5790
- stdout += appended.text;
5791
- stdoutBytes = appended.usedBytes;
5792
- notify(options.onStdout, stdoutCallbackDecoder, chunk);
5793
- });
5794
- child.stderr.on("data", (chunk) => {
5795
- stderrObservedBytes += chunk.length;
5796
- const appended = append(stderrDecoder, chunk, stderrBytes);
5797
- stderr += appended.text;
5798
- stderrBytes = appended.usedBytes;
5799
- notify(options.onStderr, stderrCallbackDecoder, chunk);
5800
- });
5801
- child.on("error", reject);
5802
- const timeoutMs = options.timeoutMs ?? 12e4;
5803
- const timeout = timeoutMs > 0 ? setTimeout(() => {
5804
- timedOut = true;
5805
- child.kill("SIGTERM");
5806
- setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
5807
- }, timeoutMs) : void 0;
5808
- child.on("close", (code) => {
5809
- if (timeout) clearTimeout(timeout);
5810
- const stdoutTail = stdoutDecoder.end();
5811
- const stderrTail = stderrDecoder.end();
5812
- if (Buffer.byteLength(stdout) + Buffer.byteLength(stdoutTail) <= maxBytes) stdout += stdoutTail;
5813
- if (Buffer.byteLength(stderr) + Buffer.byteLength(stderrTail) <= maxBytes) stderr += stderrTail;
5814
- try {
5815
- const stdoutCallbackTail = stdoutCallbackDecoder.end();
5816
- const stderrCallbackTail = stderrCallbackDecoder.end();
5817
- if (stdoutCallbackTail && options.onStdout && !callbackError) options.onStdout(stdoutCallbackTail);
5818
- if (stderrCallbackTail && options.onStderr && !callbackError) options.onStderr(stderrCallbackTail);
5819
- } catch (error) {
5820
- callbackError = error;
5821
- }
5822
- if (callbackError) {
5823
- reject(callbackError);
5824
- return;
5825
- }
5826
- resolve24({
5827
- command: [command2, ...args].join(" "),
5828
- exitCode: code ?? (timedOut ? 124 : 1),
5829
- stdout,
5830
- stderr,
5831
- timedOut,
5832
- durationMs: Date.now() - started,
5833
- stdoutBytes: stdoutObservedBytes,
5834
- stderrBytes: stderrObservedBytes,
5835
- stdoutTruncated: stdoutObservedBytes > stdoutBytes,
5836
- stderrTruncated: stderrObservedBytes > stderrBytes
5837
- });
5838
- });
5839
- if (options.stdin) child.stdin.end(options.stdin);
5840
- else child.stdin.end();
5841
- });
5842
- }
5843
- function runShell(command2, cwd, options = {}) {
5844
- const shell = process.platform === "win32" ? process.env.COMSPEC ?? "cmd.exe" : process.env.SHELL ?? "/bin/sh";
5845
- const args = process.platform === "win32" ? ["/d", "/s", "/c", command2] : ["-lc", command2];
5846
- return runProcess(shell, args, { ...options, cwd });
5847
- }
5848
-
5849
6054
  // src/hooks/runner.ts
5850
6055
  var HookError = class extends Error {
5851
6056
  constructor(message2, result) {
@@ -5915,7 +6120,7 @@ import {
5915
6120
  stat as stat5,
5916
6121
  unlink as unlink3
5917
6122
  } from "node:fs/promises";
5918
- 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";
5919
6124
  import { z as z5 } from "zod";
5920
6125
  var sessionIdSchema = z5.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/);
5921
6126
  var toolCallSchema = z5.object({
@@ -6174,9 +6379,9 @@ var SessionStore = class {
6174
6379
  managedDirectory;
6175
6380
  writes = Promise.resolve();
6176
6381
  constructor(workspace, directory) {
6177
- this.workspace = resolve12(workspace);
6382
+ this.workspace = resolve13(workspace);
6178
6383
  this.managedDirectory = directory === void 0;
6179
- this.directory = directory ? resolve12(directory) : join10(resolveProjectNamespaceSync(this.workspace).active, "sessions");
6384
+ this.directory = directory ? resolve13(directory) : join10(resolveProjectNamespaceSync(this.workspace).active, "sessions");
6180
6385
  }
6181
6386
  async create(options) {
6182
6387
  const session = createSession({ ...options, workspace: this.workspace });
@@ -6185,7 +6390,7 @@ var SessionStore = class {
6185
6390
  }
6186
6391
  async save(session) {
6187
6392
  validateId(session.id);
6188
- if (resolve12(session.workspace) !== this.workspace) {
6393
+ if (resolve13(session.workspace) !== this.workspace) {
6189
6394
  throw new Error("Session workspace does not match this store.");
6190
6395
  }
6191
6396
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -6224,7 +6429,7 @@ var SessionStore = class {
6224
6429
  const summaries = await Promise.all(
6225
6430
  entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => {
6226
6431
  const session = await tryReadSession(join10(this.directory, entry.name));
6227
- if (!session || resolve12(session.workspace) !== this.workspace) return;
6432
+ if (!session || resolve13(session.workspace) !== this.workspace) return;
6228
6433
  return toSummary(session);
6229
6434
  })
6230
6435
  );
@@ -6295,7 +6500,7 @@ var SessionStore = class {
6295
6500
  for (const candidate of candidates) {
6296
6501
  await this.assertManagedFile(candidate.path);
6297
6502
  const session = await tryReadSession(candidate.path);
6298
- if (session?.id === id && resolve12(session.workspace) === this.workspace) return session;
6503
+ if (session?.id === id && resolve13(session.workspace) === this.workspace) return session;
6299
6504
  }
6300
6505
  return void 0;
6301
6506
  }
@@ -6309,7 +6514,7 @@ var SessionStore = class {
6309
6514
  }
6310
6515
  }
6311
6516
  assertWorkspace(session) {
6312
- if (resolve12(session.workspace) !== this.workspace) {
6517
+ if (resolve13(session.workspace) !== this.workspace) {
6313
6518
  throw new Error("Stored session belongs to a different workspace.");
6314
6519
  }
6315
6520
  return session;
@@ -6368,7 +6573,7 @@ function createSession(options) {
6368
6573
  return {
6369
6574
  id,
6370
6575
  title: cleanTitle(options.title ?? "New session"),
6371
- workspace: resolve12(options.workspace),
6576
+ workspace: resolve13(options.workspace),
6372
6577
  createdAt: now,
6373
6578
  updatedAt: now,
6374
6579
  model: options.model,
@@ -6423,9 +6628,9 @@ async function exists2(path) {
6423
6628
  }
6424
6629
 
6425
6630
  // src/session/tool-artifacts.ts
6426
- import { createHash as createHash8 } from "node:crypto";
6631
+ import { createHash as createHash9 } from "node:crypto";
6427
6632
  import { lstat as lstat12, readFile as readFile7, readdir as readdir4, rm as rm2 } from "node:fs/promises";
6428
- import { join as join11, resolve as resolve13 } from "node:path";
6633
+ import { join as join11, resolve as resolve14 } from "node:path";
6429
6634
  import { z as z6 } from "zod";
6430
6635
  var MAX_ARTIFACT_BYTES = 5 * 1024 * 1024;
6431
6636
  var MAX_TOTAL_BYTES = 20 * 1024 * 1024;
@@ -6455,9 +6660,9 @@ var ToolArtifactStore = class {
6455
6660
  retentionMs;
6456
6661
  writes = Promise.resolve();
6457
6662
  constructor(workspace, options = {}) {
6458
- this.workspace = resolve13(workspace);
6663
+ this.workspace = resolve14(workspace);
6459
6664
  this.managedDirectory = options.directory === void 0;
6460
- 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");
6461
6666
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
6462
6667
  this.maxArtifactBytes = options.maxArtifactBytes ?? MAX_ARTIFACT_BYTES;
6463
6668
  this.maxTotalBytes = options.maxTotalBytes ?? MAX_TOTAL_BYTES;
@@ -6514,7 +6719,7 @@ var ToolArtifactStore = class {
6514
6719
  }
6515
6720
  if (retainedBytes + storageBytes > this.maxTotalBytes) return { stored: false, reason: "total_limit" };
6516
6721
  const path = this.pathFor(sessionId, toolCallId);
6517
- await ensureWorkspaceStorageDirectory(this.workspace, resolve13(path, ".."), {
6722
+ await ensureWorkspaceStorageDirectory(this.workspace, resolve14(path, ".."), {
6518
6723
  requireActiveNamespace: this.managedDirectory
6519
6724
  });
6520
6725
  await atomicWrite(path, `${JSON.stringify(artifact)}
@@ -6665,7 +6870,7 @@ var ToolArtifactStore = class {
6665
6870
  }
6666
6871
  }
6667
6872
  async assertRegularFile(path) {
6668
- await assertNoSymlinkPath(this.workspace, resolve13(path, ".."));
6873
+ await assertNoSymlinkPath(this.workspace, resolve14(path, ".."));
6669
6874
  try {
6670
6875
  const info = await lstat12(path);
6671
6876
  if (info.isSymbolicLink() || !info.isFile()) {
@@ -6702,7 +6907,7 @@ function reference(artifact) {
6702
6907
  };
6703
6908
  }
6704
6909
  function hash2(value) {
6705
- return createHash8("sha256").update(value).digest("hex");
6910
+ return createHash9("sha256").update(value).digest("hex");
6706
6911
  }
6707
6912
  function storedBytes(artifact) {
6708
6913
  return Buffer.byteLength(JSON.stringify(artifact)) + 1;
@@ -7249,48 +7454,6 @@ var knownCommands = /* @__PURE__ */ new Set([
7249
7454
  ...networkCommands,
7250
7455
  ...workspaceMutationCommands
7251
7456
  ]);
7252
- var unsafeInheritedGitEnvironment = [
7253
- "GIT_DIR",
7254
- "GIT_WORK_TREE",
7255
- "GIT_COMMON_DIR",
7256
- "GIT_INDEX_FILE",
7257
- "GIT_OBJECT_DIRECTORY",
7258
- "GIT_ALTERNATE_OBJECT_DIRECTORIES",
7259
- "GIT_NAMESPACE",
7260
- "GIT_CEILING_DIRECTORIES",
7261
- "GIT_DISCOVERY_ACROSS_FILESYSTEM",
7262
- "GIT_EXTERNAL_DIFF",
7263
- "GIT_DIFF_OPTS",
7264
- "GIT_SSH",
7265
- "GIT_SSH_COMMAND",
7266
- "GIT_PROXY_COMMAND",
7267
- "GIT_ASKPASS",
7268
- "SSH_ASKPASS",
7269
- "GIT_EXEC_PATH",
7270
- "GIT_TEMPLATE_DIR",
7271
- "GIT_CONFIG",
7272
- "GIT_CONFIG_PARAMETERS",
7273
- "GIT_QUARANTINE_PATH",
7274
- "Path"
7275
- ];
7276
- var isolatedGitEnvironment = {
7277
- GIT_TERMINAL_PROMPT: "0",
7278
- GIT_CONFIG_NOSYSTEM: "1",
7279
- GIT_CONFIG_GLOBAL: "/dev/null",
7280
- GIT_CONFIG_SYSTEM: "/dev/null",
7281
- GIT_PAGER: "cat",
7282
- GIT_EDITOR: "true",
7283
- GIT_SEQUENCE_EDITOR: "true",
7284
- GIT_CONFIG_COUNT: "4",
7285
- GIT_CONFIG_KEY_0: "core.hooksPath",
7286
- GIT_CONFIG_VALUE_0: "/dev/null",
7287
- GIT_CONFIG_KEY_1: "core.fsmonitor",
7288
- GIT_CONFIG_VALUE_1: "false",
7289
- GIT_CONFIG_KEY_2: "credential.helper",
7290
- GIT_CONFIG_VALUE_2: "",
7291
- GIT_CONFIG_KEY_3: "protocol.ext.allow",
7292
- GIT_CONFIG_VALUE_3: "never"
7293
- };
7294
7457
  var diffCommands = /* @__PURE__ */ new Set(["diff", "log", "show", "whatchanged"]);
7295
7458
  var gitTool = {
7296
7459
  definition: {
@@ -7338,13 +7501,9 @@ var gitTool = {
7338
7501
  }
7339
7502
  const runtime = await resolveExecutableRuntime("git", cwd, context.workspace.roots);
7340
7503
  if (!runtime) throw new Error("Git executable was not found outside the configured workspace roots.");
7341
- const status = await runProcess(runtime.executable, ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
7342
- cwd,
7504
+ const status = await runIsolatedGit(runtime, ["status", "--porcelain=v1", "-z", "--untracked-files=all"], cwd, {
7343
7505
  timeoutMs: 15e3,
7344
- maxOutputBytes: 2e6,
7345
- env: { ...isolatedGitEnvironment, PATH: runtime.path },
7346
- unsetEnv: unsafeInheritedGitEnvironment,
7347
- unsetEnvPrefixes: ["GIT_"]
7506
+ maxOutputBytes: 2e6
7348
7507
  });
7349
7508
  if (status.exitCode === 0) {
7350
7509
  for (const record of status.stdout.split("\0")) {
@@ -7371,15 +7530,11 @@ var gitTool = {
7371
7530
  const runtime = await resolveExecutableRuntime("git", cwd, context.workspace.roots);
7372
7531
  if (!runtime) throw new Error("Git executable was not found outside the configured workspace roots.");
7373
7532
  const before = worktreeTrackingCommands.has(command2) ? await captureGitState(runtime, cwd) : void 0;
7374
- const result = await runProcess(runtime.executable, protectedGitArguments(input2.args, command2), {
7375
- cwd,
7533
+ const result = await runIsolatedGit(runtime, protectedGitArguments(input2.args, command2), cwd, {
7376
7534
  timeoutMs: input2.timeout_ms ?? 12e4,
7377
7535
  maxOutputBytes: 2e6,
7378
7536
  ...input2.stdin !== void 0 ? { stdin: input2.stdin } : {},
7379
- ...context.signal ? { signal: context.signal } : {},
7380
- env: { ...isolatedGitEnvironment, PATH: runtime.path },
7381
- unsetEnv: unsafeInheritedGitEnvironment,
7382
- unsetEnvPrefixes: ["GIT_"]
7537
+ ...context.signal ? { signal: context.signal } : {}
7383
7538
  });
7384
7539
  const changedFiles = before ? await collectGitChanges(before, await captureGitState(runtime, cwd), runtime, cwd, context) : [];
7385
7540
  return {
@@ -7457,18 +7612,6 @@ function parsePorcelainStatus(output2) {
7457
7612
  }
7458
7613
  return status;
7459
7614
  }
7460
- function runIsolatedGit(runtime, args, cwd, options = {}) {
7461
- return runProcess(runtime.executable, args, {
7462
- cwd,
7463
- timeoutMs: options.timeoutMs ?? 15e3,
7464
- maxOutputBytes: options.maxOutputBytes ?? 2e6,
7465
- env: { ...isolatedGitEnvironment, PATH: runtime.path },
7466
- unsetEnv: unsafeInheritedGitEnvironment,
7467
- unsetEnvPrefixes: ["GIT_"],
7468
- ...options.stdin !== void 0 ? { stdin: options.stdin } : {},
7469
- ...options.signal ? { signal: options.signal } : {}
7470
- });
7471
- }
7472
7615
  function validateGitArguments(args) {
7473
7616
  for (const argument of args) {
7474
7617
  if (argument.includes("\0") || argument.includes("\n") || argument.includes("\r")) {
@@ -7604,7 +7747,7 @@ function positionalArguments(args, command2) {
7604
7747
 
7605
7748
  // src/tools/list.ts
7606
7749
  import { lstat as lstat14 } from "node:fs/promises";
7607
- import { relative as relative7, resolve as resolve14 } from "node:path";
7750
+ import { relative as relative7, resolve as resolve15 } from "node:path";
7608
7751
  import fg3 from "fast-glob";
7609
7752
  import { z as z9 } from "zod";
7610
7753
  var inputSchema4 = z9.object({
@@ -7662,7 +7805,7 @@ var listFilesTool = {
7662
7805
  for (const path of selected) {
7663
7806
  let safePath;
7664
7807
  try {
7665
- safePath = await context.workspace.resolvePath(resolve14(directory, path), { expect: "any" });
7808
+ safePath = await context.workspace.resolvePath(resolve15(directory, path), { expect: "any" });
7666
7809
  } catch {
7667
7810
  continue;
7668
7811
  }
@@ -7872,7 +8015,7 @@ function assertToolName(name) {
7872
8015
 
7873
8016
  // src/tools/search.ts
7874
8017
  import { readFile as readFile10, stat as stat8 } from "node:fs/promises";
7875
- import { resolve as resolve15 } from "node:path";
8018
+ import { resolve as resolve16 } from "node:path";
7876
8019
  import fg4 from "fast-glob";
7877
8020
  import { z as z12 } from "zod";
7878
8021
  var inputSchema7 = z12.object({
@@ -7942,7 +8085,7 @@ var searchCodeTool = {
7942
8085
  if (results.length >= maxResults) break;
7943
8086
  let path;
7944
8087
  try {
7945
- path = await context.workspace.resolvePath(resolve15(directory, file), { expect: "file" });
8088
+ path = await context.workspace.resolvePath(resolve16(directory, file), { expect: "file" });
7946
8089
  } catch {
7947
8090
  skipped += 1;
7948
8091
  continue;
@@ -8024,7 +8167,7 @@ ${hit.content}`
8024
8167
  }
8025
8168
 
8026
8169
  // src/tools/shell.ts
8027
- import { createHash as createHash9 } from "node:crypto";
8170
+ import { createHash as createHash10 } from "node:crypto";
8028
8171
  import { lstat as lstat15, readFile as readFile11, readdir as readdir5 } from "node:fs/promises";
8029
8172
  import { join as join13 } from "node:path";
8030
8173
  import { z as z13 } from "zod";
@@ -8238,7 +8381,7 @@ async function captureWorkspaceSnapshot(roots) {
8238
8381
  files.set(path, {
8239
8382
  size: info.size,
8240
8383
  mtimeMs: info.mtimeMs,
8241
- hash: createHash9("sha256").update(content).digest("hex")
8384
+ hash: createHash10("sha256").update(content).digest("hex")
8242
8385
  });
8243
8386
  } catch (error) {
8244
8387
  if (error.code !== "ENOENT") complete = false;
@@ -8441,15 +8584,15 @@ function compact(value, limit) {
8441
8584
  }
8442
8585
 
8443
8586
  // src/agent/completion-gate.ts
8444
- import { createHash as createHash11 } from "node:crypto";
8587
+ import { createHash as createHash12 } from "node:crypto";
8445
8588
 
8446
8589
  // src/tools/permissions.ts
8447
- import { createHash as createHash10, createHmac, randomBytes } from "node:crypto";
8590
+ import { createHash as createHash11, createHmac, randomBytes } from "node:crypto";
8448
8591
  var permissionScopeSecret = randomBytes(32);
8449
8592
  function permissionKey(call, category) {
8450
8593
  const categoryScope = category === "network" ? `\0network-request:${scopeFingerprint(normalizeRequestState(call.arguments))}` : "";
8451
- const digest = createHash10("sha256").update(`${category}\0${call.name}\0${permissionTarget(call)}${categoryScope}`).digest("hex").slice(0, 24);
8452
- 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}`;
8453
8596
  }
8454
8597
  function permissionTarget(call) {
8455
8598
  const command2 = commandForCall(call);
@@ -8642,7 +8785,7 @@ function captureVerification(call, result, changeSequence, configuredCommands) {
8642
8785
  kind,
8643
8786
  ok: result.ok,
8644
8787
  changeSequence,
8645
- commandKey: createHash11("sha256").update(normalized).digest("hex")
8788
+ commandKey: createHash12("sha256").update(normalized).digest("hex")
8646
8789
  };
8647
8790
  }
8648
8791
  function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutationTracking = "complete", taskContract, audit = [], duplication) {
@@ -8826,7 +8969,7 @@ function verificationRequirementMet(requirement, checks) {
8826
8969
  const normalized = normalizeCommand2(requirement);
8827
8970
  const broad = normalized.match(/^Record at least one successful (.+) after the final mutation\.?$/iu);
8828
8971
  if (broad) return checks.length > 0;
8829
- const commandKey = createHash11("sha256").update(normalized).digest("hex");
8972
+ const commandKey = createHash12("sha256").update(normalized).digest("hex");
8830
8973
  return checks.some((item) => item.commandKey === commandKey);
8831
8974
  }
8832
8975
  function acceptanceUnresolved(acceptance) {
@@ -9420,7 +9563,7 @@ function escapeAttribute3(value) {
9420
9563
  }
9421
9564
 
9422
9565
  // src/agent/tool-recovery.ts
9423
- import { createHash as createHash12 } from "node:crypto";
9566
+ import { createHash as createHash13 } from "node:crypto";
9424
9567
  var RETRY_BUDGET = {
9425
9568
  schema_input: 3,
9426
9569
  unknown_tool: 2,
@@ -9492,7 +9635,7 @@ var ToolRecoveryController = class {
9492
9635
  recordEvidence(call, result) {
9493
9636
  if (call.name !== "search_code" || !result.ok) return void 0;
9494
9637
  const callKey = callSignature(call);
9495
- 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");
9496
9639
  const count = typeof result.metadata?.count === "number" ? result.metadata.count : void 0;
9497
9640
  const current = this.evidence.get(callKey);
9498
9641
  const repeated = current?.fingerprint === fingerprint;
@@ -9502,7 +9645,7 @@ var ToolRecoveryController = class {
9502
9645
  status: count === 0 ? "empty" : repeated ? "repeated" : "new",
9503
9646
  repeatCount: repeats,
9504
9647
  stop: repeats >= 2,
9505
- signature: createHash12("sha256").update(`${callKey}\0${fingerprint}`).digest("hex")
9648
+ signature: createHash13("sha256").update(`${callKey}\0${fingerprint}`).digest("hex")
9506
9649
  };
9507
9650
  }
9508
9651
  receipt(call, failureClass, attempt, circuitOpen) {
@@ -9538,10 +9681,10 @@ function isRetryable(failureClass) {
9538
9681
  return RETRY_BUDGET[failureClass] > 0;
9539
9682
  }
9540
9683
  function callSignature(call) {
9541
- 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");
9542
9685
  }
9543
9686
  function failureSignature(call, failureClass) {
9544
- 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");
9545
9688
  }
9546
9689
  function redact(value, key = "") {
9547
9690
  if (/api[_-]?key|authorization|cookie|password|secret|token/i.test(key)) return "[redacted]";
@@ -9889,7 +10032,7 @@ function escapeAttribute5(value) {
9889
10032
  }
9890
10033
 
9891
10034
  // src/agent/reuse-gate.ts
9892
- import { createHash as createHash13 } from "node:crypto";
10035
+ import { createHash as createHash14 } from "node:crypto";
9893
10036
  import { readFile as readFile14, stat as stat10 } from "node:fs/promises";
9894
10037
  import { basename as basename8, extname as extname3 } from "node:path";
9895
10038
  var MAX_CANDIDATES = 5;
@@ -10098,14 +10241,14 @@ function unresolvedReceipt(input2, queryHash, targetPaths, trigger, detail) {
10098
10241
  };
10099
10242
  }
10100
10243
  function hash3(value) {
10101
- return createHash13("sha256").update(value).digest("hex");
10244
+ return createHash14("sha256").update(value).digest("hex");
10102
10245
  }
10103
10246
  function roundScore2(value) {
10104
10247
  return Number.isFinite(value) ? Math.round(value * 1e3) / 1e3 : 0;
10105
10248
  }
10106
10249
 
10107
10250
  // src/agent/duplication-audit.ts
10108
- import { createHash as createHash14 } from "node:crypto";
10251
+ import { createHash as createHash15 } from "node:crypto";
10109
10252
  import { readFile as readFile15 } from "node:fs/promises";
10110
10253
  var DEFAULT_NEAR_CLONE_THRESHOLD = 0.55;
10111
10254
  var MAX_MATCHES = 8;
@@ -10280,7 +10423,7 @@ function round(value) {
10280
10423
  return Math.round(value * 1e3) / 1e3;
10281
10424
  }
10282
10425
  function matchId(generation, changeSequence, changed, candidate, similarity) {
10283
- return createHash14("sha256").update([
10426
+ return createHash15("sha256").update([
10284
10427
  generation,
10285
10428
  String(changeSequence),
10286
10429
  changed.path,
@@ -11698,9 +11841,9 @@ function numeric(...values) {
11698
11841
  }
11699
11842
 
11700
11843
  // src/agent/team-store.ts
11701
- import { createHash as createHash15, randomUUID as randomUUID13 } from "node:crypto";
11844
+ import { createHash as createHash16, randomUUID as randomUUID13 } from "node:crypto";
11702
11845
  import { lstat as lstat18, readFile as readFile17, readdir as readdir7, rm as rm3 } from "node:fs/promises";
11703
- import { join as join16, resolve as resolve16 } from "node:path";
11846
+ import { join as join16, resolve as resolve17 } from "node:path";
11704
11847
  import { z as z18 } from "zod";
11705
11848
  var runIdSchema = z18.string().uuid();
11706
11849
  var hashSchema = z18.string().regex(/^[a-f0-9]{64}$/u);
@@ -11784,9 +11927,9 @@ var TeamRunStore = class {
11784
11927
  managedDirectory;
11785
11928
  writes = Promise.resolve();
11786
11929
  constructor(workspace, directory) {
11787
- this.workspace = resolve16(workspace);
11930
+ this.workspace = resolve17(workspace);
11788
11931
  this.managedDirectory = directory === void 0;
11789
- 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");
11790
11933
  }
11791
11934
  async create(input2) {
11792
11935
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -11873,7 +12016,7 @@ var TeamRunStore = class {
11873
12016
  const path = join16(this.runDirectory(runId), "manifest.json");
11874
12017
  await this.assertRegularFile(path);
11875
12018
  const manifest = manifestSchema2.parse(JSON.parse(await readFile17(path, "utf8")));
11876
- if (manifest.id !== runId || resolve16(manifest.workspace) !== this.workspace) {
12019
+ if (manifest.id !== runId || resolve17(manifest.workspace) !== this.workspace) {
11877
12020
  throw new Error("Team run manifest identity does not match its location.");
11878
12021
  }
11879
12022
  if (verify) {
@@ -11956,7 +12099,7 @@ var TeamRunStore = class {
11956
12099
  async writeArtifact(runId, content, truncate = true) {
11957
12100
  const data = boundedArtifactText(content, 5e5, truncate);
11958
12101
  const bytes = Buffer.byteLength(data);
11959
- const sha256 = createHash15("sha256").update(data).digest("hex");
12102
+ const sha256 = createHash16("sha256").update(data).digest("hex");
11960
12103
  const directory = join16(this.runDirectory(runId), "blobs");
11961
12104
  await ensureWorkspaceStorageDirectory(this.workspace, directory, {
11962
12105
  requireActiveNamespace: this.managedDirectory
@@ -11965,7 +12108,7 @@ var TeamRunStore = class {
11965
12108
  try {
11966
12109
  await this.assertRegularFile(path);
11967
12110
  const existing = await readFile17(path);
11968
- if (createHash15("sha256").update(existing).digest("hex") !== sha256) {
12111
+ if (createHash16("sha256").update(existing).digest("hex") !== sha256) {
11969
12112
  throw new Error(`Team artifact hash collision or corruption: ${sha256}`);
11970
12113
  }
11971
12114
  } catch (error) {
@@ -11986,7 +12129,7 @@ var TeamRunStore = class {
11986
12129
  const path = this.artifactPath(runId, artifact.sha256);
11987
12130
  await this.assertRegularFile(path);
11988
12131
  const data = await readFile17(path);
11989
- const hash4 = createHash15("sha256").update(data).digest("hex");
12132
+ const hash4 = createHash16("sha256").update(data).digest("hex");
11990
12133
  if (hash4 !== artifact.sha256 || data.byteLength !== artifact.bytes) {
11991
12134
  throw new Error(`Team artifact integrity check failed: ${artifact.sha256}`);
11992
12135
  }
@@ -12004,7 +12147,7 @@ var TeamRunStore = class {
12004
12147
  if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Team run storage is not a regular directory.");
12005
12148
  }
12006
12149
  async assertRegularFile(path) {
12007
- await assertNoSymlinkPath(this.workspace, resolve16(path, ".."));
12150
+ await assertNoSymlinkPath(this.workspace, resolve17(path, ".."));
12008
12151
  const info = await lstat18(path);
12009
12152
  if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Team run file is not a regular file: ${path}`);
12010
12153
  }
@@ -12049,10 +12192,10 @@ function resolveAgentModelRoute(team, parent, profile) {
12049
12192
  }
12050
12193
 
12051
12194
  // src/agent/writer-lane.ts
12052
- import { createHash as createHash16 } from "node:crypto";
12195
+ import { createHash as createHash17 } from "node:crypto";
12053
12196
  import { lstat as lstat19, mkdtemp, realpath as realpath7, rm as rm4 } from "node:fs/promises";
12054
12197
  import { tmpdir as tmpdir2 } from "node:os";
12055
- 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";
12056
12199
  var WriterLaneApplyError = class extends Error {
12057
12200
  constructor(message2, attempted, cause) {
12058
12201
  super(message2, cause === void 0 ? void 0 : { cause });
@@ -12065,8 +12208,8 @@ var WriterLane = class {
12065
12208
  workspace;
12066
12209
  constructor(workspace, workspaceRoots) {
12067
12210
  this.workspace = new WorkspaceAccess([
12068
- resolve17(workspace),
12069
- ...workspaceRoots.map((root) => resolve17(root))
12211
+ resolve18(workspace),
12212
+ ...workspaceRoots.map((root) => resolve18(root))
12070
12213
  ]);
12071
12214
  }
12072
12215
  async createDraft(maxPatchBytes, operation, signal) {
@@ -12147,7 +12290,7 @@ var WriterLane = class {
12147
12290
  return {
12148
12291
  baseCommit,
12149
12292
  patch,
12150
- patchSha256: createHash16("sha256").update(patch).digest("hex"),
12293
+ patchSha256: createHash17("sha256").update(patch).digest("hex"),
12151
12294
  files,
12152
12295
  worktreeCleaned,
12153
12296
  value
@@ -12289,7 +12432,7 @@ var WriterLane = class {
12289
12432
  if (topLevel.exitCode !== 0 || !topLevel.stdout.trim()) {
12290
12433
  throw new Error("The primary workspace must be a Git repository root for writer lanes.");
12291
12434
  }
12292
- const discoveredRoot = resolve17(topLevel.stdout.trim());
12435
+ const discoveredRoot = resolve18(topLevel.stdout.trim());
12293
12436
  if (await realpath7(discoveredRoot) !== await realpath7(root)) {
12294
12437
  throw new Error("The primary workspace must be the Git repository root for writer lanes.");
12295
12438
  }
@@ -12298,7 +12441,7 @@ var WriterLane = class {
12298
12441
  if (common.exitCode !== 0 || !common.stdout.trim()) {
12299
12442
  throw new Error(`Unable to resolve the Git common directory: ${processDetail(common)}`);
12300
12443
  }
12301
- 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()));
12302
12445
  return { root: repositoryRoot, commonDirectory, runtime };
12303
12446
  }
12304
12447
  async head(repository) {
@@ -12310,7 +12453,7 @@ var WriterLane = class {
12310
12453
  return value;
12311
12454
  }
12312
12455
  async cleanupWorktree(repository, worktree, added) {
12313
- const physicalWorktree = await realpath7(worktree).catch(() => resolve17(worktree));
12456
+ const physicalWorktree = await realpath7(worktree).catch(() => resolve18(worktree));
12314
12457
  if (added) {
12315
12458
  await runIsolatedGit(repository.runtime, [
12316
12459
  "worktree",
@@ -14236,7 +14379,7 @@ function formatResultDetail(result, paint = chalk2, glyphs = resolveCliGlyphs())
14236
14379
  }
14237
14380
 
14238
14381
  // src/cli/namespace-leases.ts
14239
- import { resolve as resolve18 } from "node:path";
14382
+ import { resolve as resolve19 } from "node:path";
14240
14383
  function cliNamespaceLeaseScopes(actionCommand) {
14241
14384
  const names = commandNames(actionCommand);
14242
14385
  const topLevel = names[1];
@@ -14258,7 +14401,7 @@ async function acquireCliNamespaceLeases(actionCommand) {
14258
14401
  const scopes = cliNamespaceLeaseScopes(actionCommand);
14259
14402
  const localOptions = actionCommand.opts();
14260
14403
  const globalOptions = actionCommand.optsWithGlobals();
14261
- const workspace = resolve18(localOptions.workspace ?? globalOptions.workspace ?? process.cwd());
14404
+ const workspace = resolve19(localOptions.workspace ?? globalOptions.workspace ?? process.cwd());
14262
14405
  const targets = scopes.map((scope) => scope === "project" ? projectNamespacePaths(workspace).canonical : homeNamespacePaths().canonical);
14263
14406
  const leases = [];
14264
14407
  try {
@@ -14510,7 +14653,7 @@ function graphemes(value) {
14510
14653
 
14511
14654
  // src/ui/theme.ts
14512
14655
  import { lstat as lstat20, readdir as readdir8, readFile as readFile18 } from "node:fs/promises";
14513
- 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";
14514
14657
  import React, { createContext, useContext } from "react";
14515
14658
  function defineTheme(seed) {
14516
14659
  return {
@@ -14632,7 +14775,7 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
14632
14775
  userThemeNames.clear();
14633
14776
  const loaded = [];
14634
14777
  const errors = [];
14635
- const resolvedDirectory = resolve19(directory);
14778
+ const resolvedDirectory = resolve20(directory);
14636
14779
  let entries;
14637
14780
  try {
14638
14781
  entries = await readdir8(resolvedDirectory, { encoding: "utf8" });
@@ -17096,7 +17239,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
17096
17239
  return () => clearInterval(timer);
17097
17240
  }, [busy]);
17098
17241
  const requestPermission = useCallback((call, category) => {
17099
- return new Promise((resolve24) => setPermission({ call, category, resolve: resolve24 }));
17242
+ return new Promise((resolve25) => setPermission({ call, category, resolve: resolve25 }));
17100
17243
  }, []);
17101
17244
  const onEvent = useCallback((event) => {
17102
17245
  switch (event.type) {
@@ -17932,8 +18075,8 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
17932
18075
  }, [append]);
17933
18076
  function settlePermission(grant, stop = false) {
17934
18077
  if (!permission) return;
17935
- const { call, category, resolve: resolve24 } = permission;
17936
- resolve24(grant);
18078
+ const { call, category, resolve: resolve25 } = permission;
18079
+ resolve25(grant);
17937
18080
  setPermission(void 0);
17938
18081
  if (grant === "session") {
17939
18082
  append({
@@ -19268,7 +19411,7 @@ async function runFirstRunOnboarding(initialConfig, options = {}) {
19268
19411
  }
19269
19412
 
19270
19413
  // src/runtime/extensions.ts
19271
- import { resolve as resolve22 } from "node:path";
19414
+ import { resolve as resolve23 } from "node:path";
19272
19415
 
19273
19416
  // src/mcp/manager.ts
19274
19417
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -19277,7 +19420,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
19277
19420
  import stripAnsi5 from "strip-ansi";
19278
19421
 
19279
19422
  // src/mcp/tool.ts
19280
- import { createHash as createHash17 } from "node:crypto";
19423
+ import { createHash as createHash18 } from "node:crypto";
19281
19424
  import stripAnsi4 from "strip-ansi";
19282
19425
  var MAX_ARGUMENT_BYTES = 256e3;
19283
19426
  var MAX_DESCRIPTION_LENGTH = 4e3;
@@ -19448,7 +19591,7 @@ function fitToolName(name, identity2) {
19448
19591
  return `${name.slice(0, 64 - suffix.length).replace(/_+$/g, "")}${suffix}`;
19449
19592
  }
19450
19593
  function shortHash(value) {
19451
- return createHash17("sha256").update(value).digest("hex").slice(0, 8);
19594
+ return createHash18("sha256").update(value).digest("hex").slice(0, 8);
19452
19595
  }
19453
19596
  function sanitizeOutputText(value) {
19454
19597
  return stripAnsi4(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
@@ -19481,7 +19624,7 @@ function isRecord2(value) {
19481
19624
 
19482
19625
  // src/mcp/validation.ts
19483
19626
  import { stat as stat11, realpath as realpath8 } from "node:fs/promises";
19484
- import { isAbsolute as isAbsolute7, resolve as resolve20 } from "node:path";
19627
+ import { isAbsolute as isAbsolute7, resolve as resolve21 } from "node:path";
19485
19628
  var SERVER_NAME = /^[a-z][a-z0-9_-]{0,63}$/;
19486
19629
  var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
19487
19630
  var HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
@@ -19553,9 +19696,9 @@ async function validateCwd(configured, options) {
19553
19696
  if (configured !== void 0 && (configured.length > 4e3 || CONTROL_CHARACTERS.test(configured))) {
19554
19697
  throw new Error("MCP working directory is invalid or too long");
19555
19698
  }
19556
- const defaultCwd = resolve20(options.cwd ?? process.cwd());
19557
- const candidate = configured ? isAbsolute7(configured) ? resolve20(configured) : resolve20(defaultCwd, configured) : defaultCwd;
19558
- 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];
19559
19702
  const resolvedCandidate = await realpath8(candidate).catch(() => {
19560
19703
  throw new Error(`MCP working directory does not exist: ${candidate}`);
19561
19704
  });
@@ -20209,7 +20352,7 @@ function scopeKey(scope, context) {
20209
20352
  // src/skills/catalog.ts
20210
20353
  import { lstat as lstat21, readFile as readFile20, readdir as readdir9, realpath as realpath9 } from "node:fs/promises";
20211
20354
  import { homedir as homedir4 } from "node:os";
20212
- 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";
20213
20356
  import { parse as parseYaml3 } from "yaml";
20214
20357
  var SkillCatalog = class {
20215
20358
  constructor(workspace, config) {
@@ -20278,14 +20421,14 @@ ${skill.content}
20278
20421
  }
20279
20422
  function discoveryLocations(workspace, configured) {
20280
20423
  const home = homedir4();
20281
- const workspaceRoot = resolve21(workspace);
20424
+ const workspaceRoot = resolve22(workspace);
20282
20425
  return [
20283
20426
  { path: join21(home, ".agents", "skills"), scope: "user", trusted: true },
20284
20427
  { path: join21(home, ".claude", "skills"), scope: "user", trusted: true },
20285
20428
  { path: join21(home, ".augment", "skills"), scope: "user", trusted: true },
20286
20429
  { path: join21(resolveHomeNamespace(), "skills"), scope: "user", trusted: true },
20287
20430
  ...configured.map((path) => {
20288
- const resolved = resolve21(workspaceRoot, path);
20431
+ const resolved = resolve22(workspaceRoot, path);
20289
20432
  return {
20290
20433
  path: resolved,
20291
20434
  scope: "configured",
@@ -20314,7 +20457,7 @@ async function readMetadata(path) {
20314
20457
  const { frontmatter } = splitFrontmatter(raw);
20315
20458
  if (!frontmatter) return void 0;
20316
20459
  const parsed = parseYaml3(frontmatter);
20317
- 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, ".."));
20318
20461
  const description = typeof parsed?.description === "string" ? parsed.description.trim() : "";
20319
20462
  if (!/^[a-z][a-z0-9_-]{0,63}$/.test(name) || !description || description.length > 1e3) {
20320
20463
  return void 0;
@@ -20335,7 +20478,7 @@ async function safeRead(path, maxBytes) {
20335
20478
  try {
20336
20479
  const info = await lstat21(path);
20337
20480
  if (!info.isFile() || info.isSymbolicLink() || info.size > maxBytes) return void 0;
20338
- const parent = resolve21(path, "..");
20481
+ const parent = resolve22(path, "..");
20339
20482
  const resolvedParent = await realpath9(parent);
20340
20483
  const resolvedPath = await realpath9(path);
20341
20484
  if (!resolvedPath.startsWith(`${resolvedParent}/`)) return void 0;
@@ -20520,7 +20663,7 @@ var ExtensionRuntime = class _ExtensionRuntime {
20520
20663
  delegation;
20521
20664
  initialized = false;
20522
20665
  static async create(config, registry, options = {}) {
20523
- 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);
20524
20667
  try {
20525
20668
  await runtime.initialize(registry, options.signal);
20526
20669
  return runtime;
@@ -20725,15 +20868,15 @@ function upgradeCommandOverride(env = process.env) {
20725
20868
  return trimmed ? trimmed : void 0;
20726
20869
  }
20727
20870
  function runUpgrade(plan) {
20728
- return new Promise((resolve24) => {
20871
+ return new Promise((resolve25) => {
20729
20872
  const child = spawn2(plan.command, plan.args, {
20730
20873
  stdio: "inherit",
20731
20874
  shell: plan.shell ?? false
20732
20875
  });
20733
- child.on("error", () => resolve24({ ok: false, exitCode: 127, display: plan.display }));
20876
+ child.on("error", () => resolve25({ ok: false, exitCode: 127, display: plan.display }));
20734
20877
  child.on("close", (code) => {
20735
20878
  const exitCode = code ?? 1;
20736
- resolve24({ ok: exitCode === 0, exitCode, display: plan.display });
20879
+ resolve25({ ok: exitCode === 0, exitCode, display: plan.display });
20737
20880
  });
20738
20881
  });
20739
20882
  }
@@ -20995,7 +21138,7 @@ sessionCommand.command("export <id>").description("Export a session as Markdown"
20995
21138
  const store = new SessionStore(workspaceOption(options.workspace));
20996
21139
  const session = await requireSessionSelector(store, id);
20997
21140
  const markdown = sessionMarkdown(session);
20998
- if (options.output) await writeFile3(resolve23(options.output), markdown, "utf8");
21141
+ if (options.output) await writeFile3(resolve24(options.output), markdown, "utf8");
20999
21142
  else process.stdout.write(markdown);
21000
21143
  });
21001
21144
  var checkpointCommand = program.command("checkpoint").description("Inspect and restore pre-mutation snapshots");
@@ -21372,7 +21515,7 @@ async function runChat(prompts, options) {
21372
21515
  const stdinPrompt = !process.stdin.isTTY ? await readStdin() : "";
21373
21516
  const firstPrompt = [...prompts, stdinPrompt].filter(Boolean).join("\n\n").trim();
21374
21517
  if (shouldPrint && !firstPrompt) throw new Error("Provide a prompt argument or pipe input on stdin.");
21375
- const workspace = resolve23(options.workspace);
21518
+ const workspace = resolve24(options.workspace);
21376
21519
  let config = await runtimeConfig(workspace, options);
21377
21520
  let completedOnboarding = false;
21378
21521
  if (!shouldPrint && needsFirstRunOnboarding(config)) {
@@ -21597,14 +21740,14 @@ async function runAgentSetup(options) {
21597
21740
  `);
21598
21741
  }
21599
21742
  async function runtimeConfig(workspaceInput, options) {
21600
- const workspace = resolve23(workspaceInput);
21743
+ const workspace = resolve24(workspaceInput);
21601
21744
  const loaded = await loadConfig(workspace, options.config, {
21602
21745
  trustProjectConfig: options.trustProjectConfig === true
21603
21746
  });
21604
21747
  const roots = [
21605
21748
  workspace,
21606
21749
  ...loaded.workspaceRoots,
21607
- ...(options.addWorkspace ?? []).map((root) => resolve23(workspace, root))
21750
+ ...(options.addWorkspace ?? []).map((root) => resolve24(workspace, root))
21608
21751
  ];
21609
21752
  const provider = options.provider ? validateProvider(options.provider) : loaded.model.provider;
21610
21753
  return {
@@ -21760,7 +21903,7 @@ function collect(value, previous) {
21760
21903
  }
21761
21904
  function workspaceOption(value) {
21762
21905
  const rootOptions = program.opts();
21763
- return resolve23(value ?? rootOptions.workspace ?? process.cwd());
21906
+ return resolve24(value ?? rootOptions.workspace ?? process.cwd());
21764
21907
  }
21765
21908
  function runtimeOptions(options) {
21766
21909
  const root = program.opts();