@wrongstack/tools 0.308.0 → 0.308.2

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
@@ -1034,8 +1034,8 @@ async function* spawnStream(opts) {
1034
1034
  try {
1035
1035
  for (; ; ) {
1036
1036
  while (queue.length === 0) {
1037
- await new Promise((resolve24) => {
1038
- waiter = resolve24;
1037
+ await new Promise((resolve25) => {
1038
+ waiter = resolve25;
1039
1039
  });
1040
1040
  }
1041
1041
  const chunk = queue.shift();
@@ -1138,9 +1138,9 @@ async function detectPackageManager(cwd, stopAt) {
1138
1138
  return "npm";
1139
1139
  }
1140
1140
  async function detectPackageManagerInDir(dir) {
1141
- const fs44 = await import("node:fs/promises");
1141
+ const fs45 = await import("node:fs/promises");
1142
1142
  try {
1143
- const raw = await fs44.readFile(path3.join(dir, "package.json"), "utf8");
1143
+ const raw = await fs45.readFile(path3.join(dir, "package.json"), "utf8");
1144
1144
  const declared = JSON.parse(raw).packageManager;
1145
1145
  if (typeof declared === "string") {
1146
1146
  const name = declared.split("@")[0] ?? "";
@@ -1159,7 +1159,7 @@ async function detectPackageManagerInDir(dir) {
1159
1159
  ];
1160
1160
  for (const [file, manager] of lockfiles) {
1161
1161
  try {
1162
- await fs44.stat(`${dir}/${file}`);
1162
+ await fs45.stat(`${dir}/${file}`);
1163
1163
  return manager;
1164
1164
  } catch {
1165
1165
  }
@@ -4140,15 +4140,15 @@ async function changedPaths(before, after, beforeSizes, afterSizes) {
4140
4140
  const beforeSet = new Set(before);
4141
4141
  const afterSet = new Set(after);
4142
4142
  const changed = /* @__PURE__ */ new Set();
4143
- for (const path50 of after) {
4144
- if (!beforeSet.has(path50)) changed.add(path50);
4143
+ for (const path51 of after) {
4144
+ if (!beforeSet.has(path51)) changed.add(path51);
4145
4145
  }
4146
- for (const path50 of before) {
4147
- if (!afterSet.has(path50)) changed.add(path50);
4146
+ for (const path51 of before) {
4147
+ if (!afterSet.has(path51)) changed.add(path51);
4148
4148
  }
4149
4149
  if (beforeSizes && afterSizes) {
4150
- for (const path50 of after) {
4151
- if (beforeSizes.get(path50) !== afterSizes.get(path50)) changed.add(path50);
4150
+ for (const path51 of after) {
4151
+ if (beforeSizes.get(path51) !== afterSizes.get(path51)) changed.add(path51);
4152
4152
  }
4153
4153
  }
4154
4154
  return [...changed].sort();
@@ -5643,7 +5643,7 @@ async function syncGoParse(filePath, content, lang) {
5643
5643
  }
5644
5644
  const goBinary = resolveWin32Command("go");
5645
5645
  const goResult = await new Promise(
5646
- (resolve24, reject) => {
5646
+ (resolve25, reject) => {
5647
5647
  let settled = false;
5648
5648
  const proc = spawn5(goBinary, ["run", scriptPath], {
5649
5649
  stdio: ["pipe", "pipe", "pipe"],
@@ -5672,7 +5672,7 @@ async function syncGoParse(filePath, content, lang) {
5672
5672
  if (settled) return;
5673
5673
  settled = true;
5674
5674
  clearTimeout(timer);
5675
- resolve24({ code: code2, stdout: stdout2 });
5675
+ resolve25({ code: code2, stdout: stdout2 });
5676
5676
  });
5677
5677
  }
5678
5678
  );
@@ -6348,7 +6348,7 @@ async function resolvePython() {
6348
6348
  return null;
6349
6349
  }
6350
6350
  function commandIsAvailable(command) {
6351
- return new Promise((resolve24) => {
6351
+ return new Promise((resolve25) => {
6352
6352
  let settled = false;
6353
6353
  const proc = spawn6(command, ["--version"], {
6354
6354
  stdio: "ignore",
@@ -6358,7 +6358,7 @@ function commandIsAvailable(command) {
6358
6358
  if (settled) return;
6359
6359
  settled = true;
6360
6360
  clearTimeout(timer);
6361
- resolve24(available);
6361
+ resolve25(available);
6362
6362
  };
6363
6363
  const timer = setTimeout(() => {
6364
6364
  proc.kill("SIGKILL");
@@ -6370,7 +6370,7 @@ function commandIsAvailable(command) {
6370
6370
  });
6371
6371
  }
6372
6372
  function spawnPyParser(pyBinary, scriptPath, filePath, content) {
6373
- return new Promise((resolve24, reject) => {
6373
+ return new Promise((resolve25, reject) => {
6374
6374
  let settled = false;
6375
6375
  const proc = spawn6(pyBinary, [scriptPath, filePath], {
6376
6376
  stdio: ["pipe", "pipe", "pipe"],
@@ -6399,7 +6399,7 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
6399
6399
  if (settled) return;
6400
6400
  settled = true;
6401
6401
  clearTimeout(timer);
6402
- resolve24({ code, stdout });
6402
+ resolve25({ code, stdout });
6403
6403
  });
6404
6404
  });
6405
6405
  }
@@ -7788,6 +7788,40 @@ var RULES = [
7788
7788
  },
7789
7789
  reason: "Remove-Item with -Recurse -Force"
7790
7790
  },
7791
+ // ----- Windows PowerShell Disk & Volume destruction -----
7792
+ {
7793
+ id: "powershell-disk-volume-destroy",
7794
+ level: "destructive",
7795
+ test: (cmd, args) => {
7796
+ if (cmd !== "powershell" && cmd !== "pwsh") return false;
7797
+ return args.some(
7798
+ (a) => /^(?:Format-Volume|Clear-Disk|Initialize-Disk|Remove-Partition|Clear-Volume)(?:\s|$)/i.test(a)
7799
+ );
7800
+ },
7801
+ reason: "PowerShell disk/volume partition destruction"
7802
+ },
7803
+ // ----- Windows PowerShell System Restart / Shutdown -----
7804
+ {
7805
+ id: "powershell-stop-restart-computer",
7806
+ level: "destructive",
7807
+ test: (cmd, args) => {
7808
+ if (cmd !== "powershell" && cmd !== "pwsh") return false;
7809
+ return args.some((a) => /^(?:Stop-Computer|Restart-Computer)(?:\s|$)/i.test(a));
7810
+ },
7811
+ reason: "PowerShell system shutdown or restart"
7812
+ },
7813
+ // ----- Windows PowerShell ExecutionPolicy / Payload evasion -----
7814
+ {
7815
+ id: "powershell-execution-policy-bypass",
7816
+ level: "caution",
7817
+ test: (cmd, args) => {
7818
+ if (cmd !== "powershell" && cmd !== "pwsh") return false;
7819
+ return args.some(
7820
+ (a) => /Set-ExecutionPolicy\s+(?:Bypass|Unrestricted)|-(?:EncodedCommand|enc)\b/i.test(a)
7821
+ );
7822
+ },
7823
+ reason: "PowerShell execution policy bypass or encoded command"
7824
+ },
7791
7825
  // ----- find -exec / -ok / -execdir -----
7792
7826
  {
7793
7827
  id: "find-exec",
@@ -8108,8 +8142,8 @@ function normalizeShell(value) {
8108
8142
  if (v === "pwsh" || v === "pwsh.exe") return "pwsh";
8109
8143
  return void 0;
8110
8144
  }
8111
- function resolveSessionShell(platform6, env, deps = {}) {
8112
- if (platform6 !== "win32") return void 0;
8145
+ function resolveSessionShell(platform7, env, deps = {}) {
8146
+ if (platform7 !== "win32") return void 0;
8113
8147
  const override = normalizeShell(env.get("WRONGSTACK_SHELL"));
8114
8148
  if (override) return override;
8115
8149
  const hasBinary = deps.hasBinary ?? ((bin) => resolveWin32Command(bin) !== bin);
@@ -8119,11 +8153,11 @@ function resolveSessionShell(platform6, env, deps = {}) {
8119
8153
  }
8120
8154
  function ensureSessionShell(opts = {}) {
8121
8155
  const env = opts.env ?? process.env;
8122
- const platform6 = opts.platform ?? process.platform;
8123
- if (platform6 !== "win32") return void 0;
8156
+ const platform7 = opts.platform ?? process.platform;
8157
+ if (platform7 !== "win32") return void 0;
8124
8158
  const existing = normalizeShell(env["WRONGSTACK_SHELL"]);
8125
8159
  if (existing) return existing;
8126
- const chosen = resolveSessionShell(platform6, { get: (k) => env[k] }, { hasBinary: opts.hasBinary }) ?? "cmd";
8160
+ const chosen = resolveSessionShell(platform7, { get: (k) => env[k] }, { hasBinary: opts.hasBinary }) ?? "cmd";
8127
8161
  env["WRONGSTACK_SHELL"] = chosen;
8128
8162
  return chosen;
8129
8163
  }
@@ -8989,6 +9023,18 @@ function parseKillCommand(command) {
8989
9023
  originalCommand: command
8990
9024
  };
8991
9025
  }
9026
+ const wmicPidMatch = normalized.match(
9027
+ /^wmic\s+process\s+where\s+['"]?processid\s*=\s*['"]?(\d+)/i
9028
+ );
9029
+ if (wmicPidMatch?.[1]) {
9030
+ return {
9031
+ pid: parseInt(wmicPidMatch[1], 10),
9032
+ signal: "FORCE",
9033
+ isGroupKill: false,
9034
+ isAllKill: false,
9035
+ originalCommand: command
9036
+ };
9037
+ }
8992
9038
  const killScriptMatch = normalized.match(SCRIPT_KILL_RE);
8993
9039
  if (killScriptMatch) {
8994
9040
  return {
@@ -9108,7 +9154,9 @@ async function isKillProtected(kill) {
9108
9154
  return protectedPids.length > 0;
9109
9155
  }
9110
9156
  if (kill.pid !== void 0) {
9111
- return registry.shouldBlockKill(kill.pid);
9157
+ if (await registry.shouldBlockKill(kill.pid)) return true;
9158
+ if (kill.pid === process.pid || kill.pid === process.ppid) return true;
9159
+ return false;
9112
9160
  }
9113
9161
  return false;
9114
9162
  }
@@ -9155,8 +9203,8 @@ async function checkAndBlockKillCommand(command) {
9155
9203
 
9156
9204
  // src/_shell-pick.ts
9157
9205
  var POSIX_DEFAULT = "cmd";
9158
- function pickShell(platform6, command, env) {
9159
- if (platform6 !== "win32") return POSIX_DEFAULT;
9206
+ function pickShell(platform7, command, env) {
9207
+ if (platform7 !== "win32") return POSIX_DEFAULT;
9160
9208
  const override = env.get("WRONGSTACK_SHELL")?.trim().toLowerCase();
9161
9209
  if (override === "cmd" || override === "cmd.exe") return "cmd";
9162
9210
  if (override === "powershell" || override === "powershell.exe") return "powershell";
@@ -9615,10 +9663,10 @@ var bashTool = {
9615
9663
  queue.push(c);
9616
9664
  }
9617
9665
  };
9618
- const next = () => new Promise((resolve24) => {
9666
+ const next = () => new Promise((resolve25) => {
9619
9667
  const c = queue.shift();
9620
- if (c) resolve24(c);
9621
- else resolveNext = resolve24;
9668
+ if (c) resolve25(c);
9669
+ else resolveNext = resolve25;
9622
9670
  });
9623
9671
  let lastFlush = Date.now();
9624
9672
  const flush = () => {
@@ -10071,11 +10119,11 @@ var BrowserArtifactStore = class {
10071
10119
  };
10072
10120
  async function hashFile(target) {
10073
10121
  const hash = createHash3("sha256");
10074
- await new Promise((resolve24, reject) => {
10122
+ await new Promise((resolve25, reject) => {
10075
10123
  const stream = createReadStream(target);
10076
10124
  stream.on("data", (chunk) => hash.update(chunk));
10077
10125
  stream.once("error", reject);
10078
- stream.once("end", resolve24);
10126
+ stream.once("end", resolve25);
10079
10127
  });
10080
10128
  return hash.digest("hex");
10081
10129
  }
@@ -10230,7 +10278,7 @@ var BrowserNetworkGuardProxy = class {
10230
10278
  async start() {
10231
10279
  if (this.url) return this.url;
10232
10280
  if (this.startPromise) return this.startPromise;
10233
- this.startPromise = new Promise((resolve24, reject) => {
10281
+ this.startPromise = new Promise((resolve25, reject) => {
10234
10282
  const onError = (error) => {
10235
10283
  this.server.off("listening", onListening);
10236
10284
  reject(error);
@@ -10243,7 +10291,7 @@ var BrowserNetworkGuardProxy = class {
10243
10291
  return;
10244
10292
  }
10245
10293
  this.url = `http://127.0.0.1:${address.port}`;
10246
- resolve24(this.url);
10294
+ resolve25(this.url);
10247
10295
  };
10248
10296
  this.server.once("error", onError);
10249
10297
  this.server.once("listening", onListening);
@@ -10258,7 +10306,7 @@ var BrowserNetworkGuardProxy = class {
10258
10306
  for (const socket of this.sockets) socket.destroy();
10259
10307
  this.sockets.clear();
10260
10308
  if (!this.server.listening) return;
10261
- await new Promise((resolve24) => this.server.close(() => resolve24()));
10309
+ await new Promise((resolve25) => this.server.close(() => resolve25()));
10262
10310
  }
10263
10311
  async forwardHttp(request2, response) {
10264
10312
  try {
@@ -10831,7 +10879,7 @@ function pushBounded(target, value, limit) {
10831
10879
  }
10832
10880
  async function abortable(signal, operation, onAbort) {
10833
10881
  signal.throwIfAborted();
10834
- return new Promise((resolve24, reject) => {
10882
+ return new Promise((resolve25, reject) => {
10835
10883
  let settled = false;
10836
10884
  let aborting = false;
10837
10885
  const finish = (fn) => {
@@ -10849,7 +10897,7 @@ async function abortable(signal, operation, onAbort) {
10849
10897
  signal.addEventListener("abort", abort, { once: true });
10850
10898
  operation().then(
10851
10899
  (value) => {
10852
- if (!aborting) finish(() => resolve24(value));
10900
+ if (!aborting) finish(() => resolve25(value));
10853
10901
  },
10854
10902
  (err) => {
10855
10903
  if (!aborting) finish(() => reject(err));
@@ -14387,8 +14435,8 @@ function isProjectIndexServerHealth(value) {
14387
14435
  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";
14388
14436
  }
14389
14437
  function delay(ms) {
14390
- return new Promise((resolve24) => {
14391
- const timer = setTimeout(resolve24, ms);
14438
+ return new Promise((resolve25) => {
14439
+ const timer = setTimeout(resolve25, ms);
14392
14440
  timer.unref?.();
14393
14441
  });
14394
14442
  }
@@ -14594,7 +14642,7 @@ var ProjectServerConnection = class {
14594
14642
  return Promise.reject(new Error("codebase-index server connection is not available"));
14595
14643
  }
14596
14644
  const id = this.nextId++;
14597
- return new Promise((resolve24, reject) => {
14645
+ return new Promise((resolve25, reject) => {
14598
14646
  const timer = setTimeout(() => {
14599
14647
  const entry = this.pending.get(id);
14600
14648
  if (!entry) return;
@@ -14617,7 +14665,7 @@ var ProjectServerConnection = class {
14617
14665
  entry.reject(cancellationError(signal));
14618
14666
  } : void 0;
14619
14667
  this.pending.set(id, {
14620
- resolve: resolve24,
14668
+ resolve: resolve25,
14621
14669
  reject,
14622
14670
  timer,
14623
14671
  signal,
@@ -14686,7 +14734,7 @@ var ProjectServerConnection = class {
14686
14734
  this.binaryBuffer = [];
14687
14735
  this.useBinary = false;
14688
14736
  this.textDecoder = null;
14689
- return new Promise((resolve24, reject) => {
14737
+ return new Promise((resolve25, reject) => {
14690
14738
  const socket = net3.createConnection(this.endpoint);
14691
14739
  this.socket = socket;
14692
14740
  const timer = setTimeout(() => {
@@ -14698,7 +14746,7 @@ var ProjectServerConnection = class {
14698
14746
  clearTimeout(timer);
14699
14747
  this.connectResolve = null;
14700
14748
  this.connectReject = null;
14701
- resolve24();
14749
+ resolve25();
14702
14750
  };
14703
14751
  const finishReject = (error) => {
14704
14752
  clearTimeout(timer);
@@ -15747,9 +15795,9 @@ var ParserWorkerPool = class {
15747
15795
  for (let i = 0; i < files.length; i++) {
15748
15796
  chunks[i % workerCount].push(files[i]);
15749
15797
  }
15750
- return new Promise((resolve24, reject) => {
15798
+ return new Promise((resolve25, reject) => {
15751
15799
  this.pending.set(batchId, {
15752
- resolve: resolve24,
15800
+ resolve: resolve25,
15753
15801
  reject,
15754
15802
  accumulated: [],
15755
15803
  expectedWorkers: workerCount,
@@ -15780,10 +15828,10 @@ var ParserWorkerPool = class {
15780
15828
  await Promise.allSettled(
15781
15829
  workers.map(
15782
15830
  (w) => Promise.race([
15783
- new Promise((resolve24) => {
15784
- w.once("exit", () => resolve24());
15831
+ new Promise((resolve25) => {
15832
+ w.once("exit", () => resolve25());
15785
15833
  }),
15786
- new Promise((resolve24) => setTimeout(() => resolve24(), 2e3))
15834
+ new Promise((resolve25) => setTimeout(() => resolve25(), 2e3))
15787
15835
  ]).then(() => {
15788
15836
  if (!w.threadId) return;
15789
15837
  return w.terminate().catch(() => {
@@ -15844,7 +15892,7 @@ function shouldUseParserWorkerPool(candidateFileCount, parseBatchCount) {
15844
15892
  return !isFrugalPerf() && candidateFileCount >= WORKER_POOL_THRESHOLD && parseBatchCount > 1;
15845
15893
  }
15846
15894
  function yieldEventLoop() {
15847
- return new Promise((resolve24) => setImmediate(resolve24));
15895
+ return new Promise((resolve25) => setImmediate(resolve25));
15848
15896
  }
15849
15897
  function throwIfAborted(signal) {
15850
15898
  if (!signal?.aborted) return;
@@ -15876,7 +15924,7 @@ function normalizeComparablePath(value) {
15876
15924
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
15877
15925
  }
15878
15926
  function gitOutput(projectRoot, args) {
15879
- return new Promise((resolve24, reject) => {
15927
+ return new Promise((resolve25, reject) => {
15880
15928
  execFile(
15881
15929
  "git",
15882
15930
  ["-C", projectRoot, ...args],
@@ -15887,7 +15935,7 @@ function gitOutput(projectRoot, args) {
15887
15935
  },
15888
15936
  (error, stdout) => {
15889
15937
  if (error) reject(error);
15890
- else resolve24(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
15938
+ else resolve25(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
15891
15939
  }
15892
15940
  );
15893
15941
  });
@@ -16687,7 +16735,7 @@ function callIndexOp(op, args, opts) {
16687
16735
  opts.signal.reason instanceof Error ? opts.signal.reason : new Error("Indexing cancelled")
16688
16736
  );
16689
16737
  }
16690
- return new Promise((resolve24, reject) => {
16738
+ return new Promise((resolve25, reject) => {
16691
16739
  const id = nextRpcId++;
16692
16740
  const timer = setTimeout(() => {
16693
16741
  pending.delete(id);
@@ -16709,7 +16757,7 @@ function callIndexOp(op, args, opts) {
16709
16757
  pending.set(id, {
16710
16758
  resolve: (v) => {
16711
16759
  cleanup();
16712
- resolve24(v);
16760
+ resolve25(v);
16713
16761
  },
16714
16762
  reject: (e) => {
16715
16763
  cleanup();
@@ -20279,7 +20327,7 @@ function findGitDir(cwd) {
20279
20327
  return null;
20280
20328
  }
20281
20329
  function runGit(args, cwd, signal) {
20282
- return new Promise((resolve24) => {
20330
+ return new Promise((resolve25) => {
20283
20331
  let stdout = "";
20284
20332
  let stderr = "";
20285
20333
  const child = spawn7("git", args, {
@@ -20295,8 +20343,8 @@ function runGit(args, cwd, signal) {
20295
20343
  child.stderr?.on("data", (c) => {
20296
20344
  stderr += c.toString();
20297
20345
  });
20298
- child.on("close", (code) => resolve24({ stdout, stderr, exitCode: code ?? 0 }));
20299
- child.on("error", (e) => resolve24({ stdout: "", stderr: e.message, exitCode: 1 }));
20346
+ child.on("close", (code) => resolve25({ stdout, stderr, exitCode: code ?? 0 }));
20347
+ child.on("error", (e) => resolve25({ stdout: "", stderr: e.message, exitCode: 1 }));
20300
20348
  });
20301
20349
  }
20302
20350
  async function fileDiff(input, ctx, _signal) {
@@ -22611,7 +22659,7 @@ var execTool = {
22611
22659
  }
22612
22660
  };
22613
22661
  function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
22614
- return new Promise((resolve24) => {
22662
+ return new Promise((resolve25) => {
22615
22663
  let stdout = "";
22616
22664
  let stderr = "";
22617
22665
  let killed = false;
@@ -22619,7 +22667,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
22619
22667
  const finish = (result) => {
22620
22668
  if (resolvedOnce.value) return;
22621
22669
  resolvedOnce.value = true;
22622
- resolve24(result);
22670
+ resolve25(result);
22623
22671
  };
22624
22672
  const startedAt = Date.now();
22625
22673
  let stdoutBytes = 0;
@@ -23295,10 +23343,10 @@ function parseFormatterCounts(fixer, output) {
23295
23343
  };
23296
23344
  }
23297
23345
  async function detectFixer(cwd) {
23298
- const fs44 = await import("node:fs/promises");
23346
+ const fs45 = await import("node:fs/promises");
23299
23347
  const exists = async (file) => {
23300
23348
  try {
23301
- await fs44.stat(`${cwd}/${file}`);
23349
+ await fs45.stat(`${cwd}/${file}`);
23302
23350
  return true;
23303
23351
  } catch {
23304
23352
  return false;
@@ -23321,7 +23369,7 @@ async function detectFixer(cwd) {
23321
23369
  if (await exists(cfg)) return "prettier";
23322
23370
  }
23323
23371
  try {
23324
- const raw = await fs44.readFile(`${cwd}/package.json`, "utf8");
23372
+ const raw = await fs45.readFile(`${cwd}/package.json`, "utf8");
23325
23373
  const pkg = JSON.parse(raw);
23326
23374
  if (pkg["prettier"] !== void 0) return "prettier";
23327
23375
  } catch {
@@ -23589,7 +23637,7 @@ function buildArgs(input) {
23589
23637
  }
23590
23638
  }
23591
23639
  function runGit2(args, cwd, signal) {
23592
- return new Promise((resolve24) => {
23640
+ return new Promise((resolve25) => {
23593
23641
  let stdout = "";
23594
23642
  let stderr = "";
23595
23643
  const child = spawn9("git", args, {
@@ -23610,7 +23658,7 @@ function runGit2(args, cwd, signal) {
23610
23658
  }
23611
23659
  });
23612
23660
  child.on("error", (err) => {
23613
- resolve24({
23661
+ resolve25({
23614
23662
  command: args[0],
23615
23663
  stdout: normalizeCommandOutput(stdout),
23616
23664
  stderr: err.message,
@@ -23619,7 +23667,7 @@ function runGit2(args, cwd, signal) {
23619
23667
  });
23620
23668
  });
23621
23669
  child.on("close", (code) => {
23622
- resolve24({
23670
+ resolve25({
23623
23671
  command: args[0],
23624
23672
  stdout: normalizeCommandOutput(stdout),
23625
23673
  stderr: normalizeCommandOutput(stderr),
@@ -24032,7 +24080,7 @@ var grepTool = {
24032
24080
  };
24033
24081
  var rgAvailabilityCache;
24034
24082
  function detectRg() {
24035
- rgAvailabilityCache ??= new Promise((resolve24) => {
24083
+ rgAvailabilityCache ??= new Promise((resolve25) => {
24036
24084
  try {
24037
24085
  const p = spawn10("rg", ["--version"], {
24038
24086
  env: buildChildEnv5(),
@@ -24040,10 +24088,10 @@ function detectRg() {
24040
24088
  signal: AbortSignal.timeout(1e4),
24041
24089
  windowsHide: true
24042
24090
  });
24043
- p.on("error", () => resolve24(false));
24044
- p.on("close", (code) => resolve24(code === 0));
24091
+ p.on("error", () => resolve25(false));
24092
+ p.on("close", (code) => resolve25(code === 0));
24045
24093
  } catch {
24046
- resolve24(false);
24094
+ resolve25(false);
24047
24095
  }
24048
24096
  });
24049
24097
  return rgAvailabilityCache;
@@ -25085,60 +25133,60 @@ function jmespathSearch(data, query) {
25085
25133
  }
25086
25134
  function validateJsonSchema(data, schema) {
25087
25135
  const errors = [];
25088
- function check(value, s, path50) {
25136
+ function check(value, s, path51) {
25089
25137
  if (s["type"]) {
25090
25138
  const expectedType = s["type"];
25091
25139
  const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
25092
25140
  if (expectedType === "integer") {
25093
- if (!Number.isInteger(value)) errors.push(`${path50}: expected integer, got ${actualType}`);
25141
+ if (!Number.isInteger(value)) errors.push(`${path51}: expected integer, got ${actualType}`);
25094
25142
  } else if (expectedType !== actualType) {
25095
- errors.push(`${path50}: expected ${expectedType}, got ${actualType}`);
25143
+ errors.push(`${path51}: expected ${expectedType}, got ${actualType}`);
25096
25144
  }
25097
25145
  }
25098
25146
  if (typeof value === "string" && s["format"] === "uri" && value) {
25099
25147
  try {
25100
25148
  new URL(value);
25101
25149
  } catch {
25102
- errors.push(`${path50}: not a valid URI`);
25150
+ errors.push(`${path51}: not a valid URI`);
25103
25151
  }
25104
25152
  }
25105
25153
  if (typeof value === "string" && s["pattern"]) {
25106
25154
  const compiled = compileUserRegex(s["pattern"], "");
25107
25155
  if (!compiled.ok) {
25108
- errors.push(`${path50}: invalid schema pattern \u2014 ${compiled.reason}`);
25156
+ errors.push(`${path51}: invalid schema pattern \u2014 ${compiled.reason}`);
25109
25157
  } else if (!compiled.regex.test(capSubject(value))) {
25110
- errors.push(`${path50}: does not match pattern ${s["pattern"]}`);
25158
+ errors.push(`${path51}: does not match pattern ${s["pattern"]}`);
25111
25159
  }
25112
25160
  }
25113
25161
  if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
25114
- errors.push(`${path50}: string too short (min ${s["minLength"]})`);
25162
+ errors.push(`${path51}: string too short (min ${s["minLength"]})`);
25115
25163
  }
25116
25164
  if (typeof value === "string" && s["maxLength"] !== void 0 && value.length > s["maxLength"]) {
25117
- errors.push(`${path50}: string too long (max ${s["maxLength"]})`);
25165
+ errors.push(`${path51}: string too long (max ${s["maxLength"]})`);
25118
25166
  }
25119
25167
  if (typeof value === "number" && s["minimum"] !== void 0 && value < s["minimum"]) {
25120
- errors.push(`${path50}: below minimum ${s["minimum"]}`);
25168
+ errors.push(`${path51}: below minimum ${s["minimum"]}`);
25121
25169
  }
25122
25170
  if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
25123
- errors.push(`${path50}: above maximum ${s["maximum"]}`);
25171
+ errors.push(`${path51}: above maximum ${s["maximum"]}`);
25124
25172
  }
25125
25173
  if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
25126
25174
  for (let i = 0; i < value.length; i++) {
25127
- check(value[i], s["items"], `${path50}[${i}]`);
25175
+ check(value[i], s["items"], `${path51}[${i}]`);
25128
25176
  }
25129
25177
  }
25130
25178
  if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
25131
25179
  const props = s["properties"];
25132
25180
  for (const [k, propSchema] of Object.entries(props)) {
25133
- check(value[k], propSchema, `${path50}.${k}`);
25181
+ check(value[k], propSchema, `${path51}.${k}`);
25134
25182
  }
25135
25183
  }
25136
25184
  }
25137
25185
  check(data, schema, "$");
25138
25186
  return { valid: errors.length === 0, errors };
25139
25187
  }
25140
- function simpleQuery(data, path50) {
25141
- const parts = path50.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
25188
+ function simpleQuery(data, path51) {
25189
+ const parts = path51.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
25142
25190
  let current = data;
25143
25191
  for (const part of parts) {
25144
25192
  if (current === null || current === void 0) return void 0;
@@ -25543,7 +25591,7 @@ function broadcastTodoUpdate(context, todos) {
25543
25591
  const mailbox = getSharedProjectMailbox(projectDir);
25544
25592
  void mailbox.send({
25545
25593
  from: context.agentId,
25546
- to: "*",
25594
+ to: `@session:${sessionId}`,
25547
25595
  type: "status",
25548
25596
  subject: `Kanban todo list updated (${todos.length} item${todos.length === 1 ? "" : "s"})`,
25549
25597
  body: JSON.stringify({
@@ -25601,13 +25649,17 @@ function applySessionKanbanBoardToTodos(context, board, options) {
25601
25649
  broadcastTodoUpdate(context, context.todos);
25602
25650
  return [...context.todos];
25603
25651
  }
25604
- function applyManagedKanbanBoardToTodos(context, board, suppressedTodoMirrors2) {
25652
+ function applyManagedKanbanBoardToTodos(context, board, suppressedTodoMirrors2, options = {}) {
25605
25653
  const metaKanban = context.meta["kanban"];
25606
25654
  const metaBoardId = metaKanban && typeof metaKanban === "object" ? metaKanban["boardId"] : void 0;
25607
25655
  const activeBoardId2 = context.currentKanbanBoardId ?? (typeof metaBoardId === "string" ? metaBoardId : void 0);
25608
25656
  if (!activeBoardId2 || board.id !== activeBoardId2 || board.lifecycle?.mode !== "managed") {
25609
25657
  return [...context.todos];
25610
25658
  }
25659
+ const ownerSessionId = options.sessionOwnerFromTags?.(board.tags);
25660
+ if (ownerSessionId && ownerSessionId !== (context.session?.id ?? "")) {
25661
+ return [...context.todos];
25662
+ }
25611
25663
  const projectedTodos = orderTasksForTodos(
25612
25664
  board,
25613
25665
  board.tasks.filter(
@@ -26274,7 +26326,9 @@ function applySessionKanbanBoardToTodos2(context, board) {
26274
26326
  });
26275
26327
  }
26276
26328
  function applyManagedKanbanBoardToTodos2(context, board) {
26277
- return applyManagedKanbanBoardToTodos(context, board, suppressedTodoMirrors);
26329
+ return applyManagedKanbanBoardToTodos(context, board, suppressedTodoMirrors, {
26330
+ sessionOwnerFromTags: sessionIdFromTags
26331
+ });
26278
26332
  }
26279
26333
  function applySessionKanbanTaskToSource2(context, task, options = {}) {
26280
26334
  return applySessionKanbanTaskToSource(context, task, suppressedTodoMirrors, options);
@@ -28250,7 +28304,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
28250
28304
  };
28251
28305
  }
28252
28306
  args.push("--timestamps", service);
28253
- return new Promise((resolve24) => {
28307
+ return new Promise((resolve25) => {
28254
28308
  let stdout = "";
28255
28309
  let stderr = "";
28256
28310
  const MAX = 2e5;
@@ -28266,7 +28320,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
28266
28320
  if (settled) return;
28267
28321
  settled = true;
28268
28322
  clearTimeout(timer);
28269
- resolve24(result);
28323
+ resolve25(result);
28270
28324
  };
28271
28325
  const child = spawn11("docker", args, {
28272
28326
  cwd,
@@ -28311,7 +28365,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
28311
28365
  }
28312
28366
  var DOCKER_LOGS_TIMEOUT_MS = 3e3;
28313
28367
  var MAX_TAIL_LINES = 1e5;
28314
- async function fileLogs(path50, lines, filterRe) {
28368
+ async function fileLogs(path51, lines, filterRe) {
28315
28369
  const { createInterface } = await import("node:readline");
28316
28370
  const { createReadStream: createReadStream2 } = await import("node:fs");
28317
28371
  const entries = [];
@@ -28320,7 +28374,7 @@ async function fileLogs(path50, lines, filterRe) {
28320
28374
  let writeIdx = 0;
28321
28375
  let totalLines = 0;
28322
28376
  const rl = createInterface({
28323
- input: createReadStream2(path50),
28377
+ input: createReadStream2(path51),
28324
28378
  crlfDelay: Number.POSITIVE_INFINITY
28325
28379
  });
28326
28380
  for await (const line of rl) {
@@ -28341,7 +28395,7 @@ async function fileLogs(path50, lines, filterRe) {
28341
28395
  if (parsed) entries.push(parsed);
28342
28396
  }
28343
28397
  return {
28344
- source: path50,
28398
+ source: path51,
28345
28399
  entries,
28346
28400
  total: entries.length,
28347
28401
  truncated: totalLines > effLines,
@@ -28483,7 +28537,7 @@ var outdatedTool = {
28483
28537
  }
28484
28538
  };
28485
28539
  function runOutdated(manager, args, cwd, signal) {
28486
- return new Promise((resolve24) => {
28540
+ return new Promise((resolve25) => {
28487
28541
  let stdout = "";
28488
28542
  let stderr = "";
28489
28543
  const MAX = 1e5;
@@ -28508,10 +28562,10 @@ function runOutdated(manager, args, cwd, signal) {
28508
28562
  });
28509
28563
  child.on("close", (code) => {
28510
28564
  const result = parseOutdatedOutput(stdout, code ?? 0);
28511
- resolve24(result);
28565
+ resolve25(result);
28512
28566
  });
28513
28567
  child.on("error", (e) => {
28514
- resolve24({
28568
+ resolve25({
28515
28569
  exit_code: 1,
28516
28570
  packages: [],
28517
28571
  total: 0,
@@ -28830,7 +28884,7 @@ function runPatch(args, cwd, signal, fallback) {
28830
28884
  });
28831
28885
  }
28832
28886
  function runPatchProcess(command, args, cwd, signal) {
28833
- return new Promise((resolve24) => {
28887
+ return new Promise((resolve25) => {
28834
28888
  let stdout = "";
28835
28889
  let stderr = "";
28836
28890
  const env = { ...buildChildEnv8(), LANG: "C", LC_ALL: "C" };
@@ -28849,11 +28903,11 @@ function runPatchProcess(command, args, cwd, signal) {
28849
28903
  });
28850
28904
  child.on(
28851
28905
  "close",
28852
- (code) => resolve24({ exitCode: code ?? 1, stdout, stderr, unavailable: false })
28906
+ (code) => resolve25({ exitCode: code ?? 1, stdout, stderr, unavailable: false })
28853
28907
  );
28854
28908
  child.on(
28855
28909
  "error",
28856
- (e) => resolve24({
28910
+ (e) => resolve25({
28857
28911
  exitCode: 1,
28858
28912
  stdout: "",
28859
28913
  stderr: e.message,
@@ -29642,9 +29696,428 @@ function mkResult(plan, ok, message, todos) {
29642
29696
  return result;
29643
29697
  }
29644
29698
 
29699
+ // src/pwsh.ts
29700
+ import { spawn as spawn14 } from "node:child_process";
29701
+ import * as fs36 from "node:fs";
29702
+ import * as os10 from "node:os";
29703
+ import * as path43 from "node:path";
29704
+ import { StringDecoder as StringDecoder3 } from "node:string_decoder";
29705
+ import {
29706
+ emitProcessCompleted as emitProcessCompleted4,
29707
+ emitProcessOutput as emitProcessOutput4,
29708
+ emitProcessStarted as emitProcessStarted4
29709
+ } from "@wrongstack/core/observability";
29710
+ init_output_spool();
29711
+ init_util();
29712
+ init_process_registry();
29713
+ init_win32_resolve();
29714
+ var MAX_OUTPUT4 = 32768;
29715
+ var DEFAULT_TIMEOUT_MS4 = 3e5;
29716
+ var STREAM_FLUSH_INTERVAL_MS2 = 200;
29717
+ var STREAM_FLUSH_BYTES2 = 4 * 1024;
29718
+ var MAX_QUEUE_CHUNKS2 = 500;
29719
+ 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`.";
29720
+ 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`.";
29721
+ function wrapPwshCommand(command) {
29722
+ const bootstrap = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;$ProgressPreference = 'SilentlyContinue';$ConfirmPreference = 'None';$WhatIfPreference = $false";
29723
+ return `${bootstrap}
29724
+ $ErrorActionPreference = 'Stop'
29725
+ ${command}
29726
+ if ($LASTEXITCODE -is [int]) { exit $LASTEXITCODE }`;
29727
+ }
29728
+ var pwshTool = {
29729
+ name: "pwsh",
29730
+ category: "Shell",
29731
+ description: PWSH_TOOL_DESCRIPTION,
29732
+ usageHint: PWSH_TOOL_USAGE_HINT,
29733
+ selection: {
29734
+ doNotUseWhen: "the command is an allowlisted single binary (node, git, pnpm, tsc) needing no shell expansion or pipelines.",
29735
+ useInstead: ["exec"]
29736
+ },
29737
+ permission: "confirm",
29738
+ mutating: true,
29739
+ riskTier: "destructive",
29740
+ icon: "terminal",
29741
+ subjectKey: "command",
29742
+ capabilities: ["shell.arbitrary"],
29743
+ timeoutMs: 61e4,
29744
+ maxOutputBytes: MAX_OUTPUT4,
29745
+ estimatedDurationMs: 3e4,
29746
+ inputSchema: {
29747
+ type: "object",
29748
+ properties: {
29749
+ command: {
29750
+ type: "string",
29751
+ description: "The exact PowerShell command or script block to run."
29752
+ },
29753
+ workdir: {
29754
+ type: "string",
29755
+ description: "Absolute or project-relative path to the working directory for this command. Defaults to session working directory."
29756
+ },
29757
+ timeout_ms: {
29758
+ type: "integer",
29759
+ description: "Optional timeout for this specific command in milliseconds (default 300000, max 600000)."
29760
+ },
29761
+ run_in_background: {
29762
+ type: "boolean",
29763
+ description: "If true, launch the process in the background and return the job ID / PID immediately."
29764
+ },
29765
+ background: {
29766
+ type: "boolean",
29767
+ description: "Alias for run_in_background."
29768
+ },
29769
+ sandbox_permissions: {
29770
+ type: "string",
29771
+ description: "Escalation mode if retrying a sandbox-denied command (e.g., workspace-write)."
29772
+ },
29773
+ justification: {
29774
+ type: "string",
29775
+ description: "One-sentence justification when retrying a denied command with sandbox_permissions."
29776
+ }
29777
+ },
29778
+ required: ["command"]
29779
+ },
29780
+ async execute(input, ctx, opts) {
29781
+ let final;
29782
+ const executeStream = pwshTool.executeStream;
29783
+ if (!executeStream) throw new Error("pwshTool: stream execution unavailable");
29784
+ for await (const ev of executeStream(input, ctx, opts)) {
29785
+ if (ev.type === "final") final = ev.output;
29786
+ }
29787
+ if (!final) throw new Error("pwsh: stream ended without final event");
29788
+ return final;
29789
+ },
29790
+ async *executeStream(input, ctx, opts) {
29791
+ if (!input?.command) throw new Error("pwsh: command is required");
29792
+ const isBackground = !!(input.run_in_background || input.background);
29793
+ const registry = getProcessRegistry();
29794
+ if (!registry.beforeCall(isBackground)) {
29795
+ yield {
29796
+ type: "final",
29797
+ output: {
29798
+ output: "",
29799
+ exit_code: 1,
29800
+ timed_out: false,
29801
+ pid: null,
29802
+ error: "pwsh: circuit breaker open \u2014 too many consecutive failures or slow calls. Use /kill to inspect or /kill reset to recover."
29803
+ }
29804
+ };
29805
+ return;
29806
+ }
29807
+ const killCheck = await checkAndBlockKillCommand(input.command);
29808
+ if (killCheck.blocked) {
29809
+ yield {
29810
+ type: "final",
29811
+ output: {
29812
+ output: "",
29813
+ exit_code: 1,
29814
+ timed_out: false,
29815
+ pid: null,
29816
+ error: `pwsh: ${killCheck.reason}`
29817
+ }
29818
+ };
29819
+ return;
29820
+ }
29821
+ const isWin5 = os10.platform() === "win32";
29822
+ const bin = isWin5 ? resolvePowerShell("pwsh.exe") : "pwsh";
29823
+ const args = shellArgs("pwsh");
29824
+ const stdinBody = wrapPwshCommand(input.command);
29825
+ const env = buildChildEnv2(ctx.session?.id);
29826
+ let targetCwd = ctx.workingDir ?? ctx.projectRoot;
29827
+ if (input.workdir) {
29828
+ const resolved = path43.isAbsolute(input.workdir) ? input.workdir : path43.resolve(ctx.projectRoot, input.workdir);
29829
+ if (fs36.existsSync(resolved)) {
29830
+ targetCwd = resolved;
29831
+ }
29832
+ }
29833
+ const startedAt = Date.now();
29834
+ const detached = !isWin5;
29835
+ if (isBackground) {
29836
+ const child2 = spawn14(bin, args, {
29837
+ cwd: targetCwd,
29838
+ env,
29839
+ detached,
29840
+ stdio: ["pipe", "ignore", "ignore"],
29841
+ windowsHide: true
29842
+ });
29843
+ if (child2.stdin) {
29844
+ child2.stdin.write(stdinBody);
29845
+ child2.stdin.end();
29846
+ }
29847
+ const pid2 = child2.pid;
29848
+ if (typeof pid2 === "number") {
29849
+ registry.register({
29850
+ pid: pid2,
29851
+ name: "pwsh",
29852
+ command: redactCommand(input.command),
29853
+ startedAt: Date.now(),
29854
+ sessionId: ctx.session?.id,
29855
+ child: child2,
29856
+ processGroupLeader: detached && child2.pid === pid2,
29857
+ background: true
29858
+ });
29859
+ child2.on("close", () => registry.unregister(pid2));
29860
+ child2.on("error", () => {
29861
+ registry.unregister(pid2);
29862
+ registry.afterCall(Date.now() - startedAt, true, isBackground);
29863
+ });
29864
+ child2.unref();
29865
+ const jobId = `job_${pid2}`;
29866
+ ctx.recordSideEffect?.({
29867
+ toolUseId: `pwsh-bg-${Date.now()}`,
29868
+ toolName: "pwsh",
29869
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
29870
+ input: { command: redactCommand(input.command), background: true },
29871
+ outcome: `started background job ${jobId} (PID: ${pid2})`,
29872
+ risk: "shell"
29873
+ });
29874
+ yield {
29875
+ type: "final",
29876
+ output: {
29877
+ output: `Background job started: ${jobId} (PID: ${pid2})
29878
+ Working directory: ${targetCwd}`,
29879
+ exit_code: null,
29880
+ timed_out: false,
29881
+ pid: pid2,
29882
+ job_id: jobId
29883
+ }
29884
+ };
29885
+ return;
29886
+ }
29887
+ yield {
29888
+ type: "final",
29889
+ output: {
29890
+ output: "",
29891
+ exit_code: 1,
29892
+ timed_out: false,
29893
+ pid: null,
29894
+ error: "pwsh: failed to obtain PID for background process"
29895
+ }
29896
+ };
29897
+ return;
29898
+ }
29899
+ const spool = createOutputSpool({ tool: "pwsh", thresholdBytes: MAX_OUTPUT4 });
29900
+ const child = spawn14(bin, args, {
29901
+ cwd: targetCwd,
29902
+ env,
29903
+ detached,
29904
+ stdio: ["pipe", "pipe", "pipe"],
29905
+ windowsHide: true
29906
+ });
29907
+ const pid = child.pid;
29908
+ let stdoutBytes = 0;
29909
+ let stderrBytes = 0;
29910
+ let telemetryCompleted = false;
29911
+ emitProcessStarted4({
29912
+ ...pid !== void 0 ? { pid } : {},
29913
+ parentPid: process.pid,
29914
+ command: redactCommand(`${bin} ${args.join(" ")}`),
29915
+ args: redactCommand(args.join(" ")).split(" ").filter(Boolean),
29916
+ cwd: targetCwd,
29917
+ background: false,
29918
+ startedAt: new Date(startedAt).toISOString()
29919
+ });
29920
+ const completeForeground = (exitCode, sig) => {
29921
+ if (telemetryCompleted) return;
29922
+ telemetryCompleted = true;
29923
+ emitProcessCompleted4({
29924
+ ...pid !== void 0 ? { pid } : {},
29925
+ exitCode,
29926
+ ...sig ? { signal: sig } : {},
29927
+ durationMs: Date.now() - startedAt,
29928
+ stdoutBytes,
29929
+ stderrBytes,
29930
+ timedOut,
29931
+ endedAt: (/* @__PURE__ */ new Date()).toISOString()
29932
+ });
29933
+ };
29934
+ if (typeof pid === "number") {
29935
+ registry.register({
29936
+ pid,
29937
+ name: "pwsh",
29938
+ command: redactCommand(input.command),
29939
+ startedAt: Date.now(),
29940
+ sessionId: ctx.session?.id,
29941
+ child,
29942
+ processGroupLeader: detached && child.pid === pid
29943
+ });
29944
+ }
29945
+ if (child.stdin) {
29946
+ child.stdin.write(stdinBody);
29947
+ child.stdin.end();
29948
+ }
29949
+ const timeoutMs = Math.min(
29950
+ Math.max(1e3, input.timeout_ms ?? DEFAULT_TIMEOUT_MS4),
29951
+ 6e5
29952
+ );
29953
+ let timedOut = false;
29954
+ const timers = [];
29955
+ const killWithTimeout = (timeout) => {
29956
+ if (isWin5) {
29957
+ if (typeof child.pid === "number" && child.exitCode === null) {
29958
+ const attempted = registry.kill(child.pid, { force: true, graceMs: timeout });
29959
+ if (!attempted) {
29960
+ try {
29961
+ child.kill();
29962
+ } catch {
29963
+ }
29964
+ }
29965
+ }
29966
+ return;
29967
+ }
29968
+ if (typeof child.pid === "number") {
29969
+ registry.kill(child.pid, { graceMs: timeout });
29970
+ } else {
29971
+ try {
29972
+ child.kill("SIGTERM");
29973
+ } catch {
29974
+ }
29975
+ }
29976
+ };
29977
+ const timer = setTimeout(() => {
29978
+ timedOut = true;
29979
+ killWithTimeout(2e3);
29980
+ }, timeoutMs);
29981
+ timers.push(timer);
29982
+ const onAbort = () => killWithTimeout(2e3);
29983
+ if (opts.signal.aborted) onAbort();
29984
+ else opts.signal.addEventListener("abort", onAbort, { once: true });
29985
+ const queue = [];
29986
+ let resolveNext = null;
29987
+ const push = (c) => {
29988
+ if (resolveNext) {
29989
+ const r = resolveNext;
29990
+ resolveNext = null;
29991
+ r(c);
29992
+ } else {
29993
+ queue.push(c);
29994
+ }
29995
+ };
29996
+ const next = () => new Promise((resolve25) => {
29997
+ const c = queue.shift();
29998
+ if (c) resolve25(c);
29999
+ else resolveNext = resolve25;
30000
+ });
30001
+ let buf = "";
30002
+ let pending2 = "";
30003
+ let lastFlush = Date.now();
30004
+ const flush = () => {
30005
+ if (pending2.length === 0) return null;
30006
+ const text = pending2;
30007
+ pending2 = "";
30008
+ lastFlush = Date.now();
30009
+ return text;
30010
+ };
30011
+ let paused = false;
30012
+ const pauseIfFlooded = () => {
30013
+ if (!paused && queue.length >= MAX_QUEUE_CHUNKS2) {
30014
+ paused = true;
30015
+ child.stdout?.pause();
30016
+ child.stderr?.pause();
30017
+ }
30018
+ };
30019
+ const resumeIfDrained = () => {
30020
+ if (paused && queue.length < MAX_QUEUE_CHUNKS2) {
30021
+ paused = false;
30022
+ child.stdout?.resume();
30023
+ child.stderr?.resume();
30024
+ }
30025
+ };
30026
+ const stdoutDecoder = new StringDecoder3("utf8");
30027
+ const stderrDecoder = new StringDecoder3("utf8");
30028
+ const onData = (chunk, stream) => {
30029
+ const text = (stream === "stdout" ? stdoutDecoder : stderrDecoder).write(chunk);
30030
+ if (stream === "stdout") stdoutBytes += chunk.byteLength;
30031
+ else stderrBytes += chunk.byteLength;
30032
+ emitProcessOutput4({ pid: pid ?? 0, stream, chunk });
30033
+ if (buf.length < MAX_OUTPUT4) {
30034
+ buf += text.slice(0, MAX_OUTPUT4 - buf.length);
30035
+ }
30036
+ spool.write(text);
30037
+ pending2 += text;
30038
+ push({ kind: "data", text });
30039
+ pauseIfFlooded();
30040
+ };
30041
+ child.stdout?.on("data", (chunk) => onData(chunk, "stdout"));
30042
+ child.stderr?.on("data", (chunk) => onData(chunk, "stderr"));
30043
+ child.on("error", (err) => {
30044
+ for (const t of timers) clearTimeout(t);
30045
+ registry.afterCall(Date.now() - startedAt, true);
30046
+ completeForeground(1);
30047
+ push({ kind: "error", err });
30048
+ });
30049
+ child.on("close", (code, signal) => {
30050
+ for (const t of timers) clearTimeout(t);
30051
+ if (typeof pid === "number") registry.unregister(pid);
30052
+ registry.afterCall(Date.now() - startedAt, code !== 0 && code !== null);
30053
+ completeForeground(timedOut ? 124 : code ?? (signal ? 1 : 0), signal ?? void 0);
30054
+ const tail = stdoutDecoder.end() + stderrDecoder.end();
30055
+ if (tail) {
30056
+ if (buf.length < MAX_OUTPUT4) buf += tail.slice(0, MAX_OUTPUT4 - buf.length);
30057
+ spool.write(tail);
30058
+ pending2 += tail;
30059
+ }
30060
+ push({ kind: "end", code });
30061
+ });
30062
+ try {
30063
+ while (true) {
30064
+ const c = await next();
30065
+ resumeIfDrained();
30066
+ if (c.kind === "error") throw c.err;
30067
+ if (c.kind === "end") {
30068
+ const remainder = flush();
30069
+ if (remainder !== null) {
30070
+ yield { type: "partial_output", text: remainder };
30071
+ }
30072
+ const spooled = spool.finalize();
30073
+ let formattedOutput = normalizeCommandOutput(buf);
30074
+ if (c.code !== null && c.code !== 0 && !timedOut) {
30075
+ if (!formattedOutput.includes(`[exit code: ${c.code}]`)) {
30076
+ formattedOutput = formattedOutput ? `${formattedOutput}
30077
+ [exit code: ${c.code}]` : `[exit code: ${c.code}]`;
30078
+ }
30079
+ }
30080
+ const hint = !timedOut && typeof c.code === "number" && c.code !== 0 ? diagnoseBashism(input.command, "pwsh") : void 0;
30081
+ const danger = detectDanger("pwsh", [input.command]);
30082
+ const cautionText = danger.level === "caution" && danger.reasons.length > 0 ? `
30083
+ [caution: ${danger.reasons.join("; ")}]` : "";
30084
+ const finalResultText = formattedOutput + (spooled ? spoolNote(spooled) : "") + (hint ? `
30085
+
30086
+ ${hint}` : "") + cautionText;
30087
+ ctx.recordSideEffect?.({
30088
+ toolUseId: `pwsh-${Date.now()}`,
30089
+ toolName: "pwsh",
30090
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
30091
+ input: { command: redactCommand(input.command) },
30092
+ outcome: timedOut ? `timed out (exit ${c.code})` : `exit ${c.code}`,
30093
+ risk: "shell"
30094
+ });
30095
+ yield {
30096
+ type: "final",
30097
+ output: {
30098
+ output: finalResultText,
30099
+ exit_code: timedOut ? 1 : c.code,
30100
+ timed_out: timedOut,
30101
+ pid
30102
+ }
30103
+ };
30104
+ return;
30105
+ }
30106
+ const now2 = Date.now();
30107
+ if (pending2.length >= STREAM_FLUSH_BYTES2 || now2 - lastFlush >= STREAM_FLUSH_INTERVAL_MS2) {
30108
+ const text = flush();
30109
+ if (text) yield { type: "partial_output", text };
30110
+ }
30111
+ }
30112
+ } finally {
30113
+ for (const t of timers) clearTimeout(t);
30114
+ }
30115
+ }
30116
+ };
30117
+
29645
30118
  // src/read.ts
29646
30119
  init_util();
29647
- import * as fs36 from "node:fs/promises";
30120
+ import * as fs37 from "node:fs/promises";
29648
30121
  import { FsError, ToolValidationError as ToolValidationError8 } from "@wrongstack/core/types";
29649
30122
  import { toErrorMessage as toErrorMessage15 } from "@wrongstack/core/utils";
29650
30123
  var ADVANCED_MODE_META_KEY = "tools.read.advancedMode";
@@ -29704,7 +30177,7 @@ var readTool = {
29704
30177
  const shouldIncludeSymbols = input.includeSymbols === true || input.includeSymbols !== false && ctx.meta[ADVANCED_MODE_META_KEY] === true;
29705
30178
  let stat22;
29706
30179
  try {
29707
- stat22 = await fs36.stat(absPath);
30180
+ stat22 = await fs37.stat(absPath);
29708
30181
  } catch (err) {
29709
30182
  const code = err.code;
29710
30183
  if (code === "ENOENT") {
@@ -29756,7 +30229,7 @@ var readTool = {
29756
30229
  ...symResult2?.symbols ? { symbols: symResult2.symbols } : {}
29757
30230
  };
29758
30231
  }
29759
- const buf = await fs36.readFile(absPath);
30232
+ const buf = await fs37.readFile(absPath);
29760
30233
  if (isBinaryBuffer(buf)) {
29761
30234
  throw new FsError({
29762
30235
  message: `read: "${input.path}" appears to be binary`,
@@ -29922,9 +30395,9 @@ ${interesting.join("\n")}` : "symbols/imports: (none detected)"
29922
30395
  }
29923
30396
 
29924
30397
  // src/replace.ts
29925
- import { spawn as spawn14 } from "node:child_process";
29926
- import * as fs37 from "node:fs/promises";
29927
- import * as path43 from "node:path";
30398
+ import { spawn as spawn15 } from "node:child_process";
30399
+ import * as fs38 from "node:fs/promises";
30400
+ import * as path44 from "node:path";
29928
30401
  import { ToolValidationError as ToolValidationError9 } from "@wrongstack/core/types";
29929
30402
  import {
29930
30403
  atomicWrite as atomicWrite4,
@@ -30007,14 +30480,14 @@ var replaceTool = {
30007
30480
  const dryRun = input.dry_run ?? true;
30008
30481
  const filesInput = Array.isArray(input.files) ? input.files.join(",") : input.files;
30009
30482
  const fileList = await resolveFiles2(filesInput, ctx, globRe);
30010
- const realRoot = await fs37.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
30483
+ const realRoot = await fs38.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
30011
30484
  const results = [];
30012
30485
  let totalReplacements = 0;
30013
30486
  let diffBytesUsed = 0;
30014
30487
  let diffsOmitted = 0;
30015
30488
  let diffsTruncated = 0;
30016
30489
  for (const absPath of fileList) {
30017
- const lstat2 = await fs37.lstat(absPath).catch((err) => {
30490
+ const lstat2 = await fs38.lstat(absPath).catch((err) => {
30018
30491
  if (err.code === "ENOENT") return null;
30019
30492
  throw err;
30020
30493
  });
@@ -30022,17 +30495,17 @@ var replaceTool = {
30022
30495
  if (lstat2.isSymbolicLink()) continue;
30023
30496
  let realPath;
30024
30497
  try {
30025
- realPath = await fs37.realpath(absPath);
30498
+ realPath = await fs38.realpath(absPath);
30026
30499
  } catch {
30027
30500
  continue;
30028
30501
  }
30029
- const rel = path43.relative(realRoot, realPath);
30030
- if (rel.startsWith("..") || path43.isAbsolute(rel)) continue;
30031
- const stat22 = await fs37.stat(realPath).catch(() => null);
30502
+ const rel = path44.relative(realRoot, realPath);
30503
+ if (rel.startsWith("..") || path44.isAbsolute(rel)) continue;
30504
+ const stat22 = await fs38.stat(realPath).catch(() => null);
30032
30505
  if (!stat22?.isFile()) continue;
30033
30506
  let content;
30034
30507
  try {
30035
- const buf = await fs37.readFile(realPath);
30508
+ const buf = await fs38.readFile(realPath);
30036
30509
  if (isBinaryBuffer(buf)) continue;
30037
30510
  content = buf.toString("utf8");
30038
30511
  } catch {
@@ -30055,7 +30528,7 @@ var replaceTool = {
30055
30528
  if (!dryRun) {
30056
30529
  const newContent = toStyle2(newContentLf, style);
30057
30530
  await atomicWrite4(realPath, newContent, { mode: stat22.mode & 511 });
30058
- const written = await fs37.stat(realPath).catch(() => null);
30531
+ const written = await fs38.stat(realPath).catch(() => null);
30059
30532
  if (written) {
30060
30533
  ctx.recordRead?.(realPath, written.mtimeMs, "write", sha256hex(newContent));
30061
30534
  }
@@ -30155,8 +30628,8 @@ async function resolveFiles2(filesInput, ctx, extraGlob) {
30155
30628
  const resolved = [];
30156
30629
  for (const p of parts) {
30157
30630
  const absPath = await safeResolveReal(p, ctx);
30158
- if (extraGlob && !passesExtraGlob(extraGlob, path43.basename(absPath), absPath)) continue;
30159
- const stat22 = await fs37.stat(absPath).catch(() => null);
30631
+ if (extraGlob && !passesExtraGlob(extraGlob, path44.basename(absPath), absPath)) continue;
30632
+ const stat22 = await fs38.stat(absPath).catch(() => null);
30160
30633
  if (stat22?.isFile()) {
30161
30634
  resolved.push(absPath);
30162
30635
  }
@@ -30170,7 +30643,7 @@ async function globFiles(pattern, base, extraGlob) {
30170
30643
  const { promise } = spawnRgFind(pattern, base);
30171
30644
  const files = await promise;
30172
30645
  if (extraGlob) {
30173
- return files.filter((f) => passesExtraGlob(extraGlob, path43.basename(f), f));
30646
+ return files.filter((f) => passesExtraGlob(extraGlob, path44.basename(f), f));
30174
30647
  }
30175
30648
  return files;
30176
30649
  } catch {
@@ -30180,24 +30653,24 @@ async function globFiles(pattern, base, extraGlob) {
30180
30653
  }
30181
30654
  var rgAvailabilityCache2;
30182
30655
  function checkRg() {
30183
- rgAvailabilityCache2 ??= new Promise((resolve24) => {
30656
+ rgAvailabilityCache2 ??= new Promise((resolve25) => {
30184
30657
  try {
30185
- const p = spawn14("rg", ["--version"], {
30658
+ const p = spawn15("rg", ["--version"], {
30186
30659
  env: buildChildEnv9(),
30187
30660
  stdio: "ignore",
30188
30661
  windowsHide: true
30189
30662
  });
30190
- p.on("error", () => resolve24(false));
30191
- p.on("close", (code) => resolve24(code === 0));
30663
+ p.on("error", () => resolve25(false));
30664
+ p.on("close", (code) => resolve25(code === 0));
30192
30665
  } catch {
30193
- resolve24(false);
30666
+ resolve25(false);
30194
30667
  }
30195
30668
  });
30196
30669
  return rgAvailabilityCache2;
30197
30670
  }
30198
30671
  function spawnRgFind(pattern, base) {
30199
30672
  const args = ["--files", "--glob", pattern, base];
30200
- const child = spawn14("rg", args, {
30673
+ const child = spawn15("rg", args, {
30201
30674
  signal: AbortSignal.timeout(3e4),
30202
30675
  env: buildChildEnv9(),
30203
30676
  stdio: ["ignore", "pipe", "pipe"],
@@ -30216,10 +30689,10 @@ function spawnRgFind(pattern, base) {
30216
30689
  }
30217
30690
  });
30218
30691
  return {
30219
- promise: new Promise((resolve24, reject) => {
30692
+ promise: new Promise((resolve25, reject) => {
30220
30693
  child.on("error", reject);
30221
30694
  child.on("close", () => {
30222
- resolve24(buf.split("\n").filter(Boolean));
30695
+ resolve25(buf.split("\n").filter(Boolean));
30223
30696
  });
30224
30697
  })
30225
30698
  };
@@ -30230,15 +30703,15 @@ async function globNative(pattern, base, extraGlob) {
30230
30703
  const walk2 = async (dir) => {
30231
30704
  let entries;
30232
30705
  try {
30233
- entries = await fs37.readdir(dir, { withFileTypes: true });
30706
+ entries = await fs38.readdir(dir, { withFileTypes: true });
30234
30707
  } catch {
30235
30708
  return;
30236
30709
  }
30237
30710
  for (const e of entries) {
30238
30711
  if (DEFAULT_IGNORE4.includes(e.name)) continue;
30239
- const full = path43.join(dir, e.name);
30712
+ const full = path44.join(dir, e.name);
30240
30713
  try {
30241
- const stat22 = await fs37.lstat(full);
30714
+ const stat22 = await fs38.lstat(full);
30242
30715
  if (stat22.isSymbolicLink()) continue;
30243
30716
  } catch {
30244
30717
  continue;
@@ -30262,8 +30735,8 @@ async function globNative(pattern, base, extraGlob) {
30262
30735
 
30263
30736
  // src/scaffold.ts
30264
30737
  init_util();
30265
- import * as fs38 from "node:fs/promises";
30266
- import * as path44 from "node:path";
30738
+ import * as fs39 from "node:fs/promises";
30739
+ import * as path45 from "node:path";
30267
30740
  import { atomicWrite as atomicWrite5 } from "@wrongstack/core/utils";
30268
30741
  var BUILT_IN_TEMPLATES = {
30269
30742
  "npm-package": {
@@ -30414,16 +30887,16 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
30414
30887
  let filesCreated = 0;
30415
30888
  for (const [filePath, content] of Object.entries(templateFiles)) {
30416
30889
  const resolvedPath = substituteVars(filePath, name, vars);
30417
- const joinedPath = path44.join(cwd, resolvedPath);
30418
- const root = path44.resolve(ctx.projectRoot);
30419
- const target = path44.resolve(joinedPath);
30420
- const rel = path44.relative(root, target);
30421
- if (rel.startsWith("..") || path44.isAbsolute(rel)) {
30890
+ const joinedPath = path45.join(cwd, resolvedPath);
30891
+ const root = path45.resolve(ctx.projectRoot);
30892
+ const target = path45.resolve(joinedPath);
30893
+ const rel = path45.relative(root, target);
30894
+ if (rel.startsWith("..") || path45.isAbsolute(rel)) {
30422
30895
  throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
30423
30896
  }
30424
30897
  const fullPath = target;
30425
30898
  if (!dryRun) {
30426
- await fs38.mkdir(path44.dirname(fullPath), { recursive: true });
30899
+ await fs39.mkdir(path45.dirname(fullPath), { recursive: true });
30427
30900
  await atomicWrite5(fullPath, substituteVars(content, name, vars));
30428
30901
  }
30429
30902
  files.push(resolvedPath);
@@ -30869,8 +31342,8 @@ function decodeHtmlEntities(text) {
30869
31342
  }
30870
31343
 
30871
31344
  // src/security-ast-scan-tool.ts
30872
- import * as fs39 from "node:fs/promises";
30873
- import * as path45 from "node:path";
31345
+ import * as fs40 from "node:fs/promises";
31346
+ import * as path46 from "node:path";
30874
31347
  import { toErrorMessage as toErrorMessage17 } from "@wrongstack/core/utils";
30875
31348
  var SECRET_PATTERNS = [
30876
31349
  { name: "AWS Access Key", regex: /\b(AKIA[0-9A-Z]{16})\b/ },
@@ -31037,9 +31510,9 @@ var securityAstScanTool = {
31037
31510
  let targetFile = input.file ?? "inline-code.ts";
31038
31511
  let content = input.content;
31039
31512
  if (!content && input.file) {
31040
- const absPath = path45.isAbsolute(input.file) ? input.file : path45.resolve(projectRoot, input.file);
31041
- targetFile = path45.relative(projectRoot, absPath).replace(/\\/g, "/");
31042
- content = await fs39.readFile(absPath, "utf8");
31513
+ const absPath = path46.isAbsolute(input.file) ? input.file : path46.resolve(projectRoot, input.file);
31514
+ targetFile = path46.relative(projectRoot, absPath).replace(/\\/g, "/");
31515
+ content = await fs40.readFile(absPath, "utf8");
31043
31516
  }
31044
31517
  if (!content) {
31045
31518
  return {
@@ -31087,7 +31560,7 @@ var securityAstScanTool = {
31087
31560
  };
31088
31561
 
31089
31562
  // src/set-working-dir.ts
31090
- import * as fs40 from "node:fs/promises";
31563
+ import * as fs41 from "node:fs/promises";
31091
31564
  import { toErrorMessage as toErrorMessage18 } from "@wrongstack/core/utils";
31092
31565
  var setWorkingDirTool = {
31093
31566
  name: "set_working_dir",
@@ -31127,7 +31600,7 @@ var setWorkingDirTool = {
31127
31600
  }
31128
31601
  let isDirectory = false;
31129
31602
  try {
31130
- isDirectory = (await fs40.stat(resolved)).isDirectory();
31603
+ isDirectory = (await fs41.stat(resolved)).isDirectory();
31131
31604
  } catch {
31132
31605
  isDirectory = false;
31133
31606
  }
@@ -31662,7 +32135,7 @@ ${formatTaskList2(file.tasks)}` : file.tasks.length > 0 ? formatTaskList2(file.t
31662
32135
  init_spawn_stream();
31663
32136
  init_util();
31664
32137
  init_legacy_bridge();
31665
- import * as path46 from "node:path";
32138
+ import * as path47 from "node:path";
31666
32139
  var testTool = {
31667
32140
  name: "test",
31668
32141
  category: "Code Quality",
@@ -31769,7 +32242,7 @@ async function detectRunner(cwd) {
31769
32242
  const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
31770
32243
  for (const f of candidates) {
31771
32244
  try {
31772
- await stat22(path46.join(cwd, f));
32245
+ await stat22(path47.join(cwd, f));
31773
32246
  if (f.includes("vitest")) return "vitest";
31774
32247
  if (f.includes("jest")) return "jest";
31775
32248
  if (f.includes("mocha")) return "mocha";
@@ -32146,8 +32619,8 @@ var toolUseTool = {
32146
32619
 
32147
32620
  // src/tree.ts
32148
32621
  init_util();
32149
- import * as fs41 from "node:fs/promises";
32150
- import * as path47 from "node:path";
32622
+ import * as fs42 from "node:fs/promises";
32623
+ import * as path48 from "node:path";
32151
32624
  import { DEFAULT_WALK_IGNORE_DIRS as DEFAULT_WALK_IGNORE_DIRS4, expectDefined as expectDefined10 } from "@wrongstack/core/utils";
32152
32625
  var DEFAULT_IGNORE5 = /* @__PURE__ */ new Set([
32153
32626
  ...DEFAULT_WALK_IGNORE_DIRS4,
@@ -32310,7 +32783,7 @@ var treeTool = {
32310
32783
  async function walkDir(dir, depth, opts) {
32311
32784
  opts.signal.throwIfAborted();
32312
32785
  if (opts.retention.truncated) return;
32313
- const entries = await fs41.readdir(dir, { withFileTypes: true }).catch(() => []);
32786
+ const entries = await fs42.readdir(dir, { withFileTypes: true }).catch(() => []);
32314
32787
  const filtered = entries.filter((e) => {
32315
32788
  if (!opts.showHidden && e.name.startsWith(".")) return false;
32316
32789
  if (opts.exclude.has(e.name)) return false;
@@ -32352,7 +32825,7 @@ async function walkDir(dir, depth, opts) {
32352
32825
  opts.retention.outputBytes += lineBytes;
32353
32826
  if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
32354
32827
  const childPrefix = opts.prefix + connector;
32355
- await walkDir(path47.join(dir, entry.name), depth + 1, {
32828
+ await walkDir(path48.join(dir, entry.name), depth + 1, {
32356
32829
  ...opts,
32357
32830
  prefix: childPrefix,
32358
32831
  isLast
@@ -32365,7 +32838,7 @@ async function walkDir(dir, depth, opts) {
32365
32838
  init_spawn_stream();
32366
32839
  init_util();
32367
32840
  init_legacy_bridge();
32368
- import * as path48 from "node:path";
32841
+ import * as path49 from "node:path";
32369
32842
  var typecheckTool = {
32370
32843
  name: "typecheck",
32371
32844
  category: "Code Quality",
@@ -32477,8 +32950,8 @@ async function findTsConfig(cwd) {
32477
32950
  const candidates = ["tsconfig.json", "tsconfig.base.json"];
32478
32951
  for (const f of candidates) {
32479
32952
  try {
32480
- const s = await stat22(path48.join(cwd, f));
32481
- if (s.isFile()) return path48.join(cwd, f);
32953
+ const s = await stat22(path49.join(cwd, f));
32954
+ if (s.isFile()) return path49.join(cwd, f);
32482
32955
  } catch {
32483
32956
  }
32484
32957
  }
@@ -32486,7 +32959,7 @@ async function findTsConfig(cwd) {
32486
32959
  }
32487
32960
 
32488
32961
  // src/write.ts
32489
- import * as fs42 from "node:fs/promises";
32962
+ import * as fs43 from "node:fs/promises";
32490
32963
  import { ToolValidationError as ToolValidationError11 } from "@wrongstack/core/types";
32491
32964
  import {
32492
32965
  atomicWrite as atomicWrite6,
@@ -32563,14 +33036,14 @@ async function prepareWrite(input, ctx) {
32563
33036
  let existed = false;
32564
33037
  let prev = "";
32565
33038
  try {
32566
- const stat22 = await fs42.stat(absPath);
33039
+ const stat22 = await fs43.stat(absPath);
32567
33040
  existed = stat22.isFile();
32568
33041
  if (existed) {
32569
33042
  if (!ctx.hasRead(absPath)) {
32570
- prev = await fs42.readFile(absPath, "utf8");
33043
+ prev = await fs43.readFile(absPath, "utf8");
32571
33044
  ctx.recordRead(absPath, stat22.mtimeMs, "write", sha256hex(prev));
32572
33045
  } else {
32573
- prev = await fs42.readFile(absPath, "utf8");
33046
+ prev = await fs43.readFile(absPath, "utf8");
32574
33047
  }
32575
33048
  }
32576
33049
  } catch (err) {
@@ -32591,7 +33064,7 @@ async function finishWrite(input, ctx, prepared, signal) {
32591
33064
  const rawDiff = prepared.existed ? unifiedDiff3(prepared.prev, content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
32592
33065
  + (new file, ${content.split("\n").length} lines)`;
32593
33066
  const { text: diff, truncated: diffTruncated } = truncateDiffPayload(rawDiff, MAX_DIFF_BYTES3);
32594
- const stat22 = await fs42.stat(prepared.absPath);
33067
+ const stat22 = await fs43.stat(prepared.absPath);
32595
33068
  ctx.recordRead(prepared.absPath, stat22.mtimeMs, "write", sha256hex(content));
32596
33069
  ctx.session.recordFileChange({
32597
33070
  path: prepared.absPath,
@@ -32669,6 +33142,7 @@ var TIER1_TOOLS = [
32669
33142
  var TIER2_TOOLS = [
32670
33143
  replaceTool,
32671
33144
  execTool,
33145
+ pwshTool,
32672
33146
  fetchTool,
32673
33147
  gitTool,
32674
33148
  treeTool,
@@ -32724,6 +33198,7 @@ var builtinTools = [
32724
33198
  grepTool,
32725
33199
  bashTool,
32726
33200
  execTool,
33201
+ pwshTool,
32727
33202
  fetchTool,
32728
33203
  searchTool,
32729
33204
  todoTool,
@@ -33140,7 +33615,7 @@ var builtinToolsPack = {
33140
33615
  };
33141
33616
 
33142
33617
  // src/process-guardian.ts
33143
- import * as os10 from "node:os";
33618
+ import * as os11 from "node:os";
33144
33619
  var ProcessGuardian = class {
33145
33620
  registry;
33146
33621
  config;
@@ -33276,7 +33751,7 @@ var ProcessGuardian = class {
33276
33751
  event: "process_guardian.started",
33277
33752
  instanceId: this.instanceId,
33278
33753
  mainPid: process.pid,
33279
- hostname: os10.hostname(),
33754
+ hostname: os11.hostname(),
33280
33755
  platform: process.platform
33281
33756
  })
33282
33757
  );
@@ -33410,8 +33885,8 @@ var ProcessGuardian = class {
33410
33885
  instanceId: this.instanceId,
33411
33886
  mainPid: process.pid,
33412
33887
  protectedCount: this.protectedProcesses.size,
33413
- platform: os10.platform(),
33414
- hostname: os10.hostname(),
33888
+ platform: os11.platform(),
33889
+ hostname: os11.hostname(),
33415
33890
  uptime: process.uptime()
33416
33891
  };
33417
33892
  }
@@ -33441,7 +33916,7 @@ function stopProcessGuardian() {
33441
33916
  init_process_registry();
33442
33917
 
33443
33918
  // src/ps-slash.ts
33444
- import * as os11 from "node:os";
33919
+ import * as os12 from "node:os";
33445
33920
  var IDLE_THRESHOLD_MS = 2 * 6e4;
33446
33921
  var STALE_THRESHOLD_MS2 = 5 * 6e4;
33447
33922
  function now() {
@@ -33482,7 +33957,7 @@ async function listInstances(options = {}) {
33482
33957
  const mainProc = processes.find((p) => p.spawnMode === "main");
33483
33958
  const firstProc = processes.at(0);
33484
33959
  const mainPid = mainProc?.pid ?? firstProc?.pid ?? 0;
33485
- const hostname_ = firstProc?.hostname ?? os11.hostname();
33960
+ const hostname_ = firstProc?.hostname ?? os12.hostname();
33486
33961
  const startedAt = Math.min(...processes.map((p) => p.startedAt));
33487
33962
  const lastActivity = Math.max(...processes.map((p) => p.lastHeartbeat));
33488
33963
  const age = timestamp - lastActivity;
@@ -33570,7 +34045,7 @@ async function getGlobalProcessStatus() {
33570
34045
  mainPid: process.pid,
33571
34046
  protectedCount: 0,
33572
34047
  platform: process.platform,
33573
- hostname: os11.hostname(),
34048
+ hostname: os12.hostname(),
33574
34049
  uptime: 0
33575
34050
  },
33576
34051
  allInstances: instances.map((inst) => ({
@@ -33764,8 +34239,8 @@ function createGlobalPsSlashCommand() {
33764
34239
  }
33765
34240
 
33766
34241
  // src/skill.ts
33767
- import * as fs43 from "node:fs/promises";
33768
- import * as path49 from "node:path";
34242
+ import * as fs44 from "node:fs/promises";
34243
+ import * as path50 from "node:path";
33769
34244
  import {
33770
34245
  missingRequiredRuntimeTools,
33771
34246
  missingRuntimeCapabilities,
@@ -33829,7 +34304,7 @@ function makeSkillTool(skillLoader) {
33829
34304
  field: "name"
33830
34305
  });
33831
34306
  }
33832
- const dir = path49.dirname(manifest.path);
34307
+ const dir = path50.dirname(manifest.path);
33833
34308
  let loadedResource;
33834
34309
  if (input.resource?.trim()) {
33835
34310
  loadedResource = await loadResource(dir, input.resource.trim());
@@ -33888,15 +34363,15 @@ ${listing}${warningLine}`;
33888
34363
  }
33889
34364
  async function loadResource(skillDir, rel) {
33890
34365
  const norm = rel.replace(/\\/g, "/");
33891
- if (path49.isAbsolute(rel) || norm.split("/").some((seg) => seg === "..")) {
34366
+ if (path50.isAbsolute(rel) || norm.split("/").some((seg) => seg === "..")) {
33892
34367
  throw new ToolValidationError13({
33893
34368
  message: `skill: invalid resource path "${rel}"`,
33894
34369
  field: "resource"
33895
34370
  });
33896
34371
  }
33897
- const absPath = path49.resolve(skillDir, rel);
33898
- const root = path49.resolve(skillDir);
33899
- if (absPath !== root && !absPath.startsWith(root + path49.sep)) {
34372
+ const absPath = path50.resolve(skillDir, rel);
34373
+ const root = path50.resolve(skillDir);
34374
+ if (absPath !== root && !absPath.startsWith(root + path50.sep)) {
33900
34375
  throw new ToolValidationError13({
33901
34376
  message: `skill: resource "${rel}" escapes the skill directory`,
33902
34377
  field: "resource"
@@ -33905,15 +34380,15 @@ async function loadResource(skillDir, rel) {
33905
34380
  let realPath;
33906
34381
  let realRoot;
33907
34382
  try {
33908
- realRoot = await fs43.realpath(root);
33909
- realPath = await fs43.realpath(absPath);
34383
+ realRoot = await fs44.realpath(root);
34384
+ realPath = await fs44.realpath(absPath);
33910
34385
  } catch {
33911
34386
  throw new ToolValidationError13({
33912
34387
  message: `skill: resource "${rel}" not readable`,
33913
34388
  field: "resource"
33914
34389
  });
33915
34390
  }
33916
- if (realPath !== realRoot && !realPath.startsWith(realRoot + path49.sep)) {
34391
+ if (realPath !== realRoot && !realPath.startsWith(realRoot + path50.sep)) {
33917
34392
  throw new ToolValidationError13({
33918
34393
  message: `skill: resource "${rel}" resolves outside the skill directory`,
33919
34394
  field: "resource"
@@ -33921,7 +34396,7 @@ async function loadResource(skillDir, rel) {
33921
34396
  }
33922
34397
  let buf;
33923
34398
  try {
33924
- buf = await fs43.readFile(realPath);
34399
+ buf = await fs44.readFile(realPath);
33925
34400
  } catch {
33926
34401
  throw new ToolValidationError13({
33927
34402
  message: `skill: resource "${rel}" not readable`,
@@ -33950,17 +34425,17 @@ async function walk(root, dir, out) {
33950
34425
  if (out.length >= MAX_LISTED_RESOURCES) return;
33951
34426
  let entries;
33952
34427
  try {
33953
- entries = await fs43.readdir(dir, { withFileTypes: true });
34428
+ entries = await fs44.readdir(dir, { withFileTypes: true });
33954
34429
  } catch {
33955
34430
  return;
33956
34431
  }
33957
34432
  for (const e of entries) {
33958
34433
  if (out.length >= MAX_LISTED_RESOURCES) return;
33959
- const fullPath = path49.join(dir, e.name);
34434
+ const fullPath = path50.join(dir, e.name);
33960
34435
  let isDir = e.isDirectory();
33961
34436
  if (e.isSymbolicLink()) {
33962
34437
  try {
33963
- isDir = (await fs43.stat(fullPath)).isDirectory();
34438
+ isDir = (await fs44.stat(fullPath)).isDirectory();
33964
34439
  } catch {
33965
34440
  continue;
33966
34441
  }
@@ -33971,8 +34446,8 @@ async function walk(root, dir, out) {
33971
34446
  } else if (e.isFile()) {
33972
34447
  if (e.name === "SKILL.md" || e.name === "SKILL.save.md") continue;
33973
34448
  try {
33974
- const stat22 = await fs43.stat(fullPath);
33975
- const rel = path49.relative(root, fullPath).split(path49.sep).join("/");
34449
+ const stat22 = await fs44.stat(fullPath);
34450
+ const rel = path50.relative(root, fullPath).split(path50.sep).join("/");
33976
34451
  out.push({ path: rel, bytes: stat22.size });
33977
34452
  } catch {
33978
34453
  }
@@ -33998,6 +34473,8 @@ var TOOL_ICON_MAP = {
33998
34473
  // Shell/command execution
33999
34474
  bash: "terminal",
34000
34475
  exec: "terminal",
34476
+ pwsh: "terminal",
34477
+ powershell: "terminal",
34001
34478
  run: "terminal",
34002
34479
  command: "terminal",
34003
34480
  shell: "terminal",
@@ -34205,6 +34682,7 @@ export {
34205
34682
  OFF_ONLY_TOOLS,
34206
34683
  OPTIONAL_TOOLS,
34207
34684
  PRIMARY_LANGUAGE_PROFILES,
34685
+ PWSH_TOOL_DESCRIPTION,
34208
34686
  SESSION_KANBAN_COLUMNS,
34209
34687
  TIER1_TOOLS,
34210
34688
  TIER2_TOOLS,
@@ -34332,6 +34810,7 @@ export {
34332
34810
  projectSessionPlanToKanban,
34333
34811
  projectSessionTasksToKanban,
34334
34812
  projectSessionTodosToKanban,
34813
+ pwshTool,
34335
34814
  readTool,
34336
34815
  rebindSessionKanbanTask,
34337
34816
  recordKanbanVerificationEvidence,