@cydm/pie 1.0.51 → 2.1.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.
Files changed (73) hide show
  1. package/README.md +36 -16
  2. package/dist/builtin/extensions/ask-user/index.js +4 -3
  3. package/dist/builtin/extensions/ask-user/package.json +8 -7
  4. package/dist/builtin/extensions/changelog/package.json +9 -9
  5. package/dist/builtin/extensions/computer-use/index.js +9352 -0
  6. package/dist/builtin/extensions/computer-use/package.json +11 -0
  7. package/dist/builtin/extensions/computer-use/prebuilt/linux/arm64/linux-bridge +0 -0
  8. package/dist/builtin/extensions/computer-use/prebuilt/linux/x64/linux-bridge +0 -0
  9. package/dist/builtin/extensions/computer-use/prebuilt/macos/arm64/pie-computer-use +0 -0
  10. package/dist/builtin/extensions/computer-use/prebuilt/macos/universal/pie-computer-use.app/Contents/Info.plist +24 -0
  11. package/dist/builtin/extensions/computer-use/prebuilt/macos/universal/pie-computer-use.app/Contents/MacOS/pie-computer-use +0 -0
  12. package/dist/builtin/extensions/computer-use/prebuilt/macos/universal/pie-computer-use.app/Contents/_CodeSignature/CodeResources +115 -0
  13. package/dist/builtin/extensions/computer-use/prebuilt/macos/x64/pie-computer-use +0 -0
  14. package/dist/builtin/extensions/computer-use/prebuilt/windows/windows-bridge.exe +0 -0
  15. package/dist/builtin/extensions/computer-use/scripts/build-native.mjs +277 -0
  16. package/dist/builtin/extensions/computer-use/scripts/helper-path.mjs +48 -0
  17. package/dist/builtin/extensions/computer-use/scripts/setup-helper.mjs +549 -0
  18. package/dist/builtin/extensions/document-attachments/package.json +9 -9
  19. package/dist/builtin/extensions/init/package.json +9 -9
  20. package/dist/builtin/extensions/mcp-adapter/index.js +25250 -0
  21. package/dist/builtin/extensions/mcp-adapter/package.json +11 -0
  22. package/dist/builtin/extensions/plan-mode/index.js +4 -3
  23. package/dist/builtin/extensions/plan-mode/package.json +9 -9
  24. package/dist/builtin/extensions/subagent/index.js +6 -4
  25. package/dist/builtin/extensions/subagent/package.json +16 -12
  26. package/dist/builtin/extensions/todo/index.js +4 -3
  27. package/dist/builtin/extensions/todo/package.json +9 -9
  28. package/dist/builtin/skills/chrome-devtools-axi/CHANGELOG.md +5 -0
  29. package/dist/builtin/skills/chrome-devtools-axi/LICENSE +21 -0
  30. package/dist/builtin/skills/chrome-devtools-axi/README.md +17 -0
  31. package/dist/builtin/skills/chrome-devtools-axi/SKILL.md +50 -0
  32. package/dist/builtin/skills/chrome-devtools-axi/chrome-devtools-axi-bridge.js +20034 -0
  33. package/dist/builtin/skills/chrome-devtools-axi/chrome-devtools-axi-cli.js +83 -0
  34. package/dist/builtin/skills/chrome-devtools-axi/chrome-devtools-axi-runtime.js +26 -0
  35. package/dist/builtin/skills/chrome-devtools-axi/chunk-42SGITKJ.js +11 -0
  36. package/dist/builtin/skills/chrome-devtools-axi/chunk-OJ6N23UZ.js +47 -0
  37. package/dist/builtin/skills/chrome-devtools-axi/chunk-UPATXALW.js +215 -0
  38. package/dist/builtin/skills/chrome-devtools-axi/cli-F2VCSVL4.js +4424 -0
  39. package/dist/builtin/skills/chrome-devtools-axi/package.json +12 -0
  40. package/dist/builtin/skills/skill-creator/SKILL.md +5 -2
  41. package/dist/builtin/skills/skill-creator/eval-viewer/generate_review.mjs +7 -20
  42. package/dist/builtin/skills/skill-creator/eval-viewer/viewer.html +15 -15
  43. package/dist/builtin/skills/skill-creator/scripts/aggregate_benchmark.mjs +20 -19
  44. package/dist/builtin/skills/skill-creator/scripts/generate_report.mjs +3 -3
  45. package/dist/builtin/skills/skill-creator/scripts/improve_description.mjs +12 -8
  46. package/dist/builtin/skills/skill-creator/scripts/package_skill.mjs +1 -0
  47. package/dist/builtin/skills/skill-creator/scripts/pie_runner.mjs +77 -31
  48. package/dist/builtin/skills/skill-creator/scripts/run_eval.mjs +38 -11
  49. package/dist/builtin/skills/skill-creator/scripts/run_loop.mjs +12 -6
  50. package/dist/builtin/skills/skill-creator/scripts/skill_metadata.mjs +12 -1
  51. package/dist/builtin/skills/tui-use/LICENSE +21 -0
  52. package/dist/builtin/skills/tui-use/README.md +17 -0
  53. package/dist/builtin/skills/tui-use/SKILL.md +106 -0
  54. package/dist/builtin/skills/tui-use/tui-use-cli.cjs +10608 -0
  55. package/dist/builtin/skills/tui-use/tui-use-cli.js +39 -0
  56. package/dist/builtin/skills/tui-use/tui-use-daemon.cjs +7916 -0
  57. package/dist/chunks/chunk-5353DEEC.js +130 -0
  58. package/dist/chunks/{chunk-S2J3SZZX.js → chunk-IM43UYVV.js} +827 -3385
  59. package/dist/chunks/{chunk-A644H2XX.js → chunk-LU7RKALH.js} +241 -96
  60. package/dist/chunks/{chunk-B3SMW2XW.js → chunk-QJH6YN2W.js} +1 -1
  61. package/dist/chunks/{chunk-KDPAGHYY.js → chunk-TJ2F4FL5.js} +190 -207
  62. package/dist/chunks/chunk-XXGYUYXI.js +2718 -0
  63. package/dist/chunks/{src-UKCIAX6F.js → src-QSP2OPHU.js} +3 -2
  64. package/dist/chunks/{test-stream-NCBFPM2I.js → test-stream-CVGWFLSX.js} +2 -1
  65. package/dist/chunks/webapi-MBCKO7ZV.js +2481 -0
  66. package/dist/cli.js +7866 -369
  67. package/package.json +29 -20
  68. package/scripts/repair-node-pty.mjs +45 -0
  69. package/dist/builtin/skills/browser-tools/CHANGELOG.md +0 -5
  70. package/dist/builtin/skills/browser-tools/README.md +0 -17
  71. package/dist/builtin/skills/browser-tools/SKILL.md +0 -43
  72. package/dist/builtin/skills/browser-tools/package.json +0 -12
  73. package/dist/builtin/skills/browser-tools/playwright-cli.js +0 -45
