@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/index.js CHANGED
@@ -709,8 +709,8 @@ var init_process_registry = __esm({
709
709
  if (p.killed) return true;
710
710
  if (p.protected) return false;
711
711
  const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
712
- const isWin4 = os.platform() === "win32";
713
- if (isWin4) {
712
+ const isWin5 = os.platform() === "win32";
713
+ if (isWin5) {
714
714
  const liveRealChild = p.child.exitCode === null && typeof p.child.pid === "number";
715
715
  const directFallback = () => {
716
716
  if (p.child.exitCode === null) {
@@ -2608,8 +2608,8 @@ async function scanDirectory(directory, depth, profiles, limits, state, extraIgn
2608
2608
  collectFileEvidence(directory, fullPath, entry.name, profiles, state);
2609
2609
  }
2610
2610
  }
2611
- function collectFileEvidence(directory, fullPath, basename10, profiles, state) {
2612
- const lower = basename10.toLowerCase();
2611
+ function collectFileEvidence(directory, fullPath, basename11, profiles, state) {
2612
+ const lower = basename11.toLowerCase();
2613
2613
  const extension = path4.extname(lower);
2614
2614
  for (const profile of profiles) {
2615
2615
  const detector = profile.detectors.find(
@@ -2620,7 +2620,7 @@ function collectFileEvidence(directory, fullPath, basename10, profiles, state) {
2620
2620
  candidate.evidence.push({
2621
2621
  kind: detector.kind,
2622
2622
  path: fullPath,
2623
- value: basename10,
2623
+ value: basename11,
2624
2624
  weight: detector.weight
2625
2625
  });
2626
2626
  if (detector.kind === "manifest" || detector.kind === "config") {
@@ -3937,15 +3937,15 @@ async function changedPaths(before, after, beforeSizes, afterSizes) {
3937
3937
  const beforeSet = new Set(before);
3938
3938
  const afterSet = new Set(after);
3939
3939
  const changed = /* @__PURE__ */ new Set();
3940
- for (const path33 of after) {
3941
- if (!beforeSet.has(path33)) changed.add(path33);
3940
+ for (const path34 of after) {
3941
+ if (!beforeSet.has(path34)) changed.add(path34);
3942
3942
  }
3943
- for (const path33 of before) {
3944
- if (!afterSet.has(path33)) changed.add(path33);
3943
+ for (const path34 of before) {
3944
+ if (!afterSet.has(path34)) changed.add(path34);
3945
3945
  }
3946
3946
  if (beforeSizes && afterSizes) {
3947
- for (const path33 of after) {
3948
- if (beforeSizes.get(path33) !== afterSizes.get(path33)) changed.add(path33);
3947
+ for (const path34 of after) {
3948
+ if (beforeSizes.get(path34) !== afterSizes.get(path34)) changed.add(path34);
3949
3949
  }
3950
3950
  }
3951
3951
  return [...changed].sort();
@@ -5103,8 +5103,8 @@ function normalizeShell(value) {
5103
5103
  if (v === "pwsh" || v === "pwsh.exe") return "pwsh";
5104
5104
  return void 0;
5105
5105
  }
5106
- function resolveSessionShell(platform5, env, deps = {}) {
5107
- if (platform5 !== "win32") return void 0;
5106
+ function resolveSessionShell(platform6, env, deps = {}) {
5107
+ if (platform6 !== "win32") return void 0;
5108
5108
  const override = normalizeShell(env.get("WRONGSTACK_SHELL"));
5109
5109
  if (override) return override;
5110
5110
  const hasBinary = deps.hasBinary ?? ((bin) => resolveWin32Command(bin) !== bin);
@@ -5114,11 +5114,11 @@ function resolveSessionShell(platform5, env, deps = {}) {
5114
5114
  }
5115
5115
  function ensureSessionShell(opts = {}) {
5116
5116
  const env = opts.env ?? process.env;
5117
- const platform5 = opts.platform ?? process.platform;
5118
- if (platform5 !== "win32") return void 0;
5117
+ const platform6 = opts.platform ?? process.platform;
5118
+ if (platform6 !== "win32") return void 0;
5119
5119
  const existing = normalizeShell(env["WRONGSTACK_SHELL"]);
5120
5120
  if (existing) return existing;
5121
- const chosen = resolveSessionShell(platform5, { get: (k) => env[k] }, { hasBinary: opts.hasBinary }) ?? "cmd";
5121
+ const chosen = resolveSessionShell(platform6, { get: (k) => env[k] }, { hasBinary: opts.hasBinary }) ?? "cmd";
5122
5122
  env["WRONGSTACK_SHELL"] = chosen;
5123
5123
  return chosen;
5124
5124
  }
@@ -5693,17 +5693,18 @@ function resetPersistentProcessRegistry() {
5693
5693
 
5694
5694
  // src/bash-kill-guard.ts
5695
5695
  var isWin2 = os3.platform() === "win32";
5696
+ var SCRIPT_KILL_RE = /^(?:\.\\|\.\/)?(?:kill|terminate|stop)\S*\.(?:ps1|bat|cmd|sh)(?:\s|$)/i;
5697
+ var SCRIPT_KILL_RE_POSIX = /^(?:\.\/)?(?:kill|terminate|stop)\S*\.sh(?:\s|$)/i;
5698
+ var SCRIPT_KILL_FALLBACK_RE = /^\S*(?:kill|terminate|stop)\S*\.(?:ps1|bat|cmd|sh)\b/i;
5696
5699
  function extractKillCommand(command) {
5697
5700
  const normalized = command.replace(/\s+/g, " ").trim();
5698
- const shellCMatch = normalized.match(
5699
- /^(?:\S+(?:\s+\S+)?)?\s+-c\s+['"](.+?)['"]$/
5700
- );
5701
- if (shellCMatch?.[1]) {
5702
- const inner = shellCMatch[1].trim();
5701
+ const shellCMatch = normalized.match(/^.+?\s+-c\s+(['"])([\s\S]+)\1$/);
5702
+ if (shellCMatch?.[2]) {
5703
+ const inner = shellCMatch[2].trim();
5703
5704
  return isKillRelatedCommand(inner) ? inner : null;
5704
5705
  }
5705
5706
  const shellCUnquoted = normalized.match(
5706
- /^(?:\S+(?:\s+\S+)?)?\s+-c\s+(kill(?:\s+-[a-zA-Z]+)?(?:\s+\d+)+)$/
5707
+ /^.+?\s+-c\s+(kill(?:\s+-s\s+[a-zA-Z0-9]+|\s+-[a-zA-Z0-9]+)?\s+\d+)$/
5707
5708
  );
5708
5709
  if (shellCUnquoted?.[1]) {
5709
5710
  return shellCUnquoted[1];
@@ -5715,22 +5716,47 @@ function isKillRelatedCommand(cmd) {
5715
5716
  if (isWin2) {
5716
5717
  if (/^taskkill\s/i.test(normalized)) return true;
5717
5718
  if (/^tskill\s/i.test(normalized)) return true;
5719
+ if (/^(stop-process|kill|stop)\s/i.test(normalized)) return true;
5720
+ if (/^wmic\s+process\s/i.test(normalized) && /\bdelete\b/i.test(normalized)) return true;
5721
+ if (SCRIPT_KILL_RE.test(normalized)) return true;
5718
5722
  return false;
5719
5723
  }
5720
5724
  if (/^kill(\s|$)/.test(normalized)) return true;
5721
5725
  if (/^(pkill|killall|pgrep|skill)\s/.test(normalized)) return true;
5722
5726
  if (/^\/proc\/\d+\/(?:kill|fd)/.test(normalized)) return true;
5727
+ if (SCRIPT_KILL_RE_POSIX.test(normalized)) return true;
5723
5728
  return false;
5724
5729
  }
5725
5730
  function parseKillCommand(command) {
5726
5731
  const normalized = command.replace(/\s+/g, " ").trim();
5727
5732
  if (isWin2) {
5728
- const taskkillMatch = normalized.match(/^taskkill\s+(?:\/[a-zA-Z]+\s+)*\/PID\s+(\d+)/i);
5729
- if (taskkillMatch?.[1]) {
5730
- const pidStr = taskkillMatch[1];
5733
+ const hasTaskkillForce = /(?:^|\s)\/F(?=\s|$)/i.test(normalized);
5734
+ const isSimpleTaskkill = /^taskkill\s+/i.test(normalized) && !/[|&<>]/.test(normalized);
5735
+ const taskkillPidMatch = isSimpleTaskkill ? normalized.match(/(?:^|\s)\/PID\s+(\d+)(?=\s|$)/i) : null;
5736
+ if (taskkillPidMatch?.[1]) {
5737
+ return {
5738
+ pid: parseInt(taskkillPidMatch[1], 10),
5739
+ signal: hasTaskkillForce ? "FORCE" : "TERM",
5740
+ isGroupKill: false,
5741
+ isAllKill: false,
5742
+ originalCommand: command
5743
+ };
5744
+ }
5745
+ const taskkillImMatch = isSimpleTaskkill ? normalized.match(/(?:^|\s)\/IM\s+([^\s/]+)(?=\s|$)/i) : null;
5746
+ if (taskkillImMatch?.[1]) {
5747
+ return {
5748
+ name: taskkillImMatch[1],
5749
+ signal: hasTaskkillForce ? "FORCE" : "TERM",
5750
+ isGroupKill: false,
5751
+ isAllKill: false,
5752
+ originalCommand: command
5753
+ };
5754
+ }
5755
+ const taskkillFiMatch = isSimpleTaskkill ? normalized.match(/(?:^|\s)\/FI\s+"IMAGENAME\s+eq\s+([^"]+)"(?=\s|$)/i) : null;
5756
+ if (taskkillFiMatch?.[1]) {
5731
5757
  return {
5732
- pid: parseInt(pidStr, 10),
5733
- signal: normalized.includes("/F") ? "FORCE" : "TERM",
5758
+ name: taskkillFiMatch[1],
5759
+ signal: hasTaskkillForce ? "FORCE" : "TERM",
5734
5760
  isGroupKill: false,
5735
5761
  isAllKill: false,
5736
5762
  originalCommand: command
@@ -5738,18 +5764,109 @@ function parseKillCommand(command) {
5738
5764
  }
5739
5765
  const tskillMatch = normalized.match(/^tskill\s+(\d+)/i);
5740
5766
  if (tskillMatch?.[1]) {
5741
- const pidStr = tskillMatch[1];
5742
5767
  return {
5743
- pid: parseInt(pidStr, 10),
5768
+ pid: parseInt(tskillMatch[1], 10),
5744
5769
  signal: "TERM",
5745
5770
  isGroupKill: false,
5746
5771
  isAllKill: false,
5747
5772
  originalCommand: command
5748
5773
  };
5749
5774
  }
5775
+ const isStopProcIdCommand = /^(?:stop-process|kill)(?:\s+-(?:id|pid)\s+\d+|\s+-[a-zA-Z]+)+$/i.test(normalized);
5776
+ const stopProcIdMatch = normalized.match(/(?:^|\s)-(?:id|pid)\s+(\d+)(?=\s|$)/i);
5777
+ if (isStopProcIdCommand && stopProcIdMatch?.[1]) {
5778
+ return {
5779
+ pid: parseInt(stopProcIdMatch[1], 10),
5780
+ signal: "FORCE",
5781
+ isGroupKill: false,
5782
+ isAllKill: false,
5783
+ originalCommand: command
5784
+ };
5785
+ }
5786
+ const killSignalOptionMatch = normalized.match(/^kill\s+-s\s+([a-zA-Z0-9]+)\s+(\d+)$/i);
5787
+ if (killSignalOptionMatch?.[1] && killSignalOptionMatch[2]) {
5788
+ return {
5789
+ pid: parseInt(killSignalOptionMatch[2], 10),
5790
+ signal: killSignalOptionMatch[1].toUpperCase(),
5791
+ isGroupKill: false,
5792
+ isAllKill: false,
5793
+ originalCommand: command
5794
+ };
5795
+ }
5796
+ const killPosixMatch = normalized.match(/^kill\s+(?:(-[a-zA-Z0-9]+)\s+)?(\d+)$/);
5797
+ if (killPosixMatch?.[2]) {
5798
+ const sig = killPosixMatch[1] ? killPosixMatch[1].slice(1).toUpperCase() : "TERM";
5799
+ return {
5800
+ pid: parseInt(killPosixMatch[2], 10),
5801
+ signal: sig,
5802
+ isGroupKill: false,
5803
+ isAllKill: false,
5804
+ originalCommand: command
5805
+ };
5806
+ }
5807
+ const stopProcNameMatch = normalized.match(
5808
+ /^(?:stop-process|kill)\s+-(?:name|n)\s+(?:['"]([a-zA-Z0-9_.-]+)['"]|([a-zA-Z0-9_.-]+))(?:\s|$)/i
5809
+ );
5810
+ const stopProcName = stopProcNameMatch?.[1] ?? stopProcNameMatch?.[2];
5811
+ if (stopProcName) {
5812
+ return {
5813
+ name: stopProcName,
5814
+ signal: "FORCE",
5815
+ isGroupKill: false,
5816
+ isAllKill: false,
5817
+ originalCommand: command
5818
+ };
5819
+ }
5820
+ const stopProcStandalone = normalized.match(
5821
+ /^(?:stop-process|kill)\s+['"]?([a-zA-Z][a-zA-Z0-9_.-]+)['"]?$/i
5822
+ );
5823
+ if (stopProcStandalone?.[1]) {
5824
+ return {
5825
+ name: stopProcStandalone[1],
5826
+ signal: "FORCE",
5827
+ isGroupKill: false,
5828
+ isAllKill: false,
5829
+ originalCommand: command
5830
+ };
5831
+ }
5832
+ const wmicMatch = normalized.match(
5833
+ /^wmic\s+process\s+where\s+['"]?(?:name\s*=\s*['"]?)([a-zA-Z0-9_.-]+)/i
5834
+ );
5835
+ if (wmicMatch?.[1]) {
5836
+ return {
5837
+ name: wmicMatch[1],
5838
+ signal: "FORCE",
5839
+ isGroupKill: false,
5840
+ isAllKill: false,
5841
+ originalCommand: command
5842
+ };
5843
+ }
5844
+ const killScriptMatch = normalized.match(SCRIPT_KILL_RE);
5845
+ if (killScriptMatch) {
5846
+ return {
5847
+ name: "kill-script",
5848
+ // sentinel — isKillProtected always blocks "kill-script"
5849
+ signal: "FORCE",
5850
+ isGroupKill: false,
5851
+ isAllKill: false,
5852
+ originalCommand: command
5853
+ };
5854
+ }
5750
5855
  return null;
5751
5856
  }
5752
- const simpleMatch = normalized.match(/^kill\s+(?:(-[a-zA-Z]+)\s+)?(\d+|-?\d+)$/);
5857
+ const signalOptionMatch = normalized.match(/^kill\s+-s\s+([a-zA-Z0-9]+)\s+(\d+|-?\d+)$/i);
5858
+ if (signalOptionMatch?.[1] && signalOptionMatch[2]) {
5859
+ const pidOrGroup = signalOptionMatch[2];
5860
+ const isGroupKill = pidOrGroup.startsWith("-");
5861
+ return {
5862
+ pid: parseInt(isGroupKill ? pidOrGroup.slice(1) : pidOrGroup, 10),
5863
+ signal: signalOptionMatch[1].toUpperCase(),
5864
+ isGroupKill,
5865
+ isAllKill: false,
5866
+ originalCommand: command
5867
+ };
5868
+ }
5869
+ const simpleMatch = normalized.match(/^kill\s+(?:(-[a-zA-Z0-9]+)\s+)?(\d+|-?\d+)$/);
5753
5870
  if (simpleMatch) {
5754
5871
  const signal = simpleMatch[1] ?? "-TERM";
5755
5872
  const pidOrGroup = simpleMatch[2];
@@ -5809,6 +5926,9 @@ async function getProtectedEntries() {
5809
5926
  }
5810
5927
  async function isKillProtected(kill) {
5811
5928
  const registry = getPersistentProcessRegistry();
5929
+ if (kill.name === "kill-script") {
5930
+ return true;
5931
+ }
5812
5932
  if (kill.name) {
5813
5933
  const entries = await getProtectedEntries();
5814
5934
  const killNameLower = kill.name.toLowerCase();
@@ -5848,6 +5968,12 @@ async function checkAndBlockKillCommand(command) {
5848
5968
  reason: `Blocked: complex kill pipeline detected \u2014 "${killCmd.slice(0, 50)}..."`
5849
5969
  };
5850
5970
  }
5971
+ if (SCRIPT_KILL_FALLBACK_RE.test(killCmd)) {
5972
+ return {
5973
+ blocked: true,
5974
+ reason: `Blocked: script-based kill detected \u2014 "${killCmd.slice(0, 80)}" may target protected WrongStack processes (cannot inspect script body).`
5975
+ };
5976
+ }
5851
5977
  return { blocked: false };
5852
5978
  }
5853
5979
  if (await isKillProtected(parsed)) {
@@ -5871,8 +5997,8 @@ async function checkAndBlockKillCommand(command) {
5871
5997
 
5872
5998
  // src/_shell-pick.ts
5873
5999
  var POSIX_DEFAULT = "cmd";
5874
- function pickShell(platform5, command, env) {
5875
- if (platform5 !== "win32") return POSIX_DEFAULT;
6000
+ function pickShell(platform6, command, env) {
6001
+ if (platform6 !== "win32") return POSIX_DEFAULT;
5876
6002
  const override = env.get("WRONGSTACK_SHELL")?.trim().toLowerCase();
5877
6003
  if (override === "cmd" || override === "cmd.exe") return "cmd";
5878
6004
  if (override === "powershell" || override === "powershell.exe") return "powershell";
@@ -6076,10 +6202,10 @@ var bashTool = {
6076
6202
  }));
6077
6203
  }
6078
6204
  const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT_MS2, 6e5));
6079
- const isWin4 = os4.platform() === "win32";
6205
+ const isWin5 = os4.platform() === "win32";
6080
6206
  let plan;
6081
6207
  let winShellKind;
6082
- if (isWin4) {
6208
+ if (isWin5) {
6083
6209
  const shell2 = pickShell("win32", input.command, {
6084
6210
  get: (k) => process.env[k]
6085
6211
  });
@@ -6108,7 +6234,7 @@ var bashTool = {
6108
6234
  const shell = plan.bin;
6109
6235
  const args = plan.useStdin ? [...plan.argv] : [...plan.argv, input.command];
6110
6236
  const env = buildChildEnv2(ctx.session?.id);
6111
- const detached = !isWin4;
6237
+ const detached = !isWin5;
6112
6238
  const startedAt = Date.now();
6113
6239
  if (input.background) {
6114
6240
  let buf2 = "";
@@ -6126,7 +6252,7 @@ var bashTool = {
6126
6252
  // apply: the child gets a hidden console that grandchildren inherit.
6127
6253
  // Windows children survive parent exit either way. POSIX keeps
6128
6254
  // detached for the process-group kill semantics.
6129
- detached: !isWin4,
6255
+ detached: !isWin5,
6130
6256
  windowsHide: true
6131
6257
  });
6132
6258
  if (plan.useStdin) {
@@ -6237,7 +6363,7 @@ var bashTool = {
6237
6363
  stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
6238
6364
  detached,
6239
6365
  windowsHide: true,
6240
- ...isWin4 ? {} : { signal: opts.signal }
6366
+ ...isWin5 ? {} : { signal: opts.signal }
6241
6367
  });
6242
6368
  if (plan.useStdin) {
6243
6369
  try {
@@ -6290,7 +6416,7 @@ var bashTool = {
6290
6416
  const timers = [];
6291
6417
  const spool = createOutputSpool({ tool: "bash", thresholdBytes: MAX_OUTPUT });
6292
6418
  function killWithTimeout(child2, timeoutMs2) {
6293
- if (isWin4) {
6419
+ if (isWin5) {
6294
6420
  if (typeof child2.pid === "number" && child2.exitCode === null) {
6295
6421
  const attempted = registry.kill(child2.pid, { force: true, graceMs: timeoutMs2 });
6296
6422
  if (!attempted) {
@@ -6331,7 +6457,7 @@ var bashTool = {
6331
6457
  timers.push(timer);
6332
6458
  timer.unref?.();
6333
6459
  const onAbort = () => killWithTimeout(child, 2e3);
6334
- if (isWin4) {
6460
+ if (isWin5) {
6335
6461
  if (opts.signal.aborted) onAbort();
6336
6462
  else opts.signal.addEventListener("abort", onAbort, { once: true });
6337
6463
  }
@@ -6445,7 +6571,7 @@ ${hint}` : ""),
6445
6571
  } finally {
6446
6572
  for (const t of timers) clearTimeout(t);
6447
6573
  spool.finalize();
6448
- if (isWin4) opts.signal.removeEventListener("abort", onAbort);
6574
+ if (isWin5) opts.signal.removeEventListener("abort", onAbort);
6449
6575
  child.stdout?.off("data", onStdoutData);
6450
6576
  child.stderr?.off("data", onStderrData);
6451
6577
  child.stdout?.destroy();
@@ -10323,9 +10449,9 @@ function parseSymbols5(opts) {
10323
10449
  function regexParse2(opts) {
10324
10450
  const { file, content, lang } = opts;
10325
10451
  const symbols = [];
10326
- const basename10 = path17.basename(file).toLowerCase();
10327
- const isPackageJson = basename10 === "package.json";
10328
- const isTsconfig = basename10 === "tsconfig.json" || basename10 === "tsconfig.build.json";
10452
+ const basename11 = path17.basename(file).toLowerCase();
10453
+ const isPackageJson = basename11 === "package.json";
10454
+ const isTsconfig = basename11 === "tsconfig.json" || basename11 === "tsconfig.build.json";
10329
10455
  const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
10330
10456
  const isOpenApi = content.includes("openapi") || content.includes("swagger");
10331
10457
  const lines = content.split("\n");
@@ -13289,7 +13415,244 @@ init_output_spool();
13289
13415
  init_util();
13290
13416
  init_process_registry();
13291
13417
  init_win32_resolve();
13292
- var isWin3 = process.platform === "win32";
13418
+
13419
+ // src/exec-kill-guard.ts
13420
+ import * as os7 from "node:os";
13421
+ import * as path24 from "node:path";
13422
+ var isWin3 = os7.platform() === "win32";
13423
+ async function checkExecKillCommand(cmd, args) {
13424
+ if (!cmd) return { blocked: false };
13425
+ const cmdLower = cmd.toLowerCase().trim();
13426
+ const fullCommand = [cmdLower, ...args].join(" ").replace(/\s+/g, " ").trim();
13427
+ if (isWin3) {
13428
+ if (cmdLower === "taskkill" || cmdLower === "taskkill.exe") {
13429
+ const hasForce = args.some((a) => a.toUpperCase() === "/F" || a.toUpperCase() === "-F");
13430
+ const signal = hasForce ? "FORCE" : "TERM";
13431
+ for (let i = 0; i < args.length; i++) {
13432
+ const a = args[i];
13433
+ if (a.toUpperCase() === "/IM" || a.toUpperCase() === "-IM") {
13434
+ const nameArg = args[i + 1];
13435
+ if (nameArg) {
13436
+ const result = await checkKillTarget({ name: nameArg, signal, cmd: fullCommand });
13437
+ if (result.blocked) return result;
13438
+ }
13439
+ }
13440
+ }
13441
+ for (let i = 0; i < args.length; i++) {
13442
+ const a = args[i];
13443
+ if (a.toUpperCase() === "/PID" || a.toUpperCase() === "-PID") {
13444
+ const pidArg = args[i + 1];
13445
+ if (pidArg && /^\d+$/.test(pidArg)) {
13446
+ const result = await checkKillTarget({
13447
+ pid: parseInt(pidArg, 10),
13448
+ signal,
13449
+ cmd: fullCommand
13450
+ });
13451
+ if (result.blocked) return result;
13452
+ }
13453
+ }
13454
+ }
13455
+ for (let i = 0; i < args.length; i++) {
13456
+ const a = args[i];
13457
+ if (a.toUpperCase() === "/FI" || a.toUpperCase() === "-FI") {
13458
+ const filterArg = args[i + 1];
13459
+ if (filterArg) {
13460
+ const nameMatch = filterArg.match(/IMAGENAME\s+eq\s+"?([^"\s]+)/i);
13461
+ if (nameMatch?.[1]) {
13462
+ const result = await checkKillTarget({ name: nameMatch[1], signal, cmd: fullCommand });
13463
+ if (result.blocked) return result;
13464
+ }
13465
+ }
13466
+ }
13467
+ }
13468
+ return { blocked: false };
13469
+ }
13470
+ if (cmdLower === "powershell" || cmdLower === "powershell.exe" || cmdLower === "pwsh" || cmdLower === "pwsh.exe" || cmdLower === "cmd" || cmdLower === "cmd.exe") {
13471
+ const shellFlagIndex = args.findIndex((arg) => {
13472
+ const lower = arg.toLowerCase();
13473
+ return lower === "-c" || lower === "-command" || lower === "/c";
13474
+ });
13475
+ if (shellFlagIndex >= 0) {
13476
+ const innerTokens = tokenizeShellCommand(args.slice(shellFlagIndex + 1).join(" "));
13477
+ const innerCommand = innerTokens[0];
13478
+ if (innerCommand) {
13479
+ const result = await checkExecKillCommand(innerCommand, innerTokens.slice(1));
13480
+ if (result.blocked) return result;
13481
+ }
13482
+ }
13483
+ }
13484
+ if (cmdLower === "stop-process" || cmdLower === "kill") {
13485
+ for (let i = 0; i < args.length; i++) {
13486
+ const a = args[i];
13487
+ if (a === "-Name" || a === "-n") {
13488
+ const nameArg = args[i + 1]?.replace(/^['"]|['"]$/g, "");
13489
+ if (nameArg) {
13490
+ const result = await checkKillTarget({
13491
+ name: nameArg,
13492
+ signal: "FORCE",
13493
+ cmd: fullCommand
13494
+ });
13495
+ if (result.blocked) return result;
13496
+ }
13497
+ }
13498
+ if (a === "-Id" || a === "-PID" || a === "-pid") {
13499
+ const pidArg = args[i + 1];
13500
+ if (pidArg && /^\d+$/.test(pidArg)) {
13501
+ const result = await checkKillTarget({
13502
+ pid: parseInt(pidArg, 10),
13503
+ signal: "FORCE",
13504
+ cmd: fullCommand
13505
+ });
13506
+ if (result.blocked) return result;
13507
+ }
13508
+ }
13509
+ }
13510
+ const firstNonFlag = args.find((a) => !a.startsWith("-"));
13511
+ if (firstNonFlag) {
13512
+ const name = firstNonFlag.replace(/^['"]|['"]$/g, "");
13513
+ const result = await checkKillTarget({ name, signal: "TERM", cmd: fullCommand });
13514
+ if (result.blocked) return result;
13515
+ }
13516
+ }
13517
+ if (cmdLower === "wmic" || cmdLower === "wmic.exe") {
13518
+ const joined = args.join(" ").toLowerCase();
13519
+ if (/\bprocess\b/.test(joined) && /\bdelete\b/.test(joined)) {
13520
+ const nameMatch = joined.match(/name\s*=\s*['"]?([^'"]+)/);
13521
+ if (nameMatch?.[1]) {
13522
+ const result = await checkKillTarget({
13523
+ name: nameMatch[1].trim(),
13524
+ signal: "FORCE",
13525
+ cmd: fullCommand
13526
+ });
13527
+ if (result.blocked) return result;
13528
+ }
13529
+ return {
13530
+ blocked: true,
13531
+ reason: "Blocked: wmic process delete targets all matched processes \u2014 would include protected WrongStack processes."
13532
+ };
13533
+ }
13534
+ }
13535
+ if (cmdLower === "node" || cmdLower === "node.exe") {
13536
+ if (args.includes("-e") || args.includes("--eval")) {
13537
+ const evalIdx = args.indexOf("-e") !== -1 ? args.indexOf("-e") : args.indexOf("--eval");
13538
+ const evalCode = args[evalIdx + 1] ?? "";
13539
+ if (/\bprocess\.kill\s*\(/.test(evalCode)) {
13540
+ const pidMatch = evalCode.match(/process\.kill\s*\(\s*(\d+)/);
13541
+ if (pidMatch?.[1]) {
13542
+ const pid = parseInt(pidMatch[1], 10);
13543
+ const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
13544
+ if (result.blocked) return result;
13545
+ }
13546
+ return {
13547
+ blocked: true,
13548
+ reason: "Blocked: node -e with process.kill() \u2014 would target protected WrongStack process(es)."
13549
+ };
13550
+ }
13551
+ }
13552
+ }
13553
+ } else {
13554
+ if (cmdLower === "kill") {
13555
+ for (const a of args) {
13556
+ const num = a.replace(/^-/, "");
13557
+ if (/^\d+$/.test(num)) {
13558
+ const pid = parseInt(num, 10);
13559
+ const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
13560
+ if (result.blocked) return result;
13561
+ }
13562
+ }
13563
+ }
13564
+ if (cmdLower === "pkill" || cmdLower === "killall") {
13565
+ const firstNonFlag = args.find((a) => !a.startsWith("-"));
13566
+ if (firstNonFlag) {
13567
+ const result = await checkKillTarget({
13568
+ name: firstNonFlag,
13569
+ signal: "SIGTERM",
13570
+ cmd: fullCommand
13571
+ });
13572
+ if (result.blocked) return result;
13573
+ }
13574
+ }
13575
+ }
13576
+ return { blocked: false };
13577
+ }
13578
+ function tokenizeShellCommand(command) {
13579
+ const tokens = [];
13580
+ let current = "";
13581
+ let quote = null;
13582
+ for (const char of command.trim()) {
13583
+ if (quote) {
13584
+ if (char === quote) quote = null;
13585
+ else current += char;
13586
+ continue;
13587
+ }
13588
+ if (char === '"' || char === "'") {
13589
+ quote = char;
13590
+ } else if (/\s/.test(char)) {
13591
+ if (current) {
13592
+ tokens.push(current);
13593
+ current = "";
13594
+ }
13595
+ } else {
13596
+ current += char;
13597
+ }
13598
+ }
13599
+ if (current) tokens.push(current);
13600
+ return tokens;
13601
+ }
13602
+ async function checkKillTarget(target) {
13603
+ const registry = getPersistentProcessRegistry();
13604
+ if (target.pid !== void 0) {
13605
+ const blocked = await registry.shouldBlockKill(target.pid);
13606
+ if (blocked) {
13607
+ return {
13608
+ blocked: true,
13609
+ reason: `Blocked: kill ${target.signal} PID ${target.pid} targets a protected WrongStack process (${target.cmd.slice(0, 80)}).`
13610
+ };
13611
+ }
13612
+ if (target.pid === process.pid) {
13613
+ return {
13614
+ blocked: true,
13615
+ reason: "Blocked: cannot kill the current WrongStack process."
13616
+ };
13617
+ }
13618
+ if (target.pid === process.ppid) {
13619
+ return {
13620
+ blocked: true,
13621
+ reason: "Blocked: cannot kill the parent terminal hosting WrongStack."
13622
+ };
13623
+ }
13624
+ return { blocked: false };
13625
+ }
13626
+ if (target.name) {
13627
+ const nameLower = target.name.toLowerCase().replace(/\.exe$/, "");
13628
+ if (nameLower.includes("wrongstack")) {
13629
+ return {
13630
+ blocked: true,
13631
+ reason: `Blocked: kill ${target.signal} '${target.name}' targets a WrongStack process name.`
13632
+ };
13633
+ }
13634
+ const currentImage = path24.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
13635
+ const targetsNodeRuntime = nameLower === "node" || nameLower.startsWith("node");
13636
+ if (targetsNodeRuntime && currentImage === "node") {
13637
+ return {
13638
+ blocked: true,
13639
+ reason: `Blocked: kill ${target.signal} '${target.name}' would kill the active WrongStack node.exe runtime.`
13640
+ };
13641
+ }
13642
+ const protectedPids = await registry.getAllProtectedPids();
13643
+ if (protectedPids.length > 0 && targetsNodeRuntime) {
13644
+ return {
13645
+ blocked: true,
13646
+ reason: `Blocked: kill ${target.signal} '${target.name}' would kill all node.exe processes including active WrongStack instance(s).`
13647
+ };
13648
+ }
13649
+ return { blocked: false };
13650
+ }
13651
+ return { blocked: false };
13652
+ }
13653
+
13654
+ // src/exec.ts
13655
+ var isWin4 = process.platform === "win32";
13293
13656
  var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
13294
13657
  // JS / TS toolchain
13295
13658
  "node",
@@ -14094,6 +14457,19 @@ var execTool = {
14094
14457
  const args = (input.args ?? []).slice(0, MAX_ARGS);
14095
14458
  const timeout = Math.max(1, Math.min(input.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3));
14096
14459
  const danger = detectDanger(cmd, args, dangerBypass);
14460
+ const killCheck = await checkExecKillCommand(cmd, args);
14461
+ if (killCheck.blocked) {
14462
+ return {
14463
+ command: cmd,
14464
+ args,
14465
+ stdout: "",
14466
+ stderr: killCheck.reason ?? "Kill command blocked: targets a protected WrongStack process.",
14467
+ exitCode: 1,
14468
+ truncated: false,
14469
+ allowed: false,
14470
+ danger
14471
+ };
14472
+ }
14097
14473
  const argError = validateArgs(cmd, args);
14098
14474
  if (argError) {
14099
14475
  return {
@@ -14144,7 +14520,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
14144
14520
  let timedOut = false;
14145
14521
  const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
14146
14522
  const resolved = resolveWin32Command(cmd);
14147
- const needsShell = isWin3 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
14523
+ const needsShell = isWin4 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
14148
14524
  const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
14149
14525
  const spawnCmd = shim?.command ?? resolved;
14150
14526
  const spawnArgs = shim?.args ?? args;
@@ -14169,7 +14545,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
14169
14545
  env: buildChildEnv2(sessionId),
14170
14546
  stdio: ["ignore", "pipe", "pipe"],
14171
14547
  windowsHide: true,
14172
- ...isWin3 ? {} : { signal },
14548
+ ...isWin4 ? {} : { signal },
14173
14549
  ...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {}
14174
14550
  });
14175
14551
  } catch (err) {
@@ -14208,7 +14584,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
14208
14584
  const isAbort = err && err.code === "ABORT_ERR";
14209
14585
  const stderrText = isAbort ? `Aborted: ${err.message}` : err.message;
14210
14586
  clearTimeout(timer);
14211
- if (isWin3) signal.removeEventListener("abort", onAbort);
14587
+ if (isWin4) signal.removeEventListener("abort", onAbort);
14212
14588
  if (typeof pid === "number") registry.unregister(pid);
14213
14589
  registry.afterCall(Date.now() - startedAt, true);
14214
14590
  spool.finalize();
@@ -14241,7 +14617,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
14241
14617
  if (typeof pid === "number") registry.kill(pid, { force: true });
14242
14618
  else child.kill("SIGTERM");
14243
14619
  };
14244
- if (isWin3) {
14620
+ if (isWin4) {
14245
14621
  if (signal.aborted) onAbort();
14246
14622
  else signal.addEventListener("abort", onAbort, { once: true });
14247
14623
  }
@@ -14261,7 +14637,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
14261
14637
  });
14262
14638
  child.on("close", (code) => {
14263
14639
  clearTimeout(timer);
14264
- if (isWin3) signal.removeEventListener("abort", onAbort);
14640
+ if (isWin4) signal.removeEventListener("abort", onAbort);
14265
14641
  if (typeof pid === "number") registry.unregister(pid);
14266
14642
  const durationMs = Date.now() - startedAt;
14267
14643
  const exitCode = killed ? 124 : code ?? 1;
@@ -15072,7 +15448,7 @@ function runGit2(args, cwd, signal) {
15072
15448
 
15073
15449
  // src/glob.ts
15074
15450
  import * as fs20 from "node:fs/promises";
15075
- import * as path24 from "node:path";
15451
+ import * as path25 from "node:path";
15076
15452
  import { compileGlob as compileGlob3 } from "@wrongstack/core";
15077
15453
 
15078
15454
  // src/_concurrency.ts
@@ -15176,7 +15552,7 @@ var globTool = {
15176
15552
  if (DEFAULT_IGNORE2.includes(name)) continue;
15177
15553
  if (ignored.includes(name)) continue;
15178
15554
  const rel = relPrefix ? `${relPrefix}/${name}` : name;
15179
- const full = path24.join(dir, name);
15555
+ const full = path25.join(dir, name);
15180
15556
  if (e.isDirectory()) {
15181
15557
  subdirs.push({ full, rel });
15182
15558
  } else if (e.isFile()) {
@@ -15220,7 +15596,7 @@ var globTool = {
15220
15596
  };
15221
15597
  async function readGitignore(dir) {
15222
15598
  try {
15223
- const raw = await fs20.readFile(path24.join(dir, ".gitignore"), "utf8");
15599
+ const raw = await fs20.readFile(path25.join(dir, ".gitignore"), "utf8");
15224
15600
  return raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
15225
15601
  } catch {
15226
15602
  return [];
@@ -15231,7 +15607,7 @@ async function readGitignore(dir) {
15231
15607
  import { expectDefined as expectDefined7 } from "@wrongstack/core";
15232
15608
  import { spawn as spawn10 } from "node:child_process";
15233
15609
  import * as fs21 from "node:fs/promises";
15234
- import * as path25 from "node:path";
15610
+ import * as path26 from "node:path";
15235
15611
  import { buildChildEnv as buildChildEnv5, compileGlob as compileGlob4, ToolValidationError as ToolValidationError4 } from "@wrongstack/core";
15236
15612
 
15237
15613
  // src/_regex.ts
@@ -15624,7 +16000,7 @@ async function runNative(input, base, mode, limit, signal) {
15624
16000
  if (stopped) return;
15625
16001
  if (DEFAULT_IGNORE3.includes(e.name)) continue;
15626
16002
  if (e.isSymbolicLink()) continue;
15627
- const full = path25.join(dir, e.name);
16003
+ const full = path26.join(dir, e.name);
15628
16004
  if (e.isDirectory()) {
15629
16005
  subdirs.push(full);
15630
16006
  } else if (e.isFile()) {
@@ -16212,56 +16588,56 @@ function jmespathSearch(data, query) {
16212
16588
  }
16213
16589
  function validateJsonSchema(data, schema) {
16214
16590
  const errors = [];
16215
- function check(value, s, path33) {
16591
+ function check(value, s, path34) {
16216
16592
  if (s["type"]) {
16217
16593
  const expectedType = s["type"];
16218
16594
  const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
16219
16595
  if (expectedType === "integer") {
16220
- if (!Number.isInteger(value)) errors.push(`${path33}: expected integer, got ${actualType}`);
16596
+ if (!Number.isInteger(value)) errors.push(`${path34}: expected integer, got ${actualType}`);
16221
16597
  } else if (expectedType !== actualType) {
16222
- errors.push(`${path33}: expected ${expectedType}, got ${actualType}`);
16598
+ errors.push(`${path34}: expected ${expectedType}, got ${actualType}`);
16223
16599
  }
16224
16600
  }
16225
16601
  if (typeof value === "string" && s["format"] === "uri" && value) {
16226
16602
  try {
16227
16603
  new URL(value);
16228
16604
  } catch {
16229
- errors.push(`${path33}: not a valid URI`);
16605
+ errors.push(`${path34}: not a valid URI`);
16230
16606
  }
16231
16607
  }
16232
16608
  if (typeof value === "string" && s["pattern"]) {
16233
16609
  const re = new RegExp(s["pattern"]);
16234
- if (!re.test(value)) errors.push(`${path33}: does not match pattern ${s["pattern"]}`);
16610
+ if (!re.test(value)) errors.push(`${path34}: does not match pattern ${s["pattern"]}`);
16235
16611
  }
16236
16612
  if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
16237
- errors.push(`${path33}: string too short (min ${s["minLength"]})`);
16613
+ errors.push(`${path34}: string too short (min ${s["minLength"]})`);
16238
16614
  }
16239
16615
  if (typeof value === "string" && s["maxLength"] !== void 0 && value.length > s["maxLength"]) {
16240
- errors.push(`${path33}: string too long (max ${s["maxLength"]})`);
16616
+ errors.push(`${path34}: string too long (max ${s["maxLength"]})`);
16241
16617
  }
16242
16618
  if (typeof value === "number" && s["minimum"] !== void 0 && value < s["minimum"]) {
16243
- errors.push(`${path33}: below minimum ${s["minimum"]}`);
16619
+ errors.push(`${path34}: below minimum ${s["minimum"]}`);
16244
16620
  }
16245
16621
  if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
16246
- errors.push(`${path33}: above maximum ${s["maximum"]}`);
16622
+ errors.push(`${path34}: above maximum ${s["maximum"]}`);
16247
16623
  }
16248
16624
  if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
16249
16625
  for (let i = 0; i < value.length; i++) {
16250
- check(value[i], s["items"], `${path33}[${i}]`);
16626
+ check(value[i], s["items"], `${path34}[${i}]`);
16251
16627
  }
16252
16628
  }
16253
16629
  if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
16254
16630
  const props = s["properties"];
16255
16631
  for (const [k, propSchema] of Object.entries(props)) {
16256
- check(value[k], propSchema, `${path33}.${k}`);
16632
+ check(value[k], propSchema, `${path34}.${k}`);
16257
16633
  }
16258
16634
  }
16259
16635
  }
16260
16636
  check(data, schema, "$");
16261
16637
  return { valid: errors.length === 0, errors };
16262
16638
  }
16263
- function simpleQuery(data, path33) {
16264
- const parts = path33.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
16639
+ function simpleQuery(data, path34) {
16640
+ const parts = path34.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
16265
16641
  let current = data;
16266
16642
  for (const part of parts) {
16267
16643
  if (current === null || current === void 0) return void 0;
@@ -16364,7 +16740,7 @@ import {
16364
16740
 
16365
16741
  // src/session-kanban.ts
16366
16742
  import { watch } from "node:fs";
16367
- import { basename as basename9, dirname as dirname13 } from "node:path";
16743
+ import { basename as basename10, dirname as dirname13 } from "node:path";
16368
16744
  import {
16369
16745
  deserializeTaskGraph,
16370
16746
  GlobalMailbox,
@@ -16846,8 +17222,8 @@ function attachSessionKanbanMirror(context) {
16846
17222
  const name = filename?.toString();
16847
17223
  const currentPlanPath = context.meta["plan.path"];
16848
17224
  const currentTaskPath = context.meta["task.path"];
16849
- const planName = typeof currentPlanPath === "string" ? basename9(currentPlanPath) : "";
16850
- const taskName = typeof currentTaskPath === "string" ? basename9(currentTaskPath) : "";
17225
+ const planName = typeof currentPlanPath === "string" ? basename10(currentPlanPath) : "";
17226
+ const taskName = typeof currentTaskPath === "string" ? basename10(currentTaskPath) : "";
16851
17227
  if (name && name !== planName && name !== taskName) return;
16852
17228
  if (timer) clearTimeout(timer);
16853
17229
  timer = setTimeout(() => fireAndForget(refreshFiles()), 60);
@@ -18307,7 +18683,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
18307
18683
  }
18308
18684
  var DOCKER_LOGS_TIMEOUT_MS = 3e3;
18309
18685
  var MAX_TAIL_LINES = 1e5;
18310
- async function fileLogs(path33, lines, filterRe, stream) {
18686
+ async function fileLogs(path34, lines, filterRe, stream) {
18311
18687
  const { createInterface } = await import("node:readline");
18312
18688
  const { createReadStream: createReadStream2 } = await import("node:fs");
18313
18689
  const entries = [];
@@ -18316,7 +18692,7 @@ async function fileLogs(path33, lines, filterRe, stream) {
18316
18692
  let writeIdx = 0;
18317
18693
  let totalLines = 0;
18318
18694
  const rl = createInterface({
18319
- input: createReadStream2(path33),
18695
+ input: createReadStream2(path34),
18320
18696
  crlfDelay: Number.POSITIVE_INFINITY
18321
18697
  });
18322
18698
  for await (const line of rl) {
@@ -18337,7 +18713,7 @@ async function fileLogs(path33, lines, filterRe, stream) {
18337
18713
  if (parsed) entries.push(parsed);
18338
18714
  }
18339
18715
  return {
18340
- source: path33,
18716
+ source: path34,
18341
18717
  entries,
18342
18718
  total: entries.length,
18343
18719
  truncated: totalLines > effLines,
@@ -18568,8 +18944,8 @@ function parseOutdatedOutput(json2, exitCode) {
18568
18944
  init_util();
18569
18945
  import { spawn as spawn13 } from "node:child_process";
18570
18946
  import * as fs23 from "node:fs/promises";
18571
- import * as os7 from "node:os";
18572
- import * as path26 from "node:path";
18947
+ import * as os8 from "node:os";
18948
+ import * as path27 from "node:path";
18573
18949
  import { buildChildEnv as buildChildEnv8 } from "@wrongstack/core";
18574
18950
  var patchTool = {
18575
18951
  name: "patch",
@@ -18605,9 +18981,9 @@ var patchTool = {
18605
18981
  for (const t of targets) {
18606
18982
  const stripped = stripPathComponents(t, strip);
18607
18983
  if (!stripped) continue;
18608
- const candidate = path26.resolve(dir, stripped);
18609
- const rel = path26.relative(ctx.projectRoot, candidate);
18610
- if (rel.startsWith("..") || path26.isAbsolute(rel)) {
18984
+ const candidate = path27.resolve(dir, stripped);
18985
+ const rel = path27.relative(ctx.projectRoot, candidate);
18986
+ if (rel.startsWith("..") || path27.isAbsolute(rel)) {
18611
18987
  return {
18612
18988
  applied: 0,
18613
18989
  rejected: 1,
@@ -18624,11 +19000,11 @@ var patchTool = {
18624
19000
  beforeContents.set(target, await readTextForTracking(target));
18625
19001
  }
18626
19002
  }
18627
- const tmpDir = await fs23.mkdtemp(path26.join(os7.tmpdir(), ".wstack_patch_"));
19003
+ const tmpDir = await fs23.mkdtemp(path27.join(os8.tmpdir(), ".wstack_patch_"));
18628
19004
  try {
18629
19005
  await fs23.chmod(tmpDir, 448).catch(() => {
18630
19006
  });
18631
- const patchFile = path26.join(tmpDir, "in.diff");
19007
+ const patchFile = path27.join(tmpDir, "in.diff");
18632
19008
  await fs23.writeFile(patchFile, input.patch, { mode: 384 });
18633
19009
  const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
18634
19010
  const result = await runPatch(args, dir, opts.signal);
@@ -19214,7 +19590,7 @@ ${interesting.join("\n")}` : "symbols/imports: (none detected)"
19214
19590
  import { expectDefined as expectDefined8 } from "@wrongstack/core";
19215
19591
  import { spawn as spawn14 } from "node:child_process";
19216
19592
  import * as fs25 from "node:fs/promises";
19217
- import * as path27 from "node:path";
19593
+ import * as path28 from "node:path";
19218
19594
  import {
19219
19595
  atomicWrite as atomicWrite3,
19220
19596
  buildChildEnv as buildChildEnv9,
@@ -19303,8 +19679,8 @@ var replaceTool = {
19303
19679
  } catch {
19304
19680
  continue;
19305
19681
  }
19306
- const rel = path27.relative(realRoot, realPath);
19307
- if (rel.startsWith("..") || path27.isAbsolute(rel)) continue;
19682
+ const rel = path28.relative(realRoot, realPath);
19683
+ if (rel.startsWith("..") || path28.isAbsolute(rel)) continue;
19308
19684
  const stat18 = await fs25.stat(realPath).catch(() => null);
19309
19685
  if (!stat18?.isFile()) continue;
19310
19686
  let content;
@@ -19433,7 +19809,7 @@ async function globNative(pattern, base, extraGlob) {
19433
19809
  }
19434
19810
  for (const e of entries) {
19435
19811
  if (DEFAULT_IGNORE4.includes(e.name)) continue;
19436
- const full = path27.join(dir, e.name);
19812
+ const full = path28.join(dir, e.name);
19437
19813
  try {
19438
19814
  const stat18 = await fs25.lstat(full);
19439
19815
  if (stat18.isSymbolicLink()) continue;
@@ -19460,7 +19836,7 @@ async function globNative(pattern, base, extraGlob) {
19460
19836
  // src/scaffold.ts
19461
19837
  init_util();
19462
19838
  import * as fs26 from "node:fs/promises";
19463
- import * as path28 from "node:path";
19839
+ import * as path29 from "node:path";
19464
19840
  import { atomicWrite as atomicWrite4 } from "@wrongstack/core";
19465
19841
  var BUILT_IN_TEMPLATES = {
19466
19842
  "npm-package": {
@@ -19610,16 +19986,16 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
19610
19986
  let filesCreated = 0;
19611
19987
  for (const [filePath, content] of Object.entries(templateFiles)) {
19612
19988
  const resolvedPath = substituteVars(filePath, name, vars);
19613
- const joinedPath = path28.join(cwd, resolvedPath);
19614
- const root = path28.resolve(ctx.projectRoot);
19615
- const target = path28.resolve(joinedPath);
19616
- const rel = path28.relative(root, target);
19617
- if (rel.startsWith("..") || path28.isAbsolute(rel)) {
19989
+ const joinedPath = path29.join(cwd, resolvedPath);
19990
+ const root = path29.resolve(ctx.projectRoot);
19991
+ const target = path29.resolve(joinedPath);
19992
+ const rel = path29.relative(root, target);
19993
+ if (rel.startsWith("..") || path29.isAbsolute(rel)) {
19618
19994
  throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
19619
19995
  }
19620
19996
  const fullPath = target;
19621
19997
  if (!dryRun) {
19622
- await fs26.mkdir(path28.dirname(fullPath), { recursive: true });
19998
+ await fs26.mkdir(path29.dirname(fullPath), { recursive: true });
19623
19999
  await atomicWrite4(fullPath, substituteVars(content, name, vars));
19624
20000
  }
19625
20001
  files.push(resolvedPath);
@@ -20476,7 +20852,7 @@ ${formatTaskList2(file.tasks)}` : file.tasks.length > 0 ? formatTaskList2(file.t
20476
20852
  init_spawn_stream();
20477
20853
  init_util();
20478
20854
  init_legacy_bridge();
20479
- import * as path29 from "node:path";
20855
+ import * as path30 from "node:path";
20480
20856
  var testTool = {
20481
20857
  name: "test",
20482
20858
  category: "Code Quality",
@@ -20583,7 +20959,7 @@ async function detectRunner(cwd) {
20583
20959
  const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
20584
20960
  for (const f of candidates) {
20585
20961
  try {
20586
- await stat18(path29.join(cwd, f));
20962
+ await stat18(path30.join(cwd, f));
20587
20963
  if (f.includes("vitest")) return "vitest";
20588
20964
  if (f.includes("jest")) return "jest";
20589
20965
  if (f.includes("mocha")) return "mocha";
@@ -21054,7 +21430,7 @@ var toolUseTool = {
21054
21430
  init_util();
21055
21431
  import { expectDefined as expectDefined10 } from "@wrongstack/core";
21056
21432
  import * as fs28 from "node:fs/promises";
21057
- import * as path30 from "node:path";
21433
+ import * as path31 from "node:path";
21058
21434
  var DEFAULT_IGNORE5 = [
21059
21435
  "node_modules",
21060
21436
  ".git",
@@ -21225,7 +21601,7 @@ async function walkDir(dir, depth, opts) {
21225
21601
  opts.lines.push(opts.prefix + branch + displayName);
21226
21602
  if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
21227
21603
  const childPrefix = opts.prefix + connector;
21228
- await walkDir(path30.join(dir, entry.name), depth + 1, {
21604
+ await walkDir(path31.join(dir, entry.name), depth + 1, {
21229
21605
  ...opts,
21230
21606
  prefix: childPrefix,
21231
21607
  isLast
@@ -21238,7 +21614,7 @@ async function walkDir(dir, depth, opts) {
21238
21614
  init_spawn_stream();
21239
21615
  init_util();
21240
21616
  init_legacy_bridge();
21241
- import * as path31 from "node:path";
21617
+ import * as path32 from "node:path";
21242
21618
  var typecheckTool = {
21243
21619
  name: "typecheck",
21244
21620
  category: "Code Quality",
@@ -21341,8 +21717,8 @@ async function findTsConfig(cwd) {
21341
21717
  const candidates = ["tsconfig.json", "tsconfig.base.json"];
21342
21718
  for (const f of candidates) {
21343
21719
  try {
21344
- const s = await stat18(path31.join(cwd, f));
21345
- if (s.isFile()) return path31.join(cwd, f);
21720
+ const s = await stat18(path32.join(cwd, f));
21721
+ if (s.isFile()) return path32.join(cwd, f);
21346
21722
  } catch {
21347
21723
  }
21348
21724
  }
@@ -21875,7 +22251,7 @@ var builtinToolsPack = {
21875
22251
  };
21876
22252
 
21877
22253
  // src/process-guardian.ts
21878
- import * as os8 from "node:os";
22254
+ import * as os9 from "node:os";
21879
22255
  var ProcessGuardian = class {
21880
22256
  registry;
21881
22257
  config;
@@ -21914,7 +22290,7 @@ var ProcessGuardian = class {
21914
22290
  event: "process_guardian.started",
21915
22291
  instanceId: this.instanceId,
21916
22292
  mainPid: process.pid,
21917
- hostname: os8.hostname(),
22293
+ hostname: os9.hostname(),
21918
22294
  platform: process.platform
21919
22295
  }));
21920
22296
  }
@@ -22103,8 +22479,8 @@ var ProcessGuardian = class {
22103
22479
  instanceId: this.instanceId,
22104
22480
  mainPid: process.pid,
22105
22481
  protectedCount: this.protectedProcesses.size,
22106
- platform: os8.platform(),
22107
- hostname: os8.hostname(),
22482
+ platform: os9.platform(),
22483
+ hostname: os9.hostname(),
22108
22484
  uptime: process.uptime()
22109
22485
  };
22110
22486
  }
@@ -22133,7 +22509,7 @@ function stopProcessGuardian() {
22133
22509
  init_process_registry();
22134
22510
 
22135
22511
  // src/ps-slash.ts
22136
- import * as os9 from "node:os";
22512
+ import * as os10 from "node:os";
22137
22513
  var IDLE_THRESHOLD_MS = 2 * 6e4;
22138
22514
  var STALE_THRESHOLD_MS2 = 5 * 6e4;
22139
22515
  function now() {
@@ -22174,7 +22550,7 @@ async function listInstances(options = {}) {
22174
22550
  const mainProc = processes.find((p) => p.spawnMode === "main");
22175
22551
  const firstProc = processes.at(0);
22176
22552
  const mainPid = mainProc?.pid ?? firstProc?.pid ?? 0;
22177
- const hostname_ = firstProc?.hostname ?? os9.hostname();
22553
+ const hostname_ = firstProc?.hostname ?? os10.hostname();
22178
22554
  const startedAt = Math.min(...processes.map((p) => p.startedAt));
22179
22555
  const lastActivity = Math.max(...processes.map((p) => p.lastHeartbeat));
22180
22556
  const age = timestamp - lastActivity;
@@ -22262,7 +22638,7 @@ async function getGlobalProcessStatus() {
22262
22638
  mainPid: process.pid,
22263
22639
  protectedCount: 0,
22264
22640
  platform: process.platform,
22265
- hostname: os9.hostname(),
22641
+ hostname: os10.hostname(),
22266
22642
  uptime: 0
22267
22643
  },
22268
22644
  allInstances: instances.map((inst) => ({
@@ -22447,7 +22823,7 @@ function createGlobalPsSlashCommand() {
22447
22823
 
22448
22824
  // src/skill.ts
22449
22825
  import * as fs30 from "node:fs/promises";
22450
- import * as path32 from "node:path";
22826
+ import * as path33 from "node:path";
22451
22827
  import {
22452
22828
  SKILL_LIMITS,
22453
22829
  stripFrontmatter,
@@ -22494,7 +22870,7 @@ function makeSkillTool(skillLoader) {
22494
22870
  field: "name"
22495
22871
  });
22496
22872
  }
22497
- const dir = path32.dirname(manifest.path);
22873
+ const dir = path33.dirname(manifest.path);
22498
22874
  let loadedResource;
22499
22875
  if (input.resource?.trim()) {
22500
22876
  loadedResource = await loadResource(dir, input.resource.trim());
@@ -22544,15 +22920,15 @@ ${listing}`;
22544
22920
  }
22545
22921
  async function loadResource(skillDir, rel) {
22546
22922
  const norm = rel.replace(/\\/g, "/");
22547
- if (path32.isAbsolute(rel) || norm.split("/").some((seg) => seg === "..")) {
22923
+ if (path33.isAbsolute(rel) || norm.split("/").some((seg) => seg === "..")) {
22548
22924
  throw new ToolValidationError10({
22549
22925
  message: `skill: invalid resource path "${rel}"`,
22550
22926
  field: "resource"
22551
22927
  });
22552
22928
  }
22553
- const absPath = path32.resolve(skillDir, rel);
22554
- const root = path32.resolve(skillDir);
22555
- if (absPath !== root && !absPath.startsWith(root + path32.sep)) {
22929
+ const absPath = path33.resolve(skillDir, rel);
22930
+ const root = path33.resolve(skillDir);
22931
+ if (absPath !== root && !absPath.startsWith(root + path33.sep)) {
22556
22932
  throw new ToolValidationError10({
22557
22933
  message: `skill: resource "${rel}" escapes the skill directory`,
22558
22934
  field: "resource"
@@ -22593,7 +22969,7 @@ async function walk(root, dir, out) {
22593
22969
  }
22594
22970
  for (const e of entries) {
22595
22971
  if (out.length >= MAX_LISTED_RESOURCES) return;
22596
- const fullPath = path32.join(dir, e.name);
22972
+ const fullPath = path33.join(dir, e.name);
22597
22973
  let isDir = e.isDirectory();
22598
22974
  if (e.isSymbolicLink()) {
22599
22975
  try {
@@ -22609,7 +22985,7 @@ async function walk(root, dir, out) {
22609
22985
  if (e.name === "SKILL.md" || e.name === "SKILL.save.md") continue;
22610
22986
  try {
22611
22987
  const stat18 = await fs30.stat(fullPath);
22612
- const rel = path32.relative(root, fullPath).split(path32.sep).join("/");
22988
+ const rel = path33.relative(root, fullPath).split(path33.sep).join("/");
22613
22989
  out.push({ path: rel, bytes: stat18.size });
22614
22990
  } catch {
22615
22991
  }
@@ -22832,6 +23208,7 @@ export {
22832
23208
  builtinTools,
22833
23209
  builtinToolsPack,
22834
23210
  cancelPendingReindexes,
23211
+ checkExecKillCommand,
22835
23212
  codebaseIndexStats,
22836
23213
  codebaseIndexTool,
22837
23214
  codebaseSearchTool,