@wrongstack/tools 0.277.1 → 0.280.0

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/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as fs7 from 'node:fs/promises';
2
2
  import * as Core from '@wrongstack/core';
3
- import { ToolValidationError, FsError, toErrorMessage, atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, expectDefined, buildChildEnv, ToolError, FetchError, isPrivateIPv4, isPrivateIPv6, loadPlan, setPlanItemStatus, savePlan, loadTasks, saveTasks, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, mutateTasks, formatTaskList, formatPlan, assessCommitSafety, deepMerge, recordPackageAction, detectPackageEcosystem, getDesignKitLoader, isDesignStack, loadActiveKit, applyTokenOverrides, setActiveKit, recordKitChoice, recordOverrides, setDesignOverrides, materializeTokens, runDesignVerify, computeTaskItemProgress, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
3
+ import { SKILL_LIMITS, ToolValidationError, FsError, toErrorMessage, atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, expectDefined, buildChildEnv, ToolError, FetchError, isPrivateIPv4, isPrivateIPv6, loadPlan, setPlanItemStatus, savePlan, loadTasks, saveTasks, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, mutateTasks, formatTaskList, formatPlan, assessCommitSafety, deepMerge, recordPackageAction, detectPackageEcosystem, getDesignKitLoader, isDesignStack, loadActiveKit, applyTokenOverrides, setActiveKit, recordKitChoice, recordOverrides, setDesignOverrides, materializeTokens, runDesignVerify, stripFrontmatter, computeTaskItemProgress, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
4
4
  import * as path from 'node:path';
5
5
  import { resolve, sep, dirname, join } from 'node:path';
6
6
  import { spawn, execFileSync } from 'node:child_process';
@@ -21,14 +21,14 @@ import * as ts from 'typescript';
21
21
 
22
22
  // src/read.ts
23
23
  async function detectPackageManager(cwd) {
24
- const { stat: stat11 } = await import('node:fs/promises');
24
+ const { stat: stat12 } = await import('node:fs/promises');
25
25
  try {
26
- await stat11(`${cwd}/pnpm-lock.yaml`);
26
+ await stat12(`${cwd}/pnpm-lock.yaml`);
27
27
  return "pnpm";
28
28
  } catch {
29
29
  }
30
30
  try {
31
- await stat11(`${cwd}/yarn.lock`);
31
+ await stat12(`${cwd}/yarn.lock`);
32
32
  return "yarn";
33
33
  } catch {
34
34
  }
@@ -214,9 +214,9 @@ var readTool = {
214
214
  });
215
215
  }
216
216
  const absPath = await safeResolveReal(input.path, ctx);
217
- let stat11;
217
+ let stat12;
218
218
  try {
219
- stat11 = await fs7.stat(absPath);
219
+ stat12 = await fs7.stat(absPath);
220
220
  } catch (err) {
221
221
  const code = err.code;
222
222
  if (code === "ENOENT") {
@@ -235,7 +235,7 @@ var readTool = {
235
235
  cause: err
236
236
  });
237
237
  }
238
- if (!stat11.isFile()) {
238
+ if (!stat12.isFile()) {
239
239
  throw new FsError({
240
240
  message: `read: "${input.path}" is not a regular file`,
241
241
  code: "FS_READ_FAILED",
@@ -243,22 +243,22 @@ var readTool = {
243
243
  context: { reason: "not-a-regular-file" }
244
244
  });
245
245
  }
246
- if (stat11.size > MAX_BYTES) {
246
+ if (stat12.size > MAX_BYTES) {
247
247
  throw new FsError({
248
- message: `read: file too large (${stat11.size} bytes, limit ${MAX_BYTES})`,
248
+ message: `read: file too large (${stat12.size} bytes, limit ${MAX_BYTES})`,
249
249
  code: "FS_READ_FAILED",
250
250
  path: absPath,
251
- context: { size: stat11.size, limit: MAX_BYTES, reason: "too-large" }
251
+ context: { size: stat12.size, limit: MAX_BYTES, reason: "too-large" }
252
252
  });
253
253
  }
254
254
  const offset = Math.max(1, input.offset ?? 1);
255
255
  const limit = Math.max(0, Math.min(input.limit ?? 2e3, 5e3));
256
256
  const prior = getReadRangeRecord(ctx, absPath);
257
257
  const requestedEnd = prior ? Math.min(offset + limit - 1, prior.totalLines) : offset + limit - 1;
258
- if (input.mode !== "summary" && limit > 0 && prior && coversRange(prior, stat11.mtimeMs, offset, requestedEnd)) {
259
- ctx.recordRead(absPath, stat11.mtimeMs);
258
+ if (input.mode !== "summary" && limit > 0 && prior && coversRange(prior, stat12.mtimeMs, offset, requestedEnd)) {
259
+ ctx.recordRead(absPath, stat12.mtimeMs);
260
260
  return {
261
- text: `[unchanged since previous read: "${input.path}" mtime=${Math.round(stat11.mtimeMs)}; requested lines ${offset}-${requestedEnd} were already shown. Use offset/limit for a new range if needed.]`,
261
+ text: `[unchanged since previous read: "${input.path}" mtime=${Math.round(stat12.mtimeMs)}; requested lines ${offset}-${requestedEnd} were already shown. Use offset/limit for a new range if needed.]`,
262
262
  total_lines: prior.totalLines,
263
263
  encoding: "utf8",
264
264
  truncated: requestedEnd < prior.totalLines,
@@ -274,10 +274,10 @@ var readTool = {
274
274
  const allLines = text.split(/\r\n|\r|\n/);
275
275
  const total = allLines.length;
276
276
  if (input.mode === "summary") {
277
- ctx.recordRead(absPath, stat11.mtimeMs);
278
- rememberReadRange(ctx, absPath, stat11.mtimeMs, total, 1, Math.min(total, 200));
277
+ ctx.recordRead(absPath, stat12.mtimeMs);
278
+ rememberReadRange(ctx, absPath, stat12.mtimeMs, total, 1, Math.min(total, 200));
279
279
  return {
280
- text: summarizeFile(input.path, stat11.size, allLines),
280
+ text: summarizeFile(input.path, stat12.size, allLines),
281
281
  total_lines: total,
282
282
  encoding: "utf8",
283
283
  truncated: total > 200,
@@ -285,13 +285,13 @@ var readTool = {
285
285
  };
286
286
  }
287
287
  if (limit === 0) {
288
- ctx.recordRead(absPath, stat11.mtimeMs);
289
- rememberReadRange(ctx, absPath, stat11.mtimeMs, total, 1, 0);
288
+ ctx.recordRead(absPath, stat12.mtimeMs);
289
+ rememberReadRange(ctx, absPath, stat12.mtimeMs, total, 1, 0);
290
290
  return { text: "", total_lines: total, encoding: "utf8", truncated: total > 0 };
291
291
  }
292
292
  if (offset > total) {
293
- ctx.recordRead(absPath, stat11.mtimeMs);
294
- rememberReadRange(ctx, absPath, stat11.mtimeMs, total, total + 1, total + 1);
293
+ ctx.recordRead(absPath, stat12.mtimeMs);
294
+ rememberReadRange(ctx, absPath, stat12.mtimeMs, total, total + 1, total + 1);
295
295
  return {
296
296
  text: `[offset ${offset} is past end of file "${input.path}" \u2014 file has ${total} line(s). Do not retry this offset.]`,
297
297
  total_lines: total,
@@ -303,8 +303,8 @@ var readTool = {
303
303
  const truncated = offset - 1 + slice.length < total;
304
304
  const width = String(offset + slice.length - 1).length;
305
305
  const numbered = slice.map((line, i) => `${String(offset + i).padStart(width, " ")}\u2192${line}`).join("\n");
306
- ctx.recordRead(absPath, stat11.mtimeMs);
307
- rememberReadRange(ctx, absPath, stat11.mtimeMs, total, offset, offset + slice.length - 1);
306
+ ctx.recordRead(absPath, stat12.mtimeMs);
307
+ rememberReadRange(ctx, absPath, stat12.mtimeMs, total, offset, offset + slice.length - 1);
308
308
  return {
309
309
  text: numbered,
310
310
  total_lines: total,
@@ -410,12 +410,12 @@ var writeTool = {
410
410
  let existed = false;
411
411
  let prev = "";
412
412
  try {
413
- const stat12 = await fs7.stat(absPath);
414
- existed = stat12.isFile();
413
+ const stat13 = await fs7.stat(absPath);
414
+ existed = stat13.isFile();
415
415
  if (existed) {
416
416
  if (!ctx.hasRead(absPath)) {
417
417
  prev = await fs7.readFile(absPath, "utf8");
418
- ctx.recordRead(absPath, stat12.mtimeMs, "write");
418
+ ctx.recordRead(absPath, stat13.mtimeMs, "write");
419
419
  } else {
420
420
  prev = await fs7.readFile(absPath, "utf8");
421
421
  }
@@ -428,8 +428,8 @@ var writeTool = {
428
428
  await atomicWrite(absPath, input.content);
429
429
  const diff = existed ? unifiedDiff(prev, input.content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
430
430
  + (new file, ${input.content.split("\n").length} lines)`;
431
- const stat11 = await fs7.stat(absPath);
432
- ctx.recordRead(absPath, stat11.mtimeMs, "write");
431
+ const stat12 = await fs7.stat(absPath);
432
+ ctx.recordRead(absPath, stat12.mtimeMs, "write");
433
433
  ctx.session.recordFileChange({
434
434
  path: absPath,
435
435
  action: existed ? "modified" : "created",
@@ -487,7 +487,7 @@ var editTool = {
487
487
  });
488
488
  }
489
489
  const absPath = await safeResolveReal(input.path, ctx);
490
- const stat11 = await fs7.stat(absPath).catch((err) => {
490
+ const stat12 = await fs7.stat(absPath).catch((err) => {
491
491
  if (err.code === "ENOENT") {
492
492
  throw new ToolValidationError({
493
493
  message: `edit: file "${input.path}" does not exist. Use \`write\` instead.`,
@@ -497,7 +497,7 @@ var editTool = {
497
497
  }
498
498
  throw err;
499
499
  });
500
- if (!stat11.isFile()) {
500
+ if (!stat12.isFile()) {
501
501
  throw new ToolValidationError({
502
502
  message: `edit: "${input.path}" is not a regular file`,
503
503
  field: "path"
@@ -515,7 +515,7 @@ var editTool = {
515
515
  context: { reason: "external_modification" }
516
516
  });
517
517
  }
518
- if (autoRead && updated.mtimeMs > stat11.mtimeMs + mtimeTolerance) {
518
+ if (autoRead && updated.mtimeMs > stat12.mtimeMs + mtimeTolerance) {
519
519
  throw new ToolValidationError({
520
520
  message: `edit: file "${input.path}" changed while being auto-read. Retry the edit.`,
521
521
  field: "path",
@@ -732,8 +732,8 @@ var replaceTool = {
732
732
  }
733
733
  const rel = path.relative(realRoot, realPath);
734
734
  if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
735
- const stat11 = await fs7.stat(realPath).catch(() => null);
736
- if (!stat11?.isFile()) continue;
735
+ const stat12 = await fs7.stat(realPath).catch(() => null);
736
+ if (!stat12?.isFile()) continue;
737
737
  let content;
738
738
  try {
739
739
  const buf = await fs7.readFile(realPath);
@@ -758,7 +758,7 @@ var replaceTool = {
758
758
  totalReplacements += count;
759
759
  if (!dryRun) {
760
760
  const newContent = toStyle(newContentLf, style);
761
- await atomicWrite(realPath, newContent, { mode: stat11.mode & 511 });
761
+ await atomicWrite(realPath, newContent, { mode: stat12.mode & 511 });
762
762
  }
763
763
  const diff = dryRun || matches.length > 0 ? unifiedDiff(content, toStyle(newContentLf, style), {
764
764
  fromFile: absPath,
@@ -788,8 +788,8 @@ async function resolveFiles(filesInput, ctx, extraGlob) {
788
788
  const resolved = [];
789
789
  for (const p of parts) {
790
790
  const absPath = safeResolve(p, ctx);
791
- const stat11 = await fs7.stat(absPath).catch(() => null);
792
- if (stat11?.isFile()) {
791
+ const stat12 = await fs7.stat(absPath).catch(() => null);
792
+ if (stat12?.isFile()) {
793
793
  resolved.push(absPath);
794
794
  }
795
795
  }
@@ -807,13 +807,13 @@ async function globFiles(pattern, base, extraGlob) {
807
807
  return await globNative(pattern, base, extraGlob);
808
808
  }
809
809
  function checkRg() {
810
- return new Promise((resolve7) => {
810
+ return new Promise((resolve8) => {
811
811
  try {
812
812
  const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore", windowsHide: true });
813
- p.on("error", () => resolve7(false));
814
- p.on("close", (code) => resolve7(code === 0));
813
+ p.on("error", () => resolve8(false));
814
+ p.on("close", (code) => resolve8(code === 0));
815
815
  } catch {
816
- resolve7(false);
816
+ resolve8(false);
817
817
  }
818
818
  });
819
819
  }
@@ -830,10 +830,10 @@ function spawnRgFind(pattern, base) {
830
830
  buf += chunk.toString();
831
831
  });
832
832
  return {
833
- promise: new Promise((resolve7, reject) => {
833
+ promise: new Promise((resolve8, reject) => {
834
834
  child.on("error", reject);
835
835
  child.on("close", () => {
836
- resolve7(buf.split("\n").filter(Boolean));
836
+ resolve8(buf.split("\n").filter(Boolean));
837
837
  });
838
838
  })
839
839
  };
@@ -841,7 +841,7 @@ function spawnRgFind(pattern, base) {
841
841
  async function globNative(pattern, base, extraGlob) {
842
842
  const results = [];
843
843
  const globRe = compileGlob(pattern);
844
- const walk = async (dir) => {
844
+ const walk2 = async (dir) => {
845
845
  let entries;
846
846
  try {
847
847
  entries = await fs7.readdir(dir, { withFileTypes: true });
@@ -852,13 +852,13 @@ async function globNative(pattern, base, extraGlob) {
852
852
  if (DEFAULT_IGNORE.includes(e.name)) continue;
853
853
  const full = path.join(dir, e.name);
854
854
  try {
855
- const stat11 = await fs7.lstat(full);
856
- if (stat11.isSymbolicLink()) continue;
855
+ const stat12 = await fs7.lstat(full);
856
+ if (stat12.isSymbolicLink()) continue;
857
857
  } catch {
858
858
  continue;
859
859
  }
860
860
  if (e.isDirectory()) {
861
- await walk(full);
861
+ await walk2(full);
862
862
  } else if (e.isFile()) {
863
863
  const name = e.name;
864
864
  if (globRe.test(name) || globRe.test(full)) {
@@ -870,7 +870,7 @@ async function globNative(pattern, base, extraGlob) {
870
870
  }
871
871
  }
872
872
  };
873
- await walk(base);
873
+ await walk2(base);
874
874
  return results;
875
875
  }
876
876
 
@@ -947,7 +947,7 @@ var globTool = {
947
947
  } catch {
948
948
  }
949
949
  };
950
- const walk = async (dir, relPrefix) => {
950
+ const walk2 = async (dir, relPrefix) => {
951
951
  if (results.length >= limit) {
952
952
  truncated = true;
953
953
  return;
@@ -1000,9 +1000,9 @@ var globTool = {
1000
1000
  await mapWithConcurrency(matchedFiles, WALK_CONCURRENCY, pushResult);
1001
1001
  if (truncated) return;
1002
1002
  const remainingSubdirs = truncated ? [] : subdirs;
1003
- await mapWithConcurrency(remainingSubdirs, WALK_CONCURRENCY, ({ full, rel }) => walk(full, rel));
1003
+ await mapWithConcurrency(remainingSubdirs, WALK_CONCURRENCY, ({ full, rel }) => walk2(full, rel));
1004
1004
  };
1005
- await walk(base, "");
1005
+ await walk2(base, "");
1006
1006
  results.sort((a, b) => b.mtime - a.mtime);
1007
1007
  return { files: results.map((r) => r.rel), truncated };
1008
1008
  }
@@ -1106,13 +1106,13 @@ var grepTool = {
1106
1106
  }
1107
1107
  };
1108
1108
  async function detectRg(signal) {
1109
- return new Promise((resolve7) => {
1109
+ return new Promise((resolve8) => {
1110
1110
  try {
1111
1111
  const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore", signal, windowsHide: true });
1112
- p.on("error", () => resolve7(false));
1113
- p.on("close", (code) => resolve7(code === 0));
1112
+ p.on("error", () => resolve8(false));
1113
+ p.on("close", (code) => resolve8(code === 0));
1114
1114
  } catch {
1115
- resolve7(false);
1115
+ resolve8(false);
1116
1116
  }
1117
1117
  });
1118
1118
  }
@@ -1263,8 +1263,8 @@ async function runNative(input, base, mode, limit, signal) {
1263
1263
  if (globRe && !globRe.test(name) && !globRe.test(full)) return;
1264
1264
  if (globRe) globRe.lastIndex = 0;
1265
1265
  try {
1266
- const stat11 = await fs7.stat(full);
1267
- if (!stat11.isFile() || stat11.size > maxBytes || stopped || signal.aborted) return;
1266
+ const stat12 = await fs7.stat(full);
1267
+ if (!stat12.isFile() || stat12.size > maxBytes || stopped || signal.aborted) return;
1268
1268
  const file = await fs7.open(full, "r");
1269
1269
  try {
1270
1270
  let bytesReadTotal = 0;
@@ -1340,7 +1340,7 @@ async function runNative(input, base, mode, limit, signal) {
1340
1340
  } catch {
1341
1341
  }
1342
1342
  };
1343
- const walk = async (dir) => {
1343
+ const walk2 = async (dir) => {
1344
1344
  if (stopped || signal.aborted) return;
1345
1345
  let entries;
1346
1346
  try {
@@ -1362,9 +1362,9 @@ async function runNative(input, base, mode, limit, signal) {
1362
1362
  }
1363
1363
  }
1364
1364
  await mapWithConcurrency(files, NATIVE_SCAN_CONCURRENCY, ({ full, name }) => scanFile(full, name));
1365
- await mapWithConcurrency(subdirs, Math.min(16, NATIVE_SCAN_CONCURRENCY), walk);
1365
+ await mapWithConcurrency(subdirs, Math.min(16, NATIVE_SCAN_CONCURRENCY), walk2);
1366
1366
  };
1367
- await walk(base);
1367
+ await walk2(base);
1368
1368
  return {
1369
1369
  matches,
1370
1370
  count: total,
@@ -2643,7 +2643,7 @@ function looksLikePowerShell(command) {
2643
2643
  if (/&\s+\$/.test(trimmed)) return true;
2644
2644
  if (/(^|\s)@\s*\(/.test(trimmed)) return true;
2645
2645
  if (/(^|\s)@\{/.test(trimmed)) return true;
2646
- if (/(?:^|[\s\[\(\{,;])(?:-eq|-ne|-lt|-gt|-le|-ge|-like|-notlike|-match|-notmatch|-contains|-notcontains|-in|-notin|-and|-or|-not|-band|-bor|-bxor|-replace|-isplit|-csplit|-osplit|-join|-is|-as|-f)(?:$|[\s\]\)\},;])/i.test(trimmed)) {
2646
+ if (/(?:^|[\s[({,;])(?:-eq|-ne|-lt|-gt|-le|-ge|-like|-notlike|-match|-notmatch|-contains|-notcontains|-in|-notin|-and|-or|-not|-band|-bor|-bxor|-replace|-isplit|-csplit|-osplit|-join|-is|-as|-f)(?:$|[\s\])},;])/i.test(trimmed)) {
2647
2647
  return true;
2648
2648
  }
2649
2649
  if (PS_VERB_RE.test(trimmed)) return true;
@@ -2670,7 +2670,7 @@ function looksLikePowerShellExtended(command) {
2670
2670
  return true;
2671
2671
  }
2672
2672
  if (/^\s*<#|#>\s*$/m.test(trimmed)) return true;
2673
- if (/(?:^|\s)[-/\/](?:AsPlainText|PipelineVariable|pv|FilterHashtable|OutVariable|ov)(?:\s|=|$)/i.test(trimmed)) {
2673
+ if (/(?:^|\s)[-//](?:AsPlainText|PipelineVariable|pv|FilterHashtable|OutVariable|ov)(?:\s|=|$)/i.test(trimmed)) {
2674
2674
  return true;
2675
2675
  }
2676
2676
  return false;
@@ -3093,10 +3093,10 @@ var bashTool = {
3093
3093
  queue.push(c);
3094
3094
  }
3095
3095
  };
3096
- const next = () => new Promise((resolve7) => {
3096
+ const next = () => new Promise((resolve8) => {
3097
3097
  const c = queue.shift();
3098
- if (c) resolve7(c);
3099
- else resolveNext = resolve7;
3098
+ if (c) resolve8(c);
3099
+ else resolveNext = resolve8;
3100
3100
  });
3101
3101
  let lastFlush = Date.now();
3102
3102
  const flush = () => {
@@ -3257,6 +3257,294 @@ function ensureSessionShell(opts = {}) {
3257
3257
  env["WRONGSTACK_SHELL"] = chosen;
3258
3258
  return chosen;
3259
3259
  }
3260
+
3261
+ // src/_danger-detect.ts
3262
+ var argHas = (args, value) => args.includes(value);
3263
+ var argMatches = (args, re) => args.some((a) => re.test(a));
3264
+ var hasShortFlags = (args, letters) => {
3265
+ const seen = /* @__PURE__ */ new Set();
3266
+ for (const a of args) {
3267
+ if (!a.startsWith("-") || a.startsWith("--")) continue;
3268
+ for (const ch of a.replace(/^-+/, "")) seen.add(ch);
3269
+ }
3270
+ return letters.split("").every((l) => seen.has(l));
3271
+ };
3272
+ var RULES = [
3273
+ // ----- rm / rmdir: recursive force delete (any path) -----
3274
+ // Note: BLOCKED_ARG_PATTERNS already hard-denies root/home/glob paths,
3275
+ // but `rm -rf ./build` is a normal dev workflow that the user might
3276
+ // want to do intentionally. We downgrade it to 'destructive' so the
3277
+ // confirm prompt can approve.
3278
+ {
3279
+ id: "rm-recursive",
3280
+ level: "destructive",
3281
+ test: (cmd, args) => (cmd === "rm" || cmd === "rmdir") && hasShortFlags(args, "rf"),
3282
+ reason: "recursive force-delete"
3283
+ },
3284
+ // ----- Windows PowerShell Remove-Item: -Recurse -Force -----
3285
+ {
3286
+ id: "powershell-remove-item-recursive-force",
3287
+ level: "destructive",
3288
+ test: (cmd, args) => {
3289
+ if (cmd !== "powershell" && cmd !== "pwsh") return false;
3290
+ const hasRecurse = argMatches(args, /^-(?:R|Recurse|Recurse\s)/);
3291
+ const hasForce = argHas(args, "-Force") || argHas(args, "-F");
3292
+ if (argHas(args, "-WhatIf")) return false;
3293
+ return hasRecurse && hasForce;
3294
+ },
3295
+ reason: "Remove-Item with -Recurse -Force"
3296
+ },
3297
+ // ----- find -exec / -ok / -execdir -----
3298
+ {
3299
+ id: "find-exec",
3300
+ level: "destructive",
3301
+ test: (cmd, args) => {
3302
+ if (cmd !== "find") return false;
3303
+ return args.some(
3304
+ (a) => a === "-exec" || a === "-exec;" || a === "-ok" || a === "-ok;" || a === "-execdir" || a === "-execdir;" || a.startsWith("-exec=") || a.startsWith("-ok=") || a.startsWith("-execdir=")
3305
+ );
3306
+ },
3307
+ reason: "find with -exec/-ok (executes arbitrary command on matches)"
3308
+ },
3309
+ // ----- git --exec= / --upload-pack= / --receive-pack= -----
3310
+ // These run arbitrary commands via the git transport layer.
3311
+ {
3312
+ id: "git-exec",
3313
+ level: "destructive",
3314
+ test: (cmd, args) => cmd === "git" && args.some(
3315
+ (a) => a.startsWith("--exec=") || a.startsWith("--upload-pack=") || a.startsWith("--receive-pack=") || a === "--exec" || a === "--upload-pack" || a === "--receive-pack"
3316
+ ),
3317
+ reason: "git with --exec/--upload-pack/--receive-pack (runs arbitrary code)"
3318
+ },
3319
+ // ----- Windows: format / diskpart / bcdedit -----
3320
+ {
3321
+ id: "win32-format",
3322
+ level: "destructive",
3323
+ test: (cmd) => cmd === "format" || cmd === "format.exe",
3324
+ reason: "format (Windows disk format)"
3325
+ },
3326
+ {
3327
+ id: "win32-diskpart",
3328
+ level: "destructive",
3329
+ test: (cmd) => cmd === "diskpart" || cmd === "diskpart.exe",
3330
+ reason: "diskpart (Windows partition editor)"
3331
+ },
3332
+ {
3333
+ id: "win32-bcdedit",
3334
+ level: "destructive",
3335
+ test: (cmd) => cmd === "bcdedit" || cmd === "bcdedit.exe",
3336
+ reason: "bcdedit (Windows boot config editor)"
3337
+ },
3338
+ // ----- mkfs family -----
3339
+ {
3340
+ id: "mkfs",
3341
+ level: "destructive",
3342
+ test: (cmd) => /^mkfs(\.[a-z0-9]+)?$/.test(cmd) || cmd === "mkswap",
3343
+ reason: "mkfs (filesystem creation \u2014 destroys existing data)"
3344
+ },
3345
+ // ----- dd writing to a block device -----
3346
+ {
3347
+ id: "dd-to-block-device",
3348
+ level: "destructive",
3349
+ test: (cmd, args) => {
3350
+ if (cmd !== "dd") return false;
3351
+ return args.some((a) => /of=\/dev\/(sd|hd|nvme|vd|mmcblk|xvd|loop|disk)/.test(a));
3352
+ },
3353
+ reason: "dd writing to a block device"
3354
+ },
3355
+ // ----- Secure-erase tools -----
3356
+ {
3357
+ id: "shred",
3358
+ level: "destructive",
3359
+ test: (cmd) => cmd === "shred" || cmd === "shred.exe",
3360
+ reason: "shred (secure file delete)"
3361
+ },
3362
+ {
3363
+ id: "wipefs",
3364
+ level: "destructive",
3365
+ test: (cmd) => cmd === "wipefs" || cmd === "wipefs.exe",
3366
+ reason: "wipefs (signature wipe \u2014 destroys filesystem headers)"
3367
+ },
3368
+ {
3369
+ id: "sdelete",
3370
+ level: "destructive",
3371
+ test: (cmd) => cmd === "sdelete" || cmd === "sdelete.exe",
3372
+ reason: "sdelete (Sysinternals secure delete)"
3373
+ },
3374
+ // ----- VCS history rewrite (destructive) -----
3375
+ // `git push --force` / `-f` rewrites remote history. `--force-with-lease`
3376
+ // is the safer variant (checks remote hasn't moved) but still rewrites.
3377
+ {
3378
+ id: "git-push-force",
3379
+ level: "destructive",
3380
+ test: (cmd, args) => {
3381
+ if (cmd !== "git") return false;
3382
+ const pushIdx = args.indexOf("push");
3383
+ if (pushIdx < 0) return false;
3384
+ for (let i = pushIdx + 1; i < args.length; i++) {
3385
+ const a = args[i];
3386
+ if (a === "--force" || a === "-f" || a === "--force-with-lease") return true;
3387
+ if (!a.startsWith("-") && !a.includes("=")) continue;
3388
+ if (a.startsWith("--force")) return true;
3389
+ }
3390
+ return false;
3391
+ },
3392
+ reason: "git push with --force / -f (rewrites remote history)"
3393
+ },
3394
+ // ----- git reset --hard (destructive) -----
3395
+ {
3396
+ id: "git-reset-hard",
3397
+ level: "destructive",
3398
+ test: (cmd, args) => cmd === "git" && args.some((a) => a === "--hard" || a.startsWith("--hard=")),
3399
+ reason: "git reset --hard (discards working tree + index)"
3400
+ },
3401
+ // ----- git clean -f / -fd (destructive) -----
3402
+ {
3403
+ id: "git-clean-force",
3404
+ level: "destructive",
3405
+ test: (cmd, args) => {
3406
+ if (cmd !== "git") return false;
3407
+ const cleanIdx = args.indexOf("clean");
3408
+ if (cleanIdx < 0) return false;
3409
+ return args.slice(cleanIdx + 1).some(
3410
+ (a) => a === "-f" || a === "--force" || a.startsWith("-f") || a.startsWith("--force=")
3411
+ );
3412
+ },
3413
+ reason: "git clean -f (deletes untracked files)"
3414
+ },
3415
+ // ----- package publish (destructive — public, irreversible) -----
3416
+ {
3417
+ id: "npm-publish",
3418
+ level: "destructive",
3419
+ test: (cmd, args) => {
3420
+ if (!["npm", "pnpm", "yarn", "bun", "cargo"].includes(cmd)) return false;
3421
+ return args.includes("publish") || cmd === "cargo" && args.includes("yank");
3422
+ },
3423
+ reason: "publishing to a public package registry (hard to reverse)"
3424
+ },
3425
+ // ----- k8s cluster-wide destructive ops (destructive) -----
3426
+ {
3427
+ id: "kubectl-delete-namespace",
3428
+ level: "destructive",
3429
+ test: (cmd, args) => {
3430
+ if (cmd !== "kubectl") return false;
3431
+ const delIdx = args.indexOf("delete");
3432
+ if (delIdx < 0) return false;
3433
+ const after = args.slice(delIdx + 1);
3434
+ return after[0] === "namespace" || after[0] === "ns";
3435
+ },
3436
+ reason: "kubectl delete namespace (deletes all resources in the namespace)"
3437
+ },
3438
+ {
3439
+ id: "kubectl-drain",
3440
+ level: "destructive",
3441
+ test: (cmd, args) => cmd === "kubectl" && args.includes("drain"),
3442
+ reason: "kubectl drain (evicts pods, marks node unschedulable)"
3443
+ },
3444
+ // ----- inline code evaluation (caution — high false-positive) -----
3445
+ // Common in scripts: `python -c "..."`, `node -e "..."`, `bash -c "..."`.
3446
+ // We tag 'caution' rather than 'destructive' because these are used in
3447
+ // many legitimate one-liners (e.g. `python -c "print(1)"`).
3448
+ {
3449
+ id: "inline-eval",
3450
+ level: "caution",
3451
+ test: (cmd, args) => {
3452
+ if (![
3453
+ "python",
3454
+ "python3",
3455
+ "python2",
3456
+ "node",
3457
+ "bash",
3458
+ "sh",
3459
+ "zsh",
3460
+ "ruby",
3461
+ "perl",
3462
+ "lua"
3463
+ ].includes(cmd)) {
3464
+ return false;
3465
+ }
3466
+ return args.some(
3467
+ (a) => a === "-c" || a === "-e" || a === "--eval" || a === "-eval" || a === "-E"
3468
+ );
3469
+ },
3470
+ reason: "inline script evaluation (-c / -e / --eval)"
3471
+ },
3472
+ // ----- pipe-to-shell (caution — well-known exfil pattern) -----
3473
+ // The classic `curl https://... | sh` download-and-run vector. Detected by
3474
+ // looking for a known fetcher followed by a shell sink. We use a simple
3475
+ // substring scan; false positives are limited because both tokens must
3476
+ // appear in the same argv.
3477
+ {
3478
+ id: "pipe-to-shell",
3479
+ level: "caution",
3480
+ test: (_cmd, args) => {
3481
+ const hasFetcher = args.some(
3482
+ (a) => /^(curl|wget|fetch|httpie|http)$/i.test(a) || a.startsWith("curl") || a.startsWith("wget")
3483
+ );
3484
+ const hasShellSink = args.some(
3485
+ (a) => a === "sh" || a === "bash" || a === "zsh" || a === "fish" || a === "pwsh" || a === "powershell" || a.endsWith("/sh") || a.endsWith("/bash") || a.endsWith("/zsh") || a.endsWith("/pwsh")
3486
+ );
3487
+ return hasFetcher && hasShellSink;
3488
+ },
3489
+ reason: "network fetch piped to a shell (download-and-run pattern)"
3490
+ },
3491
+ // ----- privilege escalation (caution) -----
3492
+ {
3493
+ id: "sudo",
3494
+ level: "caution",
3495
+ test: (cmd) => cmd === "sudo" || cmd === "doas",
3496
+ reason: "privilege escalation (sudo / doas)"
3497
+ },
3498
+ {
3499
+ id: "runas",
3500
+ level: "caution",
3501
+ test: (cmd) => cmd === "runas" || cmd === "runas.exe",
3502
+ reason: "Windows runas (run as different user)"
3503
+ },
3504
+ // ----- world-writable permissions (caution) -----
3505
+ // `chmod 777` is rarely correct. `chmod -R 777` is almost always wrong.
3506
+ // We only flag octal modes; symbolic modes like `chmod o+w` are
3507
+ // left to the operator's discretion.
3508
+ {
3509
+ id: "chmod-world-writable",
3510
+ level: "caution",
3511
+ test: (cmd, args) => {
3512
+ if (cmd !== "chmod") return false;
3513
+ return args.some((a) => /^[0-7]{3,4}$/.test(a) && /7/.test(a));
3514
+ },
3515
+ reason: "chmod with world-writable octal mode (e.g. 777)"
3516
+ }
3517
+ ];
3518
+ function detectDanger(cmd, args, bypass) {
3519
+ const reasons = [];
3520
+ let level = "safe";
3521
+ let matchedRule;
3522
+ for (const rule of RULES) {
3523
+ if (bypass?.has(rule.id)) continue;
3524
+ if (!rule.test(cmd, args)) continue;
3525
+ reasons.push(rule.reason);
3526
+ matchedRule = rule.id;
3527
+ if (levelRank(rule.level) > levelRank(level)) {
3528
+ level = rule.level;
3529
+ }
3530
+ }
3531
+ if (level === "safe") return { level: "safe", reasons: [] };
3532
+ const result = { level, reasons };
3533
+ if (matchedRule !== void 0) result.matchedRule = matchedRule;
3534
+ return result;
3535
+ }
3536
+ function levelRank(level) {
3537
+ switch (level) {
3538
+ case "safe":
3539
+ return 0;
3540
+ case "caution":
3541
+ return 1;
3542
+ case "destructive":
3543
+ return 2;
3544
+ }
3545
+ }
3546
+
3547
+ // src/exec.ts
3260
3548
  var isWin2 = process.platform === "win32";
3261
3549
  var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
3262
3550
  // JS / TS toolchain
@@ -3876,6 +4164,21 @@ function configureExecPolicy(opts = {}) {
3876
4164
  function resetExecPolicy() {
3877
4165
  allowedCommands = new Set(DEFAULT_ALLOWED_COMMANDS);
3878
4166
  }
4167
+ var dangerBypass = /* @__PURE__ */ new Set();
4168
+ function configureDangerBypass(opts = {}) {
4169
+ const next = /* @__PURE__ */ new Set();
4170
+ for (const id of opts.bypass ?? []) {
4171
+ const trimmed = id.trim();
4172
+ if (trimmed) next.add(trimmed);
4173
+ }
4174
+ dangerBypass = next;
4175
+ }
4176
+ function resetDangerBypass() {
4177
+ dangerBypass = /* @__PURE__ */ new Set();
4178
+ }
4179
+ function getDangerBypass() {
4180
+ return dangerBypass;
4181
+ }
3879
4182
  function isExecCommandAllowed(cmd) {
3880
4183
  return allowedCommands.has(normalizeCmd(cmd));
3881
4184
  }
@@ -3967,6 +4270,7 @@ function validateArgs(cmd, args) {
3967
4270
  }
3968
4271
  return null;
3969
4272
  }
4273
+ var SAFE_DANGER = { level: "safe", reasons: [] };
3970
4274
  var execTool = {
3971
4275
  name: "exec",
3972
4276
  category: "Shell",
@@ -4011,7 +4315,8 @@ var execTool = {
4011
4315
  stderr: "Circuit breaker is open \u2014 too many consecutive failures. Use /kill reset to recover.",
4012
4316
  exitCode: 1,
4013
4317
  truncated: false,
4014
- allowed: false
4318
+ allowed: false,
4319
+ danger: SAFE_DANGER
4015
4320
  };
4016
4321
  }
4017
4322
  const cmd = input.command.trim();
@@ -4023,7 +4328,8 @@ var execTool = {
4023
4328
  stderr: "Empty command",
4024
4329
  exitCode: 1,
4025
4330
  truncated: false,
4026
- allowed: false
4331
+ allowed: false,
4332
+ danger: SAFE_DANGER
4027
4333
  };
4028
4334
  if (!isExecCommandAllowed(cmd)) {
4029
4335
  return {
@@ -4033,11 +4339,13 @@ var execTool = {
4033
4339
  stderr: `Command "${cmd}" not in allowlist. Add it to your ~/.wrongstack/config.json under "tools": { "exec": { "allow": ["${cmd}"] } }, or use the bash tool for one-off arbitrary commands.`,
4034
4340
  exitCode: 1,
4035
4341
  truncated: false,
4036
- allowed: false
4342
+ allowed: false,
4343
+ danger: SAFE_DANGER
4037
4344
  };
4038
4345
  }
4039
4346
  const args = (input.args ?? []).slice(0, MAX_ARGS);
4040
4347
  const timeout = Math.max(1, Math.min(input.timeout ?? DEFAULT_TIMEOUT_MS2, DEFAULT_TIMEOUT_MS2));
4348
+ const danger = detectDanger(cmd, args, dangerBypass);
4041
4349
  const argError = validateArgs(cmd, args);
4042
4350
  if (argError) {
4043
4351
  return {
@@ -4047,7 +4355,8 @@ var execTool = {
4047
4355
  stderr: argError,
4048
4356
  exitCode: 1,
4049
4357
  truncated: false,
4050
- allowed: false
4358
+ allowed: false,
4359
+ danger
4051
4360
  };
4052
4361
  }
4053
4362
  let cwd;
@@ -4061,15 +4370,16 @@ var execTool = {
4061
4370
  stderr: `cwd "${input.cwd ?? ctx.cwd}" resolves outside project root`,
4062
4371
  exitCode: 1,
4063
4372
  truncated: false,
4064
- allowed: false
4373
+ allowed: false,
4374
+ danger
4065
4375
  };
4066
4376
  }
4067
4377
  const signal = opts.signal;
4068
- return runCommand(cmd, args, cwd, timeout, signal, ctx.session?.id);
4378
+ return runCommand(cmd, args, cwd, timeout, signal, ctx.session?.id, danger);
4069
4379
  }
4070
4380
  };
4071
- function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
4072
- return new Promise((resolve7) => {
4381
+ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
4382
+ return new Promise((resolve8) => {
4073
4383
  let stdout = "";
4074
4384
  let stderr = "";
4075
4385
  let killed = false;
@@ -4077,7 +4387,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
4077
4387
  const finish = (result) => {
4078
4388
  if (resolvedOnce.value) return;
4079
4389
  resolvedOnce.value = true;
4080
- resolve7(result);
4390
+ resolve8(result);
4081
4391
  };
4082
4392
  const startedAt = Date.now();
4083
4393
  const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
@@ -4105,7 +4415,8 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
4105
4415
  stderr: `spawn failed: ${toErrorMessage$1(err)}`,
4106
4416
  exitCode: 1,
4107
4417
  truncated: false,
4108
- allowed: true
4418
+ allowed: true,
4419
+ danger
4109
4420
  });
4110
4421
  return;
4111
4422
  }
@@ -4124,7 +4435,8 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
4124
4435
  stderr: stderrText,
4125
4436
  exitCode: isAbort ? 124 : 1,
4126
4437
  truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
4127
- allowed: true
4438
+ allowed: true,
4439
+ danger
4128
4440
  });
4129
4441
  });
4130
4442
  const registry = getProcessRegistry();
@@ -4172,7 +4484,8 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
4172
4484
  stderr: normalizeCommandOutput(stderr),
4173
4485
  exitCode,
4174
4486
  truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES || Buffer.byteLength(stderr, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
4175
- allowed: true
4487
+ allowed: true,
4488
+ danger
4176
4489
  });
4177
4490
  });
4178
4491
  });
@@ -5436,8 +5749,8 @@ function findGitDir(cwd, projectRoot) {
5436
5749
  let dir = cwd;
5437
5750
  for (let i = 0; i < 20; i++) {
5438
5751
  try {
5439
- const stat11 = statSync(`${dir}/.git`);
5440
- if (stat11.isDirectory() || stat11.isFile()) return dir;
5752
+ const stat12 = statSync(`${dir}/.git`);
5753
+ if (stat12.isDirectory() || stat12.isFile()) return dir;
5441
5754
  } catch {
5442
5755
  }
5443
5756
  if (dir === root) break;
@@ -5518,7 +5831,7 @@ function buildArgs(input) {
5518
5831
  }
5519
5832
  }
5520
5833
  function runGit(args, cwd, signal) {
5521
- return new Promise((resolve7) => {
5834
+ return new Promise((resolve8) => {
5522
5835
  let stdout = "";
5523
5836
  let stderr = "";
5524
5837
  const child = spawn("git", args, {
@@ -5539,7 +5852,7 @@ function runGit(args, cwd, signal) {
5539
5852
  }
5540
5853
  });
5541
5854
  child.on("error", (err) => {
5542
- resolve7({
5855
+ resolve8({
5543
5856
  command: args[0],
5544
5857
  stdout: normalizeCommandOutput(stdout),
5545
5858
  stderr: err.message,
@@ -5548,7 +5861,7 @@ function runGit(args, cwd, signal) {
5548
5861
  });
5549
5862
  });
5550
5863
  child.on("close", (code) => {
5551
- resolve7({
5864
+ resolve8({
5552
5865
  command: args[0],
5553
5866
  stdout: normalizeCommandOutput(stdout),
5554
5867
  stderr: normalizeCommandOutput(stderr),
@@ -5648,7 +5961,7 @@ function stripPathComponents(p, strip) {
5648
5961
  return parts.slice(strip).join("/");
5649
5962
  }
5650
5963
  function runPatch(args, cwd, signal) {
5651
- return new Promise((resolve7) => {
5964
+ return new Promise((resolve8) => {
5652
5965
  let stdout = "";
5653
5966
  let stderr = "";
5654
5967
  const env = { ...buildChildEnv(), LANG: "C", LC_ALL: "C" };
@@ -5659,8 +5972,8 @@ function runPatch(args, cwd, signal) {
5659
5972
  child.stderr?.on("data", (c) => {
5660
5973
  stderr += c.toString();
5661
5974
  });
5662
- child.on("close", (code) => resolve7({ exitCode: code ?? 1, stdout, stderr }));
5663
- child.on("error", (e) => resolve7({ exitCode: 1, stdout: "", stderr: e.message }));
5975
+ child.on("close", (code) => resolve8({ exitCode: code ?? 1, stdout, stderr }));
5976
+ child.on("error", (e) => resolve8({ exitCode: 1, stdout: "", stderr: e.message }));
5664
5977
  });
5665
5978
  }
5666
5979
  function extractPatchedFiles(output) {
@@ -5733,7 +6046,6 @@ var jsonTool = {
5733
6046
  return executeTransform(input, ctx);
5734
6047
  case "merge":
5735
6048
  return executeMerge(input);
5736
- case "parse":
5737
6049
  default:
5738
6050
  return executeParse(input, ctx);
5739
6051
  }
@@ -6049,56 +6361,56 @@ function jmespathSearch(data, query) {
6049
6361
  }
6050
6362
  function validateJsonSchema(data, schema) {
6051
6363
  const errors = [];
6052
- function check(value, s, path22) {
6364
+ function check(value, s, path23) {
6053
6365
  if (s["type"]) {
6054
6366
  const expectedType = s["type"];
6055
6367
  const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
6056
6368
  if (expectedType === "integer") {
6057
- if (!Number.isInteger(value)) errors.push(`${path22}: expected integer, got ${actualType}`);
6369
+ if (!Number.isInteger(value)) errors.push(`${path23}: expected integer, got ${actualType}`);
6058
6370
  } else if (expectedType !== actualType) {
6059
- errors.push(`${path22}: expected ${expectedType}, got ${actualType}`);
6371
+ errors.push(`${path23}: expected ${expectedType}, got ${actualType}`);
6060
6372
  }
6061
6373
  }
6062
6374
  if (typeof value === "string" && s["format"] === "uri" && value) {
6063
6375
  try {
6064
6376
  new URL(value);
6065
6377
  } catch {
6066
- errors.push(`${path22}: not a valid URI`);
6378
+ errors.push(`${path23}: not a valid URI`);
6067
6379
  }
6068
6380
  }
6069
6381
  if (typeof value === "string" && s["pattern"]) {
6070
6382
  const re = new RegExp(s["pattern"]);
6071
- if (!re.test(value)) errors.push(`${path22}: does not match pattern ${s["pattern"]}`);
6383
+ if (!re.test(value)) errors.push(`${path23}: does not match pattern ${s["pattern"]}`);
6072
6384
  }
6073
6385
  if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
6074
- errors.push(`${path22}: string too short (min ${s["minLength"]})`);
6386
+ errors.push(`${path23}: string too short (min ${s["minLength"]})`);
6075
6387
  }
6076
6388
  if (typeof value === "string" && s["maxLength"] !== void 0 && value.length > s["maxLength"]) {
6077
- errors.push(`${path22}: string too long (max ${s["maxLength"]})`);
6389
+ errors.push(`${path23}: string too long (max ${s["maxLength"]})`);
6078
6390
  }
6079
6391
  if (typeof value === "number" && s["minimum"] !== void 0 && value < s["minimum"]) {
6080
- errors.push(`${path22}: below minimum ${s["minimum"]}`);
6392
+ errors.push(`${path23}: below minimum ${s["minimum"]}`);
6081
6393
  }
6082
6394
  if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
6083
- errors.push(`${path22}: above maximum ${s["maximum"]}`);
6395
+ errors.push(`${path23}: above maximum ${s["maximum"]}`);
6084
6396
  }
6085
6397
  if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
6086
6398
  for (let i = 0; i < value.length; i++) {
6087
- check(value[i], s["items"], `${path22}[${i}]`);
6399
+ check(value[i], s["items"], `${path23}[${i}]`);
6088
6400
  }
6089
6401
  }
6090
6402
  if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
6091
6403
  const props = s["properties"];
6092
6404
  for (const [k, propSchema] of Object.entries(props)) {
6093
- check(value[k], propSchema, `${path22}.${k}`);
6405
+ check(value[k], propSchema, `${path23}.${k}`);
6094
6406
  }
6095
6407
  }
6096
6408
  }
6097
6409
  check(data, schema, "$");
6098
6410
  return { valid: errors.length === 0, errors };
6099
6411
  }
6100
- function simpleQuery(data, path22) {
6101
- const parts = path22.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
6412
+ function simpleQuery(data, path23) {
6413
+ const parts = path23.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
6102
6414
  let current = data;
6103
6415
  for (const part of parts) {
6104
6416
  if (current === null || current === void 0) return void 0;
@@ -6228,8 +6540,8 @@ function findGitDir2(cwd) {
6228
6540
  let dir = cwd;
6229
6541
  for (let i = 0; i < 20; i++) {
6230
6542
  try {
6231
- const stat11 = statSync(path.join(dir, ".git"));
6232
- if (stat11.isDirectory()) return dir;
6543
+ const stat12 = statSync(path.join(dir, ".git"));
6544
+ if (stat12.isDirectory()) return dir;
6233
6545
  } catch {
6234
6546
  }
6235
6547
  const parent = path.dirname(dir);
@@ -6239,7 +6551,7 @@ function findGitDir2(cwd) {
6239
6551
  return null;
6240
6552
  }
6241
6553
  function runGit2(args, cwd, signal) {
6242
- return new Promise((resolve7) => {
6554
+ return new Promise((resolve8) => {
6243
6555
  let stdout = "";
6244
6556
  let stderr = "";
6245
6557
  const child = spawn("git", args, {
@@ -6255,8 +6567,8 @@ function runGit2(args, cwd, signal) {
6255
6567
  child.stderr?.on("data", (c) => {
6256
6568
  stderr += c.toString();
6257
6569
  });
6258
- child.on("close", (code) => resolve7({ stdout, stderr, exitCode: code ?? 0 }));
6259
- child.on("error", (e) => resolve7({ stdout: "", stderr: e.message, exitCode: 1 }));
6570
+ child.on("close", (code) => resolve8({ stdout, stderr, exitCode: code ?? 0 }));
6571
+ child.on("error", (e) => resolve8({ stdout: "", stderr: e.message, exitCode: 1 }));
6260
6572
  });
6261
6573
  }
6262
6574
  async function fileDiff(input, ctx, _signal) {
@@ -6273,8 +6585,8 @@ async function fileDiff(input, ctx, _signal) {
6273
6585
  const results = [];
6274
6586
  for (const file of files) {
6275
6587
  const absPath = safeResolve(file, ctx);
6276
- const stat11 = await fs7.stat(absPath).catch(() => null);
6277
- if (!stat11?.isFile()) continue;
6588
+ const stat12 = await fs7.stat(absPath).catch(() => null);
6589
+ if (!stat12?.isFile()) continue;
6278
6590
  const content = await fs7.readFile(absPath, "utf8");
6279
6591
  const lines = content.split(/\r?\n/);
6280
6592
  results.push(formatWithLineNumbers(file, lines));
@@ -6578,8 +6890,8 @@ async function* spawnStream(opts) {
6578
6890
  try {
6579
6891
  for (; ; ) {
6580
6892
  while (queue.length === 0) {
6581
- await new Promise((resolve7) => {
6582
- waiter = resolve7;
6893
+ await new Promise((resolve8) => {
6894
+ waiter = resolve8;
6583
6895
  });
6584
6896
  }
6585
6897
  const chunk = queue.shift();
@@ -6716,11 +7028,11 @@ var lintTool = {
6716
7028
  }
6717
7029
  };
6718
7030
  async function detectLinter(cwd) {
6719
- const { stat: stat11 } = await import('node:fs/promises');
7031
+ const { stat: stat12 } = await import('node:fs/promises');
6720
7032
  const checks = ["biome.json", ".eslintrc.json", "tslint.json", ".eslintrc.js", "tsconfig.json"];
6721
7033
  for (const f of checks) {
6722
7034
  try {
6723
- await stat11(`${cwd}/${f}`);
7035
+ await stat12(`${cwd}/${f}`);
6724
7036
  if (f.includes("biome")) return "biome";
6725
7037
  if (f.includes("eslint")) return "eslint";
6726
7038
  if (f.includes("tslint")) return "tslint";
@@ -6819,13 +7131,13 @@ var formatTool = {
6819
7131
  }
6820
7132
  };
6821
7133
  async function detectFixer(cwd) {
6822
- const { stat: stat11 } = await import('node:fs/promises');
7134
+ const { stat: stat12 } = await import('node:fs/promises');
6823
7135
  try {
6824
- await stat11(`${cwd}/biome.json`);
7136
+ await stat12(`${cwd}/biome.json`);
6825
7137
  return "biome";
6826
7138
  } catch {
6827
7139
  try {
6828
- await stat11(`${cwd}/.prettierrc`);
7140
+ await stat12(`${cwd}/.prettierrc`);
6829
7141
  return "prettier";
6830
7142
  } catch {
6831
7143
  return "biome";
@@ -6910,11 +7222,11 @@ var typecheckTool = {
6910
7222
  }
6911
7223
  };
6912
7224
  async function findTsConfig(cwd) {
6913
- const { stat: stat11 } = await import('node:fs/promises');
7225
+ const { stat: stat12 } = await import('node:fs/promises');
6914
7226
  const candidates = ["tsconfig.json", "tsconfig.base.json"];
6915
7227
  for (const f of candidates) {
6916
7228
  try {
6917
- const s = await stat11(path.join(cwd, f));
7229
+ const s = await stat12(path.join(cwd, f));
6918
7230
  if (s.isFile()) return path.join(cwd, f);
6919
7231
  } catch {
6920
7232
  }
@@ -6999,11 +7311,11 @@ var testTool = {
6999
7311
  }
7000
7312
  };
7001
7313
  async function detectRunner(cwd) {
7002
- const { stat: stat11 } = await import('node:fs/promises');
7314
+ const { stat: stat12 } = await import('node:fs/promises');
7003
7315
  const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
7004
7316
  for (const f of candidates) {
7005
7317
  try {
7006
- await stat11(path.join(cwd, f));
7318
+ await stat12(path.join(cwd, f));
7007
7319
  if (f.includes("vitest")) return "vitest";
7008
7320
  if (f.includes("jest")) return "jest";
7009
7321
  if (f.includes("mocha")) return "mocha";
@@ -7385,7 +7697,7 @@ var outdatedTool = {
7385
7697
  }
7386
7698
  };
7387
7699
  function runOutdated(manager, args, cwd, signal) {
7388
- return new Promise((resolve7) => {
7700
+ return new Promise((resolve8) => {
7389
7701
  let stdout = "";
7390
7702
  let stderr = "";
7391
7703
  const MAX = 1e5;
@@ -7403,10 +7715,10 @@ function runOutdated(manager, args, cwd, signal) {
7403
7715
  });
7404
7716
  child.on("close", (code) => {
7405
7717
  const result = parseOutdatedOutput(stdout, code ?? 0);
7406
- resolve7(result);
7718
+ resolve8(result);
7407
7719
  });
7408
7720
  child.on("error", (e) => {
7409
- resolve7({
7721
+ resolve8({
7410
7722
  exit_code: 1,
7411
7723
  packages: [],
7412
7724
  total: 0,
@@ -7532,7 +7844,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
7532
7844
  };
7533
7845
  }
7534
7846
  args.push("--timestamps", service);
7535
- return new Promise((resolve7) => {
7847
+ return new Promise((resolve8) => {
7536
7848
  let stdout = "";
7537
7849
  let stderr = "";
7538
7850
  const MAX = 2e5;
@@ -7548,7 +7860,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
7548
7860
  if (settled) return;
7549
7861
  settled = true;
7550
7862
  clearTimeout(timer);
7551
- resolve7(result);
7863
+ resolve8(result);
7552
7864
  };
7553
7865
  const child = spawn("docker", args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
7554
7866
  const timer = setTimeout(() => {
@@ -7583,7 +7895,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
7583
7895
  }
7584
7896
  var DOCKER_LOGS_TIMEOUT_MS = 3e3;
7585
7897
  var MAX_TAIL_LINES = 1e5;
7586
- async function fileLogs(path22, lines, filterRe, stream) {
7898
+ async function fileLogs(path23, lines, filterRe, stream) {
7587
7899
  const { createInterface } = await import('node:readline');
7588
7900
  const { createReadStream } = await import('node:fs');
7589
7901
  const entries = [];
@@ -7592,7 +7904,7 @@ async function fileLogs(path22, lines, filterRe, stream) {
7592
7904
  let writeIdx = 0;
7593
7905
  let totalLines = 0;
7594
7906
  const rl = createInterface({
7595
- input: createReadStream(path22),
7907
+ input: createReadStream(path23),
7596
7908
  crlfDelay: Number.POSITIVE_INFINITY
7597
7909
  });
7598
7910
  for await (const line of rl) {
@@ -7613,7 +7925,7 @@ async function fileLogs(path22, lines, filterRe, stream) {
7613
7925
  if (parsed) entries.push(parsed);
7614
7926
  }
7615
7927
  return {
7616
- source: path22,
7928
+ source: path23,
7617
7929
  entries,
7618
7930
  total: entries.length,
7619
7931
  truncated: totalLines > effLines,
@@ -7734,8 +8046,8 @@ async function resolveFiles2(filesInput, cwd) {
7734
8046
  for (const f of files) {
7735
8047
  const absPath = f.trim().startsWith("/") ? f.trim() : `${cwd}/${f.trim()}`;
7736
8048
  try {
7737
- const stat11 = await fs7.stat(absPath);
7738
- if (stat11.isFile()) resolved.push(absPath);
8049
+ const stat12 = await fs7.stat(absPath);
8050
+ if (stat12.isFile()) resolved.push(absPath);
7739
8051
  } catch {
7740
8052
  }
7741
8053
  }
@@ -8789,6 +9101,169 @@ function relatedMemoryTool(memory) {
8789
9101
  }
8790
9102
  };
8791
9103
  }
9104
+ var MAX_BODY_CHARS = SKILL_LIMITS.MAX_SKILL_BODY_CHARS;
9105
+ var MAX_RESOURCE_CHARS = SKILL_LIMITS.MAX_RESOURCE_CHARS;
9106
+ var MAX_LISTED_RESOURCES = SKILL_LIMITS.MAX_LISTED_RESOURCES;
9107
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build"]);
9108
+ function makeSkillTool(skillLoader) {
9109
+ return {
9110
+ name: "skill",
9111
+ category: "Skills",
9112
+ description: "Load a skill's instructions or a bundled resource on demand (agentskills.io progressive disclosure). With only `name`: returns the SKILL.md body + a list of bundled resource files. With `name` + `resource` (a relative path like `references/REF.md` or `scripts/extract.py`): returns that file content. Prefer this over the read tool for skill files \u2014 it reaches foreign skills outside the project root.",
9113
+ usageHint: 'Load a skill body or one of its bundled resources (progressive disclosure).\n\nWHEN TO USE:\n- A task matches a skill trigger \u2192 load the body: skill({ name })\n- You need a reference/template/script bundled with the skill \u2192 skill({ name, resource: "references/REF.md" })\n\nThe body call lists every bundled resource; load the ones you need. Run scripts via bash using the returned abs path.',
9114
+ permission: "auto",
9115
+ mutating: false,
9116
+ capabilities: ["fs.read"],
9117
+ icon: "document",
9118
+ timeoutMs: 5e3,
9119
+ inputSchema: {
9120
+ type: "object",
9121
+ properties: {
9122
+ name: {
9123
+ type: "string",
9124
+ description: "Exact skill name (as shown in the available-skills list)."
9125
+ },
9126
+ resource: {
9127
+ type: "string",
9128
+ description: "Optional relative path of a bundled resource to load (e.g. references/REF.md, scripts/extract.py, assets/template.html). Omit to list resources."
9129
+ }
9130
+ },
9131
+ required: ["name"]
9132
+ },
9133
+ async execute(input, ctx) {
9134
+ const name = input?.name?.trim();
9135
+ if (!name) {
9136
+ throw new ToolValidationError({ message: "skill: name is required", field: "name" });
9137
+ }
9138
+ const manifest = await skillLoader.find(name);
9139
+ if (!manifest) {
9140
+ throw new ToolValidationError({
9141
+ message: `skill "${name}" not found \u2014 use /skill to list available skills`,
9142
+ field: "name"
9143
+ });
9144
+ }
9145
+ const dir = path.dirname(manifest.path);
9146
+ let loadedResource;
9147
+ if (input.resource?.trim()) {
9148
+ loadedResource = await loadResource(dir, input.resource.trim());
9149
+ }
9150
+ const raw = await skillLoader.readBody(name);
9151
+ const body = stripFrontmatter(raw).trim().slice(0, MAX_BODY_CHARS);
9152
+ const resources = loadedResource ? [] : await listResources(dir);
9153
+ try {
9154
+ await ctx?.session?.append({
9155
+ type: "skill_activated",
9156
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
9157
+ skillName: manifest.name
9158
+ });
9159
+ } catch {
9160
+ }
9161
+ return {
9162
+ name: manifest.name,
9163
+ description: manifest.description,
9164
+ body,
9165
+ resources,
9166
+ dir,
9167
+ loadedResource
9168
+ };
9169
+ },
9170
+ serialize(output) {
9171
+ if (output.loadedResource) {
9172
+ const lr = output.loadedResource;
9173
+ const note = lr.truncated ? ` (truncated to ${lr.content.length} chars of ${lr.bytes} B)` : "";
9174
+ return `# Resource: ${output.name}/${lr.rel}
9175
+ (abs path: ${lr.absPath})${note}
9176
+
9177
+ ${lr.content}`;
9178
+ }
9179
+ const head = `# Skill: ${output.name}
9180
+ ${output.description}
9181
+
9182
+ ${output.body}`;
9183
+ if (output.resources.length === 0) return head;
9184
+ const listing = output.resources.map((r) => `- ${r.path} (${r.bytes} B)`).join("\n");
9185
+ return `${head}
9186
+
9187
+ ## Bundled resources (load on demand)
9188
+ Load any with: \`skill({ name: "${output.name}", resource: "<path>" })\`. Run scripts via bash using their abs path under ${output.dir}.
9189
+ ${listing}`;
9190
+ }
9191
+ };
9192
+ }
9193
+ async function loadResource(skillDir, rel) {
9194
+ const norm = rel.replace(/\\/g, "/");
9195
+ if (path.isAbsolute(rel) || norm.split("/").some((seg) => seg === "..")) {
9196
+ throw new ToolValidationError({
9197
+ message: `skill: invalid resource path "${rel}"`,
9198
+ field: "resource"
9199
+ });
9200
+ }
9201
+ const absPath = path.resolve(skillDir, rel);
9202
+ const root = path.resolve(skillDir);
9203
+ if (absPath !== root && !absPath.startsWith(root + path.sep)) {
9204
+ throw new ToolValidationError({
9205
+ message: `skill: resource "${rel}" escapes the skill directory`,
9206
+ field: "resource"
9207
+ });
9208
+ }
9209
+ let buf;
9210
+ try {
9211
+ buf = await fs7.readFile(absPath);
9212
+ } catch {
9213
+ throw new ToolValidationError({
9214
+ message: `skill: resource "${rel}" not readable`,
9215
+ field: "resource"
9216
+ });
9217
+ }
9218
+ const raw = buf.toString("utf8");
9219
+ const truncated = raw.length > MAX_RESOURCE_CHARS;
9220
+ return {
9221
+ rel: norm,
9222
+ absPath,
9223
+ content: truncated ? raw.slice(0, MAX_RESOURCE_CHARS) : raw,
9224
+ bytes: buf.length,
9225
+ truncated
9226
+ };
9227
+ }
9228
+ async function listResources(skillDir) {
9229
+ const out = [];
9230
+ await walk(skillDir, skillDir, out);
9231
+ out.sort((a, b) => a.path.localeCompare(b.path));
9232
+ return out.slice(0, MAX_LISTED_RESOURCES);
9233
+ }
9234
+ async function walk(root, dir, out) {
9235
+ if (out.length >= MAX_LISTED_RESOURCES) return;
9236
+ let entries;
9237
+ try {
9238
+ entries = await fs7.readdir(dir, { withFileTypes: true });
9239
+ } catch {
9240
+ return;
9241
+ }
9242
+ for (const e of entries) {
9243
+ if (out.length >= MAX_LISTED_RESOURCES) return;
9244
+ const fullPath = path.join(dir, e.name);
9245
+ let isDir = e.isDirectory();
9246
+ if (e.isSymbolicLink()) {
9247
+ try {
9248
+ isDir = (await fs7.stat(fullPath)).isDirectory();
9249
+ } catch {
9250
+ continue;
9251
+ }
9252
+ }
9253
+ if (isDir) {
9254
+ if (SKIP_DIRS.has(e.name) || e.name.startsWith(".")) continue;
9255
+ await walk(root, fullPath, out);
9256
+ } else if (e.isFile()) {
9257
+ if (e.name === "SKILL.md" || e.name === "SKILL.save.md") continue;
9258
+ try {
9259
+ const stat12 = await fs7.stat(fullPath);
9260
+ const rel = path.relative(root, fullPath).split(path.sep).join("/");
9261
+ out.push({ path: rel, bytes: stat12.size });
9262
+ } catch {
9263
+ }
9264
+ }
9265
+ }
9266
+ }
8792
9267
 
8793
9268
  // src/mode.ts
8794
9269
  function createModeTool(modeStore) {
@@ -10734,9 +11209,8 @@ func formatType(t ast.Expr) string {
10734
11209
  }
10735
11210
  `;
10736
11211
  async function syncGoParse(filePath, content, lang) {
10737
- const tmpDir = path.join(os2.tmpdir(), "ws-go-parse");
11212
+ const tmpDir = await fs7.mkdtemp(path.join(os2.tmpdir(), "ws-go-parse-"));
10738
11213
  try {
10739
- await fs7.mkdir(tmpDir, { recursive: true });
10740
11214
  const scriptPath = path.join(tmpDir, "parse.go");
10741
11215
  await fs7.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
10742
11216
  const proc = spawn("go", ["run", scriptPath], {
@@ -10750,8 +11224,8 @@ async function syncGoParse(filePath, content, lang) {
10750
11224
  proc.stdin?.write(content);
10751
11225
  proc.stdin?.end();
10752
11226
  const { code } = await Promise.race([
10753
- new Promise((resolve7) => {
10754
- proc.on("close", (c) => resolve7({ code: c }));
11227
+ new Promise((resolve8) => {
11228
+ proc.on("close", (c) => resolve8({ code: c }));
10755
11229
  }),
10756
11230
  new Promise(
10757
11231
  (_, reject) => setTimeout(() => {
@@ -10780,6 +11254,8 @@ async function syncGoParse(filePath, content, lang) {
10780
11254
  return { file: filePath, lang, symbols, mtimeMs: Date.now() };
10781
11255
  } catch {
10782
11256
  return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
11257
+ } finally {
11258
+ await fs7.rm(tmpDir, { recursive: true, force: true });
10783
11259
  }
10784
11260
  }
10785
11261
  async function parseSymbols3(opts) {
@@ -11013,8 +11489,8 @@ async function syncPyParse(filePath, content, lang) {
11013
11489
  stdout += chunk.toString();
11014
11490
  });
11015
11491
  const { code } = await Promise.race([
11016
- new Promise((resolve7) => {
11017
- proc.on("close", (c) => resolve7({ code: c }));
11492
+ new Promise((resolve8) => {
11493
+ proc.on("close", (c) => resolve8({ code: c }));
11018
11494
  }),
11019
11495
  new Promise(
11020
11496
  (_, reject) => setTimeout(() => {
@@ -11099,8 +11575,8 @@ async function tryNativeParse(file, content) {
11099
11575
  stdout += chunk.toString();
11100
11576
  });
11101
11577
  const { code } = await Promise.race([
11102
- new Promise((resolve7) => {
11103
- proc.on("close", (c) => resolve7({ code: c }));
11578
+ new Promise((resolve8) => {
11579
+ proc.on("close", (c) => resolve8({ code: c }));
11104
11580
  }),
11105
11581
  new Promise(
11106
11582
  (_, reject) => setTimeout(() => {
@@ -11590,7 +12066,7 @@ async function loadGitignoreMatcher(projectRoot) {
11590
12066
  var YIELD_EVERY_N = 50;
11591
12067
  var PARALLEL_BATCH = 20;
11592
12068
  function yieldEventLoop() {
11593
- return new Promise((resolve7) => setImmediate(resolve7));
12069
+ return new Promise((resolve8) => setImmediate(resolve8));
11594
12070
  }
11595
12071
  function throwIfAborted(signal) {
11596
12072
  if (!signal?.aborted) return;
@@ -11629,7 +12105,7 @@ async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
11629
12105
  { ext: ".yml", pat: compileGlob("**/*.yml") }
11630
12106
  ];
11631
12107
  let dirCount = 0;
11632
- const walk = async (dir) => {
12108
+ const walk2 = async (dir) => {
11633
12109
  throwIfAborted(signal);
11634
12110
  if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
11635
12111
  await yieldEventLoop();
@@ -11648,7 +12124,7 @@ async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
11648
12124
  const rel = path.relative(projectRoot, full).replace(/\\/g, "/");
11649
12125
  if (e.isDirectory()) {
11650
12126
  if (isGitIgnored(rel, true)) continue;
11651
- await walk(full);
12127
+ await walk2(full);
11652
12128
  } else if (e.isFile()) {
11653
12129
  if (isGitIgnored(rel, false)) continue;
11654
12130
  const ext = path.extname(e.name);
@@ -11661,7 +12137,7 @@ async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
11661
12137
  }
11662
12138
  }
11663
12139
  };
11664
- await walk(projectRoot);
12140
+ await walk2(projectRoot);
11665
12141
  return results;
11666
12142
  }
11667
12143
  async function parseFile(file, content, lang) {
@@ -11735,34 +12211,34 @@ async function runIndexerWithStore(store, opts) {
11735
12211
  const statOpts = signal ? { signal } : {};
11736
12212
  const statReadParse = await Promise.allSettled(
11737
12213
  batchFiles.map(async (file) => {
11738
- let stat11;
12214
+ let stat12;
11739
12215
  try {
11740
- stat11 = await fs7.stat(file, statOpts);
12216
+ stat12 = await fs7.stat(file, statOpts);
11741
12217
  } catch (e) {
11742
12218
  if (isAbortError(e)) throw e;
11743
12219
  return { file, stat: null, lang: "", parsed: null, error: `stat error: ${e instanceof Error ? e.message : String(e)}` };
11744
12220
  }
11745
- if (!stat11.isFile()) return { file, stat: stat11, lang: "", parsed: null };
12221
+ if (!stat12.isFile()) return { file, stat: stat12, lang: "", parsed: null };
11746
12222
  const lang = detectLang(file);
11747
- if (!lang) return { file, stat: stat11, lang: "", parsed: null };
12223
+ if (!lang) return { file, stat: stat12, lang: "", parsed: null };
11748
12224
  const meta = existingMeta.get(file);
11749
- if (!force && meta && meta.mtimeMs === Math.floor(stat11.mtimeMs)) {
11750
- return { file, stat: stat11, lang, parsed: null, skippedMeta: meta };
12225
+ if (!force && meta && meta.mtimeMs === Math.floor(stat12.mtimeMs)) {
12226
+ return { file, stat: stat12, lang, parsed: null, skippedMeta: meta };
11751
12227
  }
11752
12228
  let content;
11753
12229
  try {
11754
12230
  content = await fs7.readFile(file, { encoding: "utf8", signal });
11755
12231
  } catch (e) {
11756
12232
  if (isAbortError(e)) throw e;
11757
- return { file, stat: stat11, lang, parsed: null, error: `read error: ${e instanceof Error ? e.message : String(e)}` };
12233
+ return { file, stat: stat12, lang, parsed: null, error: `read error: ${e instanceof Error ? e.message : String(e)}` };
11758
12234
  }
11759
12235
  let parsed;
11760
12236
  try {
11761
12237
  parsed = await parseFile(file, content, lang);
11762
12238
  } catch (e) {
11763
- return { file, stat: stat11, lang, parsed: null, error: `parse error: ${e instanceof Error ? e.message : String(e)}` };
12239
+ return { file, stat: stat12, lang, parsed: null, error: `parse error: ${e instanceof Error ? e.message : String(e)}` };
11764
12240
  }
11765
- return { file, stat: stat11, lang, parsed, content };
12241
+ return { file, stat: stat12, lang, parsed, content };
11766
12242
  })
11767
12243
  );
11768
12244
  const batchEntries = [];
@@ -11782,7 +12258,7 @@ async function runIndexerWithStore(store, opts) {
11782
12258
  if (result.error.includes("error:")) errors.push(result.error);
11783
12259
  continue;
11784
12260
  }
11785
- const { stat: stat11, lang, parsed } = result;
12261
+ const { stat: stat12, lang, parsed } = result;
11786
12262
  if (result.skippedMeta) {
11787
12263
  langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
11788
12264
  symbolsIndexed += result.skippedMeta.symbolCount;
@@ -11794,7 +12270,7 @@ async function runIndexerWithStore(store, opts) {
11794
12270
  store.upsertFile({
11795
12271
  file,
11796
12272
  lang,
11797
- mtimeMs: Math.floor(stat11.mtimeMs),
12273
+ mtimeMs: Math.floor(stat12.mtimeMs),
11798
12274
  symbolCount: 0,
11799
12275
  lastIndexed: Date.now()
11800
12276
  });
@@ -11806,7 +12282,7 @@ async function runIndexerWithStore(store, opts) {
11806
12282
  store.upsertFile({
11807
12283
  file,
11808
12284
  lang,
11809
- mtimeMs: Math.floor(stat11.mtimeMs),
12285
+ mtimeMs: Math.floor(stat12.mtimeMs),
11810
12286
  symbolCount: 0,
11811
12287
  lastIndexed: Date.now()
11812
12288
  });
@@ -11822,7 +12298,7 @@ async function runIndexerWithStore(store, opts) {
11822
12298
  lang,
11823
12299
  symbols: parsed.symbols,
11824
12300
  refs,
11825
- mtimeMs: Math.floor(stat11.mtimeMs),
12301
+ mtimeMs: Math.floor(stat12.mtimeMs),
11826
12302
  symbolCount: parsed.symbols.length
11827
12303
  });
11828
12304
  deleteForFiles.push(file);
@@ -12083,7 +12559,7 @@ function shutdownCodebaseIndexHost() {
12083
12559
  function callIndexOp(op, args, opts) {
12084
12560
  const w = ensureWorker();
12085
12561
  if (!w) return callInline(op, args, opts);
12086
- return new Promise((resolve7, reject) => {
12562
+ return new Promise((resolve8, reject) => {
12087
12563
  const id = nextRpcId++;
12088
12564
  const timer = setTimeout(() => {
12089
12565
  pending.delete(id);
@@ -12106,7 +12582,7 @@ function callIndexOp(op, args, opts) {
12106
12582
  pending.set(id, {
12107
12583
  resolve: (v) => {
12108
12584
  cleanup();
12109
- resolve7(v);
12585
+ resolve8(v);
12110
12586
  },
12111
12587
  reject: (e) => {
12112
12588
  cleanup();
@@ -13168,6 +13644,6 @@ var TOOL_ICON_CONFIG = {
13168
13644
  };
13169
13645
  var FALLBACK_ICON = "fallback";
13170
13646
 
13171
- export { CircuitBreaker, CircuitOpenError, FALLBACK_ICON, IndexCircuitBreaker, IndexTimeoutError, OPTIONAL_TOOLS, TIER1_TOOLS, TIER2_TOOLS, TIER3_TOOLS, TOOL_ICON_CONFIG, TOOL_ICON_MAP, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexStats, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, configureExecPolicy, createGlobalPsSlashCommand, createModeTool, designTool, diffTool, documentTool, editTool, enqueueReindex, ensureSessionShell, execTool, fetchTool, forgetTool, formatGlobalStatus, formatInstanceList, formatInstanceSummary, formatTool, getExecAllowlist, getIndexState, getInstanceCount, getPersistentProcessRegistry, getProcessGuardian, getProcessRegistry, getToolIcon, gitTool, globTool, grepTool, indexCircuitBreaker, installTool, isExecCommandAllowed, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, listInstances, logsTool, normalizeShell, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, resetExecPolicy, resetIndexCircuitBreaker, resetPersistentProcessRegistry, resolveSessionShell, runStartupIndex, scaffoldTool, searchCodebaseIndex, searchMemoryTool, searchTool, shutdownCodebaseIndexHost, startProcessGuardian, stopProcessGuardian, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
13647
+ export { CircuitBreaker, CircuitOpenError, FALLBACK_ICON, IndexCircuitBreaker, IndexTimeoutError, OPTIONAL_TOOLS, TIER1_TOOLS, TIER2_TOOLS, TIER3_TOOLS, TOOL_ICON_CONFIG, TOOL_ICON_MAP, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexStats, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, configureDangerBypass, configureExecPolicy, createGlobalPsSlashCommand, createModeTool, designTool, detectDanger, diffTool, documentTool, editTool, enqueueReindex, ensureSessionShell, execTool, fetchTool, forgetTool, formatGlobalStatus, formatInstanceList, formatInstanceSummary, formatTool, getDangerBypass, getExecAllowlist, getIndexState, getInstanceCount, getPersistentProcessRegistry, getProcessGuardian, getProcessRegistry, getToolIcon, gitTool, globTool, grepTool, indexCircuitBreaker, installTool, isExecCommandAllowed, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, listInstances, logsTool, makeSkillTool, normalizeShell, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, resetDangerBypass, resetExecPolicy, resetIndexCircuitBreaker, resetPersistentProcessRegistry, resolveSessionShell, runStartupIndex, scaffoldTool, searchCodebaseIndex, searchMemoryTool, searchTool, shutdownCodebaseIndexHost, startProcessGuardian, stopProcessGuardian, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
13172
13648
  //# sourceMappingURL=index.js.map
13173
13649
  //# sourceMappingURL=index.js.map