@wrongstack/tools 0.291.0 → 0.291.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/builtin.js CHANGED
@@ -639,8 +639,8 @@ var init_process_registry = __esm({
639
639
  if (p.killed) return true;
640
640
  if (p.protected) return false;
641
641
  const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
642
- const isWin4 = os.platform() === "win32";
643
- if (isWin4) {
642
+ const isWin5 = os.platform() === "win32";
643
+ if (isWin5) {
644
644
  const liveRealChild = p.child.exitCode === null && typeof p.child.pid === "number";
645
645
  const directFallback = () => {
646
646
  if (p.child.exitCode === null) {
@@ -2605,8 +2605,8 @@ async function scanDirectory(directory, depth, profiles, limits, state, extraIgn
2605
2605
  collectFileEvidence(directory, fullPath, entry.name, profiles, state);
2606
2606
  }
2607
2607
  }
2608
- function collectFileEvidence(directory, fullPath, basename9, profiles, state) {
2609
- const lower = basename9.toLowerCase();
2608
+ function collectFileEvidence(directory, fullPath, basename10, profiles, state) {
2609
+ const lower = basename10.toLowerCase();
2610
2610
  const extension = path4.extname(lower);
2611
2611
  for (const profile of profiles) {
2612
2612
  const detector = profile.detectors.find(
@@ -2617,7 +2617,7 @@ function collectFileEvidence(directory, fullPath, basename9, profiles, state) {
2617
2617
  candidate.evidence.push({
2618
2618
  kind: detector.kind,
2619
2619
  path: fullPath,
2620
- value: basename9,
2620
+ value: basename10,
2621
2621
  weight: detector.weight
2622
2622
  });
2623
2623
  if (detector.kind === "manifest" || detector.kind === "config") {
@@ -3934,15 +3934,15 @@ async function changedPaths(before, after, beforeSizes, afterSizes) {
3934
3934
  const beforeSet = new Set(before);
3935
3935
  const afterSet = new Set(after);
3936
3936
  const changed = /* @__PURE__ */ new Set();
3937
- for (const path32 of after) {
3938
- if (!beforeSet.has(path32)) changed.add(path32);
3937
+ for (const path33 of after) {
3938
+ if (!beforeSet.has(path33)) changed.add(path33);
3939
3939
  }
3940
- for (const path32 of before) {
3941
- if (!afterSet.has(path32)) changed.add(path32);
3940
+ for (const path33 of before) {
3941
+ if (!afterSet.has(path33)) changed.add(path33);
3942
3942
  }
3943
3943
  if (beforeSizes && afterSizes) {
3944
- for (const path32 of after) {
3945
- if (beforeSizes.get(path32) !== afterSizes.get(path32)) changed.add(path32);
3944
+ for (const path33 of after) {
3945
+ if (beforeSizes.get(path33) !== afterSizes.get(path33)) changed.add(path33);
3946
3946
  }
3947
3947
  }
3948
3948
  return [...changed].sort();
@@ -5374,17 +5374,18 @@ function getPersistentProcessRegistry() {
5374
5374
 
5375
5375
  // src/bash-kill-guard.ts
5376
5376
  var isWin2 = os3.platform() === "win32";
5377
+ var SCRIPT_KILL_RE = /^(?:\.\\|\.\/)?(?:kill|terminate|stop)\S*\.(?:ps1|bat|cmd|sh)(?:\s|$)/i;
5378
+ var SCRIPT_KILL_RE_POSIX = /^(?:\.\/)?(?:kill|terminate|stop)\S*\.sh(?:\s|$)/i;
5379
+ var SCRIPT_KILL_FALLBACK_RE = /^\S*(?:kill|terminate|stop)\S*\.(?:ps1|bat|cmd|sh)\b/i;
5377
5380
  function extractKillCommand(command) {
5378
5381
  const normalized = command.replace(/\s+/g, " ").trim();
5379
- const shellCMatch = normalized.match(
5380
- /^(?:\S+(?:\s+\S+)?)?\s+-c\s+['"](.+?)['"]$/
5381
- );
5382
- if (shellCMatch?.[1]) {
5383
- const inner = shellCMatch[1].trim();
5382
+ const shellCMatch = normalized.match(/^.+?\s+-c\s+(['"])([\s\S]+)\1$/);
5383
+ if (shellCMatch?.[2]) {
5384
+ const inner = shellCMatch[2].trim();
5384
5385
  return isKillRelatedCommand(inner) ? inner : null;
5385
5386
  }
5386
5387
  const shellCUnquoted = normalized.match(
5387
- /^(?:\S+(?:\s+\S+)?)?\s+-c\s+(kill(?:\s+-[a-zA-Z]+)?(?:\s+\d+)+)$/
5388
+ /^.+?\s+-c\s+(kill(?:\s+-s\s+[a-zA-Z0-9]+|\s+-[a-zA-Z0-9]+)?\s+\d+)$/
5388
5389
  );
5389
5390
  if (shellCUnquoted?.[1]) {
5390
5391
  return shellCUnquoted[1];
@@ -5396,22 +5397,47 @@ function isKillRelatedCommand(cmd) {
5396
5397
  if (isWin2) {
5397
5398
  if (/^taskkill\s/i.test(normalized)) return true;
5398
5399
  if (/^tskill\s/i.test(normalized)) return true;
5400
+ if (/^(stop-process|kill|stop)\s/i.test(normalized)) return true;
5401
+ if (/^wmic\s+process\s/i.test(normalized) && /\bdelete\b/i.test(normalized)) return true;
5402
+ if (SCRIPT_KILL_RE.test(normalized)) return true;
5399
5403
  return false;
5400
5404
  }
5401
5405
  if (/^kill(\s|$)/.test(normalized)) return true;
5402
5406
  if (/^(pkill|killall|pgrep|skill)\s/.test(normalized)) return true;
5403
5407
  if (/^\/proc\/\d+\/(?:kill|fd)/.test(normalized)) return true;
5408
+ if (SCRIPT_KILL_RE_POSIX.test(normalized)) return true;
5404
5409
  return false;
5405
5410
  }
5406
5411
  function parseKillCommand(command) {
5407
5412
  const normalized = command.replace(/\s+/g, " ").trim();
5408
5413
  if (isWin2) {
5409
- const taskkillMatch = normalized.match(/^taskkill\s+(?:\/[a-zA-Z]+\s+)*\/PID\s+(\d+)/i);
5410
- if (taskkillMatch?.[1]) {
5411
- const pidStr = taskkillMatch[1];
5414
+ const hasTaskkillForce = /(?:^|\s)\/F(?=\s|$)/i.test(normalized);
5415
+ const isSimpleTaskkill = /^taskkill\s+/i.test(normalized) && !/[|&<>]/.test(normalized);
5416
+ const taskkillPidMatch = isSimpleTaskkill ? normalized.match(/(?:^|\s)\/PID\s+(\d+)(?=\s|$)/i) : null;
5417
+ if (taskkillPidMatch?.[1]) {
5418
+ return {
5419
+ pid: parseInt(taskkillPidMatch[1], 10),
5420
+ signal: hasTaskkillForce ? "FORCE" : "TERM",
5421
+ isGroupKill: false,
5422
+ isAllKill: false,
5423
+ originalCommand: command
5424
+ };
5425
+ }
5426
+ const taskkillImMatch = isSimpleTaskkill ? normalized.match(/(?:^|\s)\/IM\s+([^\s/]+)(?=\s|$)/i) : null;
5427
+ if (taskkillImMatch?.[1]) {
5428
+ return {
5429
+ name: taskkillImMatch[1],
5430
+ signal: hasTaskkillForce ? "FORCE" : "TERM",
5431
+ isGroupKill: false,
5432
+ isAllKill: false,
5433
+ originalCommand: command
5434
+ };
5435
+ }
5436
+ const taskkillFiMatch = isSimpleTaskkill ? normalized.match(/(?:^|\s)\/FI\s+"IMAGENAME\s+eq\s+([^"]+)"(?=\s|$)/i) : null;
5437
+ if (taskkillFiMatch?.[1]) {
5412
5438
  return {
5413
- pid: parseInt(pidStr, 10),
5414
- signal: normalized.includes("/F") ? "FORCE" : "TERM",
5439
+ name: taskkillFiMatch[1],
5440
+ signal: hasTaskkillForce ? "FORCE" : "TERM",
5415
5441
  isGroupKill: false,
5416
5442
  isAllKill: false,
5417
5443
  originalCommand: command
@@ -5419,18 +5445,109 @@ function parseKillCommand(command) {
5419
5445
  }
5420
5446
  const tskillMatch = normalized.match(/^tskill\s+(\d+)/i);
5421
5447
  if (tskillMatch?.[1]) {
5422
- const pidStr = tskillMatch[1];
5423
5448
  return {
5424
- pid: parseInt(pidStr, 10),
5449
+ pid: parseInt(tskillMatch[1], 10),
5425
5450
  signal: "TERM",
5426
5451
  isGroupKill: false,
5427
5452
  isAllKill: false,
5428
5453
  originalCommand: command
5429
5454
  };
5430
5455
  }
5456
+ const isStopProcIdCommand = /^(?:stop-process|kill)(?:\s+-(?:id|pid)\s+\d+|\s+-[a-zA-Z]+)+$/i.test(normalized);
5457
+ const stopProcIdMatch = normalized.match(/(?:^|\s)-(?:id|pid)\s+(\d+)(?=\s|$)/i);
5458
+ if (isStopProcIdCommand && stopProcIdMatch?.[1]) {
5459
+ return {
5460
+ pid: parseInt(stopProcIdMatch[1], 10),
5461
+ signal: "FORCE",
5462
+ isGroupKill: false,
5463
+ isAllKill: false,
5464
+ originalCommand: command
5465
+ };
5466
+ }
5467
+ const killSignalOptionMatch = normalized.match(/^kill\s+-s\s+([a-zA-Z0-9]+)\s+(\d+)$/i);
5468
+ if (killSignalOptionMatch?.[1] && killSignalOptionMatch[2]) {
5469
+ return {
5470
+ pid: parseInt(killSignalOptionMatch[2], 10),
5471
+ signal: killSignalOptionMatch[1].toUpperCase(),
5472
+ isGroupKill: false,
5473
+ isAllKill: false,
5474
+ originalCommand: command
5475
+ };
5476
+ }
5477
+ const killPosixMatch = normalized.match(/^kill\s+(?:(-[a-zA-Z0-9]+)\s+)?(\d+)$/);
5478
+ if (killPosixMatch?.[2]) {
5479
+ const sig = killPosixMatch[1] ? killPosixMatch[1].slice(1).toUpperCase() : "TERM";
5480
+ return {
5481
+ pid: parseInt(killPosixMatch[2], 10),
5482
+ signal: sig,
5483
+ isGroupKill: false,
5484
+ isAllKill: false,
5485
+ originalCommand: command
5486
+ };
5487
+ }
5488
+ const stopProcNameMatch = normalized.match(
5489
+ /^(?:stop-process|kill)\s+-(?:name|n)\s+(?:['"]([a-zA-Z0-9_.-]+)['"]|([a-zA-Z0-9_.-]+))(?:\s|$)/i
5490
+ );
5491
+ const stopProcName = stopProcNameMatch?.[1] ?? stopProcNameMatch?.[2];
5492
+ if (stopProcName) {
5493
+ return {
5494
+ name: stopProcName,
5495
+ signal: "FORCE",
5496
+ isGroupKill: false,
5497
+ isAllKill: false,
5498
+ originalCommand: command
5499
+ };
5500
+ }
5501
+ const stopProcStandalone = normalized.match(
5502
+ /^(?:stop-process|kill)\s+['"]?([a-zA-Z][a-zA-Z0-9_.-]+)['"]?$/i
5503
+ );
5504
+ if (stopProcStandalone?.[1]) {
5505
+ return {
5506
+ name: stopProcStandalone[1],
5507
+ signal: "FORCE",
5508
+ isGroupKill: false,
5509
+ isAllKill: false,
5510
+ originalCommand: command
5511
+ };
5512
+ }
5513
+ const wmicMatch = normalized.match(
5514
+ /^wmic\s+process\s+where\s+['"]?(?:name\s*=\s*['"]?)([a-zA-Z0-9_.-]+)/i
5515
+ );
5516
+ if (wmicMatch?.[1]) {
5517
+ return {
5518
+ name: wmicMatch[1],
5519
+ signal: "FORCE",
5520
+ isGroupKill: false,
5521
+ isAllKill: false,
5522
+ originalCommand: command
5523
+ };
5524
+ }
5525
+ const killScriptMatch = normalized.match(SCRIPT_KILL_RE);
5526
+ if (killScriptMatch) {
5527
+ return {
5528
+ name: "kill-script",
5529
+ // sentinel — isKillProtected always blocks "kill-script"
5530
+ signal: "FORCE",
5531
+ isGroupKill: false,
5532
+ isAllKill: false,
5533
+ originalCommand: command
5534
+ };
5535
+ }
5431
5536
  return null;
5432
5537
  }
5433
- const simpleMatch = normalized.match(/^kill\s+(?:(-[a-zA-Z]+)\s+)?(\d+|-?\d+)$/);
5538
+ const signalOptionMatch = normalized.match(/^kill\s+-s\s+([a-zA-Z0-9]+)\s+(\d+|-?\d+)$/i);
5539
+ if (signalOptionMatch?.[1] && signalOptionMatch[2]) {
5540
+ const pidOrGroup = signalOptionMatch[2];
5541
+ const isGroupKill = pidOrGroup.startsWith("-");
5542
+ return {
5543
+ pid: parseInt(isGroupKill ? pidOrGroup.slice(1) : pidOrGroup, 10),
5544
+ signal: signalOptionMatch[1].toUpperCase(),
5545
+ isGroupKill,
5546
+ isAllKill: false,
5547
+ originalCommand: command
5548
+ };
5549
+ }
5550
+ const simpleMatch = normalized.match(/^kill\s+(?:(-[a-zA-Z0-9]+)\s+)?(\d+|-?\d+)$/);
5434
5551
  if (simpleMatch) {
5435
5552
  const signal = simpleMatch[1] ?? "-TERM";
5436
5553
  const pidOrGroup = simpleMatch[2];
@@ -5490,6 +5607,9 @@ async function getProtectedEntries() {
5490
5607
  }
5491
5608
  async function isKillProtected(kill) {
5492
5609
  const registry = getPersistentProcessRegistry();
5610
+ if (kill.name === "kill-script") {
5611
+ return true;
5612
+ }
5493
5613
  if (kill.name) {
5494
5614
  const entries = await getProtectedEntries();
5495
5615
  const killNameLower = kill.name.toLowerCase();
@@ -5529,6 +5649,12 @@ async function checkAndBlockKillCommand(command) {
5529
5649
  reason: `Blocked: complex kill pipeline detected \u2014 "${killCmd.slice(0, 50)}..."`
5530
5650
  };
5531
5651
  }
5652
+ if (SCRIPT_KILL_FALLBACK_RE.test(killCmd)) {
5653
+ return {
5654
+ blocked: true,
5655
+ reason: `Blocked: script-based kill detected \u2014 "${killCmd.slice(0, 80)}" may target protected WrongStack processes (cannot inspect script body).`
5656
+ };
5657
+ }
5532
5658
  return { blocked: false };
5533
5659
  }
5534
5660
  if (await isKillProtected(parsed)) {
@@ -5552,8 +5678,8 @@ async function checkAndBlockKillCommand(command) {
5552
5678
 
5553
5679
  // src/_shell-pick.ts
5554
5680
  var POSIX_DEFAULT = "cmd";
5555
- function pickShell(platform4, command, env) {
5556
- if (platform4 !== "win32") return POSIX_DEFAULT;
5681
+ function pickShell(platform5, command, env) {
5682
+ if (platform5 !== "win32") return POSIX_DEFAULT;
5557
5683
  const override = env.get("WRONGSTACK_SHELL")?.trim().toLowerCase();
5558
5684
  if (override === "cmd" || override === "cmd.exe") return "cmd";
5559
5685
  if (override === "powershell" || override === "powershell.exe") return "powershell";
@@ -5757,10 +5883,10 @@ var bashTool = {
5757
5883
  }));
5758
5884
  }
5759
5885
  const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT_MS2, 6e5));
5760
- const isWin4 = os4.platform() === "win32";
5886
+ const isWin5 = os4.platform() === "win32";
5761
5887
  let plan;
5762
5888
  let winShellKind;
5763
- if (isWin4) {
5889
+ if (isWin5) {
5764
5890
  const shell2 = pickShell("win32", input.command, {
5765
5891
  get: (k) => process.env[k]
5766
5892
  });
@@ -5789,7 +5915,7 @@ var bashTool = {
5789
5915
  const shell = plan.bin;
5790
5916
  const args = plan.useStdin ? [...plan.argv] : [...plan.argv, input.command];
5791
5917
  const env = buildChildEnv2(ctx.session?.id);
5792
- const detached = !isWin4;
5918
+ const detached = !isWin5;
5793
5919
  const startedAt = Date.now();
5794
5920
  if (input.background) {
5795
5921
  let buf2 = "";
@@ -5807,7 +5933,7 @@ var bashTool = {
5807
5933
  // apply: the child gets a hidden console that grandchildren inherit.
5808
5934
  // Windows children survive parent exit either way. POSIX keeps
5809
5935
  // detached for the process-group kill semantics.
5810
- detached: !isWin4,
5936
+ detached: !isWin5,
5811
5937
  windowsHide: true
5812
5938
  });
5813
5939
  if (plan.useStdin) {
@@ -5918,7 +6044,7 @@ var bashTool = {
5918
6044
  stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
5919
6045
  detached,
5920
6046
  windowsHide: true,
5921
- ...isWin4 ? {} : { signal: opts.signal }
6047
+ ...isWin5 ? {} : { signal: opts.signal }
5922
6048
  });
5923
6049
  if (plan.useStdin) {
5924
6050
  try {
@@ -5971,7 +6097,7 @@ var bashTool = {
5971
6097
  const timers = [];
5972
6098
  const spool = createOutputSpool({ tool: "bash", thresholdBytes: MAX_OUTPUT });
5973
6099
  function killWithTimeout(child2, timeoutMs2) {
5974
- if (isWin4) {
6100
+ if (isWin5) {
5975
6101
  if (typeof child2.pid === "number" && child2.exitCode === null) {
5976
6102
  const attempted = registry.kill(child2.pid, { force: true, graceMs: timeoutMs2 });
5977
6103
  if (!attempted) {
@@ -6012,7 +6138,7 @@ var bashTool = {
6012
6138
  timers.push(timer);
6013
6139
  timer.unref?.();
6014
6140
  const onAbort = () => killWithTimeout(child, 2e3);
6015
- if (isWin4) {
6141
+ if (isWin5) {
6016
6142
  if (opts.signal.aborted) onAbort();
6017
6143
  else opts.signal.addEventListener("abort", onAbort, { once: true });
6018
6144
  }
@@ -6126,7 +6252,7 @@ ${hint}` : ""),
6126
6252
  } finally {
6127
6253
  for (const t of timers) clearTimeout(t);
6128
6254
  spool.finalize();
6129
- if (isWin4) opts.signal.removeEventListener("abort", onAbort);
6255
+ if (isWin5) opts.signal.removeEventListener("abort", onAbort);
6130
6256
  child.stdout?.off("data", onStdoutData);
6131
6257
  child.stderr?.off("data", onStderrData);
6132
6258
  child.stdout?.destroy();
@@ -9998,9 +10124,9 @@ function parseSymbols5(opts) {
9998
10124
  function regexParse2(opts) {
9999
10125
  const { file, content, lang } = opts;
10000
10126
  const symbols = [];
10001
- const basename9 = path17.basename(file).toLowerCase();
10002
- const isPackageJson = basename9 === "package.json";
10003
- const isTsconfig = basename9 === "tsconfig.json" || basename9 === "tsconfig.build.json";
10127
+ const basename10 = path17.basename(file).toLowerCase();
10128
+ const isPackageJson = basename10 === "package.json";
10129
+ const isTsconfig = basename10 === "tsconfig.json" || basename10 === "tsconfig.build.json";
10004
10130
  const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
10005
10131
  const isOpenApi = content.includes("openapi") || content.includes("swagger");
10006
10132
  const lines = content.split("\n");
@@ -13154,8 +13280,243 @@ function levelRank(level) {
13154
13280
  }
13155
13281
  }
13156
13282
 
13283
+ // src/exec-kill-guard.ts
13284
+ import * as os7 from "node:os";
13285
+ import * as path24 from "node:path";
13286
+ var isWin3 = os7.platform() === "win32";
13287
+ async function checkExecKillCommand(cmd, args) {
13288
+ if (!cmd) return { blocked: false };
13289
+ const cmdLower = cmd.toLowerCase().trim();
13290
+ const fullCommand = [cmdLower, ...args].join(" ").replace(/\s+/g, " ").trim();
13291
+ if (isWin3) {
13292
+ if (cmdLower === "taskkill" || cmdLower === "taskkill.exe") {
13293
+ const hasForce = args.some((a) => a.toUpperCase() === "/F" || a.toUpperCase() === "-F");
13294
+ const signal = hasForce ? "FORCE" : "TERM";
13295
+ for (let i = 0; i < args.length; i++) {
13296
+ const a = args[i];
13297
+ if (a.toUpperCase() === "/IM" || a.toUpperCase() === "-IM") {
13298
+ const nameArg = args[i + 1];
13299
+ if (nameArg) {
13300
+ const result = await checkKillTarget({ name: nameArg, signal, cmd: fullCommand });
13301
+ if (result.blocked) return result;
13302
+ }
13303
+ }
13304
+ }
13305
+ for (let i = 0; i < args.length; i++) {
13306
+ const a = args[i];
13307
+ if (a.toUpperCase() === "/PID" || a.toUpperCase() === "-PID") {
13308
+ const pidArg = args[i + 1];
13309
+ if (pidArg && /^\d+$/.test(pidArg)) {
13310
+ const result = await checkKillTarget({
13311
+ pid: parseInt(pidArg, 10),
13312
+ signal,
13313
+ cmd: fullCommand
13314
+ });
13315
+ if (result.blocked) return result;
13316
+ }
13317
+ }
13318
+ }
13319
+ for (let i = 0; i < args.length; i++) {
13320
+ const a = args[i];
13321
+ if (a.toUpperCase() === "/FI" || a.toUpperCase() === "-FI") {
13322
+ const filterArg = args[i + 1];
13323
+ if (filterArg) {
13324
+ const nameMatch = filterArg.match(/IMAGENAME\s+eq\s+"?([^"\s]+)/i);
13325
+ if (nameMatch?.[1]) {
13326
+ const result = await checkKillTarget({ name: nameMatch[1], signal, cmd: fullCommand });
13327
+ if (result.blocked) return result;
13328
+ }
13329
+ }
13330
+ }
13331
+ }
13332
+ return { blocked: false };
13333
+ }
13334
+ if (cmdLower === "powershell" || cmdLower === "powershell.exe" || cmdLower === "pwsh" || cmdLower === "pwsh.exe" || cmdLower === "cmd" || cmdLower === "cmd.exe") {
13335
+ const shellFlagIndex = args.findIndex((arg) => {
13336
+ const lower = arg.toLowerCase();
13337
+ return lower === "-c" || lower === "-command" || lower === "/c";
13338
+ });
13339
+ if (shellFlagIndex >= 0) {
13340
+ const innerTokens = tokenizeShellCommand(args.slice(shellFlagIndex + 1).join(" "));
13341
+ const innerCommand = innerTokens[0];
13342
+ if (innerCommand) {
13343
+ const result = await checkExecKillCommand(innerCommand, innerTokens.slice(1));
13344
+ if (result.blocked) return result;
13345
+ }
13346
+ }
13347
+ }
13348
+ if (cmdLower === "stop-process" || cmdLower === "kill") {
13349
+ for (let i = 0; i < args.length; i++) {
13350
+ const a = args[i];
13351
+ if (a === "-Name" || a === "-n") {
13352
+ const nameArg = args[i + 1]?.replace(/^['"]|['"]$/g, "");
13353
+ if (nameArg) {
13354
+ const result = await checkKillTarget({
13355
+ name: nameArg,
13356
+ signal: "FORCE",
13357
+ cmd: fullCommand
13358
+ });
13359
+ if (result.blocked) return result;
13360
+ }
13361
+ }
13362
+ if (a === "-Id" || a === "-PID" || a === "-pid") {
13363
+ const pidArg = args[i + 1];
13364
+ if (pidArg && /^\d+$/.test(pidArg)) {
13365
+ const result = await checkKillTarget({
13366
+ pid: parseInt(pidArg, 10),
13367
+ signal: "FORCE",
13368
+ cmd: fullCommand
13369
+ });
13370
+ if (result.blocked) return result;
13371
+ }
13372
+ }
13373
+ }
13374
+ const firstNonFlag = args.find((a) => !a.startsWith("-"));
13375
+ if (firstNonFlag) {
13376
+ const name = firstNonFlag.replace(/^['"]|['"]$/g, "");
13377
+ const result = await checkKillTarget({ name, signal: "TERM", cmd: fullCommand });
13378
+ if (result.blocked) return result;
13379
+ }
13380
+ }
13381
+ if (cmdLower === "wmic" || cmdLower === "wmic.exe") {
13382
+ const joined = args.join(" ").toLowerCase();
13383
+ if (/\bprocess\b/.test(joined) && /\bdelete\b/.test(joined)) {
13384
+ const nameMatch = joined.match(/name\s*=\s*['"]?([^'"]+)/);
13385
+ if (nameMatch?.[1]) {
13386
+ const result = await checkKillTarget({
13387
+ name: nameMatch[1].trim(),
13388
+ signal: "FORCE",
13389
+ cmd: fullCommand
13390
+ });
13391
+ if (result.blocked) return result;
13392
+ }
13393
+ return {
13394
+ blocked: true,
13395
+ reason: "Blocked: wmic process delete targets all matched processes \u2014 would include protected WrongStack processes."
13396
+ };
13397
+ }
13398
+ }
13399
+ if (cmdLower === "node" || cmdLower === "node.exe") {
13400
+ if (args.includes("-e") || args.includes("--eval")) {
13401
+ const evalIdx = args.indexOf("-e") !== -1 ? args.indexOf("-e") : args.indexOf("--eval");
13402
+ const evalCode = args[evalIdx + 1] ?? "";
13403
+ if (/\bprocess\.kill\s*\(/.test(evalCode)) {
13404
+ const pidMatch = evalCode.match(/process\.kill\s*\(\s*(\d+)/);
13405
+ if (pidMatch?.[1]) {
13406
+ const pid = parseInt(pidMatch[1], 10);
13407
+ const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
13408
+ if (result.blocked) return result;
13409
+ }
13410
+ return {
13411
+ blocked: true,
13412
+ reason: "Blocked: node -e with process.kill() \u2014 would target protected WrongStack process(es)."
13413
+ };
13414
+ }
13415
+ }
13416
+ }
13417
+ } else {
13418
+ if (cmdLower === "kill") {
13419
+ for (const a of args) {
13420
+ const num = a.replace(/^-/, "");
13421
+ if (/^\d+$/.test(num)) {
13422
+ const pid = parseInt(num, 10);
13423
+ const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
13424
+ if (result.blocked) return result;
13425
+ }
13426
+ }
13427
+ }
13428
+ if (cmdLower === "pkill" || cmdLower === "killall") {
13429
+ const firstNonFlag = args.find((a) => !a.startsWith("-"));
13430
+ if (firstNonFlag) {
13431
+ const result = await checkKillTarget({
13432
+ name: firstNonFlag,
13433
+ signal: "SIGTERM",
13434
+ cmd: fullCommand
13435
+ });
13436
+ if (result.blocked) return result;
13437
+ }
13438
+ }
13439
+ }
13440
+ return { blocked: false };
13441
+ }
13442
+ function tokenizeShellCommand(command) {
13443
+ const tokens = [];
13444
+ let current = "";
13445
+ let quote = null;
13446
+ for (const char of command.trim()) {
13447
+ if (quote) {
13448
+ if (char === quote) quote = null;
13449
+ else current += char;
13450
+ continue;
13451
+ }
13452
+ if (char === '"' || char === "'") {
13453
+ quote = char;
13454
+ } else if (/\s/.test(char)) {
13455
+ if (current) {
13456
+ tokens.push(current);
13457
+ current = "";
13458
+ }
13459
+ } else {
13460
+ current += char;
13461
+ }
13462
+ }
13463
+ if (current) tokens.push(current);
13464
+ return tokens;
13465
+ }
13466
+ async function checkKillTarget(target) {
13467
+ const registry = getPersistentProcessRegistry();
13468
+ if (target.pid !== void 0) {
13469
+ const blocked = await registry.shouldBlockKill(target.pid);
13470
+ if (blocked) {
13471
+ return {
13472
+ blocked: true,
13473
+ reason: `Blocked: kill ${target.signal} PID ${target.pid} targets a protected WrongStack process (${target.cmd.slice(0, 80)}).`
13474
+ };
13475
+ }
13476
+ if (target.pid === process.pid) {
13477
+ return {
13478
+ blocked: true,
13479
+ reason: "Blocked: cannot kill the current WrongStack process."
13480
+ };
13481
+ }
13482
+ if (target.pid === process.ppid) {
13483
+ return {
13484
+ blocked: true,
13485
+ reason: "Blocked: cannot kill the parent terminal hosting WrongStack."
13486
+ };
13487
+ }
13488
+ return { blocked: false };
13489
+ }
13490
+ if (target.name) {
13491
+ const nameLower = target.name.toLowerCase().replace(/\.exe$/, "");
13492
+ if (nameLower.includes("wrongstack")) {
13493
+ return {
13494
+ blocked: true,
13495
+ reason: `Blocked: kill ${target.signal} '${target.name}' targets a WrongStack process name.`
13496
+ };
13497
+ }
13498
+ const currentImage = path24.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
13499
+ const targetsNodeRuntime = nameLower === "node" || nameLower.startsWith("node");
13500
+ if (targetsNodeRuntime && currentImage === "node") {
13501
+ return {
13502
+ blocked: true,
13503
+ reason: `Blocked: kill ${target.signal} '${target.name}' would kill the active WrongStack node.exe runtime.`
13504
+ };
13505
+ }
13506
+ const protectedPids = await registry.getAllProtectedPids();
13507
+ if (protectedPids.length > 0 && targetsNodeRuntime) {
13508
+ return {
13509
+ blocked: true,
13510
+ reason: `Blocked: kill ${target.signal} '${target.name}' would kill all node.exe processes including active WrongStack instance(s).`
13511
+ };
13512
+ }
13513
+ return { blocked: false };
13514
+ }
13515
+ return { blocked: false };
13516
+ }
13517
+
13157
13518
  // src/exec.ts
13158
- var isWin3 = process.platform === "win32";
13519
+ var isWin4 = process.platform === "win32";
13159
13520
  var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
13160
13521
  // JS / TS toolchain
13161
13522
  "node",
@@ -13931,6 +14292,19 @@ var execTool = {
13931
14292
  const args = (input.args ?? []).slice(0, MAX_ARGS);
13932
14293
  const timeout = Math.max(1, Math.min(input.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3));
13933
14294
  const danger = detectDanger(cmd, args, dangerBypass);
14295
+ const killCheck = await checkExecKillCommand(cmd, args);
14296
+ if (killCheck.blocked) {
14297
+ return {
14298
+ command: cmd,
14299
+ args,
14300
+ stdout: "",
14301
+ stderr: killCheck.reason ?? "Kill command blocked: targets a protected WrongStack process.",
14302
+ exitCode: 1,
14303
+ truncated: false,
14304
+ allowed: false,
14305
+ danger
14306
+ };
14307
+ }
13934
14308
  const argError = validateArgs(cmd, args);
13935
14309
  if (argError) {
13936
14310
  return {
@@ -13981,7 +14355,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
13981
14355
  let timedOut = false;
13982
14356
  const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
13983
14357
  const resolved = resolveWin32Command(cmd);
13984
- const needsShell = isWin3 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
14358
+ const needsShell = isWin4 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
13985
14359
  const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
13986
14360
  const spawnCmd = shim?.command ?? resolved;
13987
14361
  const spawnArgs = shim?.args ?? args;
@@ -14006,7 +14380,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
14006
14380
  env: buildChildEnv2(sessionId),
14007
14381
  stdio: ["ignore", "pipe", "pipe"],
14008
14382
  windowsHide: true,
14009
- ...isWin3 ? {} : { signal },
14383
+ ...isWin4 ? {} : { signal },
14010
14384
  ...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {}
14011
14385
  });
14012
14386
  } catch (err) {
@@ -14045,7 +14419,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
14045
14419
  const isAbort = err && err.code === "ABORT_ERR";
14046
14420
  const stderrText = isAbort ? `Aborted: ${err.message}` : err.message;
14047
14421
  clearTimeout(timer);
14048
- if (isWin3) signal.removeEventListener("abort", onAbort);
14422
+ if (isWin4) signal.removeEventListener("abort", onAbort);
14049
14423
  if (typeof pid === "number") registry.unregister(pid);
14050
14424
  registry.afterCall(Date.now() - startedAt, true);
14051
14425
  spool.finalize();
@@ -14078,7 +14452,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
14078
14452
  if (typeof pid === "number") registry.kill(pid, { force: true });
14079
14453
  else child.kill("SIGTERM");
14080
14454
  };
14081
- if (isWin3) {
14455
+ if (isWin4) {
14082
14456
  if (signal.aborted) onAbort();
14083
14457
  else signal.addEventListener("abort", onAbort, { once: true });
14084
14458
  }
@@ -14098,7 +14472,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
14098
14472
  });
14099
14473
  child.on("close", (code) => {
14100
14474
  clearTimeout(timer);
14101
- if (isWin3) signal.removeEventListener("abort", onAbort);
14475
+ if (isWin4) signal.removeEventListener("abort", onAbort);
14102
14476
  if (typeof pid === "number") registry.unregister(pid);
14103
14477
  const durationMs = Date.now() - startedAt;
14104
14478
  const exitCode = killed ? 124 : code ?? 1;
@@ -14909,7 +15283,7 @@ function runGit2(args, cwd, signal) {
14909
15283
 
14910
15284
  // src/glob.ts
14911
15285
  import * as fs20 from "node:fs/promises";
14912
- import * as path24 from "node:path";
15286
+ import * as path25 from "node:path";
14913
15287
  import { compileGlob as compileGlob3 } from "@wrongstack/core";
14914
15288
 
14915
15289
  // src/_concurrency.ts
@@ -15013,7 +15387,7 @@ var globTool = {
15013
15387
  if (DEFAULT_IGNORE2.includes(name)) continue;
15014
15388
  if (ignored.includes(name)) continue;
15015
15389
  const rel = relPrefix ? `${relPrefix}/${name}` : name;
15016
- const full = path24.join(dir, name);
15390
+ const full = path25.join(dir, name);
15017
15391
  if (e.isDirectory()) {
15018
15392
  subdirs.push({ full, rel });
15019
15393
  } else if (e.isFile()) {
@@ -15057,7 +15431,7 @@ var globTool = {
15057
15431
  };
15058
15432
  async function readGitignore(dir) {
15059
15433
  try {
15060
- const raw = await fs20.readFile(path24.join(dir, ".gitignore"), "utf8");
15434
+ const raw = await fs20.readFile(path25.join(dir, ".gitignore"), "utf8");
15061
15435
  return raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
15062
15436
  } catch {
15063
15437
  return [];
@@ -15068,7 +15442,7 @@ async function readGitignore(dir) {
15068
15442
  import { expectDefined as expectDefined7 } from "@wrongstack/core";
15069
15443
  import { spawn as spawn10 } from "node:child_process";
15070
15444
  import * as fs21 from "node:fs/promises";
15071
- import * as path25 from "node:path";
15445
+ import * as path26 from "node:path";
15072
15446
  import { buildChildEnv as buildChildEnv5, compileGlob as compileGlob4, ToolValidationError as ToolValidationError4 } from "@wrongstack/core";
15073
15447
 
15074
15448
  // src/_regex.ts
@@ -15461,7 +15835,7 @@ async function runNative(input, base, mode, limit, signal) {
15461
15835
  if (stopped) return;
15462
15836
  if (DEFAULT_IGNORE3.includes(e.name)) continue;
15463
15837
  if (e.isSymbolicLink()) continue;
15464
- const full = path25.join(dir, e.name);
15838
+ const full = path26.join(dir, e.name);
15465
15839
  if (e.isDirectory()) {
15466
15840
  subdirs.push(full);
15467
15841
  } else if (e.isFile()) {
@@ -16049,56 +16423,56 @@ function jmespathSearch(data, query) {
16049
16423
  }
16050
16424
  function validateJsonSchema(data, schema) {
16051
16425
  const errors = [];
16052
- function check(value, s, path32) {
16426
+ function check(value, s, path33) {
16053
16427
  if (s["type"]) {
16054
16428
  const expectedType = s["type"];
16055
16429
  const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
16056
16430
  if (expectedType === "integer") {
16057
- if (!Number.isInteger(value)) errors.push(`${path32}: expected integer, got ${actualType}`);
16431
+ if (!Number.isInteger(value)) errors.push(`${path33}: expected integer, got ${actualType}`);
16058
16432
  } else if (expectedType !== actualType) {
16059
- errors.push(`${path32}: expected ${expectedType}, got ${actualType}`);
16433
+ errors.push(`${path33}: expected ${expectedType}, got ${actualType}`);
16060
16434
  }
16061
16435
  }
16062
16436
  if (typeof value === "string" && s["format"] === "uri" && value) {
16063
16437
  try {
16064
16438
  new URL(value);
16065
16439
  } catch {
16066
- errors.push(`${path32}: not a valid URI`);
16440
+ errors.push(`${path33}: not a valid URI`);
16067
16441
  }
16068
16442
  }
16069
16443
  if (typeof value === "string" && s["pattern"]) {
16070
16444
  const re = new RegExp(s["pattern"]);
16071
- if (!re.test(value)) errors.push(`${path32}: does not match pattern ${s["pattern"]}`);
16445
+ if (!re.test(value)) errors.push(`${path33}: does not match pattern ${s["pattern"]}`);
16072
16446
  }
16073
16447
  if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
16074
- errors.push(`${path32}: string too short (min ${s["minLength"]})`);
16448
+ errors.push(`${path33}: string too short (min ${s["minLength"]})`);
16075
16449
  }
16076
16450
  if (typeof value === "string" && s["maxLength"] !== void 0 && value.length > s["maxLength"]) {
16077
- errors.push(`${path32}: string too long (max ${s["maxLength"]})`);
16451
+ errors.push(`${path33}: string too long (max ${s["maxLength"]})`);
16078
16452
  }
16079
16453
  if (typeof value === "number" && s["minimum"] !== void 0 && value < s["minimum"]) {
16080
- errors.push(`${path32}: below minimum ${s["minimum"]}`);
16454
+ errors.push(`${path33}: below minimum ${s["minimum"]}`);
16081
16455
  }
16082
16456
  if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
16083
- errors.push(`${path32}: above maximum ${s["maximum"]}`);
16457
+ errors.push(`${path33}: above maximum ${s["maximum"]}`);
16084
16458
  }
16085
16459
  if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
16086
16460
  for (let i = 0; i < value.length; i++) {
16087
- check(value[i], s["items"], `${path32}[${i}]`);
16461
+ check(value[i], s["items"], `${path33}[${i}]`);
16088
16462
  }
16089
16463
  }
16090
16464
  if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
16091
16465
  const props = s["properties"];
16092
16466
  for (const [k, propSchema] of Object.entries(props)) {
16093
- check(value[k], propSchema, `${path32}.${k}`);
16467
+ check(value[k], propSchema, `${path33}.${k}`);
16094
16468
  }
16095
16469
  }
16096
16470
  }
16097
16471
  check(data, schema, "$");
16098
16472
  return { valid: errors.length === 0, errors };
16099
16473
  }
16100
- function simpleQuery(data, path32) {
16101
- const parts = path32.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
16474
+ function simpleQuery(data, path33) {
16475
+ const parts = path33.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
16102
16476
  let current = data;
16103
16477
  for (const part of parts) {
16104
16478
  if (current === null || current === void 0) return void 0;
@@ -17705,7 +18079,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
17705
18079
  }
17706
18080
  var DOCKER_LOGS_TIMEOUT_MS = 3e3;
17707
18081
  var MAX_TAIL_LINES = 1e5;
17708
- async function fileLogs(path32, lines, filterRe, stream) {
18082
+ async function fileLogs(path33, lines, filterRe, stream) {
17709
18083
  const { createInterface } = await import("node:readline");
17710
18084
  const { createReadStream: createReadStream2 } = await import("node:fs");
17711
18085
  const entries = [];
@@ -17714,7 +18088,7 @@ async function fileLogs(path32, lines, filterRe, stream) {
17714
18088
  let writeIdx = 0;
17715
18089
  let totalLines = 0;
17716
18090
  const rl = createInterface({
17717
- input: createReadStream2(path32),
18091
+ input: createReadStream2(path33),
17718
18092
  crlfDelay: Number.POSITIVE_INFINITY
17719
18093
  });
17720
18094
  for await (const line of rl) {
@@ -17735,7 +18109,7 @@ async function fileLogs(path32, lines, filterRe, stream) {
17735
18109
  if (parsed) entries.push(parsed);
17736
18110
  }
17737
18111
  return {
17738
- source: path32,
18112
+ source: path33,
17739
18113
  entries,
17740
18114
  total: entries.length,
17741
18115
  truncated: totalLines > effLines,
@@ -17966,8 +18340,8 @@ function parseOutdatedOutput(json2, exitCode) {
17966
18340
  init_util();
17967
18341
  import { spawn as spawn13 } from "node:child_process";
17968
18342
  import * as fs23 from "node:fs/promises";
17969
- import * as os7 from "node:os";
17970
- import * as path26 from "node:path";
18343
+ import * as os8 from "node:os";
18344
+ import * as path27 from "node:path";
17971
18345
  import { buildChildEnv as buildChildEnv8 } from "@wrongstack/core";
17972
18346
  var patchTool = {
17973
18347
  name: "patch",
@@ -18003,9 +18377,9 @@ var patchTool = {
18003
18377
  for (const t of targets) {
18004
18378
  const stripped = stripPathComponents(t, strip);
18005
18379
  if (!stripped) continue;
18006
- const candidate = path26.resolve(dir, stripped);
18007
- const rel = path26.relative(ctx.projectRoot, candidate);
18008
- if (rel.startsWith("..") || path26.isAbsolute(rel)) {
18380
+ const candidate = path27.resolve(dir, stripped);
18381
+ const rel = path27.relative(ctx.projectRoot, candidate);
18382
+ if (rel.startsWith("..") || path27.isAbsolute(rel)) {
18009
18383
  return {
18010
18384
  applied: 0,
18011
18385
  rejected: 1,
@@ -18022,11 +18396,11 @@ var patchTool = {
18022
18396
  beforeContents.set(target, await readTextForTracking(target));
18023
18397
  }
18024
18398
  }
18025
- const tmpDir = await fs23.mkdtemp(path26.join(os7.tmpdir(), ".wstack_patch_"));
18399
+ const tmpDir = await fs23.mkdtemp(path27.join(os8.tmpdir(), ".wstack_patch_"));
18026
18400
  try {
18027
18401
  await fs23.chmod(tmpDir, 448).catch(() => {
18028
18402
  });
18029
- const patchFile = path26.join(tmpDir, "in.diff");
18403
+ const patchFile = path27.join(tmpDir, "in.diff");
18030
18404
  await fs23.writeFile(patchFile, input.patch, { mode: 384 });
18031
18405
  const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
18032
18406
  const result = await runPatch(args, dir, opts.signal);
@@ -18612,7 +18986,7 @@ ${interesting.join("\n")}` : "symbols/imports: (none detected)"
18612
18986
  import { expectDefined as expectDefined8 } from "@wrongstack/core";
18613
18987
  import { spawn as spawn14 } from "node:child_process";
18614
18988
  import * as fs25 from "node:fs/promises";
18615
- import * as path27 from "node:path";
18989
+ import * as path28 from "node:path";
18616
18990
  import {
18617
18991
  atomicWrite as atomicWrite3,
18618
18992
  buildChildEnv as buildChildEnv9,
@@ -18701,8 +19075,8 @@ var replaceTool = {
18701
19075
  } catch {
18702
19076
  continue;
18703
19077
  }
18704
- const rel = path27.relative(realRoot, realPath);
18705
- if (rel.startsWith("..") || path27.isAbsolute(rel)) continue;
19078
+ const rel = path28.relative(realRoot, realPath);
19079
+ if (rel.startsWith("..") || path28.isAbsolute(rel)) continue;
18706
19080
  const stat17 = await fs25.stat(realPath).catch(() => null);
18707
19081
  if (!stat17?.isFile()) continue;
18708
19082
  let content;
@@ -18831,7 +19205,7 @@ async function globNative(pattern, base, extraGlob) {
18831
19205
  }
18832
19206
  for (const e of entries) {
18833
19207
  if (DEFAULT_IGNORE4.includes(e.name)) continue;
18834
- const full = path27.join(dir, e.name);
19208
+ const full = path28.join(dir, e.name);
18835
19209
  try {
18836
19210
  const stat17 = await fs25.lstat(full);
18837
19211
  if (stat17.isSymbolicLink()) continue;
@@ -18858,7 +19232,7 @@ async function globNative(pattern, base, extraGlob) {
18858
19232
  // src/scaffold.ts
18859
19233
  init_util();
18860
19234
  import * as fs26 from "node:fs/promises";
18861
- import * as path28 from "node:path";
19235
+ import * as path29 from "node:path";
18862
19236
  import { atomicWrite as atomicWrite4 } from "@wrongstack/core";
18863
19237
  var BUILT_IN_TEMPLATES = {
18864
19238
  "npm-package": {
@@ -19008,16 +19382,16 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
19008
19382
  let filesCreated = 0;
19009
19383
  for (const [filePath, content] of Object.entries(templateFiles)) {
19010
19384
  const resolvedPath = substituteVars(filePath, name, vars);
19011
- const joinedPath = path28.join(cwd, resolvedPath);
19012
- const root = path28.resolve(ctx.projectRoot);
19013
- const target = path28.resolve(joinedPath);
19014
- const rel = path28.relative(root, target);
19015
- if (rel.startsWith("..") || path28.isAbsolute(rel)) {
19385
+ const joinedPath = path29.join(cwd, resolvedPath);
19386
+ const root = path29.resolve(ctx.projectRoot);
19387
+ const target = path29.resolve(joinedPath);
19388
+ const rel = path29.relative(root, target);
19389
+ if (rel.startsWith("..") || path29.isAbsolute(rel)) {
19016
19390
  throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
19017
19391
  }
19018
19392
  const fullPath = target;
19019
19393
  if (!dryRun) {
19020
- await fs26.mkdir(path28.dirname(fullPath), { recursive: true });
19394
+ await fs26.mkdir(path29.dirname(fullPath), { recursive: true });
19021
19395
  await atomicWrite4(fullPath, substituteVars(content, name, vars));
19022
19396
  }
19023
19397
  files.push(resolvedPath);
@@ -19874,7 +20248,7 @@ ${formatTaskList2(file.tasks)}` : file.tasks.length > 0 ? formatTaskList2(file.t
19874
20248
  init_spawn_stream();
19875
20249
  init_util();
19876
20250
  init_legacy_bridge();
19877
- import * as path29 from "node:path";
20251
+ import * as path30 from "node:path";
19878
20252
  var testTool = {
19879
20253
  name: "test",
19880
20254
  category: "Code Quality",
@@ -19981,7 +20355,7 @@ async function detectRunner(cwd) {
19981
20355
  const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
19982
20356
  for (const f of candidates) {
19983
20357
  try {
19984
- await stat17(path29.join(cwd, f));
20358
+ await stat17(path30.join(cwd, f));
19985
20359
  if (f.includes("vitest")) return "vitest";
19986
20360
  if (f.includes("jest")) return "jest";
19987
20361
  if (f.includes("mocha")) return "mocha";
@@ -20452,7 +20826,7 @@ var toolUseTool = {
20452
20826
  init_util();
20453
20827
  import { expectDefined as expectDefined10 } from "@wrongstack/core";
20454
20828
  import * as fs28 from "node:fs/promises";
20455
- import * as path30 from "node:path";
20829
+ import * as path31 from "node:path";
20456
20830
  var DEFAULT_IGNORE5 = [
20457
20831
  "node_modules",
20458
20832
  ".git",
@@ -20623,7 +20997,7 @@ async function walkDir(dir, depth, opts) {
20623
20997
  opts.lines.push(opts.prefix + branch + displayName);
20624
20998
  if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
20625
20999
  const childPrefix = opts.prefix + connector;
20626
- await walkDir(path30.join(dir, entry.name), depth + 1, {
21000
+ await walkDir(path31.join(dir, entry.name), depth + 1, {
20627
21001
  ...opts,
20628
21002
  prefix: childPrefix,
20629
21003
  isLast
@@ -20636,7 +21010,7 @@ async function walkDir(dir, depth, opts) {
20636
21010
  init_spawn_stream();
20637
21011
  init_util();
20638
21012
  init_legacy_bridge();
20639
- import * as path31 from "node:path";
21013
+ import * as path32 from "node:path";
20640
21014
  var typecheckTool = {
20641
21015
  name: "typecheck",
20642
21016
  category: "Code Quality",
@@ -20739,8 +21113,8 @@ async function findTsConfig(cwd) {
20739
21113
  const candidates = ["tsconfig.json", "tsconfig.base.json"];
20740
21114
  for (const f of candidates) {
20741
21115
  try {
20742
- const s = await stat17(path31.join(cwd, f));
20743
- if (s.isFile()) return path31.join(cwd, f);
21116
+ const s = await stat17(path32.join(cwd, f));
21117
+ if (s.isFile()) return path32.join(cwd, f);
20744
21118
  } catch {
20745
21119
  }
20746
21120
  }