@@ -1,4 +1,8 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
+ import {
3
+ getBuiltinDir,
4
+ getConfigDir
5
+ } from "./chunk-5353DEEC.js";
2
6
  import {
3
7
  classifyShellCommand,
4
8
  createCodeIntelTool,
@@ -8,129 +12,16 @@ import {
8
12
  createSharedWebSearchTool,
9
13
  interpretShellExit,
10
14
  requestInteraction
11
- } from "./chunk-A644H2XX.js";
15
+ } from "./chunk-LU7RKALH.js";
12
16
  import {
13
17
  Type
14
- } from "./chunk-S2J3SZZX.js";
15
-
16
- // src/config.ts
17
- import { existsSync, mkdirSync, readFileSync, renameSync } from "fs";
18
- import { homedir } from "os";
19
- import { dirname, join } from "path";
20
- import { fileURLToPath } from "url";
21
- var __filename = fileURLToPath(import.meta.url);
22
- var __dirname = dirname(__filename);
23
- var isBunRuntime = !!process.versions.bun;
24
- function getPackageDir() {
25
- const envDir = process.env.PIE_PACKAGE_DIR;
26
- if (envDir) {
27
- if (envDir === "~") return homedir();
28
- if (envDir.startsWith("~/")) return homedir() + envDir.slice(1);
29
- return envDir;
30
- }
31
- let dir = __dirname;
32
- while (dir !== dirname(dir)) {
33
- if (existsSync(join(dir, "package.json"))) {
34
- return dir;
35
- }
36
- dir = dirname(dir);
37
- }
38
- return __dirname;
39
- }
40
- function getThemesDir() {
41
- const packageDir = getPackageDir();
42
- const srcOrDist = existsSync(join(packageDir, "src")) ? "src" : "dist";
43
- return join(packageDir, srcOrDist, "theme");
44
- }
45
- function getBuiltinDir() {
46
- const packageDir = getPackageDir();
47
- const distBuiltinDir = join(packageDir, "dist", "builtin");
48
- if (existsSync(distBuiltinDir)) {
49
- return distBuiltinDir;
50
- }
51
- return join(packageDir, "builtin");
52
- }
53
- function getPackageJsonPath() {
54
- return join(getPackageDir(), "package.json");
55
- }
56
- var pkg = JSON.parse(readFileSync(getPackageJsonPath(), "utf-8"));
57
- var CONFIG_DIR_NAME = ".pie";
58
- var VERSION = pkg.version;
59
- var ENV_AGENT_DIR = "PIE_CODING_AGENT_DIR";
60
- function getConfigDir() {
61
- const envDir = process.env[ENV_AGENT_DIR];
62
- if (envDir) {
63
- if (envDir === "~") return homedir();
64
- if (envDir.startsWith("~/")) return homedir() + envDir.slice(1);
65
- return envDir;
66
- }
67
- return join(homedir(), CONFIG_DIR_NAME);
68
- }
69
- function getAgentDir() {
70
- return getConfigDir();
71
- }
72
- function getCustomThemesDir() {
73
- return join(getConfigDir(), "themes");
74
- }
75
- function getSettingsPath() {
76
- return join(getConfigDir(), "settings.json");
77
- }
78
- function getProjectSettingsPath(cwd) {
79
- return join(cwd, CONFIG_DIR_NAME, "settings.json");
80
- }
81
- function getSessionsDir() {
82
- return join(getConfigDir(), "sessions");
83
- }
84
- function getLogsDir() {
85
- return join(getConfigDir(), "logs");
86
- }
87
- function getRuntimeLogPath() {
88
- return join(getLogsDir(), "pie-cli.log");
89
- }
90
- function migrateConfigFromAgentDir() {
91
- const oldAgentDir = join(homedir(), CONFIG_DIR_NAME, "agent");
92
- const newConfigDir = getConfigDir();
93
- if (!existsSync(oldAgentDir)) {
94
- return;
95
- }
96
- if (!existsSync(newConfigDir)) {
97
- mkdirSync(newConfigDir, { recursive: true });
98
- }
99
- const filesToMigrate = [
100
- "keybindings.json",
101
- "settings.json",
102
- "pie-debug.log"
103
- ];
104
- for (const file of filesToMigrate) {
105
- const oldPath = join(oldAgentDir, file);
106
- const newPath = file === "pie-debug.log" ? getRuntimeLogPath() : join(newConfigDir, file);
107
- if (existsSync(oldPath) && !existsSync(newPath)) {
108
- try {
109
- if (file === "pie-debug.log") {
110
- mkdirSync(getLogsDir(), { recursive: true });
111
- }
112
- renameSync(oldPath, newPath);
113
- console.log(`[Config] Migrated ${file} to ${newPath}`);
114
- } catch (e) {
115
- }
116
- }
117
- }
118
- const oldThemesDir = join(oldAgentDir, "themes");
119
- const newThemesDir = getCustomThemesDir();
120
- if (existsSync(oldThemesDir) && !existsSync(newThemesDir)) {
121
- try {
122
- renameSync(oldThemesDir, newThemesDir);
123
- console.log(`[Config] Migrated themes to ${newThemesDir}`);
124
- } catch (e) {
125
- }
126
- }
127
- }
18
+ } from "./chunk-XXGYUYXI.js";
128
19
 
