@wrongstack/tools 0.307.1 → 0.308.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/pack.js CHANGED
@@ -1031,8 +1031,8 @@ async function* spawnStream(opts) {
1031
1031
  try {
1032
1032
  for (; ; ) {
1033
1033
  while (queue.length === 0) {
1034
- await new Promise((resolve23) => {
1035
- waiter = resolve23;
1034
+ await new Promise((resolve24) => {
1035
+ waiter = resolve24;
1036
1036
  });
1037
1037
  }
1038
1038
  const chunk = queue.shift();
@@ -1135,9 +1135,9 @@ async function detectPackageManager(cwd, stopAt) {
1135
1135
  return "npm";
1136
1136
  }
1137
1137
  async function detectPackageManagerInDir(dir) {
1138
- const fs43 = await import("node:fs/promises");
1138
+ const fs44 = await import("node:fs/promises");
1139
1139
  try {
1140
- const raw = await fs43.readFile(path3.join(dir, "package.json"), "utf8");
1140
+ const raw = await fs44.readFile(path3.join(dir, "package.json"), "utf8");
1141
1141
  const declared = JSON.parse(raw).packageManager;
1142
1142
  if (typeof declared === "string") {
1143
1143
  const name = declared.split("@")[0] ?? "";
@@ -1156,7 +1156,7 @@ async function detectPackageManagerInDir(dir) {
1156
1156
  ];
1157
1157
  for (const [file, manager] of lockfiles) {
1158
1158
  try {
1159
- await fs43.stat(`${dir}/${file}`);
1159
+ await fs44.stat(`${dir}/${file}`);
1160
1160
  return manager;
1161
1161
  } catch {
1162
1162
  }
@@ -4137,15 +4137,15 @@ async function changedPaths(before, after, beforeSizes, afterSizes) {
4137
4137
  const beforeSet = new Set(before);
4138
4138
  const afterSet = new Set(after);
4139
4139
  const changed = /* @__PURE__ */ new Set();
4140
- for (const path49 of after) {
4141
- if (!beforeSet.has(path49)) changed.add(path49);
4140
+ for (const path50 of after) {
4141
+ if (!beforeSet.has(path50)) changed.add(path50);
4142
4142
  }
4143
- for (const path49 of before) {
4144
- if (!afterSet.has(path49)) changed.add(path49);
4143
+ for (const path50 of before) {
4144
+ if (!afterSet.has(path50)) changed.add(path50);
4145
4145
  }
4146
4146
  if (beforeSizes && afterSizes) {
4147
- for (const path49 of after) {
4148
- if (beforeSizes.get(path49) !== afterSizes.get(path49)) changed.add(path49);
4147
+ for (const path50 of after) {
4148
+ if (beforeSizes.get(path50) !== afterSizes.get(path50)) changed.add(path50);
4149
4149
  }
4150
4150
  }
4151
4151
  return [...changed].sort();
@@ -5640,7 +5640,7 @@ async function syncGoParse(filePath, content, lang) {
5640
5640
  }
5641
5641
  const goBinary = resolveWin32Command("go");
5642
5642
  const goResult = await new Promise(
5643
- (resolve23, reject) => {
5643
+ (resolve24, reject) => {
5644
5644
  let settled = false;
5645
5645
  const proc = spawn5(goBinary, ["run", scriptPath], {
5646
5646
  stdio: ["pipe", "pipe", "pipe"],
@@ -5669,7 +5669,7 @@ async function syncGoParse(filePath, content, lang) {
5669
5669
  if (settled) return;
5670
5670
  settled = true;
5671
5671
  clearTimeout(timer);
5672
- resolve23({ code: code2, stdout: stdout2 });
5672
+ resolve24({ code: code2, stdout: stdout2 });
5673
5673
  });
5674
5674
  }
5675
5675
  );
@@ -6345,7 +6345,7 @@ async function resolvePython() {
6345
6345
  return null;
6346
6346
  }
6347
6347
  function commandIsAvailable(command) {
6348
- return new Promise((resolve23) => {
6348
+ return new Promise((resolve24) => {
6349
6349
  let settled = false;
6350
6350
  const proc = spawn6(command, ["--version"], {
6351
6351
  stdio: "ignore",
@@ -6355,7 +6355,7 @@ function commandIsAvailable(command) {
6355
6355
  if (settled) return;
6356
6356
  settled = true;
6357
6357
  clearTimeout(timer);
6358
- resolve23(available);
6358
+ resolve24(available);
6359
6359
  };
6360
6360
  const timer = setTimeout(() => {
6361
6361
  proc.kill("SIGKILL");
@@ -6367,7 +6367,7 @@ function commandIsAvailable(command) {
6367
6367
  });
6368
6368
  }
6369
6369
  function spawnPyParser(pyBinary, scriptPath, filePath, content) {
6370
- return new Promise((resolve23, reject) => {
6370
+ return new Promise((resolve24, reject) => {
6371
6371
  let settled = false;
6372
6372
  const proc = spawn6(pyBinary, [scriptPath, filePath], {
6373
6373
  stdio: ["pipe", "pipe", "pipe"],
@@ -6396,7 +6396,7 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
6396
6396
  if (settled) return;
6397
6397
  settled = true;
6398
6398
  clearTimeout(timer);
6399
- resolve23({ code, stdout });
6399
+ resolve24({ code, stdout });
6400
6400
  });
6401
6401
  });
6402
6402
  }
@@ -8604,6 +8604,18 @@ function parseKillCommand(command) {
8604
8604
  originalCommand: command
8605
8605
  };
8606
8606
  }
8607
+ const wmicPidMatch = normalized.match(
8608
+ /^wmic\s+process\s+where\s+['"]?processid\s*=\s*['"]?(\d+)/i
8609
+ );
8610
+ if (wmicPidMatch?.[1]) {
8611
+ return {
8612
+ pid: parseInt(wmicPidMatch[1], 10),
8613
+ signal: "FORCE",
8614
+ isGroupKill: false,
8615
+ isAllKill: false,
8616
+ originalCommand: command
8617
+ };
8618
+ }
8607
8619
  const killScriptMatch = normalized.match(SCRIPT_KILL_RE);
8608
8620
  if (killScriptMatch) {
8609
8621
  return {
@@ -8723,7 +8735,9 @@ async function isKillProtected(kill) {
8723
8735
  return protectedPids.length > 0;
8724
8736
  }
8725
8737
  if (kill.pid !== void 0) {
8726
- return registry.shouldBlockKill(kill.pid);
8738
+ if (await registry.shouldBlockKill(kill.pid)) return true;
8739
+ if (kill.pid === process.pid || kill.pid === process.ppid) return true;
8740
+ return false;
8727
8741
  }
8728
8742
  return false;
8729
8743
  }
@@ -8770,8 +8784,8 @@ async function checkAndBlockKillCommand(command) {
8770
8784
 
8771
8785
  // src/_shell-pick.ts
8772
8786
  var POSIX_DEFAULT = "cmd";
8773
- function pickShell(platform5, command, env) {
8774
- if (platform5 !== "win32") return POSIX_DEFAULT;
8787
+ function pickShell(platform6, command, env) {
8788
+ if (platform6 !== "win32") return POSIX_DEFAULT;
8775
8789
  const override = env.get("WRONGSTACK_SHELL")?.trim().toLowerCase();
8776
8790
  if (override === "cmd" || override === "cmd.exe") return "cmd";
8777
8791
  if (override === "powershell" || override === "powershell.exe") return "powershell";
@@ -9230,10 +9244,10 @@ var bashTool = {
9230
9244
  queue.push(c);
9231
9245
  }
9232
9246
  };
9233
- const next = () => new Promise((resolve23) => {
9247
+ const next = () => new Promise((resolve24) => {
9234
9248
  const c = queue.shift();
9235
- if (c) resolve23(c);
9236
- else resolveNext = resolve23;
9249
+ if (c) resolve24(c);
9250
+ else resolveNext = resolve24;
9237
9251
  });
9238
9252
  let lastFlush = Date.now();
9239
9253
  const flush = () => {
@@ -9696,11 +9710,11 @@ var BrowserArtifactStore = class {
9696
9710
  };
9697
9711
  async function hashFile(target) {
9698
9712
  const hash = createHash3("sha256");
9699
- await new Promise((resolve23, reject) => {
9713
+ await new Promise((resolve24, reject) => {
9700
9714
  const stream = createReadStream(target);
9701
9715
  stream.on("data", (chunk) => hash.update(chunk));
9702
9716
  stream.once("error", reject);
9703
- stream.once("end", resolve23);
9717
+ stream.once("end", resolve24);
9704
9718
  });
9705
9719
  return hash.digest("hex");
9706
9720
  }
@@ -9850,7 +9864,7 @@ var BrowserNetworkGuardProxy = class {
9850
9864
  async start() {
9851
9865
  if (this.url) return this.url;
9852
9866
  if (this.startPromise) return this.startPromise;
9853
- this.startPromise = new Promise((resolve23, reject) => {
9867
+ this.startPromise = new Promise((resolve24, reject) => {
9854
9868
  const onError = (error) => {
9855
9869
  this.server.off("listening", onListening);
9856
9870
  reject(error);
@@ -9863,7 +9877,7 @@ var BrowserNetworkGuardProxy = class {
9863
9877
  return;
9864
9878
  }
9865
9879
  this.url = `http://127.0.0.1:${address.port}`;
9866
- resolve23(this.url);
9880
+ resolve24(this.url);
9867
9881
  };
9868
9882
  this.server.once("error", onError);
9869
9883
  this.server.once("listening", onListening);
@@ -9878,7 +9892,7 @@ var BrowserNetworkGuardProxy = class {
9878
9892
  for (const socket of this.sockets) socket.destroy();
9879
9893
  this.sockets.clear();
9880
9894
  if (!this.server.listening) return;
9881
- await new Promise((resolve23) => this.server.close(() => resolve23()));
9895
+ await new Promise((resolve24) => this.server.close(() => resolve24()));
9882
9896
  }
9883
9897
  async forwardHttp(request2, response) {
9884
9898
  try {
@@ -10451,7 +10465,7 @@ function pushBounded(target, value, limit) {
10451
10465
  }
10452
10466
  async function abortable(signal, operation, onAbort) {
10453
10467
  signal.throwIfAborted();
10454
- return new Promise((resolve23, reject) => {
10468
+ return new Promise((resolve24, reject) => {
10455
10469
  let settled = false;
10456
10470
  let aborting = false;
10457
10471
  const finish = (fn) => {
@@ -10469,7 +10483,7 @@ async function abortable(signal, operation, onAbort) {
10469
10483
  signal.addEventListener("abort", abort, { once: true });
10470
10484
  operation().then(
10471
10485
  (value) => {
10472
- if (!aborting) finish(() => resolve23(value));
10486
+ if (!aborting) finish(() => resolve24(value));
10473
10487
  },
10474
10488
  (err) => {
10475
10489
  if (!aborting) finish(() => reject(err));
@@ -13996,8 +14010,8 @@ function isProjectIndexServerHealth(value) {
13996
14010
  return typeof health.checkedAt === "number" && typeof health.uptimeMs === "number" && typeof memory?.rss === "number" && typeof memory.heapUsed === "number" && typeof memory.heapTotal === "number" && typeof memory.external === "number" && typeof health.clients === "number" && typeof health.activeRequests === "number" && typeof health.activeWrites === "number" && typeof health.queuedWrites === "number" && typeof health.pendingExternalFiles === "number" && typeof health.watchingExternal === "boolean" && typeof activity?.indexing === "boolean" && typeof activity.currentFile === "number" && typeof activity.totalFiles === "number" && typeof activity.generation === "number";
13997
14011
  }
13998
14012
  function delay(ms) {
13999
- return new Promise((resolve23) => {
14000
- const timer = setTimeout(resolve23, ms);
14013
+ return new Promise((resolve24) => {
14014
+ const timer = setTimeout(resolve24, ms);
14001
14015
  timer.unref?.();
14002
14016
  });
14003
14017
  }
@@ -14203,7 +14217,7 @@ var ProjectServerConnection = class {
14203
14217
  return Promise.reject(new Error("codebase-index server connection is not available"));
14204
14218
  }
14205
14219
  const id = this.nextId++;
14206
- return new Promise((resolve23, reject) => {
14220
+ return new Promise((resolve24, reject) => {
14207
14221
  const timer = setTimeout(() => {
14208
14222
  const entry = this.pending.get(id);
14209
14223
  if (!entry) return;
@@ -14226,7 +14240,7 @@ var ProjectServerConnection = class {
14226
14240
  entry.reject(cancellationError(signal));
14227
14241
  } : void 0;
14228
14242
  this.pending.set(id, {
14229
- resolve: resolve23,
14243
+ resolve: resolve24,
14230
14244
  reject,
14231
14245
  timer,
14232
14246
  signal,
@@ -14295,7 +14309,7 @@ var ProjectServerConnection = class {
14295
14309
  this.binaryBuffer = [];
14296
14310
  this.useBinary = false;
14297
14311
  this.textDecoder = null;
14298
- return new Promise((resolve23, reject) => {
14312
+ return new Promise((resolve24, reject) => {
14299
14313
  const socket = net3.createConnection(this.endpoint);
14300
14314
  this.socket = socket;
14301
14315
  const timer = setTimeout(() => {
@@ -14307,7 +14321,7 @@ var ProjectServerConnection = class {
14307
14321
  clearTimeout(timer);
14308
14322
  this.connectResolve = null;
14309
14323
  this.connectReject = null;
14310
- resolve23();
14324
+ resolve24();
14311
14325
  };
14312
14326
  const finishReject = (error) => {
14313
14327
  clearTimeout(timer);
@@ -15321,9 +15335,9 @@ var ParserWorkerPool = class {
15321
15335
  for (let i = 0; i < files.length; i++) {
15322
15336
  chunks[i % workerCount].push(files[i]);
15323
15337
  }
15324
- return new Promise((resolve23, reject) => {
15338
+ return new Promise((resolve24, reject) => {
15325
15339
  this.pending.set(batchId, {
15326
- resolve: resolve23,
15340
+ resolve: resolve24,
15327
15341
  reject,
15328
15342
  accumulated: [],
15329
15343
  expectedWorkers: workerCount,
@@ -15354,10 +15368,10 @@ var ParserWorkerPool = class {
15354
15368
  await Promise.allSettled(
15355
15369
  workers.map(
15356
15370
  (w) => Promise.race([
15357
- new Promise((resolve23) => {
15358
- w.once("exit", () => resolve23());
15371
+ new Promise((resolve24) => {
15372
+ w.once("exit", () => resolve24());
15359
15373
  }),
15360
- new Promise((resolve23) => setTimeout(() => resolve23(), 2e3))
15374
+ new Promise((resolve24) => setTimeout(() => resolve24(), 2e3))
15361
15375
  ]).then(() => {
15362
15376
  if (!w.threadId) return;
15363
15377
  return w.terminate().catch(() => {
@@ -15418,7 +15432,7 @@ function shouldUseParserWorkerPool(candidateFileCount, parseBatchCount) {
15418
15432
  return !isFrugalPerf() && candidateFileCount >= WORKER_POOL_THRESHOLD && parseBatchCount > 1;
15419
15433
  }
15420
15434
  function yieldEventLoop() {
15421
- return new Promise((resolve23) => setImmediate(resolve23));
15435
+ return new Promise((resolve24) => setImmediate(resolve24));
15422
15436
  }
15423
15437
  function throwIfAborted(signal) {
15424
15438
  if (!signal?.aborted) return;
@@ -15450,7 +15464,7 @@ function normalizeComparablePath(value) {
15450
15464
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
15451
15465
  }
15452
15466
  function gitOutput(projectRoot, args) {
15453
- return new Promise((resolve23, reject) => {
15467
+ return new Promise((resolve24, reject) => {
15454
15468
  execFile(
15455
15469
  "git",
15456
15470
  ["-C", projectRoot, ...args],
@@ -15461,7 +15475,7 @@ function gitOutput(projectRoot, args) {
15461
15475
  },
15462
15476
  (error, stdout) => {
15463
15477
  if (error) reject(error);
15464
- else resolve23(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
15478
+ else resolve24(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
15465
15479
  }
15466
15480
  );
15467
15481
  });
@@ -16237,7 +16251,7 @@ function callIndexOp(op, args, opts) {
16237
16251
  opts.signal.reason instanceof Error ? opts.signal.reason : new Error("Indexing cancelled")
16238
16252
  );
16239
16253
  }
16240
- return new Promise((resolve23, reject) => {
16254
+ return new Promise((resolve24, reject) => {
16241
16255
  const id = nextRpcId++;
16242
16256
  const timer = setTimeout(() => {
16243
16257
  pending.delete(id);
@@ -16259,7 +16273,7 @@ function callIndexOp(op, args, opts) {
16259
16273
  pending.set(id, {
16260
16274
  resolve: (v) => {
16261
16275
  cleanup();
16262
- resolve23(v);
16276
+ resolve24(v);
16263
16277
  },
16264
16278
  reject: (e) => {
16265
16279
  cleanup();
@@ -19792,7 +19806,7 @@ function findGitDir(cwd) {
19792
19806
  return null;
19793
19807
  }
19794
19808
  function runGit(args, cwd, signal) {
19795
- return new Promise((resolve23) => {
19809
+ return new Promise((resolve24) => {
19796
19810
  let stdout = "";
19797
19811
  let stderr = "";
19798
19812
  const child = spawn7("git", args, {
@@ -19808,8 +19822,8 @@ function runGit(args, cwd, signal) {
19808
19822
  child.stderr?.on("data", (c) => {
19809
19823
  stderr += c.toString();
19810
19824
  });
19811
- child.on("close", (code) => resolve23({ stdout, stderr, exitCode: code ?? 0 }));
19812
- child.on("error", (e) => resolve23({ stdout: "", stderr: e.message, exitCode: 1 }));
19825
+ child.on("close", (code) => resolve24({ stdout, stderr, exitCode: code ?? 0 }));
19826
+ child.on("error", (e) => resolve24({ stdout: "", stderr: e.message, exitCode: 1 }));
19813
19827
  });
19814
19828
  }
19815
19829
  async function fileDiff(input, ctx, _signal) {
@@ -21024,6 +21038,40 @@ var RULES = [
21024
21038
  },
21025
21039
  reason: "Remove-Item with -Recurse -Force"
21026
21040
  },
21041
+ // ----- Windows PowerShell Disk & Volume destruction -----
21042
+ {
21043
+ id: "powershell-disk-volume-destroy",
21044
+ level: "destructive",
21045
+ test: (cmd, args) => {
21046
+ if (cmd !== "powershell" && cmd !== "pwsh") return false;
21047
+ return args.some(
21048
+ (a) => /^(?:Format-Volume|Clear-Disk|Initialize-Disk|Remove-Partition|Clear-Volume)(?:\s|$)/i.test(a)
21049
+ );
21050
+ },
21051
+ reason: "PowerShell disk/volume partition destruction"
21052
+ },
21053
+ // ----- Windows PowerShell System Restart / Shutdown -----
21054
+ {
21055
+ id: "powershell-stop-restart-computer",
21056
+ level: "destructive",
21057
+ test: (cmd, args) => {
21058
+ if (cmd !== "powershell" && cmd !== "pwsh") return false;
21059
+ return args.some((a) => /^(?:Stop-Computer|Restart-Computer)(?:\s|$)/i.test(a));
21060
+ },
21061
+ reason: "PowerShell system shutdown or restart"
21062
+ },
21063
+ // ----- Windows PowerShell ExecutionPolicy / Payload evasion -----
21064
+ {
21065
+ id: "powershell-execution-policy-bypass",
21066
+ level: "caution",
21067
+ test: (cmd, args) => {
21068
+ if (cmd !== "powershell" && cmd !== "pwsh") return false;
21069
+ return args.some(
21070
+ (a) => /Set-ExecutionPolicy\s+(?:Bypass|Unrestricted)|-(?:EncodedCommand|enc)\b/i.test(a)
21071
+ );
21072
+ },
21073
+ reason: "PowerShell execution policy bypass or encoded command"
21074
+ },
21027
21075
  // ----- find -exec / -ok / -execdir -----
21028
21076
  {
21029
21077
  id: "find-exec",
@@ -22444,7 +22492,7 @@ var execTool = {
22444
22492
  }
22445
22493
  };
22446
22494
  function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
22447
- return new Promise((resolve23) => {
22495
+ return new Promise((resolve24) => {
22448
22496
  let stdout = "";
22449
22497
  let stderr = "";
22450
22498
  let killed = false;
@@ -22452,7 +22500,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
22452
22500
  const finish = (result) => {
22453
22501
  if (resolvedOnce.value) return;
22454
22502
  resolvedOnce.value = true;
22455
- resolve23(result);
22503
+ resolve24(result);
22456
22504
  };
22457
22505
  const startedAt = Date.now();
22458
22506
  let stdoutBytes = 0;
@@ -23128,10 +23176,10 @@ function parseFormatterCounts(fixer, output) {
23128
23176
  };
23129
23177
  }
23130
23178
  async function detectFixer(cwd) {
23131
- const fs43 = await import("node:fs/promises");
23179
+ const fs44 = await import("node:fs/promises");
23132
23180
  const exists = async (file) => {
23133
23181
  try {
23134
- await fs43.stat(`${cwd}/${file}`);
23182
+ await fs44.stat(`${cwd}/${file}`);
23135
23183
  return true;
23136
23184
  } catch {
23137
23185
  return false;
@@ -23154,7 +23202,7 @@ async function detectFixer(cwd) {
23154
23202
  if (await exists(cfg)) return "prettier";
23155
23203
  }
23156
23204
  try {
23157
- const raw = await fs43.readFile(`${cwd}/package.json`, "utf8");
23205
+ const raw = await fs44.readFile(`${cwd}/package.json`, "utf8");
23158
23206
  const pkg = JSON.parse(raw);
23159
23207
  if (pkg["prettier"] !== void 0) return "prettier";
23160
23208
  } catch {
@@ -23422,7 +23470,7 @@ function buildArgs(input) {
23422
23470
  }
23423
23471
  }
23424
23472
  function runGit2(args, cwd, signal) {
23425
- return new Promise((resolve23) => {
23473
+ return new Promise((resolve24) => {
23426
23474
  let stdout = "";
23427
23475
  let stderr = "";
23428
23476
  const child = spawn9("git", args, {
@@ -23443,7 +23491,7 @@ function runGit2(args, cwd, signal) {
23443
23491
  }
23444
23492
  });
23445
23493
  child.on("error", (err) => {
23446
- resolve23({
23494
+ resolve24({
23447
23495
  command: args[0],
23448
23496
  stdout: normalizeCommandOutput(stdout),
23449
23497
  stderr: err.message,
@@ -23452,7 +23500,7 @@ function runGit2(args, cwd, signal) {
23452
23500
  });
23453
23501
  });
23454
23502
  child.on("close", (code) => {
23455
- resolve23({
23503
+ resolve24({
23456
23504
  command: args[0],
23457
23505
  stdout: normalizeCommandOutput(stdout),
23458
23506
  stderr: normalizeCommandOutput(stderr),
@@ -23865,7 +23913,7 @@ var grepTool = {
23865
23913
  };
23866
23914
  var rgAvailabilityCache;
23867
23915
  function detectRg() {
23868
- rgAvailabilityCache ??= new Promise((resolve23) => {
23916
+ rgAvailabilityCache ??= new Promise((resolve24) => {
23869
23917
  try {
23870
23918
  const p = spawn10("rg", ["--version"], {
23871
23919
  env: buildChildEnv5(),
@@ -23873,10 +23921,10 @@ function detectRg() {
23873
23921
  signal: AbortSignal.timeout(1e4),
23874
23922
  windowsHide: true
23875
23923
  });
23876
- p.on("error", () => resolve23(false));
23877
- p.on("close", (code) => resolve23(code === 0));
23924
+ p.on("error", () => resolve24(false));
23925
+ p.on("close", (code) => resolve24(code === 0));
23878
23926
  } catch {
23879
- resolve23(false);
23927
+ resolve24(false);
23880
23928
  }
23881
23929
  });
23882
23930
  return rgAvailabilityCache;
@@ -24918,60 +24966,60 @@ function jmespathSearch(data, query) {
24918
24966
  }
24919
24967
  function validateJsonSchema(data, schema) {
24920
24968
  const errors = [];
24921
- function check(value, s, path49) {
24969
+ function check(value, s, path50) {
24922
24970
  if (s["type"]) {
24923
24971
  const expectedType = s["type"];
24924
24972
  const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
24925
24973
  if (expectedType === "integer") {
24926
- if (!Number.isInteger(value)) errors.push(`${path49}: expected integer, got ${actualType}`);
24974
+ if (!Number.isInteger(value)) errors.push(`${path50}: expected integer, got ${actualType}`);
24927
24975
  } else if (expectedType !== actualType) {
24928
- errors.push(`${path49}: expected ${expectedType}, got ${actualType}`);
24976
+ errors.push(`${path50}: expected ${expectedType}, got ${actualType}`);
24929
24977
  }
24930
24978
  }
24931
24979
  if (typeof value === "string" && s["format"] === "uri" && value) {
24932
24980
  try {
24933
24981
  new URL(value);
24934
24982
  } catch {
24935
- errors.push(`${path49}: not a valid URI`);
24983
+ errors.push(`${path50}: not a valid URI`);
24936
24984
  }
24937
24985
  }
24938
24986
  if (typeof value === "string" && s["pattern"]) {
24939
24987
  const compiled = compileUserRegex(s["pattern"], "");
24940
24988
  if (!compiled.ok) {
24941
- errors.push(`${path49}: invalid schema pattern \u2014 ${compiled.reason}`);
24989
+ errors.push(`${path50}: invalid schema pattern \u2014 ${compiled.reason}`);
24942
24990
  } else if (!compiled.regex.test(capSubject(value))) {
24943
- errors.push(`${path49}: does not match pattern ${s["pattern"]}`);
24991
+ errors.push(`${path50}: does not match pattern ${s["pattern"]}`);
24944
24992
  }
24945
24993
  }
24946
24994
  if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
24947
- errors.push(`${path49}: string too short (min ${s["minLength"]})`);
24995
+ errors.push(`${path50}: string too short (min ${s["minLength"]})`);
24948
24996
  }
24949
24997
  if (typeof value === "string" && s["maxLength"] !== void 0 && value.length > s["maxLength"]) {
24950
- errors.push(`${path49}: string too long (max ${s["maxLength"]})`);
24998
+ errors.push(`${path50}: string too long (max ${s["maxLength"]})`);
24951
24999
  }
24952
25000
  if (typeof value === "number" && s["minimum"] !== void 0 && value < s["minimum"]) {
24953
- errors.push(`${path49}: below minimum ${s["minimum"]}`);
25001
+ errors.push(`${path50}: below minimum ${s["minimum"]}`);
24954
25002
  }
24955
25003
  if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
24956
- errors.push(`${path49}: above maximum ${s["maximum"]}`);
25004
+ errors.push(`${path50}: above maximum ${s["maximum"]}`);
24957
25005
  }
24958
25006
  if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
24959
25007
  for (let i = 0; i < value.length; i++) {
24960
- check(value[i], s["items"], `${path49}[${i}]`);
25008
+ check(value[i], s["items"], `${path50}[${i}]`);
24961
25009
  }
24962
25010
  }
24963
25011
  if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
24964
25012
  const props = s["properties"];
24965
25013
  for (const [k, propSchema] of Object.entries(props)) {
24966
- check(value[k], propSchema, `${path49}.${k}`);
25014
+ check(value[k], propSchema, `${path50}.${k}`);
24967
25015
  }
24968
25016
  }
24969
25017
  }
24970
25018
  check(data, schema, "$");
24971
25019
  return { valid: errors.length === 0, errors };
24972
25020
  }
24973
- function simpleQuery(data, path49) {
24974
- const parts = path49.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
25021
+ function simpleQuery(data, path50) {
25022
+ const parts = path50.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
24975
25023
  let current = data;
24976
25024
  for (const part of parts) {
24977
25025
  if (current === null || current === void 0) return void 0;
@@ -27643,7 +27691,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
27643
27691
  };
27644
27692
  }
27645
27693
  args.push("--timestamps", service);
27646
- return new Promise((resolve23) => {
27694
+ return new Promise((resolve24) => {
27647
27695
  let stdout = "";
27648
27696
  let stderr = "";
27649
27697
  const MAX = 2e5;
@@ -27659,7 +27707,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
27659
27707
  if (settled) return;
27660
27708
  settled = true;
27661
27709
  clearTimeout(timer);
27662
- resolve23(result);
27710
+ resolve24(result);
27663
27711
  };
27664
27712
  const child = spawn11("docker", args, {
27665
27713
  cwd,
@@ -27704,7 +27752,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
27704
27752
  }
27705
27753
  var DOCKER_LOGS_TIMEOUT_MS = 3e3;
27706
27754
  var MAX_TAIL_LINES = 1e5;
27707
- async function fileLogs(path49, lines, filterRe) {
27755
+ async function fileLogs(path50, lines, filterRe) {
27708
27756
  const { createInterface } = await import("node:readline");
27709
27757
  const { createReadStream: createReadStream2 } = await import("node:fs");
27710
27758
  const entries = [];
@@ -27713,7 +27761,7 @@ async function fileLogs(path49, lines, filterRe) {
27713
27761
  let writeIdx = 0;
27714
27762
  let totalLines = 0;
27715
27763
  const rl = createInterface({
27716
- input: createReadStream2(path49),
27764
+ input: createReadStream2(path50),
27717
27765
  crlfDelay: Number.POSITIVE_INFINITY
27718
27766
  });
27719
27767
  for await (const line of rl) {
@@ -27734,7 +27782,7 @@ async function fileLogs(path49, lines, filterRe) {
27734
27782
  if (parsed) entries.push(parsed);
27735
27783
  }
27736
27784
  return {
27737
- source: path49,
27785
+ source: path50,
27738
27786
  entries,
27739
27787
  total: entries.length,
27740
27788
  truncated: totalLines > effLines,
@@ -27876,7 +27924,7 @@ var outdatedTool = {
27876
27924
  }
27877
27925
  };
27878
27926
  function runOutdated(manager, args, cwd, signal) {
27879
- return new Promise((resolve23) => {
27927
+ return new Promise((resolve24) => {
27880
27928
  let stdout = "";
27881
27929
  let stderr = "";
27882
27930
  const MAX = 1e5;
@@ -27901,10 +27949,10 @@ function runOutdated(manager, args, cwd, signal) {
27901
27949
  });
27902
27950
  child.on("close", (code) => {
27903
27951
  const result = parseOutdatedOutput(stdout, code ?? 0);
27904
- resolve23(result);
27952
+ resolve24(result);
27905
27953
  });
27906
27954
  child.on("error", (e) => {
27907
- resolve23({
27955
+ resolve24({
27908
27956
  exit_code: 1,
27909
27957
  packages: [],
27910
27958
  total: 0,
@@ -28223,7 +28271,7 @@ function runPatch(args, cwd, signal, fallback) {
28223
28271
  });
28224
28272
  }
28225
28273
  function runPatchProcess(command, args, cwd, signal) {
28226
- return new Promise((resolve23) => {
28274
+ return new Promise((resolve24) => {
28227
28275
  let stdout = "";
28228
28276
  let stderr = "";
28229
28277
  const env = { ...buildChildEnv8(), LANG: "C", LC_ALL: "C" };
@@ -28242,11 +28290,11 @@ function runPatchProcess(command, args, cwd, signal) {
28242
28290
  });
28243
28291
  child.on(
28244
28292
  "close",
28245
- (code) => resolve23({ exitCode: code ?? 1, stdout, stderr, unavailable: false })
28293
+ (code) => resolve24({ exitCode: code ?? 1, stdout, stderr, unavailable: false })
28246
28294
  );
28247
28295
  child.on(
28248
28296
  "error",
28249
- (e) => resolve23({
28297
+ (e) => resolve24({
28250
28298
  exitCode: 1,
28251
28299
  stdout: "",
28252
28300
  stderr: e.message,
@@ -29035,9 +29083,428 @@ function mkResult(plan, ok, message, todos) {
29035
29083
  return result;
29036
29084
  }
29037
29085
 
29086
+ // src/pwsh.ts
29087
+ import { spawn as spawn14 } from "node:child_process";
29088
+ import * as fs36 from "node:fs";
29089
+ import * as os10 from "node:os";
29090
+ import * as path43 from "node:path";
29091
+ import { StringDecoder as StringDecoder3 } from "node:string_decoder";
29092
+ import {
29093
+ emitProcessCompleted as emitProcessCompleted4,
29094
+ emitProcessOutput as emitProcessOutput4,
29095
+ emitProcessStarted as emitProcessStarted4
29096
+ } from "@wrongstack/core/observability";
29097
+ init_output_spool();
29098
+ init_util();
29099
+ init_process_registry();
29100
+ init_win32_resolve();
29101
+ var MAX_OUTPUT4 = 32768;
29102
+ var DEFAULT_TIMEOUT_MS4 = 3e5;
29103
+ var STREAM_FLUSH_INTERVAL_MS2 = 200;
29104
+ var STREAM_FLUSH_BYTES2 = 4 * 1024;
29105
+ var MAX_QUEUE_CHUNKS2 = 500;
29106
+ var PWSH_TOOL_DESCRIPTION = "Execute a PowerShell command (`pwsh -Command`) in a fresh process on Windows and return its stdout/stderr. Stateless per call: pass `workdir` instead of using `cd`.";
29107
+ var PWSH_TOOL_USAGE_HINT = "Best practices & sandbox protocol for pwsh:\n- **Stateless**: No cwd, variables, or functions persist between calls. Use `workdir` to set the directory.\n- **Paths & Environs**: Use native Windows paths (`C:\\...`) and read env vars via `$env:NAME` (and `$env:DSH_*`).\n- **Exit Codes**: Non-zero exits are reported as `[exit code: N]`. On Windows, a force-killed command settles as exit code 1 (interruption).\n- **Background**: Set `run_in_background: true` for long-running processes (returns job id; manage via job_output/job_kill).\n- **Sandboxing**: Under read-only sandbox, pwsh runs in `ConstrainedLanguage` mode (prefer cmdlets, basic types; .NET reflection and COM fail). Workspace-write runs in `FullLanguage`.\n- **Escalation**: If denied by policy (`[sandbox: file access denied]`), retry once with `sandbox_permissions` plus a one-sentence `justification`.";
29108
+ function wrapPwshCommand(command) {
29109
+ const bootstrap = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;$ProgressPreference = 'SilentlyContinue';$ConfirmPreference = 'None';$WhatIfPreference = $false";
29110
+ return `${bootstrap}
29111
+ $ErrorActionPreference = 'Stop'
29112
+ ${command}
29113
+ if ($LASTEXITCODE -is [int]) { exit $LASTEXITCODE }`;
29114
+ }
29115
+ var pwshTool = {
29116
+ name: "pwsh",
29117
+ category: "Shell",
29118
+ description: PWSH_TOOL_DESCRIPTION,
29119
+ usageHint: PWSH_TOOL_USAGE_HINT,
29120
+ selection: {
29121
+ doNotUseWhen: "the command is an allowlisted single binary (node, git, pnpm, tsc) needing no shell expansion or pipelines.",
29122
+ useInstead: ["exec"]
29123
+ },
29124
+ permission: "confirm",
29125
+ mutating: true,
29126
+ riskTier: "destructive",
29127
+ icon: "terminal",
29128
+ subjectKey: "command",
29129
+ capabilities: ["shell.arbitrary"],
29130
+ timeoutMs: 61e4,
29131
+ maxOutputBytes: MAX_OUTPUT4,
29132
+ estimatedDurationMs: 3e4,
29133
+ inputSchema: {
29134
+ type: "object",
29135
+ properties: {
29136
+ command: {
29137
+ type: "string",
29138
+ description: "The exact PowerShell command or script block to run."
29139
+ },
29140
+ workdir: {
29141
+ type: "string",
29142
+ description: "Absolute or project-relative path to the working directory for this command. Defaults to session working directory."
29143
+ },
29144
+ timeout_ms: {
29145
+ type: "integer",
29146
+ description: "Optional timeout for this specific command in milliseconds (default 300000, max 600000)."
29147
+ },
29148
+ run_in_background: {
29149
+ type: "boolean",
29150
+ description: "If true, launch the process in the background and return the job ID / PID immediately."
29151
+ },
29152
+ background: {
29153
+ type: "boolean",
29154
+ description: "Alias for run_in_background."
29155
+ },
29156
+ sandbox_permissions: {
29157
+ type: "string",
29158
+ description: "Escalation mode if retrying a sandbox-denied command (e.g., workspace-write)."
29159
+ },
29160
+ justification: {
29161
+ type: "string",
29162
+ description: "One-sentence justification when retrying a denied command with sandbox_permissions."
29163
+ }
29164
+ },
29165
+ required: ["command"]
29166
+ },
29167
+ async execute(input, ctx, opts) {
29168
+ let final;
29169
+ const executeStream = pwshTool.executeStream;
29170
+ if (!executeStream) throw new Error("pwshTool: stream execution unavailable");
29171
+ for await (const ev of executeStream(input, ctx, opts)) {
29172
+ if (ev.type === "final") final = ev.output;
29173
+ }
29174
+ if (!final) throw new Error("pwsh: stream ended without final event");
29175
+ return final;
29176
+ },
29177
+ async *executeStream(input, ctx, opts) {
29178
+ if (!input?.command) throw new Error("pwsh: command is required");
29179
+ const isBackground = !!(input.run_in_background || input.background);
29180
+ const registry = getProcessRegistry();
29181
+ if (!registry.beforeCall(isBackground)) {
29182
+ yield {
29183
+ type: "final",
29184
+ output: {
29185
+ output: "",
29186
+ exit_code: 1,
29187
+ timed_out: false,
29188
+ pid: null,
29189
+ error: "pwsh: circuit breaker open \u2014 too many consecutive failures or slow calls. Use /kill to inspect or /kill reset to recover."
29190
+ }
29191
+ };
29192
+ return;
29193
+ }
29194
+ const killCheck = await checkAndBlockKillCommand(input.command);
29195
+ if (killCheck.blocked) {
29196
+ yield {
29197
+ type: "final",
29198
+ output: {
29199
+ output: "",
29200
+ exit_code: 1,
29201
+ timed_out: false,
29202
+ pid: null,
29203
+ error: `pwsh: ${killCheck.reason}`
29204
+ }
29205
+ };
29206
+ return;
29207
+ }
29208
+ const isWin5 = os10.platform() === "win32";
29209
+ const bin = isWin5 ? resolvePowerShell("pwsh.exe") : "pwsh";
29210
+ const args = shellArgs("pwsh");
29211
+ const stdinBody = wrapPwshCommand(input.command);
29212
+ const env = buildChildEnv2(ctx.session?.id);
29213
+ let targetCwd = ctx.workingDir ?? ctx.projectRoot;
29214
+ if (input.workdir) {
29215
+ const resolved = path43.isAbsolute(input.workdir) ? input.workdir : path43.resolve(ctx.projectRoot, input.workdir);
29216
+ if (fs36.existsSync(resolved)) {
29217
+ targetCwd = resolved;
29218
+ }
29219
+ }
29220
+ const startedAt = Date.now();
29221
+ const detached = !isWin5;
29222
+ if (isBackground) {
29223
+ const child2 = spawn14(bin, args, {
29224
+ cwd: targetCwd,
29225
+ env,
29226
+ detached,
29227
+ stdio: ["pipe", "ignore", "ignore"],
29228
+ windowsHide: true
29229
+ });
29230
+ if (child2.stdin) {
29231
+ child2.stdin.write(stdinBody);
29232
+ child2.stdin.end();
29233
+ }
29234
+ const pid2 = child2.pid;
29235
+ if (typeof pid2 === "number") {
29236
+ registry.register({
29237
+ pid: pid2,
29238
+ name: "pwsh",
29239
+ command: redactCommand(input.command),
29240
+ startedAt: Date.now(),
29241
+ sessionId: ctx.session?.id,
29242
+ child: child2,
29243
+ processGroupLeader: detached && child2.pid === pid2,
29244
+ background: true
29245
+ });
29246
+ child2.on("close", () => registry.unregister(pid2));
29247
+ child2.on("error", () => {
29248
+ registry.unregister(pid2);
29249
+ registry.afterCall(Date.now() - startedAt, true, isBackground);
29250
+ });
29251
+ child2.unref();
29252
+ const jobId = `job_${pid2}`;
29253
+ ctx.recordSideEffect?.({
29254
+ toolUseId: `pwsh-bg-${Date.now()}`,
29255
+ toolName: "pwsh",
29256
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
29257
+ input: { command: redactCommand(input.command), background: true },
29258
+ outcome: `started background job ${jobId} (PID: ${pid2})`,
29259
+ risk: "shell"
29260
+ });
29261
+ yield {
29262
+ type: "final",
29263
+ output: {
29264
+ output: `Background job started: ${jobId} (PID: ${pid2})
29265
+ Working directory: ${targetCwd}`,
29266
+ exit_code: null,
29267
+ timed_out: false,
29268
+ pid: pid2,
29269
+ job_id: jobId
29270
+ }
29271
+ };
29272
+ return;
29273
+ }
29274
+ yield {
29275
+ type: "final",
29276
+ output: {
29277
+ output: "",
29278
+ exit_code: 1,
29279
+ timed_out: false,
29280
+ pid: null,
29281
+ error: "pwsh: failed to obtain PID for background process"
29282
+ }
29283
+ };
29284
+ return;
29285
+ }
29286
+ const spool = createOutputSpool({ tool: "pwsh", thresholdBytes: MAX_OUTPUT4 });
29287
+ const child = spawn14(bin, args, {
29288
+ cwd: targetCwd,
29289
+ env,
29290
+ detached,
29291
+ stdio: ["pipe", "pipe", "pipe"],
29292
+ windowsHide: true
29293
+ });
29294
+ const pid = child.pid;
29295
+ let stdoutBytes = 0;
29296
+ let stderrBytes = 0;
29297
+ let telemetryCompleted = false;
29298
+ emitProcessStarted4({
29299
+ ...pid !== void 0 ? { pid } : {},
29300
+ parentPid: process.pid,
29301
+ command: redactCommand(`${bin} ${args.join(" ")}`),
29302
+ args: redactCommand(args.join(" ")).split(" ").filter(Boolean),
29303
+ cwd: targetCwd,
29304
+ background: false,
29305
+ startedAt: new Date(startedAt).toISOString()
29306
+ });
29307
+ const completeForeground = (exitCode, sig) => {
29308
+ if (telemetryCompleted) return;
29309
+ telemetryCompleted = true;
29310
+ emitProcessCompleted4({
29311
+ ...pid !== void 0 ? { pid } : {},
29312
+ exitCode,
29313
+ ...sig ? { signal: sig } : {},
29314
+ durationMs: Date.now() - startedAt,
29315
+ stdoutBytes,
29316
+ stderrBytes,
29317
+ timedOut,
29318
+ endedAt: (/* @__PURE__ */ new Date()).toISOString()
29319
+ });
29320
+ };
29321
+ if (typeof pid === "number") {
29322
+ registry.register({
29323
+ pid,
29324
+ name: "pwsh",
29325
+ command: redactCommand(input.command),
29326
+ startedAt: Date.now(),
29327
+ sessionId: ctx.session?.id,
29328
+ child,
29329
+ processGroupLeader: detached && child.pid === pid
29330
+ });
29331
+ }
29332
+ if (child.stdin) {
29333
+ child.stdin.write(stdinBody);
29334
+ child.stdin.end();
29335
+ }
29336
+ const timeoutMs = Math.min(
29337
+ Math.max(1e3, input.timeout_ms ?? DEFAULT_TIMEOUT_MS4),
29338
+ 6e5
29339
+ );
29340
+ let timedOut = false;
29341
+ const timers = [];
29342
+ const killWithTimeout = (timeout) => {
29343
+ if (isWin5) {
29344
+ if (typeof child.pid === "number" && child.exitCode === null) {
29345
+ const attempted = registry.kill(child.pid, { force: true, graceMs: timeout });
29346
+ if (!attempted) {
29347
+ try {
29348
+ child.kill();
29349
+ } catch {
29350
+ }
29351
+ }
29352
+ }
29353
+ return;
29354
+ }
29355
+ if (typeof child.pid === "number") {
29356
+ registry.kill(child.pid, { graceMs: timeout });
29357
+ } else {
29358
+ try {
29359
+ child.kill("SIGTERM");
29360
+ } catch {
29361
+ }
29362
+ }
29363
+ };
29364
+ const timer = setTimeout(() => {
29365
+ timedOut = true;
29366
+ killWithTimeout(2e3);
29367
+ }, timeoutMs);
29368
+ timers.push(timer);
29369
+ const onAbort = () => killWithTimeout(2e3);
29370
+ if (opts.signal.aborted) onAbort();
29371
+ else opts.signal.addEventListener("abort", onAbort, { once: true });
29372
+ const queue = [];
29373
+ let resolveNext = null;
29374
+ const push = (c) => {
29375
+ if (resolveNext) {
29376
+ const r = resolveNext;
29377
+ resolveNext = null;
29378
+ r(c);
29379
+ } else {
29380
+ queue.push(c);
29381
+ }
29382
+ };
29383
+ const next = () => new Promise((resolve24) => {
29384
+ const c = queue.shift();
29385
+ if (c) resolve24(c);
29386
+ else resolveNext = resolve24;
29387
+ });
29388
+ let buf = "";
29389
+ let pending2 = "";
29390
+ let lastFlush = Date.now();
29391
+ const flush = () => {
29392
+ if (pending2.length === 0) return null;
29393
+ const text = pending2;
29394
+ pending2 = "";
29395
+ lastFlush = Date.now();
29396
+ return text;
29397
+ };
29398
+ let paused = false;
29399
+ const pauseIfFlooded = () => {
29400
+ if (!paused && queue.length >= MAX_QUEUE_CHUNKS2) {
29401
+ paused = true;
29402
+ child.stdout?.pause();
29403
+ child.stderr?.pause();
29404
+ }
29405
+ };
29406
+ const resumeIfDrained = () => {
29407
+ if (paused && queue.length < MAX_QUEUE_CHUNKS2) {
29408
+ paused = false;
29409
+ child.stdout?.resume();
29410
+ child.stderr?.resume();
29411
+ }
29412
+ };
29413
+ const stdoutDecoder = new StringDecoder3("utf8");
29414
+ const stderrDecoder = new StringDecoder3("utf8");
29415
+ const onData = (chunk, stream) => {
29416
+ const text = (stream === "stdout" ? stdoutDecoder : stderrDecoder).write(chunk);
29417
+ if (stream === "stdout") stdoutBytes += chunk.byteLength;
29418
+ else stderrBytes += chunk.byteLength;
29419
+ emitProcessOutput4({ pid: pid ?? 0, stream, chunk });
29420
+ if (buf.length < MAX_OUTPUT4) {
29421
+ buf += text.slice(0, MAX_OUTPUT4 - buf.length);
29422
+ }
29423
+ spool.write(text);
29424
+ pending2 += text;
29425
+ push({ kind: "data", text });
29426
+ pauseIfFlooded();
29427
+ };
29428
+ child.stdout?.on("data", (chunk) => onData(chunk, "stdout"));
29429
+ child.stderr?.on("data", (chunk) => onData(chunk, "stderr"));
29430
+ child.on("error", (err) => {
29431
+ for (const t of timers) clearTimeout(t);
29432
+ registry.afterCall(Date.now() - startedAt, true);
29433
+ completeForeground(1);
29434
+ push({ kind: "error", err });
29435
+ });
29436
+ child.on("close", (code, signal) => {
29437
+ for (const t of timers) clearTimeout(t);
29438
+ if (typeof pid === "number") registry.unregister(pid);
29439
+ registry.afterCall(Date.now() - startedAt, code !== 0 && code !== null);
29440
+ completeForeground(timedOut ? 124 : code ?? (signal ? 1 : 0), signal ?? void 0);
29441
+ const tail = stdoutDecoder.end() + stderrDecoder.end();
29442
+ if (tail) {
29443
+ if (buf.length < MAX_OUTPUT4) buf += tail.slice(0, MAX_OUTPUT4 - buf.length);
29444
+ spool.write(tail);
29445
+ pending2 += tail;
29446
+ }
29447
+ push({ kind: "end", code });
29448
+ });
29449
+ try {
29450
+ while (true) {
29451
+ const c = await next();
29452
+ resumeIfDrained();
29453
+ if (c.kind === "error") throw c.err;
29454
+ if (c.kind === "end") {
29455
+ const remainder = flush();
29456
+ if (remainder !== null) {
29457
+ yield { type: "partial_output", text: remainder };
29458
+ }
29459
+ const spooled = spool.finalize();
29460
+ let formattedOutput = normalizeCommandOutput(buf);
29461
+ if (c.code !== null && c.code !== 0 && !timedOut) {
29462
+ if (!formattedOutput.includes(`[exit code: ${c.code}]`)) {
29463
+ formattedOutput = formattedOutput ? `${formattedOutput}
29464
+ [exit code: ${c.code}]` : `[exit code: ${c.code}]`;
29465
+ }
29466
+ }
29467
+ const hint = !timedOut && typeof c.code === "number" && c.code !== 0 ? diagnoseBashism(input.command, "pwsh") : void 0;
29468
+ const danger = detectDanger("pwsh", [input.command]);
29469
+ const cautionText = danger.level === "caution" && danger.reasons.length > 0 ? `
29470
+ [caution: ${danger.reasons.join("; ")}]` : "";
29471
+ const finalResultText = formattedOutput + (spooled ? spoolNote(spooled) : "") + (hint ? `
29472
+
29473
+ ${hint}` : "") + cautionText;
29474
+ ctx.recordSideEffect?.({
29475
+ toolUseId: `pwsh-${Date.now()}`,
29476
+ toolName: "pwsh",
29477
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
29478
+ input: { command: redactCommand(input.command) },
29479
+ outcome: timedOut ? `timed out (exit ${c.code})` : `exit ${c.code}`,
29480
+ risk: "shell"
29481
+ });
29482
+ yield {
29483
+ type: "final",
29484
+ output: {
29485
+ output: finalResultText,
29486
+ exit_code: timedOut ? 1 : c.code,
29487
+ timed_out: timedOut,
29488
+ pid
29489
+ }
29490
+ };
29491
+ return;
29492
+ }
29493
+ const now = Date.now();
29494
+ if (pending2.length >= STREAM_FLUSH_BYTES2 || now - lastFlush >= STREAM_FLUSH_INTERVAL_MS2) {
29495
+ const text = flush();
29496
+ if (text) yield { type: "partial_output", text };
29497
+ }
29498
+ }
29499
+ } finally {
29500
+ for (const t of timers) clearTimeout(t);
29501
+ }
29502
+ }
29503
+ };
29504
+
29038
29505
  // src/read.ts
29039
29506
  init_util();
29040
- import * as fs36 from "node:fs/promises";
29507
+ import * as fs37 from "node:fs/promises";
29041
29508
  import { FsError, ToolValidationError as ToolValidationError8 } from "@wrongstack/core/types";
29042
29509
  import { toErrorMessage as toErrorMessage15 } from "@wrongstack/core/utils";
29043
29510
  var ADVANCED_MODE_META_KEY = "tools.read.advancedMode";
@@ -29097,7 +29564,7 @@ var readTool = {
29097
29564
  const shouldIncludeSymbols = input.includeSymbols === true || input.includeSymbols !== false && ctx.meta[ADVANCED_MODE_META_KEY] === true;
29098
29565
  let stat21;
29099
29566
  try {
29100
- stat21 = await fs36.stat(absPath);
29567
+ stat21 = await fs37.stat(absPath);
29101
29568
  } catch (err) {
29102
29569
  const code = err.code;
29103
29570
  if (code === "ENOENT") {
@@ -29149,7 +29616,7 @@ var readTool = {
29149
29616
  ...symResult2?.symbols ? { symbols: symResult2.symbols } : {}
29150
29617
  };
29151
29618
  }
29152
- const buf = await fs36.readFile(absPath);
29619
+ const buf = await fs37.readFile(absPath);
29153
29620
  if (isBinaryBuffer(buf)) {
29154
29621
  throw new FsError({
29155
29622
  message: `read: "${input.path}" appears to be binary`,
@@ -29315,9 +29782,9 @@ ${interesting.join("\n")}` : "symbols/imports: (none detected)"
29315
29782
  }
29316
29783
 
29317
29784
  // src/replace.ts
29318
- import { spawn as spawn14 } from "node:child_process";
29319
- import * as fs37 from "node:fs/promises";
29320
- import * as path43 from "node:path";
29785
+ import { spawn as spawn15 } from "node:child_process";
29786
+ import * as fs38 from "node:fs/promises";
29787
+ import * as path44 from "node:path";
29321
29788
  import { ToolValidationError as ToolValidationError9 } from "@wrongstack/core/types";
29322
29789
  import {
29323
29790
  atomicWrite as atomicWrite4,
@@ -29400,14 +29867,14 @@ var replaceTool = {
29400
29867
  const dryRun = input.dry_run ?? true;
29401
29868
  const filesInput = Array.isArray(input.files) ? input.files.join(",") : input.files;
29402
29869
  const fileList = await resolveFiles2(filesInput, ctx, globRe);
29403
- const realRoot = await fs37.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
29870
+ const realRoot = await fs38.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
29404
29871
  const results = [];
29405
29872
  let totalReplacements = 0;
29406
29873
  let diffBytesUsed = 0;
29407
29874
  let diffsOmitted = 0;
29408
29875
  let diffsTruncated = 0;
29409
29876
  for (const absPath of fileList) {
29410
- const lstat2 = await fs37.lstat(absPath).catch((err) => {
29877
+ const lstat2 = await fs38.lstat(absPath).catch((err) => {
29411
29878
  if (err.code === "ENOENT") return null;
29412
29879
  throw err;
29413
29880
  });
@@ -29415,17 +29882,17 @@ var replaceTool = {
29415
29882
  if (lstat2.isSymbolicLink()) continue;
29416
29883
  let realPath;
29417
29884
  try {
29418
- realPath = await fs37.realpath(absPath);
29885
+ realPath = await fs38.realpath(absPath);
29419
29886
  } catch {
29420
29887
  continue;
29421
29888
  }
29422
- const rel = path43.relative(realRoot, realPath);
29423
- if (rel.startsWith("..") || path43.isAbsolute(rel)) continue;
29424
- const stat21 = await fs37.stat(realPath).catch(() => null);
29889
+ const rel = path44.relative(realRoot, realPath);
29890
+ if (rel.startsWith("..") || path44.isAbsolute(rel)) continue;
29891
+ const stat21 = await fs38.stat(realPath).catch(() => null);
29425
29892
  if (!stat21?.isFile()) continue;
29426
29893
  let content;
29427
29894
  try {
29428
- const buf = await fs37.readFile(realPath);
29895
+ const buf = await fs38.readFile(realPath);
29429
29896
  if (isBinaryBuffer(buf)) continue;
29430
29897
  content = buf.toString("utf8");
29431
29898
  } catch {
@@ -29448,7 +29915,7 @@ var replaceTool = {
29448
29915
  if (!dryRun) {
29449
29916
  const newContent = toStyle2(newContentLf, style);
29450
29917
  await atomicWrite4(realPath, newContent, { mode: stat21.mode & 511 });
29451
- const written = await fs37.stat(realPath).catch(() => null);
29918
+ const written = await fs38.stat(realPath).catch(() => null);
29452
29919
  if (written) {
29453
29920
  ctx.recordRead?.(realPath, written.mtimeMs, "write", sha256hex(newContent));
29454
29921
  }
@@ -29548,8 +30015,8 @@ async function resolveFiles2(filesInput, ctx, extraGlob) {
29548
30015
  const resolved = [];
29549
30016
  for (const p of parts) {
29550
30017
  const absPath = await safeResolveReal(p, ctx);
29551
- if (extraGlob && !passesExtraGlob(extraGlob, path43.basename(absPath), absPath)) continue;
29552
- const stat21 = await fs37.stat(absPath).catch(() => null);
30018
+ if (extraGlob && !passesExtraGlob(extraGlob, path44.basename(absPath), absPath)) continue;
30019
+ const stat21 = await fs38.stat(absPath).catch(() => null);
29553
30020
  if (stat21?.isFile()) {
29554
30021
  resolved.push(absPath);
29555
30022
  }
@@ -29563,7 +30030,7 @@ async function globFiles(pattern, base, extraGlob) {
29563
30030
  const { promise } = spawnRgFind(pattern, base);
29564
30031
  const files = await promise;
29565
30032
  if (extraGlob) {
29566
- return files.filter((f) => passesExtraGlob(extraGlob, path43.basename(f), f));
30033
+ return files.filter((f) => passesExtraGlob(extraGlob, path44.basename(f), f));
29567
30034
  }
29568
30035
  return files;
29569
30036
  } catch {
@@ -29573,24 +30040,24 @@ async function globFiles(pattern, base, extraGlob) {
29573
30040
  }
29574
30041
  var rgAvailabilityCache2;
29575
30042
  function checkRg() {
29576
- rgAvailabilityCache2 ??= new Promise((resolve23) => {
30043
+ rgAvailabilityCache2 ??= new Promise((resolve24) => {
29577
30044
  try {
29578
- const p = spawn14("rg", ["--version"], {
30045
+ const p = spawn15("rg", ["--version"], {
29579
30046
  env: buildChildEnv9(),
29580
30047
  stdio: "ignore",
29581
30048
  windowsHide: true
29582
30049
  });
29583
- p.on("error", () => resolve23(false));
29584
- p.on("close", (code) => resolve23(code === 0));
30050
+ p.on("error", () => resolve24(false));
30051
+ p.on("close", (code) => resolve24(code === 0));
29585
30052
  } catch {
29586
- resolve23(false);
30053
+ resolve24(false);
29587
30054
  }
29588
30055
  });
29589
30056
  return rgAvailabilityCache2;
29590
30057
  }
29591
30058
  function spawnRgFind(pattern, base) {
29592
30059
  const args = ["--files", "--glob", pattern, base];
29593
- const child = spawn14("rg", args, {
30060
+ const child = spawn15("rg", args, {
29594
30061
  signal: AbortSignal.timeout(3e4),
29595
30062
  env: buildChildEnv9(),
29596
30063
  stdio: ["ignore", "pipe", "pipe"],
@@ -29609,10 +30076,10 @@ function spawnRgFind(pattern, base) {
29609
30076
  }
29610
30077
  });
29611
30078
  return {
29612
- promise: new Promise((resolve23, reject) => {
30079
+ promise: new Promise((resolve24, reject) => {
29613
30080
  child.on("error", reject);
29614
30081
  child.on("close", () => {
29615
- resolve23(buf.split("\n").filter(Boolean));
30082
+ resolve24(buf.split("\n").filter(Boolean));
29616
30083
  });
29617
30084
  })
29618
30085
  };
@@ -29623,15 +30090,15 @@ async function globNative(pattern, base, extraGlob) {
29623
30090
  const walk = async (dir) => {
29624
30091
  let entries;
29625
30092
  try {
29626
- entries = await fs37.readdir(dir, { withFileTypes: true });
30093
+ entries = await fs38.readdir(dir, { withFileTypes: true });
29627
30094
  } catch {
29628
30095
  return;
29629
30096
  }
29630
30097
  for (const e of entries) {
29631
30098
  if (DEFAULT_IGNORE4.includes(e.name)) continue;
29632
- const full = path43.join(dir, e.name);
30099
+ const full = path44.join(dir, e.name);
29633
30100
  try {
29634
- const stat21 = await fs37.lstat(full);
30101
+ const stat21 = await fs38.lstat(full);
29635
30102
  if (stat21.isSymbolicLink()) continue;
29636
30103
  } catch {
29637
30104
  continue;
@@ -29655,8 +30122,8 @@ async function globNative(pattern, base, extraGlob) {
29655
30122
 
29656
30123
  // src/scaffold.ts
29657
30124
  init_util();
29658
- import * as fs38 from "node:fs/promises";
29659
- import * as path44 from "node:path";
30125
+ import * as fs39 from "node:fs/promises";
30126
+ import * as path45 from "node:path";
29660
30127
  import { atomicWrite as atomicWrite5 } from "@wrongstack/core/utils";
29661
30128
  var BUILT_IN_TEMPLATES = {
29662
30129
  "npm-package": {
@@ -29807,16 +30274,16 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
29807
30274
  let filesCreated = 0;
29808
30275
  for (const [filePath, content] of Object.entries(templateFiles)) {
29809
30276
  const resolvedPath = substituteVars(filePath, name, vars);
29810
- const joinedPath = path44.join(cwd, resolvedPath);
29811
- const root = path44.resolve(ctx.projectRoot);
29812
- const target = path44.resolve(joinedPath);
29813
- const rel = path44.relative(root, target);
29814
- if (rel.startsWith("..") || path44.isAbsolute(rel)) {
30277
+ const joinedPath = path45.join(cwd, resolvedPath);
30278
+ const root = path45.resolve(ctx.projectRoot);
30279
+ const target = path45.resolve(joinedPath);
30280
+ const rel = path45.relative(root, target);
30281
+ if (rel.startsWith("..") || path45.isAbsolute(rel)) {
29815
30282
  throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
29816
30283
  }
29817
30284
  const fullPath = target;
29818
30285
  if (!dryRun) {
29819
- await fs38.mkdir(path44.dirname(fullPath), { recursive: true });
30286
+ await fs39.mkdir(path45.dirname(fullPath), { recursive: true });
29820
30287
  await atomicWrite5(fullPath, substituteVars(content, name, vars));
29821
30288
  }
29822
30289
  files.push(resolvedPath);
@@ -30262,8 +30729,8 @@ function decodeHtmlEntities(text) {
30262
30729
  }
30263
30730
 
30264
30731
  // src/security-ast-scan-tool.ts
30265
- import * as fs39 from "node:fs/promises";
30266
- import * as path45 from "node:path";
30732
+ import * as fs40 from "node:fs/promises";
30733
+ import * as path46 from "node:path";
30267
30734
  import { toErrorMessage as toErrorMessage17 } from "@wrongstack/core/utils";
30268
30735
  var SECRET_PATTERNS = [
30269
30736
  { name: "AWS Access Key", regex: /\b(AKIA[0-9A-Z]{16})\b/ },
@@ -30430,9 +30897,9 @@ var securityAstScanTool = {
30430
30897
  let targetFile = input.file ?? "inline-code.ts";
30431
30898
  let content = input.content;
30432
30899
  if (!content && input.file) {
30433
- const absPath = path45.isAbsolute(input.file) ? input.file : path45.resolve(projectRoot, input.file);
30434
- targetFile = path45.relative(projectRoot, absPath).replace(/\\/g, "/");
30435
- content = await fs39.readFile(absPath, "utf8");
30900
+ const absPath = path46.isAbsolute(input.file) ? input.file : path46.resolve(projectRoot, input.file);
30901
+ targetFile = path46.relative(projectRoot, absPath).replace(/\\/g, "/");
30902
+ content = await fs40.readFile(absPath, "utf8");
30436
30903
  }
30437
30904
  if (!content) {
30438
30905
  return {
@@ -30480,7 +30947,7 @@ var securityAstScanTool = {
30480
30947
  };
30481
30948
 
30482
30949
  // src/set-working-dir.ts
30483
- import * as fs40 from "node:fs/promises";
30950
+ import * as fs41 from "node:fs/promises";
30484
30951
  import { toErrorMessage as toErrorMessage18 } from "@wrongstack/core/utils";
30485
30952
  var setWorkingDirTool = {
30486
30953
  name: "set_working_dir",
@@ -30520,7 +30987,7 @@ var setWorkingDirTool = {
30520
30987
  }
30521
30988
  let isDirectory = false;
30522
30989
  try {
30523
- isDirectory = (await fs40.stat(resolved)).isDirectory();
30990
+ isDirectory = (await fs41.stat(resolved)).isDirectory();
30524
30991
  } catch {
30525
30992
  isDirectory = false;
30526
30993
  }
@@ -31055,7 +31522,7 @@ ${formatTaskList2(file.tasks)}` : file.tasks.length > 0 ? formatTaskList2(file.t
31055
31522
  init_spawn_stream();
31056
31523
  init_util();
31057
31524
  init_legacy_bridge();
31058
- import * as path46 from "node:path";
31525
+ import * as path47 from "node:path";
31059
31526
  var testTool = {
31060
31527
  name: "test",
31061
31528
  category: "Code Quality",
@@ -31162,7 +31629,7 @@ async function detectRunner(cwd) {
31162
31629
  const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
31163
31630
  for (const f of candidates) {
31164
31631
  try {
31165
- await stat21(path46.join(cwd, f));
31632
+ await stat21(path47.join(cwd, f));
31166
31633
  if (f.includes("vitest")) return "vitest";
31167
31634
  if (f.includes("jest")) return "jest";
31168
31635
  if (f.includes("mocha")) return "mocha";
@@ -31539,8 +32006,8 @@ var toolUseTool = {
31539
32006
 
31540
32007
  // src/tree.ts
31541
32008
  init_util();
31542
- import * as fs41 from "node:fs/promises";
31543
- import * as path47 from "node:path";
32009
+ import * as fs42 from "node:fs/promises";
32010
+ import * as path48 from "node:path";
31544
32011
  import { DEFAULT_WALK_IGNORE_DIRS as DEFAULT_WALK_IGNORE_DIRS4, expectDefined as expectDefined10 } from "@wrongstack/core/utils";
31545
32012
  var DEFAULT_IGNORE5 = /* @__PURE__ */ new Set([
31546
32013
  ...DEFAULT_WALK_IGNORE_DIRS4,
@@ -31703,7 +32170,7 @@ var treeTool = {
31703
32170
  async function walkDir(dir, depth, opts) {
31704
32171
  opts.signal.throwIfAborted();
31705
32172
  if (opts.retention.truncated) return;
31706
- const entries = await fs41.readdir(dir, { withFileTypes: true }).catch(() => []);
32173
+ const entries = await fs42.readdir(dir, { withFileTypes: true }).catch(() => []);
31707
32174
  const filtered = entries.filter((e) => {
31708
32175
  if (!opts.showHidden && e.name.startsWith(".")) return false;
31709
32176
  if (opts.exclude.has(e.name)) return false;
@@ -31745,7 +32212,7 @@ async function walkDir(dir, depth, opts) {
31745
32212
  opts.retention.outputBytes += lineBytes;
31746
32213
  if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
31747
32214
  const childPrefix = opts.prefix + connector;
31748
- await walkDir(path47.join(dir, entry.name), depth + 1, {
32215
+ await walkDir(path48.join(dir, entry.name), depth + 1, {
31749
32216
  ...opts,
31750
32217
  prefix: childPrefix,
31751
32218
  isLast
@@ -31758,7 +32225,7 @@ async function walkDir(dir, depth, opts) {
31758
32225
  init_spawn_stream();
31759
32226
  init_util();
31760
32227
  init_legacy_bridge();
31761
- import * as path48 from "node:path";
32228
+ import * as path49 from "node:path";
31762
32229
  var typecheckTool = {
31763
32230
  name: "typecheck",
31764
32231
  category: "Code Quality",
@@ -31870,8 +32337,8 @@ async function findTsConfig(cwd) {
31870
32337
  const candidates = ["tsconfig.json", "tsconfig.base.json"];
31871
32338
  for (const f of candidates) {
31872
32339
  try {
31873
- const s = await stat21(path48.join(cwd, f));
31874
- if (s.isFile()) return path48.join(cwd, f);
32340
+ const s = await stat21(path49.join(cwd, f));
32341
+ if (s.isFile()) return path49.join(cwd, f);
31875
32342
  } catch {
31876
32343
  }
31877
32344
  }
@@ -31879,7 +32346,7 @@ async function findTsConfig(cwd) {
31879
32346
  }
31880
32347
 
31881
32348
  // src/write.ts
31882
- import * as fs42 from "node:fs/promises";
32349
+ import * as fs43 from "node:fs/promises";
31883
32350
  import { ToolValidationError as ToolValidationError11 } from "@wrongstack/core/types";
31884
32351
  import {
31885
32352
  atomicWrite as atomicWrite6,
@@ -31956,14 +32423,14 @@ async function prepareWrite(input, ctx) {
31956
32423
  let existed = false;
31957
32424
  let prev = "";
31958
32425
  try {
31959
- const stat21 = await fs42.stat(absPath);
32426
+ const stat21 = await fs43.stat(absPath);
31960
32427
  existed = stat21.isFile();
31961
32428
  if (existed) {
31962
32429
  if (!ctx.hasRead(absPath)) {
31963
- prev = await fs42.readFile(absPath, "utf8");
32430
+ prev = await fs43.readFile(absPath, "utf8");
31964
32431
  ctx.recordRead(absPath, stat21.mtimeMs, "write", sha256hex(prev));
31965
32432
  } else {
31966
- prev = await fs42.readFile(absPath, "utf8");
32433
+ prev = await fs43.readFile(absPath, "utf8");
31967
32434
  }
31968
32435
  }
31969
32436
  } catch (err) {
@@ -31984,7 +32451,7 @@ async function finishWrite(input, ctx, prepared, signal) {
31984
32451
  const rawDiff = prepared.existed ? unifiedDiff3(prepared.prev, content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
31985
32452
  + (new file, ${content.split("\n").length} lines)`;
31986
32453
  const { text: diff, truncated: diffTruncated } = truncateDiffPayload(rawDiff, MAX_DIFF_BYTES3);
31987
- const stat21 = await fs42.stat(prepared.absPath);
32454
+ const stat21 = await fs43.stat(prepared.absPath);
31988
32455
  ctx.recordRead(prepared.absPath, stat21.mtimeMs, "write", sha256hex(content));
31989
32456
  ctx.session.recordFileChange({
31990
32457
  path: prepared.absPath,
@@ -32059,6 +32526,7 @@ var builtinTools = [
32059
32526
  grepTool,
32060
32527
  bashTool,
32061
32528
  execTool,
32529
+ pwshTool,
32062
32530
  fetchTool,
32063
32531
  searchTool,
32064
32532
  todoTool,