129
20
  // src/capabilities/bash/index.ts
130
21
  import { spawn } from "child_process";
131
- import { closeSync, createWriteStream, existsSync as existsSync2, openSync, statSync, unlinkSync, writeFileSync } from "fs";
22
+ import { closeSync, createWriteStream, existsSync, openSync, statSync, unlinkSync, writeFileSync } from "fs";
132
23
  import { tmpdir } from "os";
133
- import { join as join2, resolve as resolve2 } from "path";
24
+ import { join, resolve } from "path";
134
25
  import { createHash, randomBytes } from "crypto";
135
26
 
136
27
  // src/capabilities/bash/truncate.ts
@@ -273,6 +164,7 @@ var bashSchema = Type.Object({
273
164
  });
274
165
  var DEFAULT_TIMEOUT_SECONDS = 120;
275
166
  var MAX_GENERIC_TIMEOUT_SECONDS = 120;
167
+ var OUTPUT_DRAIN_TIMEOUT_MS = 1e3;
276
168
  function resolveTimeoutPolicy(requestedTimeout, commandClass) {
277
169
  const requestedSeconds = requestedTimeout ?? DEFAULT_TIMEOUT_SECONDS;
278
170
  if (requestedSeconds > MAX_GENERIC_TIMEOUT_SECONDS && commandClass !== "package_manager" && commandClass !== "test_or_build") {
@@ -302,12 +194,15 @@ function validateBashCommand(command) {
302
194
  throw new Error("Invalid bash command. The command argument must be only a real shell command, not tool-call markup.");
303
195
  }
304
196
  }
305
- function buildEffectiveCommand(command, options) {
197
+ function buildEffectiveCommand(command, options, shell) {
306
198
  const prefix = options?.commandPrefix?.trim();
307
199
  if (!prefix) {
308
200
  return command;
309
201
  }
310
202
  validateBashCommand(prefix);
203
+ if (shell === "powershell") {
204
+ return `& { ${prefix} }; if (-not $?) { exit 1 }; ${command}`;
205
+ }
311
206
  return `${prefix} && ${command}`;
312
207
  }
313
208
  function resolveShell(shell) {
@@ -318,7 +213,8 @@ function resolveShell(shell) {
318
213
  }
319
214
  function buildSpawnCommand(shell, command) {
320
215
  if (shell === "powershell") {
321
- return { file: process.platform === "win32" ? "powershell.exe" : "pwsh", args: ["-NoProfile", "-NonInteractive", "-Command", command], shell: false };
216
+ const utf8Command = `[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding; $OutputEncoding = [Console]::OutputEncoding; ${command}`;
217
+ return { file: process.platform === "win32" ? "powershell.exe" : "pwsh", args: ["-NoProfile", "-NonInteractive", "-Command", utf8Command], shell: false };
322
218
  }
323
219
  if (shell === "cmd") {
324
220
  return { file: "cmd.exe", args: ["/d", "/s", "/c", command], shell: false };
@@ -341,7 +237,7 @@ async function requestHighRiskConfirmation(request) {
341
237
  return response.type === "confirm" && response.confirmed === true;
342
238
  }
343
239
  function validateWorkingDirectory(cwd) {
344
- if (!existsSync2(cwd)) {
240
+ if (!existsSync(cwd)) {
345
241
  throw new Error(`Invalid bash cwd: ${cwd} does not exist. Use the current workspace path or pass an existing directory in cwd.`);
346
242
  }
347
243
  let stat;
@@ -356,11 +252,11 @@ function validateWorkingDirectory(cwd) {
356
252
  }
357
253
  function normalizeWorkingDirectory(cwd, defaultCwd) {
358
254
  const requestedCwd = typeof cwd === "string" && cwd.trim().length > 0 ? cwd.trim() : void 0;
359
- return resolve2(requestedCwd ?? defaultCwd ?? process.cwd());
255
+ return resolve(requestedCwd ?? defaultCwd ?? process.cwd());
360
256
  }
361
257
  function getTempFilePath() {
362
258
  const id = randomBytes(8).toString("hex");
363
- return join2(tmpdir(), `pie-bash-${id}.log`);
259
+ return join(tmpdir(), `pie-bash-${id}.log`);
364
260
  }
365
261
  var retainedOutputArtifacts = /* @__PURE__ */ new Set();
366
262
  function getErrorCode(error) {
@@ -370,15 +266,15 @@ function getErrorCode(error) {
370
266
  }
371
267
  function createPrivateOutputArtifact() {
372
268
  for (let attempt = 0; attempt < 10; attempt++) {
373
- const path3 = getTempFilePath();
269
+ const path4 = getTempFilePath();
374
270
  try {
375
- const fd = openSync(path3, "wx", 384);
376
- retainedOutputArtifacts.add(path3);
271
+ const fd = openSync(path4, "wx", 384);
272
+ retainedOutputArtifacts.add(path4);
377
273
  try {
378
- return { path: path3, stream: createWriteStream(path3, { fd, autoClose: true }) };
274
+ return { path: path4, stream: createWriteStream(path4, { fd, autoClose: true }) };
379
275
  } catch (error) {
380
276
  closeSync(fd);
381
- removeOutputArtifact(path3);
277
+ removeOutputArtifact(path4);
382
278
  throw error;
383
279
  }
384
280
  } catch (error) {
@@ -387,34 +283,34 @@ function createPrivateOutputArtifact() {
387
283
  }
388
284
  throw new Error("Unable to allocate a private bash output artifact.");
389
285
  }
390
- function removeOutputArtifact(path3) {
391
- if (!path3) return;
286
+ function removeOutputArtifact(path4) {
287
+ if (!path4) return;
392
288
  try {
393
- unlinkSync(path3);
289
+ unlinkSync(path4);
394
290
  } catch (error) {
395
291
  if (getErrorCode(error) !== "ENOENT") throw error;
396
292
  }
397
- retainedOutputArtifacts.delete(path3);
293
+ retainedOutputArtifacts.delete(path4);
398
294
  }
399
- function discardOutputArtifact(path3, stream) {
295
+ function discardOutputArtifact(path4, stream) {
400
296
  if (stream && !stream.closed) {
401
297
  stream.once("close", () => {
402
298
  try {
403
- removeOutputArtifact(path3);
299
+ removeOutputArtifact(path4);
404
300
  } catch {
405
301
  }
406
302
  });
407
303
  stream.destroy();
408
304
  }
409
305
  try {
410
- removeOutputArtifact(path3);
306
+ removeOutputArtifact(path4);
411
307
  } catch {
412
308
  }
413
309
  }
414
310
  function cleanupBashOutputArtifacts() {
415
- for (const path3 of [...retainedOutputArtifacts]) {
311
+ for (const path4 of [...retainedOutputArtifacts]) {
416
312
  try {
417
- removeOutputArtifact(path3);
313
+ removeOutputArtifact(path4);
418
314
  } catch {
419
315
  }
420
316
  }
@@ -422,9 +318,13 @@ function cleanupBashOutputArtifacts() {
422
318
  process.once("exit", cleanupBashOutputArtifacts);
423
319
  function finishTempFileStream(stream) {
424
320
  if (!stream || stream.closed) return Promise.resolve();
425
- return new Promise((resolve3, reject) => {
321
+ return new Promise((resolve2, reject) => {
426
322
  let done = false;
323
+ const timer = setTimeout(() => {
324
+ stream.destroy(new Error("Timed out flushing shell output."));
325
+ }, OUTPUT_DRAIN_TIMEOUT_MS);
427
326
  const cleanup = () => {
327
+ clearTimeout(timer);
428
328
  stream.off("close", onClose);
429
329
  stream.off("error", onError);
430
330
  };
@@ -432,7 +332,7 @@ function finishTempFileStream(stream) {
432
332
  if (done) return;
433
333
  done = true;
434
334
  cleanup();
435
- resolve3();
335
+ resolve2();
436
336
  };
437
337
  const onError = (error) => {
438
338
  if (done) return;
@@ -457,7 +357,7 @@ function buildBashRecoveryHint(command, output) {
457
357
  if (/\bnpm ERR!|\bnpm error\b/i.test(output) && /\b(?:ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|fetch failed|socket hang up|registry|proxy)\b/i.test(output)) {
458
358
  return "npm could not reach the registry. Treat this as an environment/network/proxy issue, not a source-code failure; retry npm install/npm ci when network is available or check npm proxy/registry settings.";
459
359
  }
460
- if (/\bnpm\s+run\b/.test(command) && (/(?:^|\n)\s*(?:sh|bash|zsh):\s+[\w@./-]+:\s+command not found\b/i.test(output) || /(?:^|\r?\n)\s*'[^']+'\s+is not recognized as an internal or external command\b/i.test(output))) {
360
+ if (/\bnpm\s+run\b/.test(command) && (/(?:^|\n)\s*(?:sh|bash|zsh):\s+[\w@./-]+:\s+command not found\b/i.test(output) || /(?:^|\r?\n)\s*'[^']+'\s+is not recognized as an internal or external command\b/i.test(output) || /\u4e0d\u662f\u5185\u90e8\u6216\u5916\u90e8\u547d\u4ee4/.test(output))) {
461
361
  return "This npm script could not find a local executable. Check whether dependencies were installed successfully in cwd, whether node_modules exists, and whether a prior npm install/npm ci failed before treating this as a code failure.";
462
362
  }
463
363
  return void 0;
@@ -506,8 +406,9 @@ function killProcessTree(pid) {
506
406
  try {
507
407
  spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
508
408
  stdio: "ignore",
509
- detached: true
510
- });
409
+ windowsHide: true
410
+ }).on("error", () => {
411
+ }).unref();
511
412
  } catch {
512
413
  }
513
414
  } else {
@@ -542,7 +443,7 @@ function createBashTool(defaultCwd, options) {
542
443
  execute: async (_toolCallId, { command, timeout, cwd, shell }, signal, onUpdate) => {
543
444
  validateBashCommand(command);
544
445
  const resolvedShell = resolveShell(shell);
545
- const effectiveCommand = buildEffectiveCommand(command, options);
446
+ const effectiveCommand = buildEffectiveCommand(command, options, resolvedShell);
546
447
  const workingDir = normalizeWorkingDirectory(cwd, defaultCwd);
547
448
  validateWorkingDirectory(workingDir);
548
449
  const risk = classifyShellCommand(effectiveCommand, resolvedShell, {
@@ -581,7 +482,7 @@ function createBashTool(defaultCwd, options) {
581
482
  }
582
483
  const timeoutPolicy = resolveTimeoutPolicy(timeout, risk.commandClass);
583
484
  const timeoutMs = timeoutPolicy.effectiveSeconds * 1e3;
584
- return new Promise((resolve3, reject) => {
485
+ return new Promise((resolve2, reject) => {
585
486
  if (signal?.aborted) {
586
487
  reject(new Error("Operation aborted"));
587
488
  return;
@@ -605,25 +506,42 @@ function createBashTool(defaultCwd, options) {
605
506
  headBytes += prefix.length;
606
507
  };
607
508
  let timedOut = false;
509
+ let processExited = false;
510
+ let exitCode = null;
511
+ let exitSignal = null;
512
+ let drainTimer;
608
513
  const spawnCommand = buildSpawnCommand(resolvedShell, effectiveCommand);
609
514
  const child = (options?.spawnProcess ?? spawn)(spawnCommand.file, spawnCommand.args, {
610
515
  shell: spawnCommand.shell,
611
516
  cwd: workingDir,
612
517
  env: process.env,
613
- detached: process.platform !== "win32"
518
+ detached: process.platform !== "win32",
519
+ windowsHide: true
614
520
  });
521
+ const closePipes = () => {
522
+ child.stdin?.destroy();
523
+ child.stdout?.destroy();
524
+ child.stderr?.destroy();
525
+ };
615
526
  const timeoutId = setTimeout(() => {
527
+ if (settled) return;
616
528
  timedOut = true;
617
- if (child.pid) {
529
+ if (child.pid && !processExited) {
618
530
  killProcessTree(child.pid);
619
531
  }
532
+ closePipes();
533
+ child.unref?.();
534
+ void finishExecution(exitCode, exitSignal, true);
620
535
  }, timeoutMs);
621
536
  const failExecution = (error) => {
622
537
  if (settled) return;
623
538
  settled = true;
624
539
  clearTimeout(timeoutId);
540
+ if (drainTimer) clearTimeout(drainTimer);
625
541
  signal?.removeEventListener("abort", onAbort);
626
- if (child.pid) killProcessTree(child.pid);
542
+ if (child.pid && !processExited) killProcessTree(child.pid);
543
+ closePipes();
544
+ child.unref?.();
627
545
  discardOutputArtifact(tempFilePath, tempFileStream);
628
546
  reject(error);
629
547
  };
@@ -715,11 +633,13 @@ function createBashTool(defaultCwd, options) {
715
633
  chunksBytes -= removed.length;
716
634
  }
717
635
  });
718
- child.on("close", async (code, sig) => {
636
+ const finishExecution = async (code, sig, stdioIncomplete = false) => {
637
+ if (settled) return;
638
+ settled = true;
719
639
  try {
720
640
  clearTimeout(timeoutId);
641
+ if (drainTimer) clearTimeout(drainTimer);
721
642
  signal?.removeEventListener("abort", onAbort);
722
- if (settled) return;
723
643
  const fullBuffer = Buffer.concat(chunks);
724
644
  let fullOutput = fullBuffer.toString("utf-8");
725
645
  if (totalBytes > fullBuffer.length) {
@@ -736,10 +656,10 @@ ${fullOutput}`;
736
656
  }
737
657
  let outputText = truncation.content || "(no output)";
738
658
  if (truncation.truncated && !tempFilePath) {
739
- const path3 = getTempFilePath();
740
- const fd = openSync(path3, "wx", 384);
741
- tempFilePath = path3;
742
- retainedOutputArtifacts.add(path3);
659
+ const path4 = getTempFilePath();
660
+ const fd = openSync(path4, "wx", 384);
661
+ tempFilePath = path4;
662
+ retainedOutputArtifacts.add(path4);
743
663
  try {
744
664
  writeFileSync(fd, fullOutput, "utf-8");
745
665
  } finally {
@@ -751,6 +671,7 @@ ${fullOutput}`;
751
671
  exitCode: code,
752
672
  signal: sig?.toString() ?? null,
753
673
  timedOut,
674
+ ...stdioIncomplete ? { stdioIncomplete: true } : {},
754
675
  shell: resolvedShell,
755
676
  commandClass: risk.commandClass,
756
677
  riskWarnings: risk.riskWarnings,
@@ -806,6 +727,9 @@ ${commandNonzeroHint}`;
806
727
  [Command timed out after ${timeoutPolicy.effectiveSeconds} seconds]`;
807
728
  outputText += "\n[Timeout hint: split custom searches or service polls into smaller bounded steps with progress output, inspect partial results/logs, then make a decision. Avoid repeating long waits. Reserve long timeouts for installs, builds, and tests.]";
808
729
  }
730
+ if (stdioIncomplete && !timedOut) {
731
+ outputText += "\n\n[Output capture ended after the command exited because a descendant kept stdio open. Captured output may be incomplete.]";
732
+ }
809
733
  if (timeoutPolicy.capped) {
810
734
  outputText += `
811
735
 
@@ -826,13 +750,26 @@ ${commandNonzeroHint}`;
826
750
  outputText += "\n\n[Repeat hint: this exact command already returned the same result in this session. If it proves the task, stop; otherwise change the command or inspect a different file/range.]";
827
751
  }
828
752
  const content = [{ type: "text", text: outputText }];
829
- settled = true;
830
- resolve3({ content, details, isError: outcome.toolFailure });
753
+ resolve2({ content, details, isError: outcome.toolFailure });
831
754
  } catch (error) {
832
755
  discardOutputArtifact(tempFilePath, tempFileStream);
833
- settled = true;
834
756
  reject(error);
835
757
  }
758
+ };
759
+ child.on("exit", (code, sig) => {
760
+ processExited = true;
761
+ exitCode = code;
762
+ exitSignal = sig;
763
+ if (settled) return;
764
+ drainTimer = setTimeout(() => {
765
+ closePipes();
766
+ child.unref?.();
767
+ void finishExecution(code, sig, true);
768
+ }, OUTPUT_DRAIN_TIMEOUT_MS);
769
+ });
770
+ child.on("close", (code, sig) => {
771
+ processExited = true;
772
+ void finishExecution(code, sig);
836
773
  });
837
774
  child.on("error", (err) => {
838
775
  failExecution(new Error(`Failed to start bash command in ${workingDir}: ${err.message}. Check that the command is executable and cwd exists.`));
@@ -845,7 +782,7 @@ ${commandNonzeroHint}`;
845
782
  // src/capabilities/code-intel.ts
846
783
  import * as fs from "node:fs";
847
784
  import * as path from "node:path";
848
- import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
785
+ import { fileURLToPath, pathToFileURL } from "node:url";
849
786
  import { Worker } from "node:worker_threads";
850
787
  var WORKER_TIMEOUT_MS = 3e4;
851
788
  var MAX_ACTIVE_CODE_INTEL_WORKERS = 1;
@@ -873,8 +810,8 @@ function acquireWorkerSlot(deadline, signal) {
873
810
  if (codeIntelWorkerWaiters.length >= MAX_QUEUED_CODE_INTEL_WORKERS) {
874
811
  return Promise.reject(new Error("TypeScript code intelligence queue is full"));
875
812
  }
876
- return new Promise((resolve3, reject) => {
877
- const waiter = { resolve: () => resolve3(releaseWorkerSlot), reject, signal };
813
+ return new Promise((resolve2, reject) => {
814
+ const waiter = { resolve: () => resolve2(releaseWorkerSlot), reject, signal };
878
815
  waiter.onAbort = () => {
879
816
  const index = codeIntelWorkerWaiters.indexOf(waiter);
880
817
  if (index >= 0) codeIntelWorkerWaiters.splice(index, 1);
@@ -893,7 +830,7 @@ function acquireWorkerSlot(deadline, signal) {
893
830
  });
894
831
  }
895
832
  function resolveWorkerPath() {
896
- const moduleDirectory = path.dirname(fileURLToPath2(import.meta.url));
833
+ const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));
897
834
  const candidates = [
898
835
  path.join(moduleDirectory, "code-intel-worker.cjs"),
899
836
  path.join(moduleDirectory, "..", "code-intel-worker.cjs"),
@@ -904,7 +841,7 @@ function resolveWorkerPath() {
904
841
  return workerPath;
905
842
  }
906
843
  function runWorker(request, timeoutMs, signal) {
907
- return new Promise((resolve3, reject) => {
844
+ return new Promise((resolve2, reject) => {
908
845
  if (signal?.aborted) {
909
846
  reject(new Error("Operation aborted"));
910
847
  return;
@@ -929,7 +866,7 @@ function runWorker(request, timeoutMs, signal) {
929
866
  }
930
867
  }
931
868
  if (error) reject(error);
932
- else resolve3(result);
869
+ else resolve2(result);
933
870
  })();
934
871
  };
935
872
  const onAbort = () => finish(new Error("Operation aborted"));
@@ -1017,21 +954,33 @@ function createCodeIntelTool2(options) {
1017
954
  // src/capabilities/rendered-page-reader.ts
1018
955
  import { spawn as spawn2 } from "node:child_process";
1019
956
  import { createRequire } from "node:module";
957
+ import * as fs2 from "node:fs";
958
+ import * as os from "node:os";
959
+ import * as path2 from "node:path";
1020
960
  var require2 = createRequire(import.meta.url);
1021
- function resolvePlaywrightCli() {
1022
- return require2.resolve("@playwright/cli/playwright-cli.js");
961
+ function resolveAxiCli() {
962
+ const runtime = path2.join(getBuiltinDir(), "skills", "chrome-devtools-axi", "chrome-devtools-axi-runtime.js");
963
+ if (!fs2.existsSync(runtime)) throw new Error("Pie's packaged browser runtime is missing. Rebuild or reinstall Pie.");
964
+ return runtime;
965
+ }
966
+ function resolveMcpScript() {
967
+ const packageJson = require2.resolve("chrome-devtools-mcp/package.json");
968
+ return path2.join(path2.dirname(packageJson), "build", "src", "bin", "chrome-devtools-mcp.js");
1023
969
  }
1024
- function runPlaywright(args, timeoutMs = 6e4) {
1025
- return new Promise((resolve3, reject) => {
1026
- const child = spawn2(process.execPath, [resolvePlaywrightCli(), ...args], {
970
+ function runAxi(args, options = {}) {
971
+ const timeoutMs = options.timeoutMs ?? 6e4;
972
+ return new Promise((resolve2, reject) => {
973
+ const child = spawn2(process.execPath, [resolveAxiCli(), ...args], {
1027
974
  stdio: ["ignore", "pipe", "pipe"],
1028
- shell: false
975
+ shell: false,
976
+ env: options.env,
977
+ cwd: options.cwd
1029
978
  });
1030
979
  let stdout = "";
1031
980
  let stderr = "";
1032
981
  const timer = setTimeout(() => {
1033
982
  child.kill("SIGTERM");
1034
- reject(new Error(`Playwright CLI timed out after ${Math.ceil(timeoutMs / 1e3)} seconds`));
983
+ reject(new Error(`chrome-devtools-axi timed out after ${Math.ceil(timeoutMs / 1e3)} seconds`));
1035
984
  }, timeoutMs);
1036
985
  child.stdout.setEncoding("utf8");
1037
986
  child.stderr.setEncoding("utf8");
@@ -1047,24 +996,40 @@ function runPlaywright(args, timeoutMs = 6e4) {
1047
996
  });
1048
997
  child.on("close", (code) => {
1049
998
  clearTimeout(timer);
1050
- if (code === 0) resolve3(stdout.trim());
1051
- else reject(new Error((stderr || stdout || `Playwright CLI exited with ${code}`).trim()));
999
+ if (code === 0) resolve2(stdout.trim());
1000
+ else reject(new Error((stderr || stdout || `chrome-devtools-axi exited with ${code}`).trim()));
1052
1001
  });
1053
1002
  });
1054
1003
  }
1055
1004
  function linesOf(output, max = 50) {
1056
- return output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(0, max);
1005
+ if (/^(console|network)_failed:/i.test(output.trim())) return [];
1006
+ return output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).filter((line) => !/^(console:|network:|##\s|Showing \d|\(\d+ args\)$|<no .+ found>$|no \w+ found\.$)/i.test(line)).slice(0, max);
1057
1007
  }
1058
1008
  function parseScreenshotPath(output) {
1059
- const match = output.match(/\(([^)]+\.png)\)/i) || output.match(/([^\s]+\.png)/i);
1009
+ if (output.startsWith("screenshot_failed:")) return void 0;
1010
+ const match = output.match(/(?:^|\n)screenshot:\s*(\S+)/i) || output.match(/([^\s]+\.(?:png|jpe?g|webp))/i);
1060
1011
  return match?.[1];
1061
1012
  }
1013
+ function unwrapJsonValue(payload) {
1014
+ let value = payload;
1015
+ for (let depth = 0; depth < 4; depth += 1) {
1016
+ if (typeof value !== "string") break;
1017
+ const text = value.trim();
1018
+ if (!text.startsWith('"') && !text.startsWith("{") && !text.startsWith("[")) break;
1019
+ try {
1020
+ value = JSON.parse(text);
1021
+ } catch {
1022
+ break;
1023
+ }
1024
+ }
1025
+ return value;
1026
+ }
1062
1027
  function parsePageInfo(raw) {
1063
1028
  const trimmed = raw.trim();
1064
1029
  if (!trimmed) return {};
1030
+ const payload = trimmed.replace(/^result:\s*/i, "");
1065
1031
  try {
1066
- const decoded = trimmed.startsWith('"') ? JSON.parse(trimmed) : trimmed;
1067
- const parsed = typeof decoded === "string" ? JSON.parse(decoded) : decoded;
1032
+ const parsed = unwrapJsonValue(payload);
1068
1033
  if (!parsed || typeof parsed !== "object") return {};
1069
1034
  return {
1070
1035
  finalUrl: typeof parsed.href === "string" ? parsed.href : void 0,
@@ -1075,23 +1040,53 @@ function parsePageInfo(raw) {
1075
1040
  return {};
1076
1041
  }
1077
1042
  }
1043
+ function stripHelpHints(output) {
1044
+ return output.replace(/(?:^|\n)help\[\d+\]:[\s\S]*$/i, "").trim();
1045
+ }
1046
+ function parseSnapshot(output) {
1047
+ const match = output.match(/(?:^|\n)snapshot:\n([\s\S]*)$/i);
1048
+ return stripHelpHints(match ? match[1] : output);
1049
+ }
1050
+ function cleanStaleScreenshotDirs(retentionMs = 60 * 60 * 1e3) {
1051
+ try {
1052
+ const tmp = os.tmpdir();
1053
+ const cutoff = Date.now() - retentionMs;
1054
+ for (const entry of fs2.readdirSync(tmp)) {
1055
+ if (!entry.startsWith("pie-axi-shot-")) continue;
1056
+ const dir = path2.join(tmp, entry);
1057
+ try {
1058
+ if (fs2.statSync(dir).mtimeMs < cutoff) fs2.rmSync(dir, { recursive: true, force: true });
1059
+ } catch {
1060
+ }
1061
+ }
1062
+ } catch {
1063
+ }
1064
+ }
1078
1065
  function createCliRenderedPageReader() {
1079
1066
  return async ({ url }) => {
1080
1067
  const session = `pie-web-research-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1068
+ const env = { ...process.env, CHROME_DEVTOOLS_AXI_SESSION: session };
1069
+ delete env.CHROME_DEVTOOLS_AXI_PORT;
1070
+ if (!env.CHROME_DEVTOOLS_AXI_MCP_PATH?.trim()) env.CHROME_DEVTOOLS_AXI_MCP_PATH = resolveMcpScript();
1071
+ if (!env.CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS) env.CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS = "1";
1072
+ cleanStaleScreenshotDirs();
1073
+ const screenshotDir = fs2.mkdtempSync(path2.join(os.tmpdir(), "pie-axi-shot-"));
1081
1074
  try {
1082
- await runPlaywright([`-s=${session}`, "open", "about:blank"]);
1083
- await runPlaywright([`-s=${session}`, "goto", url]);
1084
- const [snapshot, pageInfoRaw, screenshotOutput, consoleOutput, requestsOutput] = await Promise.all([
1085
- runPlaywright([`-s=${session}`, "snapshot"]),
1086
- runPlaywright([`-s=${session}`, "--raw", "eval", "() => JSON.stringify({ title: document.title, href: location.href, text: document.body ? document.body.innerText : '' })"]),
1087
- runPlaywright([`-s=${session}`, "--raw", "screenshot"]).catch((error) => `screenshot_failed: ${error instanceof Error ? error.message : String(error)}`),
1088
- runPlaywright([`-s=${session}`, "--raw", "console"]).catch((error) => `console_failed: ${error instanceof Error ? error.message : String(error)}`),
1089
- runPlaywright([`-s=${session}`, "--raw", "requests"]).catch((error) => `requests_failed: ${error instanceof Error ? error.message : String(error)}`)
1075
+ await runAxi(["open", `data:text/html,<title>pie-warm-${session}</title>`], { env });
1076
+ await runAxi(["open", url], { env });
1077
+ const [snapshotOutput, pageInfoRaw, screenshotOutput, consoleOutput, networkOutput] = await Promise.all([
1078
+ runAxi(["snapshot"], { env }),
1079
+ // --full: eval output is truncated at ~8KB by default, which would
1080
+ // break JSON parsing and drop title/finalUrl/text on real pages.
1081
+ runAxi(["eval", "() => JSON.stringify({ title: document.title, href: location.href, text: document.body ? document.body.innerText : '' })", "--full"], { env }),
1082
+ runAxi(["screenshot", path2.join(screenshotDir, "page.png")], { env }).catch((error) => `screenshot_failed: ${error instanceof Error ? error.message : String(error)}`),
1083
+ runAxi(["console"], { env }).catch((error) => `console_failed: ${error instanceof Error ? error.message : String(error)}`),
1084
+ runAxi(["network"], { env }).catch((error) => `network_failed: ${error instanceof Error ? error.message : String(error)}`)
1090
1085
  ]);
1091
- const pageInfo = parsePageInfo(pageInfoRaw);
1092
- const text = pageInfo.text?.trim() || snapshot.trim();
1086
+ const pageInfo = parsePageInfo(stripHelpHints(pageInfoRaw));
1087
+ const text = pageInfo.text?.trim() || parseSnapshot(snapshotOutput);
1093
1088
  if (!text) {
1094
- throw new Error("Playwright snapshot returned no text.");
1089
+ throw new Error("chrome-devtools-axi snapshot returned no text.");
1095
1090
  }
1096
1091
  return {
1097
1092
  url,
@@ -1100,18 +1095,18 @@ function createCliRenderedPageReader() {
1100
1095
  text,
1101
1096
  citationAnchors: [{ url: pageInfo.finalUrl || url, title: pageInfo.title, startOffset: 0, endOffset: Math.min(120, text.length) }],
1102
1097
  screenshotPath: parseScreenshotPath(screenshotOutput),
1103
- consoleMessages: linesOf(consoleOutput),
1104
- networkRequests: linesOf(requestsOutput)
1098
+ consoleMessages: linesOf(stripHelpHints(consoleOutput)),
1099
+ networkRequests: linesOf(stripHelpHints(networkOutput))
1105
1100
  };
1106
1101
  } finally {
1107
- runPlaywright([`-s=${session}`, "close"], 1e4).catch(() => void 0);
1102
+ runAxi(["stop"], { env, timeoutMs: 1e4 }).catch(() => void 0);
1108
1103
  }
1109
1104
  };
1110
1105
  }
1111
1106
 
1112
1107
  // src/capabilities/web-research-health.ts
1113
- import fs2 from "node:fs";
1114
- import path2 from "node:path";
1108
+ import fs3 from "node:fs";
1109
+ import path3 from "node:path";
1115
1110
  function isRecord(value) {
1116
1111
  return !!value && typeof value === "object" && !Array.isArray(value);
1117
1112
  }
@@ -1133,11 +1128,11 @@ function parseHealthEntry(value) {
1133
1128
  };
1134
1129
  }
1135
1130
  function createCliWebResearchHealthStore(configDir) {
1136
- const filePath = path2.join(configDir, "web-research-health.json");
1131
+ const filePath = path3.join(configDir, "web-research-health.json");
1137
1132
  return {
1138
1133
  loadProviderHealth: () => {
1139
1134
  try {
1140
- const parsed = JSON.parse(fs2.readFileSync(filePath, "utf8"));
1135
+ const parsed = JSON.parse(fs3.readFileSync(filePath, "utf8"));
1141
1136
  if (!Array.isArray(parsed)) return [];
1142
1137
  return parsed.map(parseHealthEntry).filter((entry) => !!entry);
1143
1138
  } catch {
@@ -1146,8 +1141,8 @@ function createCliWebResearchHealthStore(configDir) {
1146
1141
  },
1147
1142
  saveProviderHealth: (entries) => {
1148
1143
  try {
1149
- fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
1150
- fs2.writeFileSync(filePath, JSON.stringify(entries, null, 2));
1144
+ fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
1145
+ fs3.writeFileSync(filePath, JSON.stringify(entries, null, 2));
1151
1146
  } catch {
1152
1147
  }
1153
1148
  }
@@ -1213,22 +1208,10 @@ function createCliHostCapabilities(cwd, options = {}) {
1213
1208
  }
1214
1209
 
1215
1210
  export {
1216
- getPackageDir,
1217
- getThemesDir,
1218
- getBuiltinDir,
1219
- VERSION,
1220
- getConfigDir,
1221
- getAgentDir,
1222
- getCustomThemesDir,
1223
- getSettingsPath,
1224
- getProjectSettingsPath,
1225
- getSessionsDir,
1226
- getLogsDir,
1227
- getRuntimeLogPath,
1228
- migrateConfigFromAgentDir,
1229
1211
  createCliWebResearchHealthStore,
1230
1212
  truncateHeadTail,
1231
1213
  formatSize,
1232
1214
  cleanupBashOutputArtifacts,
1215
+ createBashTool,
1233
1216
  createCliHostCapabilities
1234
1217
  };