terminal-pilot 0.0.19 → 0.0.21

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 (52) hide show
  1. package/dist/cli.js +1076 -496
  2. package/dist/cli.js.map +4 -4
  3. package/dist/commands/close-session.js.map +1 -1
  4. package/dist/commands/create-session.js.map +1 -1
  5. package/dist/commands/fill.js.map +1 -1
  6. package/dist/commands/get-session.js.map +1 -1
  7. package/dist/commands/index.js +481 -163
  8. package/dist/commands/index.js.map +4 -4
  9. package/dist/commands/install.js +448 -130
  10. package/dist/commands/install.js.map +4 -4
  11. package/dist/commands/installer.js +154 -60
  12. package/dist/commands/installer.js.map +4 -4
  13. package/dist/commands/list-sessions.js.map +1 -1
  14. package/dist/commands/press-key.js.map +1 -1
  15. package/dist/commands/read-history.js.map +1 -1
  16. package/dist/commands/read-screen.js.map +1 -1
  17. package/dist/commands/resize.js.map +1 -1
  18. package/dist/commands/runtime.js.map +1 -1
  19. package/dist/commands/screenshot.js.map +1 -1
  20. package/dist/commands/send-signal.js.map +1 -1
  21. package/dist/commands/type.js.map +1 -1
  22. package/dist/commands/uninstall.js +187 -93
  23. package/dist/commands/uninstall.js.map +4 -4
  24. package/dist/commands/wait-for-exit.js.map +1 -1
  25. package/dist/commands/wait-for.js.map +1 -1
  26. package/dist/testing/cli-repl.js +1072 -492
  27. package/dist/testing/cli-repl.js.map +4 -4
  28. package/dist/testing/qa-cli.js +1082 -502
  29. package/dist/testing/qa-cli.js.map +4 -4
  30. package/node_modules/@poe-code/agent-skill-config/README.md +103 -0
  31. package/node_modules/@poe-code/agent-skill-config/dist/apply.d.ts +25 -0
  32. package/node_modules/@poe-code/agent-skill-config/dist/apply.js +159 -0
  33. package/node_modules/@poe-code/agent-skill-config/dist/bridge-active-skills.d.ts +23 -0
  34. package/node_modules/@poe-code/agent-skill-config/dist/bridge-active-skills.js +341 -0
  35. package/node_modules/@poe-code/agent-skill-config/dist/configs.d.ts +16 -0
  36. package/node_modules/@poe-code/agent-skill-config/dist/configs.js +70 -0
  37. package/node_modules/@poe-code/agent-skill-config/dist/exports.compile-check.d.ts +1 -0
  38. package/node_modules/@poe-code/agent-skill-config/dist/exports.compile-check.js +1 -0
  39. package/node_modules/@poe-code/agent-skill-config/dist/git-exclude.d.ts +8 -0
  40. package/node_modules/@poe-code/agent-skill-config/dist/git-exclude.js +159 -0
  41. package/node_modules/@poe-code/agent-skill-config/dist/index.d.ts +11 -0
  42. package/node_modules/@poe-code/agent-skill-config/dist/index.js +6 -0
  43. package/node_modules/@poe-code/agent-skill-config/dist/resolve-skill-reference.d.ts +22 -0
  44. package/node_modules/@poe-code/agent-skill-config/dist/resolve-skill-reference.js +87 -0
  45. package/node_modules/@poe-code/agent-skill-config/dist/templates/poe-generate.md +47 -0
  46. package/node_modules/@poe-code/agent-skill-config/dist/templates/terminal-pilot.md +45 -0
  47. package/node_modules/@poe-code/agent-skill-config/dist/templates.d.ts +3 -0
  48. package/node_modules/@poe-code/agent-skill-config/dist/templates.js +63 -0
  49. package/node_modules/@poe-code/agent-skill-config/dist/types.d.ts +16 -0
  50. package/node_modules/@poe-code/agent-skill-config/dist/types.js +1 -0
  51. package/node_modules/@poe-code/agent-skill-config/package.json +24 -0
  52. package/package.json +4 -2
package/dist/cli.js CHANGED
@@ -6,13 +6,13 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/cli.ts
9
- import { realpath as realpath2 } from "node:fs/promises";
10
- import path22 from "node:path";
9
+ import { realpath as realpath3 } from "node:fs/promises";
10
+ import path23 from "node:path";
11
11
  import { fileURLToPath as fileURLToPath5 } from "node:url";
12
12
 
13
13
  // ../toolcraft/src/cli.ts
14
14
  import { access as access2, lstat as lstat3, readFile as readFile4, rename as rename3, unlink as unlink2, writeFile as writeFile5 } from "node:fs/promises";
15
- import path13 from "node:path";
15
+ import path14 from "node:path";
16
16
  import {
17
17
  Command as CommanderCommand,
18
18
  CommanderError as CommanderError2,
@@ -20,7 +20,7 @@ import {
20
20
  Option
21
21
  } from "commander";
22
22
 
23
- // ../design-system/src/internal/color-support.ts
23
+ // ../toolcraft-design/src/internal/color-support.ts
24
24
  function supportsColor(env = process.env, stream = process.stdout) {
25
25
  if (env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "0") {
26
26
  return true;
@@ -34,7 +34,7 @@ function supportsColor(env = process.env, stream = process.stdout) {
34
34
  return typeof env.TERM === "string" && env.TERM.length > 0 && env.TERM !== "dumb";
35
35
  }
36
36
 
37
- // ../design-system/src/components/color.ts
37
+ // ../toolcraft-design/src/components/color.ts
38
38
  var reset = "\x1B[0m";
39
39
  var ansiStyles = {
40
40
  reset: { open: reset },
@@ -136,41 +136,135 @@ function createColor(styles = []) {
136
136
  }
137
137
  var color = createColor();
138
138
 
139
- // ../design-system/src/tokens/colors.ts
140
- var dark = {
141
- header: (text5) => color.magentaBright.bold(text5),
142
- divider: (text5) => color.dim(text5),
143
- prompt: (text5) => color.cyan(text5),
144
- number: (text5) => color.cyanBright(text5),
145
- intro: (text5) => color.bgMagenta.white(` Poe - ${text5} `),
146
- resolvedSymbol: color.magenta("\u25C7"),
147
- errorSymbol: color.red("\u25A0"),
148
- accent: (text5) => color.cyan(text5),
149
- muted: (text5) => color.dim(text5),
150
- success: (text5) => color.green(text5),
151
- warning: (text5) => color.yellow(text5),
152
- error: (text5) => color.red(text5),
153
- info: (text5) => color.magenta(text5),
154
- badge: (text5) => color.bgYellow.black(` ${text5} `)
139
+ // ../toolcraft-design/src/tokens/brand.ts
140
+ var brands = {
141
+ purple: { name: "purple", primary: "#a200ff" },
142
+ blue: { name: "blue", primary: "#2f6fed" },
143
+ green: { name: "green", primary: "#1f9d57" }
155
144
  };
156
- var light = {
157
- header: (text5) => color.hex("#a200ff").bold(text5),
158
- divider: (text5) => color.hex("#666666")(text5),
159
- prompt: (text5) => color.hex("#006699").bold(text5),
160
- number: (text5) => color.hex("#0077cc").bold(text5),
161
- intro: (text5) => color.bgHex("#a200ff").white(` Poe - ${text5} `),
162
- resolvedSymbol: color.hex("#a200ff")("\u25C7"),
163
- errorSymbol: color.hex("#cc0000")("\u25A0"),
164
- accent: (text5) => color.hex("#006699").bold(text5),
165
- muted: (text5) => color.hex("#666666")(text5),
166
- success: (text5) => color.hex("#008800")(text5),
167
- warning: (text5) => color.hex("#cc6600")(text5),
168
- error: (text5) => color.hex("#cc0000")(text5),
169
- info: (text5) => color.hex("#a200ff")(text5),
170
- badge: (text5) => color.bgHex("#cc6600").white(` ${text5} `)
145
+
146
+ // ../toolcraft-design/src/internal/theme-state.ts
147
+ var defaults = {
148
+ brand: "purple",
149
+ label: "Poe"
171
150
  };
151
+ var config = { ...defaults };
152
+ var revision = 0;
153
+ var brandConfigured = false;
154
+ function configureTheme(patch) {
155
+ if (patch.brand !== void 0 && !Object.hasOwn(brands, patch.brand)) {
156
+ throw new Error(`Unknown brand: ${patch.brand}`);
157
+ }
158
+ config = {
159
+ brand: patch.brand ?? config.brand,
160
+ label: patch.label ?? config.label
161
+ };
162
+ if (patch.brand !== void 0) {
163
+ brandConfigured = true;
164
+ }
165
+ revision += 1;
166
+ }
167
+ function getThemeConfig() {
168
+ return { ...config };
169
+ }
170
+ function getThemeRevision() {
171
+ return revision;
172
+ }
173
+ function isThemeBrandConfigured() {
174
+ return brandConfigured;
175
+ }
176
+
177
+ // ../toolcraft-design/src/tokens/colors.ts
178
+ var brand = brands.purple.primary;
179
+ function withStyles(palette, styles) {
180
+ return Object.defineProperty(palette, "styles", {
181
+ value: styles,
182
+ enumerable: false
183
+ });
184
+ }
185
+ function brandColor(activeBrand, purple) {
186
+ return activeBrand.name === "purple" ? purple : color.hex(activeBrand.primary);
187
+ }
188
+ function brandBackground(activeBrand, purple) {
189
+ return activeBrand.name === "purple" ? purple : color.bgHex(activeBrand.primary);
190
+ }
191
+ function createPalette(activeBrand, mode) {
192
+ const isPurple = activeBrand.name === "purple";
193
+ if (mode === "light") {
194
+ const active2 = color.hex(activeBrand.primary);
195
+ const prompt2 = isPurple ? color.hex("#006699") : active2;
196
+ const number2 = isPurple ? color.hex("#0077cc") : active2;
197
+ return withStyles(
198
+ {
199
+ header: (text5) => active2.bold(text5),
200
+ divider: (text5) => color.hex("#666666")(text5),
201
+ prompt: (text5) => prompt2.bold(text5),
202
+ number: (text5) => number2.bold(text5),
203
+ intro: (text5) => color.bgHex(activeBrand.primary).white(` ${getThemeConfig().label} - ${text5} `),
204
+ get resolvedSymbol() {
205
+ return active2("\u25C7");
206
+ },
207
+ get errorSymbol() {
208
+ return color.hex("#cc0000")("\u25A0");
209
+ },
210
+ accent: (text5) => prompt2.bold(text5),
211
+ muted: (text5) => color.hex("#666666")(text5),
212
+ success: (text5) => color.hex("#008800")(text5),
213
+ warning: (text5) => color.hex("#cc6600")(text5),
214
+ error: (text5) => color.hex("#cc0000")(text5),
215
+ info: (text5) => active2(text5),
216
+ badge: (text5) => color.bgHex("#cc6600").white(` ${text5} `)
217
+ },
218
+ {
219
+ accent: { fg: isPurple ? "#006699" : activeBrand.primary, bold: true },
220
+ muted: { fg: "#666666" },
221
+ success: { fg: "#008800" },
222
+ warning: { fg: "#cc6600" },
223
+ error: { fg: "#cc0000" },
224
+ info: { fg: activeBrand.primary }
225
+ }
226
+ );
227
+ }
228
+ const active = brandColor(activeBrand, color.magenta);
229
+ const activeBright = brandColor(activeBrand, color.magentaBright);
230
+ const activeBackground = brandBackground(activeBrand, color.bgMagenta);
231
+ const prompt = isPurple ? color.cyan : active;
232
+ const number = isPurple ? color.cyanBright : active;
233
+ return withStyles(
234
+ {
235
+ header: (text5) => activeBright.bold(text5),
236
+ divider: (text5) => color.dim(text5),
237
+ prompt: (text5) => prompt(text5),
238
+ number: (text5) => number(text5),
239
+ intro: (text5) => activeBackground.white(` ${getThemeConfig().label} - ${text5} `),
240
+ get resolvedSymbol() {
241
+ return active("\u25C7");
242
+ },
243
+ get errorSymbol() {
244
+ return color.red("\u25A0");
245
+ },
246
+ accent: (text5) => prompt(text5),
247
+ muted: (text5) => color.dim(text5),
248
+ success: (text5) => color.green(text5),
249
+ warning: (text5) => color.yellow(text5),
250
+ error: (text5) => color.red(text5),
251
+ info: (text5) => active(text5),
252
+ badge: (text5) => color.bgYellow.black(` ${text5} `)
253
+ },
254
+ {
255
+ accent: { fg: isPurple ? "cyan" : activeBrand.primary, bold: true },
256
+ muted: { dim: true },
257
+ success: { fg: "green" },
258
+ warning: { fg: "yellow" },
259
+ error: { fg: "red" },
260
+ info: { fg: isPurple ? "magenta" : activeBrand.primary }
261
+ }
262
+ );
263
+ }
264
+ var dark = createPalette(brands.purple, "dark");
265
+ var light = createPalette(brands.purple, "light");
172
266
 
173
- // ../design-system/src/tokens/typography.ts
267
+ // ../toolcraft-design/src/tokens/typography.ts
174
268
  var typography = {
175
269
  bold: (text5) => color.bold(text5),
176
270
  dim: (text5) => color.dim(text5),
@@ -179,14 +273,14 @@ var typography = {
179
273
  strikethrough: (text5) => color.strikethrough(text5)
180
274
  };
181
275
 
182
- // ../design-system/src/tokens/widths.ts
276
+ // ../toolcraft-design/src/tokens/widths.ts
183
277
  var widths = {
184
278
  header: 60,
185
279
  helpColumn: 24,
186
280
  maxLine: 80
187
281
  };
188
282
 
189
- // ../design-system/src/internal/output-format.ts
283
+ // ../toolcraft-design/src/internal/output-format.ts
190
284
  import { AsyncLocalStorage } from "node:async_hooks";
191
285
  var VALID_FORMATS = /* @__PURE__ */ new Set(["terminal", "markdown", "json"]);
192
286
  var formatStorage = new AsyncLocalStorage();
@@ -207,7 +301,7 @@ function resetOutputFormatCache() {
207
301
  cached = void 0;
208
302
  }
209
303
 
210
- // ../design-system/src/internal/theme-detect.ts
304
+ // ../toolcraft-design/src/internal/theme-detect.ts
211
305
  function detectThemeFromEnv(env) {
212
306
  const apple = env.APPLE_INTERFACE_STYLE;
213
307
  if (typeof apple === "string") {
@@ -244,17 +338,30 @@ function resolveThemeName(env = process.env) {
244
338
  }
245
339
  return "dark";
246
340
  }
247
- var cachedTheme;
341
+ var themeCache = /* @__PURE__ */ new Map();
342
+ var cachedRevision = -1;
248
343
  function getTheme(env) {
344
+ const themeName = resolveThemeName(env);
345
+ const config2 = getThemeConfig();
346
+ const requestedBrand = env?.POE_BRAND?.toLowerCase();
347
+ const activeBrandName = !isThemeBrandConfigured() && requestedBrand && Object.hasOwn(brands, requestedBrand) ? requestedBrand : config2.brand;
348
+ const revision2 = getThemeRevision();
349
+ if (revision2 !== cachedRevision) {
350
+ themeCache.clear();
351
+ cachedRevision = revision2;
352
+ }
353
+ const cacheKey = `${activeBrandName}:${themeName}`;
354
+ const cachedTheme = themeCache.get(cacheKey);
249
355
  if (cachedTheme) {
250
356
  return cachedTheme;
251
357
  }
252
- const themeName = resolveThemeName(env);
253
- cachedTheme = themeName === "light" ? light : dark;
254
- return cachedTheme;
358
+ const activeBrand = brands[activeBrandName];
359
+ const theme = activeBrandName === "purple" ? themeName === "light" ? light : dark : createPalette(activeBrand, themeName);
360
+ themeCache.set(cacheKey, theme);
361
+ return theme;
255
362
  }
256
363
 
257
- // ../design-system/src/components/text.ts
364
+ // ../toolcraft-design/src/components/text.ts
258
365
  function renderMarkdownInline(content) {
259
366
  return content.replaceAll("\r\n", " ").replaceAll("\n", " ").replaceAll("\r", " ");
260
367
  }
@@ -370,7 +477,7 @@ var text = {
370
477
  }
371
478
  };
372
479
 
373
- // ../design-system/src/components/symbols.ts
480
+ // ../toolcraft-design/src/components/symbols.ts
374
481
  var symbols = {
375
482
  get info() {
376
483
  const format = resolveOutputFormat();
@@ -424,12 +531,12 @@ var symbols = {
424
531
  }
425
532
  };
426
533
 
427
- // ../design-system/src/internal/strip-ansi.ts
534
+ // ../toolcraft-design/src/internal/strip-ansi.ts
428
535
  function stripAnsi(value) {
429
536
  return value.replace(/\u001b\[[0-9;]*m/g, "");
430
537
  }
431
538
 
432
- // ../design-system/src/prompts/primitives/log.ts
539
+ // ../toolcraft-design/src/prompts/primitives/log.ts
433
540
  function renderMarkdownInline2(value) {
434
541
  return stripAnsi(value).replaceAll("\r\n", " ").replaceAll("\n", " ").replaceAll("\r", " ");
435
542
  }
@@ -556,7 +663,7 @@ var log = {
556
663
  error
557
664
  };
558
665
 
559
- // ../design-system/src/components/logger.ts
666
+ // ../toolcraft-design/src/components/logger.ts
560
667
  function createLogger(emitter) {
561
668
  const emit = (level, message2) => {
562
669
  if (emitter) {
@@ -617,7 +724,7 @@ function createLogger(emitter) {
617
724
  }
618
725
  var logger = createLogger();
619
726
 
620
- // ../design-system/src/components/help-formatter.ts
727
+ // ../toolcraft-design/src/components/help-formatter.ts
621
728
  var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
622
729
  function normalizeInline(value) {
623
730
  return value.replaceAll("\r\n", " ").replaceAll("\n", " ").replaceAll("\r", " ");
@@ -793,7 +900,7 @@ function formatOptionList(options) {
793
900
  });
794
901
  }
795
902
 
796
- // ../design-system/src/components/help-formatter-plain.ts
903
+ // ../toolcraft-design/src/components/help-formatter-plain.ts
797
904
  var help_formatter_plain_exports = {};
798
905
  __export(help_formatter_plain_exports, {
799
906
  formatColumns: () => formatColumns2,
@@ -930,7 +1037,7 @@ function formatOptionList2(options) {
930
1037
  });
931
1038
  }
932
1039
 
933
- // ../design-system/src/components/table.ts
1040
+ // ../toolcraft-design/src/components/table.ts
934
1041
  var reset2 = "\x1B[0m";
935
1042
  var ellipsis = "\u2026";
936
1043
  var graphemeSegmenter2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
@@ -1214,7 +1321,7 @@ function renderTable(options) {
1214
1321
  }
1215
1322
  }
1216
1323
 
1217
- // ../design-system/src/components/detail-card.ts
1324
+ // ../toolcraft-design/src/components/detail-card.ts
1218
1325
  function wrap(value, width) {
1219
1326
  const lines = [];
1220
1327
  for (const paragraph of value.split("\n")) {
@@ -1277,7 +1384,8 @@ ${options.theme.muted(options.badges.map((badge) => badge[0] + badge.slice(1).to
1277
1384
  return blocks.join("\n\n");
1278
1385
  }
1279
1386
 
1280
- // ../design-system/src/components/template.ts
1387
+ // ../toolcraft-design/src/components/template.ts
1388
+ var MAX_PARTIAL_DEPTH = 100;
1281
1389
  var HTML_ESCAPE = {
1282
1390
  "&": "&",
1283
1391
  "<": "&lt;",
@@ -1289,14 +1397,27 @@ var HTML_ESCAPE = {
1289
1397
  "=": "&#x3D;"
1290
1398
  };
1291
1399
  function renderTemplate(template, view, options = {}) {
1292
- const prepared = options.yield === void 0 ? template : template.split("{{yield}}").join(options.yield);
1400
+ const prepared = options.yield === void 0 ? template : resolveTemplatePartials(template, options.partials ?? {}).split("{{yield}}").join(options.yield);
1293
1401
  const tokens = parseTemplate(prepared);
1294
- const escape = options.escape === "none" ? String : escapeHtml;
1295
- const preserveMissing = options.yield !== void 0 && options.escape === "none";
1296
- return renderTokens(tokens, { view }, prepared, escape, preserveMissing);
1402
+ validatePartialReferences(tokens, options.partials ?? {}, []);
1403
+ if (options.validate === true) {
1404
+ const expanded = resolveTemplatePartials(prepared, options.partials ?? {});
1405
+ validateVariables(parseTemplate(expanded), { view });
1406
+ }
1407
+ const state = {
1408
+ escape: options.escape === "none" ? String : escapeHtml,
1409
+ partials: options.partials ?? {},
1410
+ partialStack: [],
1411
+ preserveMissing: options.yield !== void 0 && options.escape === "none",
1412
+ validate: options.validate === true
1413
+ };
1414
+ return renderTokens(tokens, { view }, prepared, state);
1415
+ }
1416
+ function resolveTemplatePartials(template, partials) {
1417
+ return expandTemplatePartials(template, partials, []);
1297
1418
  }
1298
- function renderTemplateInContext(template, context, escape, preserveMissing) {
1299
- return renderTokens(parseTemplate(template), context, template, escape, preserveMissing);
1419
+ function renderTemplateInContext(template, context, state) {
1420
+ return renderTokens(parseTemplate(template), context, template, state);
1300
1421
  }
1301
1422
  function parseTemplate(template) {
1302
1423
  const root = [];
@@ -1319,9 +1440,6 @@ function parseTemplate(template) {
1319
1440
  index = standalone?.nextIndex ?? parsed.end;
1320
1441
  continue;
1321
1442
  }
1322
- if (parsed.kind === "partial") {
1323
- throw new Error(`Partials are not supported: "${parsed.name}"`);
1324
- }
1325
1443
  if (parsed.kind === "delimiter") {
1326
1444
  throw new Error("Custom delimiters are not supported");
1327
1445
  }
@@ -1339,6 +1457,15 @@ function parseTemplate(template) {
1339
1457
  index = standalone?.nextIndex ?? parsed.end;
1340
1458
  continue;
1341
1459
  }
1460
+ if (parsed.kind === "partial") {
1461
+ tokens.push({
1462
+ type: "partial",
1463
+ name: parsed.name,
1464
+ indent: standalone === void 0 ? "" : template.slice(standalone.lineStart, open)
1465
+ });
1466
+ index = standalone?.nextIndex ?? parsed.end;
1467
+ continue;
1468
+ }
1342
1469
  if (parsed.kind === "close") {
1343
1470
  const frame2 = stack.pop();
1344
1471
  if (frame2 === void 0) {
@@ -1409,7 +1536,7 @@ function getStandalone(template, tagStart, tagEnd, kind) {
1409
1536
  }
1410
1537
  return void 0;
1411
1538
  }
1412
- function renderTokens(tokens, context, template, escape, preserveMissing) {
1539
+ function renderTokens(tokens, context, template, state) {
1413
1540
  let output = "";
1414
1541
  for (const token of tokens) {
1415
1542
  switch (token.type) {
@@ -1418,32 +1545,62 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
1418
1545
  continue;
1419
1546
  case "name":
1420
1547
  case "unescaped": {
1421
- const value = lookup(context, token.name);
1422
- if (value == null) {
1423
- if (preserveMissing) {
1548
+ const result = lookup(context, token.name);
1549
+ if (!result.hit || result.value == null) {
1550
+ if (state.validate) {
1551
+ throw new Error(`Template variable "${token.name}" not found.`);
1552
+ }
1553
+ if (state.preserveMissing) {
1424
1554
  output += token.raw;
1425
1555
  }
1426
1556
  continue;
1427
1557
  }
1428
- const rendered = String(value);
1429
- output += token.type === "name" ? escape(rendered) : rendered;
1558
+ const rendered = String(result.value);
1559
+ output += token.type === "name" ? state.escape(rendered) : rendered;
1560
+ continue;
1561
+ }
1562
+ case "partial": {
1563
+ if (!Object.hasOwn(state.partials, token.name)) {
1564
+ throw new Error(`Partial "${token.name}" not found.`);
1565
+ }
1566
+ if (state.partialStack.includes(token.name)) {
1567
+ throw new Error(
1568
+ `Circular partial reference detected: ${[...state.partialStack, token.name].join(" -> ")}.`
1569
+ );
1570
+ }
1571
+ if (state.partialStack.length >= MAX_PARTIAL_DEPTH) {
1572
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
1573
+ }
1574
+ const partial = indentPartial(state.partials[token.name], token.indent);
1575
+ output += renderTokens(parseTemplate(partial), context, partial, {
1576
+ ...state,
1577
+ partialStack: [...state.partialStack, token.name]
1578
+ });
1430
1579
  continue;
1431
1580
  }
1432
1581
  case "inverted": {
1433
- const value = lookup(context, token.name);
1582
+ const result = lookup(context, token.name);
1583
+ if (!result.hit && state.validate) {
1584
+ throw new Error(`Template variable "${token.name}" not found.`);
1585
+ }
1586
+ const value = result.value;
1434
1587
  if (!value || Array.isArray(value) && value.length === 0) {
1435
- output += renderTokens(token.children, context, template, escape, preserveMissing);
1588
+ output += renderTokens(token.children, context, template, state);
1436
1589
  }
1437
1590
  continue;
1438
1591
  }
1439
1592
  case "section": {
1440
- const value = lookup(context, token.name);
1593
+ const result = lookup(context, token.name);
1594
+ if (!result.hit && state.validate) {
1595
+ throw new Error(`Template variable "${token.name}" not found.`);
1596
+ }
1597
+ const value = result.value;
1441
1598
  if (!value) {
1442
1599
  continue;
1443
1600
  }
1444
1601
  if (Array.isArray(value)) {
1445
1602
  for (const item of value) {
1446
- output += renderTokens(token.children, pushContext(context, item), template, escape, preserveMissing);
1603
+ output += renderTokens(token.children, pushContext(context, item), template, state);
1447
1604
  }
1448
1605
  continue;
1449
1606
  }
@@ -1452,7 +1609,7 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
1452
1609
  const rendered = value.call(
1453
1610
  context.view,
1454
1611
  raw,
1455
- (nextTemplate) => renderTemplateInContext(nextTemplate, context, escape, preserveMissing)
1612
+ (nextTemplate) => renderTemplateInContext(nextTemplate, context, state)
1456
1613
  );
1457
1614
  if (rendered != null) {
1458
1615
  output += String(rendered);
@@ -1460,10 +1617,10 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
1460
1617
  continue;
1461
1618
  }
1462
1619
  if (typeof value === "object" || typeof value === "string" || typeof value === "number") {
1463
- output += renderTokens(token.children, pushContext(context, value), template, escape, preserveMissing);
1620
+ output += renderTokens(token.children, pushContext(context, value), template, state);
1464
1621
  continue;
1465
1622
  }
1466
- output += renderTokens(token.children, context, template, escape, preserveMissing);
1623
+ output += renderTokens(token.children, context, template, state);
1467
1624
  }
1468
1625
  }
1469
1626
  }
@@ -1471,17 +1628,115 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
1471
1628
  }
1472
1629
  function lookup(context, name) {
1473
1630
  if (name === ".") {
1474
- return callLambda(context.view, context.view);
1631
+ return { hit: true, value: callLambda(context.view, context.view) };
1475
1632
  }
1476
1633
  let cursor = context;
1477
1634
  while (cursor !== void 0) {
1478
1635
  const result = name.includes(".") ? lookupDotted(cursor.view, name) : lookupName(cursor.view, name);
1479
1636
  if (result.hit) {
1480
- return callLambda(result.value, cursor.view);
1637
+ return { hit: true, value: callLambda(result.value, cursor.view) };
1481
1638
  }
1482
1639
  cursor = cursor.parent;
1483
1640
  }
1484
- return void 0;
1641
+ return { hit: false, value: void 0 };
1642
+ }
1643
+ function validateVariables(tokens, context) {
1644
+ for (const token of tokens) {
1645
+ if (token.type === "text" || token.type === "partial") {
1646
+ continue;
1647
+ }
1648
+ if (token.type === "name" || token.type === "unescaped") {
1649
+ if (!lookup(context, token.name).hit) {
1650
+ throw new Error(`Template variable "${token.name}" not found.`);
1651
+ }
1652
+ continue;
1653
+ }
1654
+ if (token.type !== "section" && token.type !== "inverted") {
1655
+ continue;
1656
+ }
1657
+ const result = lookup(context, token.name);
1658
+ if (!result.hit) {
1659
+ throw new Error(`Template variable "${token.name}" not found.`);
1660
+ }
1661
+ if (Array.isArray(result.value) && result.value.length > 0) {
1662
+ for (const item of result.value) {
1663
+ validateVariables(token.children, pushContext(context, item));
1664
+ }
1665
+ continue;
1666
+ }
1667
+ if (typeof result.value === "object" && result.value !== null || typeof result.value === "string" || typeof result.value === "number") {
1668
+ validateVariables(token.children, pushContext(context, result.value));
1669
+ continue;
1670
+ }
1671
+ validateVariables(token.children, context);
1672
+ }
1673
+ }
1674
+ function validatePartialReferences(tokens, partials, partialStack) {
1675
+ for (const token of tokens) {
1676
+ if (token.type === "section" || token.type === "inverted") {
1677
+ validatePartialReferences(token.children, partials, partialStack);
1678
+ continue;
1679
+ }
1680
+ if (token.type !== "partial") {
1681
+ continue;
1682
+ }
1683
+ if (!Object.hasOwn(partials, token.name)) {
1684
+ throw new Error(`Partial "${token.name}" not found.`);
1685
+ }
1686
+ if (partialStack.includes(token.name)) {
1687
+ throw new Error(
1688
+ `Circular partial reference detected: ${[...partialStack, token.name].join(" -> ")}.`
1689
+ );
1690
+ }
1691
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
1692
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
1693
+ }
1694
+ validatePartialReferences(parseTemplate(partials[token.name]), partials, [
1695
+ ...partialStack,
1696
+ token.name
1697
+ ]);
1698
+ }
1699
+ }
1700
+ function indentPartial(partial, indent) {
1701
+ if (indent === "") {
1702
+ return partial;
1703
+ }
1704
+ return partial.split("\n").map((line) => line === "" ? "" : `${indent}${line}`).join("\n");
1705
+ }
1706
+ function expandTemplatePartials(template, partials, partialStack) {
1707
+ let output = "";
1708
+ let index = 0;
1709
+ while (index < template.length) {
1710
+ const open = template.indexOf("{{", index);
1711
+ if (open === -1) {
1712
+ output += template.slice(index);
1713
+ break;
1714
+ }
1715
+ const parsed = parseTag(template, open);
1716
+ if (parsed.kind !== "partial") {
1717
+ output += template.slice(index, parsed.end);
1718
+ index = parsed.end;
1719
+ continue;
1720
+ }
1721
+ if (!Object.hasOwn(partials, parsed.name)) {
1722
+ throw new Error(`Partial "${parsed.name}" not found.`);
1723
+ }
1724
+ if (partialStack.includes(parsed.name)) {
1725
+ throw new Error(
1726
+ `Circular partial reference detected: ${[...partialStack, parsed.name].join(" -> ")}.`
1727
+ );
1728
+ }
1729
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
1730
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
1731
+ }
1732
+ const standalone = getStandalone(template, open, parsed.end, parsed.kind);
1733
+ const beforePartial = standalone === void 0 ? template.slice(index, open) : template.slice(index, standalone.lineStart);
1734
+ const indent = standalone === void 0 ? "" : template.slice(standalone.lineStart, open);
1735
+ const partial = indentPartial(partials[parsed.name], indent);
1736
+ output += beforePartial + expandTemplatePartials(partial, partials, [...partialStack, parsed.name]);
1737
+ index = standalone?.nextIndex ?? parsed.end;
1738
+ }
1739
+ return output;
1485
1740
  }
1486
1741
  function lookupName(view, name) {
1487
1742
  if (!isPropertyContainer(view) || !hasProperty(view, name)) {
@@ -1541,22 +1796,22 @@ function hasProperty(value, key2) {
1541
1796
  return Object.prototype.hasOwnProperty.call(value, key2);
1542
1797
  }
1543
1798
 
1544
- // ../design-system/src/components/browser.ts
1799
+ // ../toolcraft-design/src/components/browser.ts
1545
1800
  import { spawn } from "node:child_process";
1546
1801
  import process2 from "node:process";
1547
1802
 
1548
- // ../design-system/src/acp/writer.ts
1803
+ // ../toolcraft-design/src/acp/writer.ts
1549
1804
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
1550
1805
  var storage = new AsyncLocalStorage2();
1551
1806
 
1552
- // ../design-system/src/dashboard/terminal-width.ts
1807
+ // ../toolcraft-design/src/dashboard/terminal-width.ts
1553
1808
  var graphemeSegmenter3 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
1554
1809
 
1555
- // ../design-system/src/dashboard/terminal.ts
1810
+ // ../toolcraft-design/src/dashboard/terminal.ts
1556
1811
  import readline from "node:readline";
1557
1812
  import { PassThrough } from "node:stream";
1558
1813
 
1559
- // ../design-system/src/explorer/state.ts
1814
+ // ../toolcraft-design/src/explorer/state.ts
1560
1815
  var REGION_HEADER = 1 << 0;
1561
1816
  var REGION_LIST = 1 << 1;
1562
1817
  var REGION_DETAIL = 1 << 2;
@@ -1565,10 +1820,10 @@ var REGION_MODAL = 1 << 4;
1565
1820
  var REGION_TOAST = 1 << 5;
1566
1821
  var REGION_ALL = REGION_HEADER | REGION_LIST | REGION_DETAIL | REGION_FOOTER | REGION_MODAL | REGION_TOAST;
1567
1822
 
1568
- // ../design-system/src/prompts/index.ts
1823
+ // ../toolcraft-design/src/prompts/index.ts
1569
1824
  import * as clack from "@clack/prompts";
1570
1825
 
1571
- // ../design-system/src/prompts/primitives/cancel.ts
1826
+ // ../toolcraft-design/src/prompts/primitives/cancel.ts
1572
1827
  import { isCancel } from "@clack/prompts";
1573
1828
  function cancel(msg = "") {
1574
1829
  if (resolveOutputFormat() !== "terminal") {
@@ -1579,7 +1834,7 @@ function cancel(msg = "") {
1579
1834
  `);
1580
1835
  }
1581
1836
 
1582
- // ../design-system/src/prompts/primitives/note.ts
1837
+ // ../toolcraft-design/src/prompts/primitives/note.ts
1583
1838
  function getVisibleWidth(value) {
1584
1839
  return stripAnsi(value).length;
1585
1840
  }
@@ -1628,10 +1883,10 @@ function note(message2, title) {
1628
1883
  `);
1629
1884
  }
1630
1885
 
1631
- // ../design-system/src/static/spinner.ts
1886
+ // ../toolcraft-design/src/static/spinner.ts
1632
1887
  var SPINNER_FRAMES = Object.freeze(["\u25D2", "\u25D0", "\u25D3", "\u25D1"]);
1633
1888
 
1634
- // ../design-system/src/prompts/index.ts
1889
+ // ../toolcraft-design/src/prompts/index.ts
1635
1890
  async function select2(opts) {
1636
1891
  return clack.select(opts);
1637
1892
  }
@@ -1674,19 +1929,19 @@ var ApprovalDeclinedError = class extends UserError {
1674
1929
  };
1675
1930
 
1676
1931
  // ../toolcraft/src/human-in-loop/config.ts
1677
- function validateHumanInLoopOnDefine(config) {
1678
- const label = Array.isArray(config.children) ? "group" : "command";
1679
- if (config.confirm === true && config.humanInLoop !== void 0 && config.humanInLoop !== null) {
1680
- throw new Error(`${label} '${config.name}': use either confirm or humanInLoop, not both`);
1932
+ function validateHumanInLoopOnDefine(config2) {
1933
+ const label = Array.isArray(config2.children) ? "group" : "command";
1934
+ if (config2.confirm === true && config2.humanInLoop !== void 0 && config2.humanInLoop !== null) {
1935
+ throw new Error(`${label} '${config2.name}': use either confirm or humanInLoop, not both`);
1681
1936
  }
1682
- if (config.humanInLoop === void 0 || config.humanInLoop === null) {
1937
+ if (config2.humanInLoop === void 0 || config2.humanInLoop === null) {
1683
1938
  return;
1684
1939
  }
1685
- if (config.humanInLoop.mode !== "sync" && config.humanInLoop.mode !== "async") {
1686
- throw new Error(`${label} '${config.name}': humanInLoop.mode must be "sync" or "async"`);
1940
+ if (config2.humanInLoop.mode !== "sync" && config2.humanInLoop.mode !== "async") {
1941
+ throw new Error(`${label} '${config2.name}': humanInLoop.mode must be "sync" or "async"`);
1687
1942
  }
1688
- if (typeof config.humanInLoop.message !== "function") {
1689
- throw new Error(`${label} '${config.name}': humanInLoop.message must be a function`);
1943
+ if (typeof config2.humanInLoop.message !== "function") {
1944
+ throw new Error(`${label} '${config2.name}': humanInLoop.message must be a function`);
1690
1945
  }
1691
1946
  }
1692
1947
  function mergeHumanInLoopFromGroup(groupHumanInLoop, childHumanInLoop) {
@@ -1762,12 +2017,12 @@ function assertValidBranches(branches, discriminator) {
1762
2017
  }
1763
2018
  }
1764
2019
  }
1765
- function OneOf(config) {
1766
- assertValidBranches(config.branches, config.discriminator);
2020
+ function OneOf(config2) {
2021
+ assertValidBranches(config2.branches, config2.discriminator);
1767
2022
  return {
1768
2023
  kind: "oneOf",
1769
- discriminator: config.discriminator,
1770
- branches: config.branches
2024
+ discriminator: config2.discriminator,
2025
+ branches: config2.branches
1771
2026
  };
1772
2027
  }
1773
2028
 
@@ -2017,22 +2272,22 @@ function cloneStringArray(values) {
2017
2272
  function cloneStringRecord(values) {
2018
2273
  return values === void 0 ? void 0 : { ...values };
2019
2274
  }
2020
- function cloneMcpServerConfig(config) {
2021
- if (config === void 0) {
2275
+ function cloneMcpServerConfig(config2) {
2276
+ if (config2 === void 0) {
2022
2277
  return void 0;
2023
2278
  }
2024
- if (config.transport === "stdio") {
2279
+ if (config2.transport === "stdio") {
2025
2280
  return {
2026
2281
  transport: "stdio",
2027
- command: config.command,
2028
- args: cloneStringArray(config.args),
2029
- env: cloneStringRecord(config.env)
2282
+ command: config2.command,
2283
+ args: cloneStringArray(config2.args),
2284
+ env: cloneStringRecord(config2.env)
2030
2285
  };
2031
2286
  }
2032
2287
  return {
2033
2288
  transport: "http",
2034
- url: config.url,
2035
- headers: cloneStringRecord(config.headers)
2289
+ url: config2.url,
2290
+ headers: cloneStringRecord(config2.headers)
2036
2291
  };
2037
2292
  }
2038
2293
  function cloneRenameMap(rename5) {
@@ -2273,57 +2528,57 @@ function resolveCommandScope(ownScope, inheritedScope) {
2273
2528
  function resolveGroupScope(ownScope, inheritedScope) {
2274
2529
  return cloneScope(ownScope ?? inheritedScope);
2275
2530
  }
2276
- function createBaseCommand(config) {
2531
+ function createBaseCommand(config2) {
2277
2532
  const command = {
2278
2533
  kind: "command",
2279
- name: config.name,
2280
- description: config.description,
2281
- aliases: [...config.aliases ?? []],
2282
- positional: [...config.positional ?? []],
2283
- params: config.params,
2284
- secrets: cloneSecrets(config.secrets),
2285
- scope: resolveCommandScope(config.scope, void 0),
2286
- confirm: config.confirm ?? false,
2287
- humanInLoop: config.humanInLoop,
2288
- requires: cloneRequires(config.requires),
2289
- handler: config.handler,
2290
- render: config.render
2534
+ name: config2.name,
2535
+ description: config2.description,
2536
+ aliases: [...config2.aliases ?? []],
2537
+ positional: [...config2.positional ?? []],
2538
+ params: config2.params,
2539
+ secrets: cloneSecrets(config2.secrets),
2540
+ scope: resolveCommandScope(config2.scope, void 0),
2541
+ confirm: config2.confirm ?? false,
2542
+ humanInLoop: config2.humanInLoop,
2543
+ requires: cloneRequires(config2.requires),
2544
+ handler: config2.handler,
2545
+ render: config2.render
2291
2546
  };
2292
2547
  Object.defineProperty(command, commandConfigSymbol, {
2293
2548
  value: {
2294
- scope: cloneScope(config.scope),
2295
- humanInLoop: config.humanInLoop,
2296
- secrets: cloneSecrets(config.secrets),
2297
- requires: cloneRequires(config.requires),
2549
+ scope: cloneScope(config2.scope),
2550
+ humanInLoop: config2.humanInLoop,
2551
+ secrets: cloneSecrets(config2.secrets),
2552
+ requires: cloneRequires(config2.requires),
2298
2553
  sourcePath: inferCommandSourcePath()
2299
2554
  }
2300
2555
  });
2301
2556
  return command;
2302
2557
  }
2303
- function createBaseGroup(config) {
2558
+ function createBaseGroup(config2) {
2304
2559
  const group = {
2305
2560
  kind: "group",
2306
- name: config.name,
2307
- description: config.description,
2308
- aliases: [...config.aliases ?? []],
2309
- scope: resolveGroupScope(config.scope, void 0),
2310
- humanInLoop: config.humanInLoop,
2311
- secrets: cloneSecrets(config.secrets),
2312
- requires: cloneRequires(config.requires),
2561
+ name: config2.name,
2562
+ description: config2.description,
2563
+ aliases: [...config2.aliases ?? []],
2564
+ scope: resolveGroupScope(config2.scope, void 0),
2565
+ humanInLoop: config2.humanInLoop,
2566
+ secrets: cloneSecrets(config2.secrets),
2567
+ requires: cloneRequires(config2.requires),
2313
2568
  children: [],
2314
2569
  default: void 0
2315
2570
  };
2316
2571
  Object.defineProperty(group, groupConfigSymbol, {
2317
2572
  value: {
2318
- mcp: cloneMcpServerConfig(config.mcp),
2319
- scope: cloneScope(config.scope),
2320
- humanInLoop: config.humanInLoop,
2321
- secrets: cloneSecrets(config.secrets),
2322
- tools: cloneStringArray(config.tools),
2323
- rename: cloneRenameMap(config.rename),
2324
- requires: cloneRequires(config.requires),
2325
- children: [...config.children],
2326
- default: config.default
2573
+ mcp: cloneMcpServerConfig(config2.mcp),
2574
+ scope: cloneScope(config2.scope),
2575
+ humanInLoop: config2.humanInLoop,
2576
+ secrets: cloneSecrets(config2.secrets),
2577
+ tools: cloneStringArray(config2.tools),
2578
+ rename: cloneRenameMap(config2.rename),
2579
+ requires: cloneRequires(config2.requires),
2580
+ children: [...config2.children],
2581
+ default: config2.default
2327
2582
  }
2328
2583
  });
2329
2584
  return group;
@@ -2426,10 +2681,10 @@ function materializeNode(node, inherited) {
2426
2681
  }
2427
2682
  return materializeGroup(node, inherited);
2428
2683
  }
2429
- function defineCommand(config) {
2430
- validateHumanInLoopOnDefine(config);
2684
+ function defineCommand(config2) {
2685
+ validateHumanInLoopOnDefine(config2);
2431
2686
  return materializeCommand(
2432
- createBaseCommand(config),
2687
+ createBaseCommand(config2),
2433
2688
  {
2434
2689
  scope: void 0,
2435
2690
  humanInLoop: void 0,
@@ -2438,10 +2693,10 @@ function defineCommand(config) {
2438
2693
  }
2439
2694
  );
2440
2695
  }
2441
- function defineGroup(config) {
2442
- validateRenameMap(config.rename);
2443
- validateHumanInLoopOnDefine(config);
2444
- return materializeGroup(createBaseGroup(config), {
2696
+ function defineGroup(config2) {
2697
+ validateRenameMap(config2.rename);
2698
+ validateHumanInLoopOnDefine(config2);
2699
+ return materializeGroup(createBaseGroup(config2), {
2445
2700
  scope: void 0,
2446
2701
  humanInLoop: void 0,
2447
2702
  secrets: {},
@@ -2602,17 +2857,22 @@ import { randomBytes } from "node:crypto";
2602
2857
  // ../process-runner/src/docker/args.ts
2603
2858
  import path2 from "node:path";
2604
2859
 
2860
+ // ../process-runner/src/docker/env-file.ts
2861
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2862
+ import { tmpdir } from "node:os";
2863
+ import path3 from "node:path";
2864
+
2605
2865
  // ../process-runner/src/docker/docker-execution-env.ts
2606
2866
  import { createHash as createHash2, randomBytes as randomBytes2 } from "node:crypto";
2607
- import { mkdtempSync, rmSync } from "node:fs";
2608
- import { readdir, readFile, writeFile } from "node:fs/promises";
2609
- import { tmpdir } from "node:os";
2610
- import path4 from "node:path";
2867
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "node:fs";
2868
+ import { readdir, readFile, realpath, writeFile } from "node:fs/promises";
2869
+ import { tmpdir as tmpdir2 } from "node:os";
2870
+ import path5 from "node:path";
2611
2871
 
2612
2872
  // ../process-runner/src/host/host-runner.ts
2613
2873
  import { spawn as spawnChildProcess } from "node:child_process";
2614
2874
  function createHostRunner(options = {}) {
2615
- const detached = options.detached === true;
2875
+ const detachedByDefault = options.detached === true;
2616
2876
  return {
2617
2877
  name: "host",
2618
2878
  exec(spec) {
@@ -2630,18 +2890,19 @@ function createHostRunner(options = {}) {
2630
2890
  const stdinMode = spec.stdin ?? "ignore";
2631
2891
  const stdoutMode = spec.stdout ?? "pipe";
2632
2892
  const stderrMode = spec.stderr ?? "pipe";
2893
+ const killProcessGroup = detachedByDefault || spec.killProcessGroup === true;
2633
2894
  const stdio = stdinMode === "inherit" && stdoutMode === "inherit" && stderrMode === "inherit" ? "inherit" : [stdinMode, stdoutMode, stderrMode];
2634
2895
  const child = spawnChildProcess(spec.command, spec.args ?? [], {
2635
2896
  cwd: spec.cwd,
2636
2897
  env: spec.env,
2637
2898
  stdio,
2638
- ...detached ? { detached: true } : {}
2899
+ ...killProcessGroup ? { detached: true } : {}
2639
2900
  });
2640
- if (detached) {
2901
+ if (killProcessGroup) {
2641
2902
  child.unref();
2642
2903
  }
2643
2904
  const kill = (signal) => {
2644
- if (detached && process.platform !== "win32" && child.pid !== void 0) {
2905
+ if (killProcessGroup && process.platform !== "win32" && child.pid !== void 0) {
2645
2906
  process.kill(-child.pid, signal);
2646
2907
  return;
2647
2908
  }
@@ -2699,9 +2960,9 @@ function bindAbortSignal(signal, onAbort) {
2699
2960
  }
2700
2961
 
2701
2962
  // ../process-runner/src/workspace-transfer.ts
2702
- import { createHash } from "node:crypto";
2963
+ import { createHash, randomUUID } from "node:crypto";
2703
2964
  import { promises as nodeFs } from "node:fs";
2704
- import path3 from "node:path";
2965
+ import path4 from "node:path";
2705
2966
 
2706
2967
  // ../process-runner/src/testing/mock-runner.ts
2707
2968
  import { Readable, Writable } from "node:stream";
@@ -2772,7 +3033,7 @@ function resolveEndpoint(options = {}) {
2772
3033
  }
2773
3034
 
2774
3035
  // ../task-list/src/backends/utils.ts
2775
- import path5 from "node:path";
3036
+ import path6 from "node:path";
2776
3037
  function compareCreated(left, right) {
2777
3038
  const leftCreated = typeof left.raw.created === "string" ? left.raw.created : "";
2778
3039
  const rightCreated = typeof right.raw.created === "string" ? right.raw.created : "";
@@ -2822,12 +3083,12 @@ async function statIfExists(fs4, filePath) {
2822
3083
  }
2823
3084
  }
2824
3085
  async function rejectSymbolicLinkComponents(fs4, filePath) {
2825
- const resolvedPath = path5.resolve(filePath);
2826
- const rootPath = path5.parse(resolvedPath).root;
2827
- const components = resolvedPath.slice(rootPath.length).split(path5.sep).filter(Boolean);
3086
+ const resolvedPath = path6.resolve(filePath);
3087
+ const rootPath = path6.parse(resolvedPath).root;
3088
+ const components = resolvedPath.slice(rootPath.length).split(path6.sep).filter(Boolean);
2828
3089
  let currentPath = rootPath;
2829
3090
  for (const component of components) {
2830
- currentPath = path5.join(currentPath, component);
3091
+ currentPath = path6.join(currentPath, component);
2831
3092
  try {
2832
3093
  if ((await fs4.lstat(currentPath)).isSymbolicLink()) {
2833
3094
  throw new Error(`Path "${filePath}" contains a symbolic link.`);
@@ -2843,7 +3104,7 @@ async function rejectSymbolicLinkComponents(fs4, filePath) {
2843
3104
  async function writeAtomically(fs4, filePath, content) {
2844
3105
  const tempPath = `${filePath}.tmp-${process.pid}-${tmpFileCounter}`;
2845
3106
  tmpFileCounter += 1;
2846
- await fs4.mkdir(path5.dirname(filePath), { recursive: true });
3107
+ await fs4.mkdir(path6.dirname(filePath), { recursive: true });
2847
3108
  try {
2848
3109
  await fs4.writeFile(tempPath, content, { encoding: "utf8", flag: "wx" });
2849
3110
  await fs4.rename(tempPath, filePath);
@@ -2859,7 +3120,7 @@ async function writeAtomically(fs4, filePath, content) {
2859
3120
  }
2860
3121
  }
2861
3122
  async function withFileLock(fs4, lockPath, operation) {
2862
- await fs4.mkdir(path5.dirname(lockPath), { recursive: true });
3123
+ await fs4.mkdir(path6.dirname(lockPath), { recursive: true });
2863
3124
  for (; ; ) {
2864
3125
  try {
2865
3126
  await fs4.writeFile(lockPath, String(process.pid), { encoding: "utf8", flag: "wx" });
@@ -3946,7 +4207,7 @@ function parseQualifiedId(qualifiedId, listName) {
3946
4207
  }
3947
4208
 
3948
4209
  // ../task-list/src/backends/markdown-dir.ts
3949
- import path6 from "node:path";
4210
+ import path7 from "node:path";
3950
4211
  import { parseDocument, stringify } from "yaml";
3951
4212
 
3952
4213
  // ../task-list/src/schema/task.schema.json
@@ -4065,16 +4326,16 @@ function parseQualifiedId2(qualifiedId) {
4065
4326
  };
4066
4327
  }
4067
4328
  function listPath(rootPath, layout, list) {
4068
- return layout.kind === "single" ? rootPath : path6.join(rootPath, list);
4329
+ return layout.kind === "single" ? rootPath : path7.join(rootPath, list);
4069
4330
  }
4070
4331
  function archiveDirectoryPath(rootPath, layout, list) {
4071
- return layout.kind === "single" ? path6.join(rootPath, ARCHIVE_DIRECTORY_NAME) : path6.join(rootPath, list, ARCHIVE_DIRECTORY_NAME);
4332
+ return layout.kind === "single" ? path7.join(rootPath, ARCHIVE_DIRECTORY_NAME) : path7.join(rootPath, list, ARCHIVE_DIRECTORY_NAME);
4072
4333
  }
4073
4334
  function activeTaskFilename(id, order, width) {
4074
4335
  return `${String(order).padStart(width, "0")}-${id}${MARKDOWN_EXTENSION}`;
4075
4336
  }
4076
4337
  function archivedTaskPath(rootPath, layout, list, id) {
4077
- return path6.join(archiveDirectoryPath(rootPath, layout, list), `${id}${MARKDOWN_EXTENSION}`);
4338
+ return path7.join(archiveDirectoryPath(rootPath, layout, list), `${id}${MARKDOWN_EXTENSION}`);
4078
4339
  }
4079
4340
  function isMarkdownFile(entryName) {
4080
4341
  return entryName.endsWith(MARKDOWN_EXTENSION);
@@ -4200,7 +4461,7 @@ function createTask(list, id, frontmatter, body, mode, sourcePath) {
4200
4461
  state: frontmatter.state,
4201
4462
  description: body,
4202
4463
  metadata: metadataFromFrontmatter(frontmatter, mode),
4203
- ...sourcePath !== void 0 && { sourcePath: path6.resolve(sourcePath) }
4464
+ ...sourcePath !== void 0 && { sourcePath: path7.resolve(sourcePath) }
4204
4465
  };
4205
4466
  }
4206
4467
  function serializeTaskDocument(frontmatter, description) {
@@ -4240,7 +4501,7 @@ async function readTaskFile(fs4, list, id, filePath, validStates, initialState,
4240
4501
  task: createTask(list, id, frontmatter, document.body, mode, filePath)
4241
4502
  };
4242
4503
  }
4243
- const parsedFilename = parseActiveFilename(path6.basename(filePath));
4504
+ const parsedFilename = parseActiveFilename(path7.basename(filePath));
4244
4505
  const defaultName = parsedFilename?.id ?? id;
4245
4506
  const effectiveFrontmatter = {
4246
4507
  ...frontmatter,
@@ -4269,7 +4530,7 @@ async function findTaskLocation(fs4, rootPath, layout, list, id) {
4269
4530
  await rejectSymbolicLinkComponents(fs4, listDirectoryPath);
4270
4531
  const activeName = await findActiveTaskFilename(fs4, listDirectoryPath, id);
4271
4532
  if (activeName) {
4272
- const activePath = path6.join(listDirectoryPath, activeName);
4533
+ const activePath = path7.join(listDirectoryPath, activeName);
4273
4534
  await rejectSymbolicLinkComponents(fs4, activePath);
4274
4535
  const activeStat = await statIfExists(fs4, activePath);
4275
4536
  if (activeStat?.isFile()) {
@@ -4292,7 +4553,7 @@ async function readTaskAtLocation(fs4, rootPath, layout, list, id, validStates,
4292
4553
  }
4293
4554
  return readTaskFile(fs4, list, id, location.path, validStates, initialState, mode);
4294
4555
  }
4295
- function createdFrontmatter(defaults, input, initialState, mode) {
4556
+ function createdFrontmatter(defaults2, input, initialState, mode) {
4296
4557
  const frontmatter = mode !== "passthrough" ? {
4297
4558
  $schema: TASK_SCHEMA_ID,
4298
4559
  kind: TASK_KIND,
@@ -4304,7 +4565,7 @@ function createdFrontmatter(defaults, input, initialState, mode) {
4304
4565
  state: initialState
4305
4566
  };
4306
4567
  const reservedKeys = reservedFrontmatterKeys(mode);
4307
- for (const [key2, value] of Object.entries(defaults.metadata)) {
4568
+ for (const [key2, value] of Object.entries(defaults2.metadata)) {
4308
4569
  if (!reservedKeys.has(key2)) {
4309
4570
  setOwnValue(frontmatter, key2, value);
4310
4571
  }
@@ -4391,7 +4652,7 @@ function createTasksView2(deps, layout, list) {
4391
4652
  if (isHiddenEntry(entryName)) continue;
4392
4653
  const parsed = parseActiveFilename(entryName);
4393
4654
  if (!parsed) continue;
4394
- const entryPath = path6.join(listDirectoryPath, entryName);
4655
+ const entryPath = path7.join(listDirectoryPath, entryName);
4395
4656
  await rejectSymbolicLinkComponents(deps.fs, entryPath);
4396
4657
  const entryStat = await statIfExists(deps.fs, entryPath);
4397
4658
  if (!entryStat?.isFile()) continue;
@@ -4409,7 +4670,7 @@ function createTasksView2(deps, layout, list) {
4409
4670
  const entries = await readActiveEntries();
4410
4671
  const tasks = /* @__PURE__ */ new Map();
4411
4672
  for (const entry of entries) {
4412
- const filePath = path6.join(listDirectoryPath, entry.filename);
4673
+ const filePath = path7.join(listDirectoryPath, entry.filename);
4413
4674
  const file = await readTaskFile(
4414
4675
  deps.fs,
4415
4676
  list,
@@ -4430,7 +4691,7 @@ function createTasksView2(deps, layout, list) {
4430
4691
  const result = [];
4431
4692
  for (const entryName of entries) {
4432
4693
  if (isHiddenEntry(entryName) || !isMarkdownFile(entryName)) continue;
4433
- const entryPath = path6.join(archivePath, entryName);
4694
+ const entryPath = path7.join(archivePath, entryName);
4434
4695
  await rejectSymbolicLinkComponents(deps.fs, entryPath);
4435
4696
  const entryStat = await statIfExists(deps.fs, entryPath);
4436
4697
  if (!entryStat?.isFile()) continue;
@@ -4460,12 +4721,12 @@ function createTasksView2(deps, layout, list) {
4460
4721
  if (desiredOrder === void 0) continue;
4461
4722
  const desiredFilename = activeTaskFilename(entry.id, desiredOrder, width);
4462
4723
  if (entry.filename !== desiredFilename) {
4463
- const fromPath = path6.join(listDirectoryPath, entry.filename);
4464
- const stagingPath = path6.join(
4724
+ const fromPath = path7.join(listDirectoryPath, entry.filename);
4725
+ const stagingPath = path7.join(
4465
4726
  listDirectoryPath,
4466
4727
  `${desiredFilename}.staging-${process.pid}-${index}`
4467
4728
  );
4468
- const targetPath = path6.join(listDirectoryPath, desiredFilename);
4729
+ const targetPath = path7.join(listDirectoryPath, desiredFilename);
4469
4730
  try {
4470
4731
  await deps.fs.rename(fromPath, stagingPath);
4471
4732
  staged.push({ original: fromPath, staging: stagingPath, target: targetPath, finalized: false });
@@ -4614,7 +4875,7 @@ function createTasksView2(deps, layout, list) {
4614
4875
  assertCreateHasId(input);
4615
4876
  validateTaskId(input.id);
4616
4877
  await rejectSymbolicLinkComponents(deps.fs, listDirectoryPath);
4617
- return withFileLock(deps.fs, path6.join(listDirectoryPath, ".transition.lock"), async () => {
4878
+ return withFileLock(deps.fs, path7.join(listDirectoryPath, ".transition.lock"), async () => {
4618
4879
  const existing = await findTaskLocation(deps.fs, deps.path, layout, list, input.id);
4619
4880
  if (existing) {
4620
4881
  throw new TaskAlreadyExistsError(`Task "${list}/${input.id}" already exists.`);
@@ -4627,7 +4888,7 @@ function createTasksView2(deps, layout, list) {
4627
4888
  const nextOrder = maxOrder + 1;
4628
4889
  const width = padWidthForCount(activeEntries.length + 1);
4629
4890
  const filename = activeTaskFilename(input.id, nextOrder, width);
4630
- const targetPath = path6.join(listDirectoryPath, filename);
4891
+ const targetPath = path7.join(listDirectoryPath, filename);
4631
4892
  const frontmatter = createdFrontmatter(
4632
4893
  deps.defaults,
4633
4894
  input,
@@ -4735,7 +4996,7 @@ function createTasksView2(deps, layout, list) {
4735
4996
  validateTaskId(id);
4736
4997
  const { event, nextTask } = await withFileLock(
4737
4998
  deps.fs,
4738
- path6.join(listDirectoryPath, ".transition.lock"),
4999
+ path7.join(listDirectoryPath, ".transition.lock"),
4739
5000
  fireTask
4740
5001
  );
4741
5002
  await event.onEnter?.(nextTask);
@@ -4828,7 +5089,7 @@ async function markdownDirBackend(deps) {
4828
5089
  if (entryName === ARCHIVE_DIRECTORY_NAME || isHiddenEntry(entryName)) {
4829
5090
  continue;
4830
5091
  }
4831
- const entryPath = path6.join(deps.path, entryName);
5092
+ const entryPath = path7.join(deps.path, entryName);
4832
5093
  await rejectSymbolicLinkComponents(deps.fs, entryPath);
4833
5094
  const entryStat = await statIfExists(deps.fs, entryPath);
4834
5095
  if (entryStat?.isDirectory()) {
@@ -4870,7 +5131,7 @@ async function markdownDirBackend(deps) {
4870
5131
  }
4871
5132
  const targetListDir = listPath(deps.path, layout, targetListName);
4872
5133
  await rejectSymbolicLinkComponents(deps.fs, targetListDir);
4873
- return withFileLock(deps.fs, path6.join(targetListDir, ".transition.lock"), async () => {
5134
+ return withFileLock(deps.fs, path7.join(targetListDir, ".transition.lock"), async () => {
4874
5135
  const targetExisting = await findTaskLocation(deps.fs, deps.path, layout, targetListName, id);
4875
5136
  if (targetExisting) {
4876
5137
  throw new TaskAlreadyExistsError(`Task "${targetListName}/${id}" already exists.`);
@@ -4920,7 +5181,7 @@ async function markdownDirBackend(deps) {
4920
5181
  );
4921
5182
  const width = padWidthForCount(targetEntries.length + 1);
4922
5183
  const targetFilename = activeTaskFilename(id, maxOrder + 1, width);
4923
- const targetPath = path6.join(targetListDir, targetFilename);
5184
+ const targetPath = path7.join(targetListDir, targetFilename);
4924
5185
  await deps.fs.rename(sourceLocation.path, targetPath);
4925
5186
  return createTask(
4926
5187
  targetListName,
@@ -4942,7 +5203,7 @@ async function markdownDirBackend(deps) {
4942
5203
  }
4943
5204
 
4944
5205
  // ../task-list/src/backends/yaml-file.ts
4945
- import path7 from "node:path";
5206
+ import path8 from "node:path";
4946
5207
  import { isMap, parseDocument as parseDocument2 } from "yaml";
4947
5208
 
4948
5209
  // ../task-list/src/schema/store.schema.json
@@ -5038,7 +5299,7 @@ function createTask2(list, id, taskRecord, sourcePath) {
5038
5299
  state: taskRecord.state,
5039
5300
  description: descriptionFromTaskRecord(taskRecord),
5040
5301
  metadata: metadataFromTaskRecord(taskRecord),
5041
- ...sourcePath !== void 0 && { sourcePath: path7.resolve(sourcePath) }
5302
+ ...sourcePath !== void 0 && { sourcePath: path8.resolve(sourcePath) }
5042
5303
  };
5043
5304
  }
5044
5305
  function matchesFilter(task, filter) {
@@ -5050,13 +5311,13 @@ function matchesFilter(task, filter) {
5050
5311
  }
5051
5312
  return true;
5052
5313
  }
5053
- function createTaskRecord(defaults, input, initialState) {
5314
+ function createTaskRecord(defaults2, input, initialState) {
5054
5315
  const taskRecord = Object.assign(/* @__PURE__ */ Object.create(null), {
5055
5316
  name: input.name,
5056
5317
  state: initialState,
5057
5318
  description: input.description ?? ""
5058
5319
  });
5059
- for (const [key2, value] of Object.entries(defaults.metadata)) {
5320
+ for (const [key2, value] of Object.entries(defaults2.metadata)) {
5060
5321
  if (!RESERVED_TASK_KEYS.has(key2)) {
5061
5322
  taskRecord[key2] = value;
5062
5323
  }
@@ -5603,7 +5864,7 @@ async function openGhIssuesBackend(options) {
5603
5864
 
5604
5865
  // ../task-list/src/move.ts
5605
5866
  import * as fsPromises2 from "node:fs/promises";
5606
- import path8 from "node:path";
5867
+ import path9 from "node:path";
5607
5868
 
5608
5869
  // ../toolcraft/src/human-in-loop/approval-tasks.ts
5609
5870
  import { randomBytes as randomBytes3 } from "node:crypto";
@@ -6099,13 +6360,13 @@ function formatAvailableApprovalCommandPaths(root) {
6099
6360
  }
6100
6361
  function enumerateApprovalCommandPaths(root) {
6101
6362
  const paths = [];
6102
- const visit = (node, path23) => {
6363
+ const visit = (node, path24) => {
6103
6364
  if (node.kind === "command") {
6104
- paths.push(path23.join("."));
6365
+ paths.push(path24.join("."));
6105
6366
  return;
6106
6367
  }
6107
6368
  for (const child of getVisibleCliChildren(node)) {
6108
- visit(child, [...path23, child.name]);
6369
+ visit(child, [...path24, child.name]);
6109
6370
  }
6110
6371
  };
6111
6372
  if (root.kind === "command") {
@@ -6140,21 +6401,21 @@ function createHandlerContext(command, params17) {
6140
6401
  }
6141
6402
  function createFs() {
6142
6403
  return {
6143
- readFile: async (path23, encoding = "utf8") => readFile2(path23, { encoding }),
6144
- writeFile: async (path23, contents) => {
6145
- await writeFile2(path23, contents);
6404
+ readFile: async (path24, encoding = "utf8") => readFile2(path24, { encoding }),
6405
+ writeFile: async (path24, contents) => {
6406
+ await writeFile2(path24, contents);
6146
6407
  },
6147
- exists: async (path23) => {
6408
+ exists: async (path24) => {
6148
6409
  try {
6149
- await access(path23);
6410
+ await access(path24);
6150
6411
  return true;
6151
6412
  } catch {
6152
6413
  return false;
6153
6414
  }
6154
6415
  },
6155
- lstat: async (path23) => lstat(path23),
6416
+ lstat: async (path24) => lstat(path24),
6156
6417
  rename: async (fromPath, toPath) => rename(fromPath, toPath),
6157
- unlink: async (path23) => unlink(path23)
6418
+ unlink: async (path24) => unlink(path24)
6158
6419
  };
6159
6420
  }
6160
6421
  function createEnv(values = process.env) {
@@ -6430,17 +6691,17 @@ function isMissingStateError(error3) {
6430
6691
  }
6431
6692
 
6432
6693
  // ../toolcraft/src/error-report.ts
6433
- import { mkdir as mkdir2, realpath, writeFile as writeFile4 } from "node:fs/promises";
6434
- import { randomUUID as randomUUID2 } from "node:crypto";
6694
+ import { mkdir as mkdir2, realpath as realpath2, writeFile as writeFile4 } from "node:fs/promises";
6695
+ import { randomUUID as randomUUID3 } from "node:crypto";
6435
6696
  import os from "node:os";
6436
- import path12 from "node:path";
6697
+ import path13 from "node:path";
6437
6698
  import { CommanderError } from "commander";
6438
6699
 
6439
6700
  // ../toolcraft/src/mcp-proxy.ts
6440
6701
  import { existsSync as existsSync2 } from "node:fs";
6441
6702
  import { lstat as lstat2, mkdir, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
6442
- import path11 from "node:path";
6443
- import { createHash as createHash3, randomUUID } from "node:crypto";
6703
+ import path12 from "node:path";
6704
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
6444
6705
 
6445
6706
  // ../tiny-mcp-client/src/internal.ts
6446
6707
  import { spawn as spawn5 } from "node:child_process";
@@ -6448,13 +6709,13 @@ import { PassThrough as PassThrough2 } from "node:stream";
6448
6709
 
6449
6710
  // ../mcp-oauth/src/client/auth-store-session-store.ts
6450
6711
  import crypto from "node:crypto";
6451
- import path10 from "node:path";
6712
+ import path11 from "node:path";
6452
6713
 
6453
6714
  // ../auth-store/src/encrypted-file-store.ts
6454
6715
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes4, scrypt } from "node:crypto";
6455
6716
  import { promises as fs } from "node:fs";
6456
6717
  import { homedir, hostname, userInfo } from "node:os";
6457
- import path9 from "node:path";
6718
+ import path10 from "node:path";
6458
6719
  var derivedKeyCache = /* @__PURE__ */ new Map();
6459
6720
  var ENCRYPTION_ALGORITHM = "aes-256-gcm";
6460
6721
  var ENCRYPTION_VERSION = 1;
@@ -6466,6 +6727,7 @@ var temporaryFileSequence = 0;
6466
6727
  var EncryptedFileStore = class {
6467
6728
  fs;
6468
6729
  filePath;
6730
+ symbolicLinkCheckStartPath;
6469
6731
  salt;
6470
6732
  getMachineIdentity;
6471
6733
  getRandomBytes;
@@ -6473,11 +6735,22 @@ var EncryptedFileStore = class {
6473
6735
  constructor(input) {
6474
6736
  this.fs = input.fs ?? fs;
6475
6737
  this.salt = input.salt;
6476
- this.filePath = input.filePath ?? path9.join(
6477
- (input.getHomeDirectory ?? homedir)(),
6478
- input.defaultDirectory ?? ".auth-store",
6479
- input.defaultFileName ?? "credentials.enc"
6480
- );
6738
+ if (input.filePath === void 0) {
6739
+ const homeDirectory = (input.getHomeDirectory ?? homedir)();
6740
+ const defaultDirectory = input.defaultDirectory ?? ".auth-store";
6741
+ this.filePath = path10.join(
6742
+ homeDirectory,
6743
+ defaultDirectory,
6744
+ input.defaultFileName ?? "credentials.enc"
6745
+ );
6746
+ this.symbolicLinkCheckStartPath = resolveDefaultDirectoryCheckStart(
6747
+ homeDirectory,
6748
+ defaultDirectory
6749
+ );
6750
+ } else {
6751
+ this.filePath = input.filePath;
6752
+ this.symbolicLinkCheckStartPath = null;
6753
+ }
6481
6754
  this.getMachineIdentity = input.getMachineIdentity ?? defaultMachineIdentity;
6482
6755
  this.getRandomBytes = input.getRandomBytes ?? randomBytes4;
6483
6756
  }
@@ -6528,7 +6801,7 @@ var EncryptedFileStore = class {
6528
6801
  authTag: authTag.toString("base64"),
6529
6802
  ciphertext: ciphertext.toString("base64")
6530
6803
  };
6531
- await this.fs.mkdir(path9.dirname(this.filePath), { recursive: true });
6804
+ await this.fs.mkdir(path10.dirname(this.filePath), { recursive: true });
6532
6805
  await this.assertRegularCredentialPath();
6533
6806
  const temporaryPath = `${this.filePath}.${process.pid}.${temporaryFileSequence++}.tmp`;
6534
6807
  try {
@@ -6557,8 +6830,11 @@ var EncryptedFileStore = class {
6557
6830
  }
6558
6831
  }
6559
6832
  async assertRegularCredentialPath() {
6560
- const resolvedPath = path9.resolve(this.filePath);
6561
- const protectedPaths = [path9.dirname(resolvedPath), resolvedPath];
6833
+ const resolvedPath = path10.resolve(this.filePath);
6834
+ const protectedPaths = getProtectedCredentialPaths(
6835
+ resolvedPath,
6836
+ this.symbolicLinkCheckStartPath
6837
+ );
6562
6838
  for (const currentPath of protectedPaths) {
6563
6839
  try {
6564
6840
  const stats = await this.fs.lstat(currentPath);
@@ -6586,6 +6862,30 @@ var EncryptedFileStore = class {
6586
6862
  return this.keyPromise;
6587
6863
  }
6588
6864
  };
6865
+ function resolveDefaultDirectoryCheckStart(homeDirectory, defaultDirectory) {
6866
+ const [firstSegment] = defaultDirectory.split(/[\\/]+/).filter(Boolean);
6867
+ return path10.resolve(homeDirectory, firstSegment ?? ".");
6868
+ }
6869
+ function getProtectedCredentialPaths(resolvedPath, symbolicLinkCheckStartPath) {
6870
+ if (symbolicLinkCheckStartPath === null) {
6871
+ return [path10.dirname(resolvedPath), resolvedPath];
6872
+ }
6873
+ const resolvedStartPath = path10.resolve(symbolicLinkCheckStartPath);
6874
+ if (!isPathInsideOrEqual(resolvedPath, resolvedStartPath)) {
6875
+ return [path10.dirname(resolvedPath), resolvedPath];
6876
+ }
6877
+ const protectedPaths = [resolvedStartPath];
6878
+ let currentPath = resolvedStartPath;
6879
+ for (const segment of path10.relative(resolvedStartPath, resolvedPath).split(path10.sep).filter(Boolean)) {
6880
+ currentPath = path10.join(currentPath, segment);
6881
+ protectedPaths.push(currentPath);
6882
+ }
6883
+ return protectedPaths;
6884
+ }
6885
+ function isPathInsideOrEqual(childPath, parentPath) {
6886
+ const relativePath = path10.relative(parentPath, childPath);
6887
+ return relativePath === "" || !relativePath.startsWith("..") && !path10.isAbsolute(relativePath);
6888
+ }
6589
6889
  async function removeIfPresent(fileSystem, filePath) {
6590
6890
  try {
6591
6891
  await fileSystem.unlink(filePath);
@@ -6736,6 +7036,17 @@ function runSecurityCommand(command, args, options) {
6736
7036
  });
6737
7037
  let stdout = "";
6738
7038
  let stderr = "";
7039
+ let stdinErrorMessage;
7040
+ const appendStderr = (message2) => {
7041
+ stderr = stderr.length === 0 ? message2 : `${stderr}${stderr.endsWith("\n") ? "" : "\n"}${message2}`;
7042
+ };
7043
+ const appendStdinError = () => {
7044
+ if (stdinErrorMessage === void 0) {
7045
+ return;
7046
+ }
7047
+ appendStderr(stdinErrorMessage);
7048
+ stdinErrorMessage = void 0;
7049
+ };
6739
7050
  child.stdout?.setEncoding("utf8");
6740
7051
  child.stdout?.on("data", (chunk) => {
6741
7052
  stdout += chunk.toString();
@@ -6745,17 +7056,23 @@ function runSecurityCommand(command, args, options) {
6745
7056
  stderr += chunk.toString();
6746
7057
  });
6747
7058
  if (options?.stdin !== void 0) {
7059
+ child.stdin?.once("error", (error3) => {
7060
+ stdinErrorMessage = error3 instanceof Error ? error3.message : String(error3);
7061
+ });
6748
7062
  child.stdin?.end(options.stdin);
6749
7063
  }
6750
7064
  child.on("error", (error3) => {
6751
7065
  const message2 = error3 instanceof Error ? error3.message : String(error3 ?? "Unknown error");
7066
+ appendStdinError();
7067
+ appendStderr(message2);
6752
7068
  resolve({
6753
7069
  stdout,
6754
- stderr: stderr ? `${stderr}${message2}` : message2,
7070
+ stderr,
6755
7071
  exitCode: 127
6756
7072
  });
6757
7073
  });
6758
7074
  child.on("close", (code) => {
7075
+ appendStdinError();
6759
7076
  resolve({
6760
7077
  stdout,
6761
7078
  stderr,
@@ -6893,19 +7210,24 @@ function createAuthStoreClientStore(options) {
6893
7210
  }
6894
7211
  };
6895
7212
  }
6896
- function createNamedSecretStore(key2, options, defaults) {
7213
+ function createNamedSecretStore(key2, options, defaults2) {
6897
7214
  const hash = crypto.createHash("sha256").update(key2).digest("hex");
6898
- const parsedFilePath = options.fileStore?.filePath === void 0 ? null : path10.parse(options.fileStore.filePath);
7215
+ const configuredFilePath = options.fileStore?.filePath;
7216
+ const parsedFilePath = configuredFilePath === void 0 ? null : path11.parse(configuredFilePath);
6899
7217
  const fileStore = {
6900
7218
  ...options.fileStore,
6901
- salt: options.fileStore?.salt ?? defaults.salt,
6902
- defaultDirectory: parsedFilePath?.dir || options.fileStore?.defaultDirectory || defaults.directory,
7219
+ filePath: parsedFilePath === null ? void 0 : path11.join(
7220
+ parsedFilePath.dir,
7221
+ `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`
7222
+ ),
7223
+ salt: options.fileStore?.salt ?? defaults2.salt,
7224
+ defaultDirectory: options.fileStore?.defaultDirectory || defaults2.directory,
6903
7225
  defaultFileName: parsedFilePath === null ? `${hash}.enc` : `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`
6904
7226
  };
6905
7227
  const keychainStore = {
6906
7228
  ...options.keychainStore,
6907
- service: options.keychainStore?.service ?? defaults.service,
6908
- account: `${options.keychainStore?.account ?? defaults.accountPrefix}:${hash}`
7229
+ service: options.keychainStore?.service ?? defaults2.service,
7230
+ account: `${options.keychainStore?.account ?? defaults2.accountPrefix}:${hash}`
6909
7231
  };
6910
7232
  return createSecretStore({ ...options, fileStore, keychainStore }).store;
6911
7233
  }
@@ -8478,10 +8800,19 @@ var McpClient = class {
8478
8800
  }
8479
8801
  try {
8480
8802
  let requestId;
8803
+ let cancellationSent = false;
8804
+ const sendCancellationNotification = () => {
8805
+ if (requestId === void 0 || cancellationSent) {
8806
+ return;
8807
+ }
8808
+ cancellationSent = true;
8809
+ messageLayer.sendNotification("notifications/cancelled", { requestId });
8810
+ };
8481
8811
  const requestPromise = messageLayer.sendRequest("tools/call", requestParams, {
8482
8812
  onRequestId: (nextRequestId) => {
8483
8813
  requestId = nextRequestId;
8484
- }
8814
+ },
8815
+ onTimeout: sendCancellationNotification
8485
8816
  }).then((result) => {
8486
8817
  if (!isCallToolResult(result)) {
8487
8818
  throw new McpError(ERROR_INVALID_REQUEST, "Invalid tool result");
@@ -8495,8 +8826,9 @@ var McpClient = class {
8495
8826
  let abortListener;
8496
8827
  const abortPromise = new Promise((_, reject) => {
8497
8828
  const rejectWithAbortReason = () => {
8829
+ sendCancellationNotification();
8498
8830
  if (requestId !== void 0) {
8499
- messageLayer.sendNotification("notifications/cancelled", { requestId });
8831
+ messageLayer.cancelRequest(requestId, signal.reason);
8500
8832
  }
8501
8833
  reject(signal.reason);
8502
8834
  };
@@ -8672,11 +9004,13 @@ var StdioTransport = class _StdioTransport {
8672
9004
  const child = this.child;
8673
9005
  this.readable = child.stdout;
8674
9006
  this.writable = child.stdin;
9007
+ const stderrDecoder = new TextDecoder();
8675
9008
  child.stderr.on("data", (chunk) => {
8676
- this.stderrOutput += chunkToString(chunk);
8677
- if (this.stderrOutput.length > _StdioTransport.STDERR_MAX_LENGTH) {
8678
- this.stderrOutput = this.stderrOutput.slice(-_StdioTransport.STDERR_MAX_LENGTH);
8679
- }
9009
+ const decoded = chunk instanceof Uint8Array ? stderrDecoder.decode(chunk, { stream: true }) : `${stderrDecoder.decode()}${String(chunk)}`;
9010
+ this.appendStderrOutput(decoded);
9011
+ });
9012
+ child.stderr.once("end", () => {
9013
+ this.appendStderrOutput(stderrDecoder.decode());
8680
9014
  });
8681
9015
  this.closed = new Promise((resolve) => {
8682
9016
  let settled = false;
@@ -8716,6 +9050,15 @@ var StdioTransport = class _StdioTransport {
8716
9050
  getStderrOutput() {
8717
9051
  return this.stderrOutput;
8718
9052
  }
9053
+ appendStderrOutput(chunk) {
9054
+ if (chunk.length === 0) {
9055
+ return;
9056
+ }
9057
+ this.stderrOutput += chunk;
9058
+ if (this.stderrOutput.length > _StdioTransport.STDERR_MAX_LENGTH) {
9059
+ this.stderrOutput = this.stderrOutput.slice(-_StdioTransport.STDERR_MAX_LENGTH);
9060
+ }
9061
+ }
8719
9062
  dispose(reason = new Error("Stdio transport disposed")) {
8720
9063
  void reason;
8721
9064
  if (this.disposed) {
@@ -9123,15 +9466,6 @@ function serializeJsonRpcMessage(message2) {
9123
9466
  return `${JSON.stringify(message2)}
9124
9467
  `;
9125
9468
  }
9126
- function chunkToString(chunk) {
9127
- if (typeof chunk === "string") {
9128
- return chunk;
9129
- }
9130
- if (chunk instanceof Uint8Array) {
9131
- return Buffer.from(chunk).toString("utf8");
9132
- }
9133
- return String(chunk);
9134
- }
9135
9469
  function normalizeLine(line) {
9136
9470
  return line.endsWith("\r") ? line.slice(0, -1) : line;
9137
9471
  }
@@ -9308,6 +9642,7 @@ var JsonRpcMessageLayer = class {
9308
9642
  return new Promise((resolve, reject) => {
9309
9643
  const timeout = setTimeout(() => {
9310
9644
  this.pendingRequests.delete(id);
9645
+ options.onTimeout?.(id);
9311
9646
  reject(new Error(`JSON-RPC request "${method}" timed out after ${timeoutMs}ms`));
9312
9647
  }, timeoutMs);
9313
9648
  this.pendingRequests.set(id, { resolve, reject, timeout });
@@ -9320,6 +9655,16 @@ var JsonRpcMessageLayer = class {
9320
9655
  }
9321
9656
  });
9322
9657
  }
9658
+ cancelRequest(requestId, reason) {
9659
+ const pending = this.pendingRequests.get(requestId);
9660
+ if (pending === void 0) {
9661
+ return false;
9662
+ }
9663
+ this.pendingRequests.delete(requestId);
9664
+ clearTimeout(pending.timeout);
9665
+ pending.reject(reason);
9666
+ return true;
9667
+ }
9323
9668
  dispose(reason = new Error("JSON-RPC message layer disposed")) {
9324
9669
  if (this.disposedError !== void 0) {
9325
9670
  return;
@@ -9720,13 +10065,13 @@ function convertJsonSchema(schema) {
9720
10065
  }
9721
10066
  return convertSchema(schema, schema, []);
9722
10067
  }
9723
- function convertSchema(schema, root, path23) {
9724
- const resolvedSchema = resolveReferencedSchema(schema, root, path23);
10068
+ function convertSchema(schema, root, path24) {
10069
+ const resolvedSchema = resolveReferencedSchema(schema, root, path24);
9725
10070
  const normalizedSchema = normalizeNullability(resolvedSchema);
9726
10071
  const composition = getComposition(normalizedSchema.schema);
9727
10072
  if (Array.isArray(normalizedSchema.schema.type)) {
9728
10073
  throw new Error(
9729
- `JSON Schema "${formatJsonSchemaPath(path23)}" has an unsupported type "${formatJsonSchemaType(
10074
+ `JSON Schema "${formatJsonSchemaPath(path24)}" has an unsupported type "${formatJsonSchemaType(
9730
10075
  normalizedSchema.schema.type
9731
10076
  )}". Supported: string, number, integer, boolean, array, object.`
9732
10077
  );
@@ -9738,13 +10083,13 @@ function convertSchema(schema, root, path23) {
9738
10083
  return convertEnumSchema(resolvedSchema, normalizedSchema.nullable);
9739
10084
  }
9740
10085
  if (composition !== void 0) {
9741
- return convertCompositionSchema(normalizedSchema.schema, root, normalizedSchema.nullable, path23);
10086
+ return convertCompositionSchema(normalizedSchema.schema, root, normalizedSchema.nullable, path24);
9742
10087
  }
9743
10088
  if (isRecordSchema(normalizedSchema.schema)) {
9744
10089
  return applyMetadata(
9745
10090
  S.Record(
9746
10091
  convertSchema(normalizedSchema.schema.additionalProperties, root, [
9747
- ...path23,
10092
+ ...path24,
9748
10093
  "additionalProperties"
9749
10094
  ])
9750
10095
  ),
@@ -9793,12 +10138,12 @@ function convertSchema(schema, root, path23) {
9793
10138
  if (normalizedSchema.schema.items === void 0) {
9794
10139
  throw new Error(
9795
10140
  `JSON Schema "${formatJsonSchemaPath(
9796
- path23
10141
+ path24
9797
10142
  )}" is an array but is missing the "items" field. Add "items": { ... } to declare the element type.`
9798
10143
  );
9799
10144
  }
9800
10145
  return S.Array(
9801
- convertSchema(normalizedSchema.schema.items, root, [...path23, "items"]),
10146
+ convertSchema(normalizedSchema.schema.items, root, [...path24, "items"]),
9802
10147
  createCommonOptions(
9803
10148
  normalizedSchema.schema,
9804
10149
  normalizedSchema.nullable,
@@ -9808,7 +10153,7 @@ function convertSchema(schema, root, path23) {
9808
10153
  case "object":
9809
10154
  return convertObjectSchema(normalizedSchema.schema, root, {
9810
10155
  nullable: normalizedSchema.nullable,
9811
- path: path23
10156
+ path: path24
9812
10157
  });
9813
10158
  case "null":
9814
10159
  return applyMetadata(S.Json(), normalizedSchema.schema, {
@@ -9823,12 +10168,12 @@ function convertSchema(schema, root, path23) {
9823
10168
  }
9824
10169
  throw new Error(
9825
10170
  `JSON Schema "${formatJsonSchemaPath(
9826
- path23
10171
+ path24
9827
10172
  )}" must declare one of: "type", "enum", "const", "oneOf", "anyOf", or "allOf".`
9828
10173
  );
9829
10174
  }
9830
10175
  throw new Error(
9831
- `JSON Schema "${formatJsonSchemaPath(path23)}" has an unsupported type "${formatJsonSchemaType(
10176
+ `JSON Schema "${formatJsonSchemaPath(path24)}" has an unsupported type "${formatJsonSchemaType(
9832
10177
  normalizedSchema.schema.type
9833
10178
  )}". Supported: string, number, integer, boolean, array, object.`
9834
10179
  );
@@ -9872,21 +10217,21 @@ function convertEnumSchema(schema, nullable) {
9872
10217
  )
9873
10218
  });
9874
10219
  }
9875
- function convertCompositionSchema(schema, root, nullable, path23) {
10220
+ function convertCompositionSchema(schema, root, nullable, path24) {
9876
10221
  const composition = getComposition(schema);
9877
10222
  const branchSchemas = composition?.branches ?? [];
9878
10223
  const keyword = composition?.keyword ?? "oneOf";
9879
10224
  const branches = branchSchemas.map(
9880
- (branch, index) => resolveReferencedSchema(branch, root, [...path23, keyword, String(index)])
10225
+ (branch, index) => resolveReferencedSchema(branch, root, [...path24, keyword, String(index)])
9881
10226
  );
9882
- const discriminator = findDiscriminator(branches, root, path23);
10227
+ const discriminator = findDiscriminator(branches, root, path24);
9883
10228
  if (discriminator !== void 0) {
9884
10229
  const convertedBranches = Object.fromEntries(
9885
10230
  branches.map((branch, index) => [
9886
10231
  getDiscriminatorLiteral(branch, discriminator, root),
9887
10232
  convertObjectSchema(branch, root, {
9888
10233
  omitProperty: discriminator,
9889
- path: [...path23, keyword, String(index)]
10234
+ path: [...path24, keyword, String(index)]
9890
10235
  })
9891
10236
  ])
9892
10237
  );
@@ -9905,7 +10250,7 @@ function convertCompositionSchema(schema, root, nullable, path23) {
9905
10250
  S.Union(
9906
10251
  branches.map(
9907
10252
  (branch, index) => convertObjectSchema(branch, root, {
9908
- path: [...path23, keyword, String(index)]
10253
+ path: [...path24, keyword, String(index)]
9909
10254
  })
9910
10255
  )
9911
10256
  ),
@@ -10007,11 +10352,11 @@ function getComposition(schema) {
10007
10352
  }
10008
10353
  return void 0;
10009
10354
  }
10010
- function formatJsonSchemaPath(path23) {
10011
- if (path23.length === 0) {
10355
+ function formatJsonSchemaPath(path24) {
10356
+ if (path24.length === 0) {
10012
10357
  return "#";
10013
10358
  }
10014
- return `#/${path23.map(escapeJsonPointerSegment).join("/")}`;
10359
+ return `#/${path24.map(escapeJsonPointerSegment).join("/")}`;
10015
10360
  }
10016
10361
  function formatJsonSchemaType(type2) {
10017
10362
  return Array.isArray(type2) ? JSON.stringify(type2) : String(type2);
@@ -10027,11 +10372,11 @@ function isRecordSchema(schema) {
10027
10372
  const propertyKeys = Object.keys(schema.properties ?? {});
10028
10373
  return schema.type === "object" && propertyKeys.length === 0 && typeof schema.additionalProperties === "object" && schema.additionalProperties !== null;
10029
10374
  }
10030
- function findDiscriminator(branches, root, path23) {
10375
+ function findDiscriminator(branches, root, path24) {
10031
10376
  const [firstBranch] = branches;
10032
10377
  if (firstBranch === void 0) {
10033
10378
  throw new Error(
10034
- `JSON Schema "${formatJsonSchemaPath(path23)}" uses oneOf/anyOf/allOf but has no branches.`
10379
+ `JSON Schema "${formatJsonSchemaPath(path24)}" uses oneOf/anyOf/allOf but has no branches.`
10035
10380
  );
10036
10381
  }
10037
10382
  const candidateKeys = Object.keys(firstBranch.properties ?? {});
@@ -10071,19 +10416,19 @@ function getDiscriminatorLiteral(branch, key2, root) {
10071
10416
  }
10072
10417
  return void 0;
10073
10418
  }
10074
- function resolveReferencedSchema(schema, root, path23) {
10419
+ function resolveReferencedSchema(schema, root, path24) {
10075
10420
  if (schema.$ref === void 0) {
10076
10421
  return schema;
10077
10422
  }
10078
10423
  const resolvedTarget = resolveLocalRef(root, schema.$ref);
10079
10424
  if (resolvedTarget === void 0) {
10080
10425
  throw new Error(
10081
- `JSON Schema "${formatJsonSchemaPath(path23)}" uses "$ref": ${schema.$ref}. toolcraft only supports internal refs like "#/components/schemas/Foo".`
10426
+ `JSON Schema "${formatJsonSchemaPath(path24)}" uses "$ref": ${schema.$ref}. toolcraft only supports internal refs like "#/components/schemas/Foo".`
10082
10427
  );
10083
10428
  }
10084
10429
  const { $ref: ignoredRef, ...siblingKeywords } = schema;
10085
10430
  void ignoredRef;
10086
- const resolvedSchema = resolveReferencedSchema(resolvedTarget, root, path23);
10431
+ const resolvedSchema = resolveReferencedSchema(resolvedTarget, root, path24);
10087
10432
  if (Object.keys(siblingKeywords).length === 0) {
10088
10433
  return resolvedSchema;
10089
10434
  }
@@ -10107,9 +10452,9 @@ function mergeJsonSchemas(base, overlay) {
10107
10452
  ...mergedRequired === void 0 ? {} : { required: mergedRequired }
10108
10453
  };
10109
10454
  }
10110
- function hasSelfReferencingRef(schema, root, path23 = "#", activePaths = /* @__PURE__ */ new Set()) {
10455
+ function hasSelfReferencingRef(schema, root, path24 = "#", activePaths = /* @__PURE__ */ new Set()) {
10111
10456
  const nextActivePaths = new Set(activePaths);
10112
- nextActivePaths.add(path23);
10457
+ nextActivePaths.add(path24);
10113
10458
  const localRefPath = getLocalRefPath(schema.$ref);
10114
10459
  if (localRefPath !== void 0) {
10115
10460
  if (nextActivePaths.has(localRefPath)) {
@@ -10120,13 +10465,13 @@ function hasSelfReferencingRef(schema, root, path23 = "#", activePaths = /* @__P
10120
10465
  return true;
10121
10466
  }
10122
10467
  }
10123
- if (schema.items !== void 0 && hasSelfReferencingRef(schema.items, root, `${path23}/items`, nextActivePaths)) {
10468
+ if (schema.items !== void 0 && hasSelfReferencingRef(schema.items, root, `${path24}/items`, nextActivePaths)) {
10124
10469
  return true;
10125
10470
  }
10126
10471
  if (typeof schema.additionalProperties === "object" && schema.additionalProperties !== null && hasSelfReferencingRef(
10127
10472
  schema.additionalProperties,
10128
10473
  root,
10129
- `${path23}/additionalProperties`,
10474
+ `${path24}/additionalProperties`,
10130
10475
  nextActivePaths
10131
10476
  )) {
10132
10477
  return true;
@@ -10135,7 +10480,7 @@ function hasSelfReferencingRef(schema, root, path23 = "#", activePaths = /* @__P
10135
10480
  if (hasSelfReferencingRef(
10136
10481
  childSchema,
10137
10482
  root,
10138
- `${path23}/properties/${escapeJsonPointerSegment(key2)}`,
10483
+ `${path24}/properties/${escapeJsonPointerSegment(key2)}`,
10139
10484
  nextActivePaths
10140
10485
  )) {
10141
10486
  return true;
@@ -10145,24 +10490,24 @@ function hasSelfReferencingRef(schema, root, path23 = "#", activePaths = /* @__P
10145
10490
  if (hasSelfReferencingRef(
10146
10491
  childSchema,
10147
10492
  root,
10148
- `${path23}/$defs/${escapeJsonPointerSegment(key2)}`,
10493
+ `${path24}/$defs/${escapeJsonPointerSegment(key2)}`,
10149
10494
  nextActivePaths
10150
10495
  )) {
10151
10496
  return true;
10152
10497
  }
10153
10498
  }
10154
10499
  for (const [index, childSchema] of (schema.oneOf ?? []).entries()) {
10155
- if (hasSelfReferencingRef(childSchema, root, `${path23}/oneOf/${index}`, nextActivePaths)) {
10500
+ if (hasSelfReferencingRef(childSchema, root, `${path24}/oneOf/${index}`, nextActivePaths)) {
10156
10501
  return true;
10157
10502
  }
10158
10503
  }
10159
10504
  for (const [index, childSchema] of (schema.anyOf ?? []).entries()) {
10160
- if (hasSelfReferencingRef(childSchema, root, `${path23}/anyOf/${index}`, nextActivePaths)) {
10505
+ if (hasSelfReferencingRef(childSchema, root, `${path24}/anyOf/${index}`, nextActivePaths)) {
10161
10506
  return true;
10162
10507
  }
10163
10508
  }
10164
10509
  for (const [index, childSchema] of (schema.allOf ?? []).entries()) {
10165
- if (hasSelfReferencingRef(childSchema, root, `${path23}/allOf/${index}`, nextActivePaths)) {
10510
+ if (hasSelfReferencingRef(childSchema, root, `${path24}/allOf/${index}`, nextActivePaths)) {
10166
10511
  return true;
10167
10512
  }
10168
10513
  }
@@ -10178,14 +10523,14 @@ function getLocalRefPath(ref) {
10178
10523
  return ref.startsWith("#/") ? ref : void 0;
10179
10524
  }
10180
10525
  function resolveLocalRef(root, ref) {
10181
- const path23 = getLocalRefPath(ref);
10182
- if (path23 === void 0) {
10526
+ const path24 = getLocalRefPath(ref);
10527
+ if (path24 === void 0) {
10183
10528
  return void 0;
10184
10529
  }
10185
- if (path23 === "#") {
10530
+ if (path24 === "#") {
10186
10531
  return root;
10187
10532
  }
10188
- const segments = path23.slice(2).split("/").map(unescapeJsonPointerSegment);
10533
+ const segments = path24.slice(2).split("/").map(unescapeJsonPointerSegment);
10189
10534
  let current = root;
10190
10535
  for (const segment of segments) {
10191
10536
  if (Array.isArray(current)) {
@@ -10388,10 +10733,10 @@ function validateRenameMap2(name, tools, rename5) {
10388
10733
  }
10389
10734
  }
10390
10735
  }
10391
- function createConnection(name, config) {
10736
+ function createConnection(name, config2) {
10392
10737
  const connection = {
10393
10738
  name,
10394
- config,
10739
+ config: config2,
10395
10740
  async dispose() {
10396
10741
  shutdownDisposers.delete(connection.dispose);
10397
10742
  connection.connecting = void 0;
@@ -10446,8 +10791,8 @@ async function readCache(cachePath) {
10446
10791
  }
10447
10792
  }
10448
10793
  async function writeCache(cachePath, cache) {
10449
- const directory = path11.dirname(cachePath);
10450
- const tempPath = `${cachePath}.tmp-${randomUUID()}`;
10794
+ const directory = path12.dirname(cachePath);
10795
+ const tempPath = `${cachePath}.tmp-${randomUUID2()}`;
10451
10796
  await assertCachePathHasNoSymlinks(cachePath);
10452
10797
  await assertCachePathHasNoSymlinks(tempPath);
10453
10798
  await mkdir(directory, { recursive: true });
@@ -10458,13 +10803,13 @@ async function writeCache(cachePath, cache) {
10458
10803
  await assertCachePathHasNoSymlinks(cachePath);
10459
10804
  await rename2(tempPath, cachePath);
10460
10805
  }
10461
- async function fetchCache(name, config) {
10806
+ async function fetchCache(name, config2) {
10462
10807
  const logger2 = createLogger((message2) => {
10463
10808
  process.stderr.write(`${message2}
10464
10809
  `);
10465
10810
  });
10466
10811
  logger2.info(`MCP ${name}: connecting`);
10467
- const client = await dialUpstream(name, config);
10812
+ const client = await dialUpstream(name, config2);
10468
10813
  try {
10469
10814
  logger2.info(`MCP ${name}: listing tools`);
10470
10815
  const tools = [];
@@ -10483,7 +10828,7 @@ async function fetchCache(name, config) {
10483
10828
  $schema: MCP_PROXY_SCHEMA_URL,
10484
10829
  version: 1,
10485
10830
  upstream,
10486
- configFingerprint: fingerprintMcpServerConfig(config),
10831
+ configFingerprint: fingerprintMcpServerConfig(config2),
10487
10832
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
10488
10833
  tools
10489
10834
  };
@@ -10553,8 +10898,8 @@ function isRefreshRequested(name, refresh) {
10553
10898
  }
10554
10899
  async function resolveSingleProxy(group, options) {
10555
10900
  const internal = getInternalGroupConfig2(group);
10556
- const config = internal.mcp;
10557
- if (config === void 0) {
10901
+ const config2 = internal.mcp;
10902
+ if (config2 === void 0) {
10558
10903
  return;
10559
10904
  }
10560
10905
  const name = group.name;
@@ -10564,21 +10909,21 @@ async function resolveSingleProxy(group, options) {
10564
10909
  let cache;
10565
10910
  let shouldWriteCache = false;
10566
10911
  if (isRefreshRequested(name, refresh)) {
10567
- cache = await fetchCache(name, config);
10912
+ cache = await fetchCache(name, config2);
10568
10913
  shouldWriteCache = true;
10569
10914
  } else {
10570
10915
  const storedCache = await readCache(cachePath);
10571
- if (storedCache && cacheMatchesConfig(storedCache, config)) {
10916
+ if (storedCache && cacheMatchesConfig(storedCache, config2)) {
10572
10917
  cache = storedCache;
10573
10918
  } else {
10574
- cache = await fetchCache(name, config);
10919
+ cache = await fetchCache(name, config2);
10575
10920
  shouldWriteCache = true;
10576
10921
  }
10577
10922
  }
10578
10923
  const tools = filterAllowlistedTools(cache.tools, internal.tools);
10579
10924
  validateRenameMap2(name, tools, internal.rename);
10580
10925
  const previousConnection = getProxyConnection(group);
10581
- const nextConnection = createConnection(name, config);
10926
+ const nextConnection = createConnection(name, config2);
10582
10927
  try {
10583
10928
  replaceProxyChildrenSafely(group, tools, internal.rename, nextConnection);
10584
10929
  if (shouldWriteCache) {
@@ -10603,11 +10948,11 @@ async function resolveSingleProxy(group, options) {
10603
10948
  );
10604
10949
  }
10605
10950
  }
10606
- function cacheMatchesConfig(cache, config) {
10607
- return cache.configFingerprint === fingerprintMcpServerConfig(config);
10951
+ function cacheMatchesConfig(cache, config2) {
10952
+ return cache.configFingerprint === fingerprintMcpServerConfig(config2);
10608
10953
  }
10609
- function fingerprintMcpServerConfig(config) {
10610
- return createHash3("sha256").update(JSON.stringify(config)).digest("hex");
10954
+ function fingerprintMcpServerConfig(config2) {
10955
+ return createHash3("sha256").update(JSON.stringify(config2)).digest("hex");
10611
10956
  }
10612
10957
  function collectProxyGroups(root) {
10613
10958
  const groups = [];
@@ -10627,13 +10972,13 @@ function collectProxyGroups(root) {
10627
10972
  function findProjectRoot(from = process.cwd()) {
10628
10973
  let current = process.cwd();
10629
10974
  if (from !== current) {
10630
- current = path11.resolve(from);
10975
+ current = path12.resolve(from);
10631
10976
  }
10632
10977
  while (true) {
10633
- if (existsSync2(path11.join(current, "package.json"))) {
10978
+ if (existsSync2(path12.join(current, "package.json"))) {
10634
10979
  return current;
10635
10980
  }
10636
- const parent = path11.dirname(current);
10981
+ const parent = path12.dirname(current);
10637
10982
  if (parent === current) {
10638
10983
  return void 0;
10639
10984
  }
@@ -10650,7 +10995,7 @@ function resolveCachePath(name, projectRoot) {
10650
10995
  if (name.length === 0 || name === "." || name === ".." || name.includes("/") || name.includes("\\")) {
10651
10996
  throw new Error(`MCP proxy group name must be a file-safe name: "${name}".`);
10652
10997
  }
10653
- return path11.join(resolvedProjectRoot, ".toolcraft", "mcp", `${name}.json`);
10998
+ return path12.join(resolvedProjectRoot, ".toolcraft", "mcp", `${name}.json`);
10654
10999
  }
10655
11000
  async function assertCachePathHasNoSymlinks(filePath) {
10656
11001
  let currentPath = filePath;
@@ -10664,10 +11009,10 @@ async function assertCachePathHasNoSymlinks(filePath) {
10664
11009
  throw error3;
10665
11010
  }
10666
11011
  }
10667
- if (path11.basename(currentPath) === ".toolcraft") {
11012
+ if (path12.basename(currentPath) === ".toolcraft") {
10668
11013
  return;
10669
11014
  }
10670
- const parentPath = path11.dirname(currentPath);
11015
+ const parentPath = path12.dirname(currentPath);
10671
11016
  if (parentPath === currentPath) {
10672
11017
  return;
10673
11018
  }
@@ -10685,20 +11030,20 @@ function parseRefreshEnv(value) {
10685
11030
  const names = trimmed.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
10686
11031
  return names.length === 0 ? void 0 : new Set(names);
10687
11032
  }
10688
- async function dialUpstream(name, config) {
11033
+ async function dialUpstream(name, config2) {
10689
11034
  const client = new McpClient({
10690
11035
  clientInfo: {
10691
11036
  name: `${DEFAULT_CLIENT_INFO.name}-${name}`,
10692
11037
  version: DEFAULT_CLIENT_INFO.version
10693
11038
  }
10694
11039
  });
10695
- const transport = config.transport === "stdio" ? new StdioTransport({
10696
- command: config.command,
10697
- ...config.args === void 0 ? {} : { args: config.args },
10698
- ...config.env === void 0 ? {} : { env: config.env }
11040
+ const transport = config2.transport === "stdio" ? new StdioTransport({
11041
+ command: config2.command,
11042
+ ...config2.args === void 0 ? {} : { args: config2.args },
11043
+ ...config2.env === void 0 ? {} : { env: config2.env }
10699
11044
  }) : new HttpTransport({
10700
- url: config.url,
10701
- ...config.headers === void 0 ? {} : { headers: config.headers }
11045
+ url: config2.url,
11046
+ ...config2.headers === void 0 ? {} : { headers: config2.headers }
10702
11047
  });
10703
11048
  await client.connect(transport);
10704
11049
  return client;
@@ -10708,10 +11053,82 @@ async function resolveMcpProxies(root, options = {}) {
10708
11053
  await Promise.all(groups.map((group) => resolveSingleProxy(group, options)));
10709
11054
  }
10710
11055
 
11056
+ // ../toolcraft/src/redaction.ts
11057
+ var REDACTED_VALUE = "<redacted>";
11058
+ var SENSITIVE_NAME_PARTS = ["password", "token", "apikey", "secret"];
11059
+ var AUTHORIZATION_HEADER_NAMES = /* @__PURE__ */ new Set(["authorization", "proxyauthorization"]);
11060
+ var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set(["cookie", "setcookie"]);
11061
+ function isPlainObject2(value) {
11062
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11063
+ }
11064
+ function normalizeName(name) {
11065
+ return name.toLowerCase().replace(/[^a-z0-9]/g, "");
11066
+ }
11067
+ function isSensitiveName(name) {
11068
+ const normalized = normalizeName(name);
11069
+ return SENSITIVE_NAME_PARTS.some((candidate) => normalized.includes(candidate));
11070
+ }
11071
+ function redactSecretLikeFieldsValue(value, name, seen) {
11072
+ if (name.length > 0 && isSensitiveName(name)) {
11073
+ return REDACTED_VALUE;
11074
+ }
11075
+ if (Array.isArray(value)) {
11076
+ if (seen.has(value)) {
11077
+ return "[Circular]";
11078
+ }
11079
+ seen.add(value);
11080
+ return value.map((entry) => redactSecretLikeFieldsValue(entry, name, seen));
11081
+ }
11082
+ if (isPlainObject2(value)) {
11083
+ if (seen.has(value)) {
11084
+ return "[Circular]";
11085
+ }
11086
+ seen.add(value);
11087
+ return Object.fromEntries(
11088
+ Object.entries(value).map(([key2, entry]) => [
11089
+ key2,
11090
+ redactSecretLikeFieldsValue(entry, key2, seen)
11091
+ ])
11092
+ );
11093
+ }
11094
+ return value;
11095
+ }
11096
+ function redactSecretLikeFields(value, name = "") {
11097
+ return redactSecretLikeFieldsValue(value, name, /* @__PURE__ */ new WeakSet());
11098
+ }
11099
+ function parseJsonObjectOrArray(value) {
11100
+ const trimmed = value.trim();
11101
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
11102
+ return void 0;
11103
+ }
11104
+ try {
11105
+ const parsed = JSON.parse(trimmed);
11106
+ return isPlainObject2(parsed) || Array.isArray(parsed) ? parsed : void 0;
11107
+ } catch {
11108
+ return void 0;
11109
+ }
11110
+ }
11111
+ function redactHttpBody(body) {
11112
+ if (typeof body === "string") {
11113
+ const parsed = parseJsonObjectOrArray(body);
11114
+ return parsed === void 0 ? body : redactSecretLikeFields(parsed);
11115
+ }
11116
+ return redactSecretLikeFields(body);
11117
+ }
11118
+ function redactHttpHeaderValue(name, value) {
11119
+ const normalized = normalizeName(name);
11120
+ if (AUTHORIZATION_HEADER_NAMES.has(normalized)) {
11121
+ return "Bearer ****";
11122
+ }
11123
+ if (SECRET_HEADER_NAMES.has(normalized) || isSensitiveName(name)) {
11124
+ return REDACTED_VALUE;
11125
+ }
11126
+ return value;
11127
+ }
11128
+
10711
11129
  // ../toolcraft/src/error-report.ts
10712
11130
  var ERROR_REPORTS_ENV = "TOOLCRAFT_ERROR_REPORTS";
10713
- var DEFAULT_SENSITIVE_NAMES = ["password", "token", "apikey", "secret"];
10714
- function isPlainObject2(value) {
11131
+ function isPlainObject3(value) {
10715
11132
  return typeof value === "object" && value !== null && !Array.isArray(value);
10716
11133
  }
10717
11134
  function unwrapOptional(schema) {
@@ -10721,7 +11138,7 @@ function unwrapOptional(schema) {
10721
11138
  return schema;
10722
11139
  }
10723
11140
  function hasHttpContext(error3) {
10724
- return error3 instanceof Error && error3.name === "HttpError" && isPlainObject2(error3.request) && isPlainObject2(error3.response);
11141
+ return error3 instanceof Error && error3.name === "HttpError" && isPlainObject3(error3.request) && isPlainObject3(error3.response);
10725
11142
  }
10726
11143
  function isSkippedError(error3) {
10727
11144
  if (error3 instanceof ApprovalDeclinedError) {
@@ -10741,25 +11158,25 @@ function reportsEnabled(option, env) {
10741
11158
  function resolveReportDir(option, projectRoot) {
10742
11159
  const configuredDir = typeof option === "object" ? option.dir : void 0;
10743
11160
  if (configuredDir === void 0 || configuredDir.length === 0) {
10744
- return path12.join(projectRoot, ".toolcraft", "errors");
11161
+ return path13.join(projectRoot, ".toolcraft", "errors");
10745
11162
  }
10746
- return path12.isAbsolute(configuredDir) ? configuredDir : path12.join(projectRoot, configuredDir);
11163
+ return path13.isAbsolute(configuredDir) ? configuredDir : path13.join(projectRoot, configuredDir);
10747
11164
  }
10748
11165
  function reportDirMustStayWithinProject(option) {
10749
11166
  const configuredDir = typeof option === "object" ? option.dir : void 0;
10750
- return configuredDir === void 0 || configuredDir.length === 0 || !path12.isAbsolute(configuredDir);
11167
+ return configuredDir === void 0 || configuredDir.length === 0 || !path13.isAbsolute(configuredDir);
10751
11168
  }
10752
11169
  function isWithinDirectory(parent, child) {
10753
- const relative = path12.relative(parent, child);
10754
- return relative === "" || !path12.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path12.sep}`);
11170
+ const relative = path13.relative(parent, child);
11171
+ return relative === "" || !path13.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path13.sep}`);
10755
11172
  }
10756
11173
  async function assertReportDirWithinProject(projectRoot, reportDir) {
10757
- if (!isWithinDirectory(path12.resolve(projectRoot), path12.resolve(reportDir))) {
11174
+ if (!isWithinDirectory(path13.resolve(projectRoot), path13.resolve(reportDir))) {
10758
11175
  throw new Error("Error report directory resolves outside project root.");
10759
11176
  }
10760
11177
  const [canonicalProjectRoot, canonicalReportDir] = await Promise.all([
10761
- realpath(projectRoot),
10762
- realpath(reportDir)
11178
+ realpath2(projectRoot),
11179
+ realpath2(reportDir)
10763
11180
  ]);
10764
11181
  if (!isWithinDirectory(canonicalProjectRoot, canonicalReportDir)) {
10765
11182
  throw new Error("Error report directory resolves outside project root.");
@@ -10805,7 +11222,7 @@ function slugifyCommandPath(commandPath) {
10805
11222
  return output.length === 0 ? "root" : output;
10806
11223
  }
10807
11224
  function relativeDisplayPath(projectRoot, absolutePath) {
10808
- const relative = path12.relative(projectRoot, absolutePath);
11225
+ const relative = path13.relative(projectRoot, absolutePath);
10809
11226
  return relative.length === 0 || relative.startsWith("..") ? absolutePath : relative;
10810
11227
  }
10811
11228
  function redactValue(value) {
@@ -10814,9 +11231,24 @@ function redactValue(value) {
10814
11231
  }
10815
11232
  return `<set, ${value.length} chars>`;
10816
11233
  }
10817
- function isSensitiveName(name) {
10818
- const normalized = name.toLowerCase();
10819
- return DEFAULT_SENSITIVE_NAMES.some((candidate) => normalized.includes(candidate));
11234
+ function collectStringLeaves(value, output) {
11235
+ if (typeof value === "string") {
11236
+ if (value.length > 0) {
11237
+ output.add(value);
11238
+ }
11239
+ return;
11240
+ }
11241
+ if (Array.isArray(value)) {
11242
+ for (const entry of value) {
11243
+ collectStringLeaves(entry, output);
11244
+ }
11245
+ return;
11246
+ }
11247
+ if (isPlainObject3(value)) {
11248
+ for (const entry of Object.values(value)) {
11249
+ collectStringLeaves(entry, output);
11250
+ }
11251
+ }
10820
11252
  }
10821
11253
  function schemaSecretValue(schema) {
10822
11254
  const unwrapped = unwrapOptional(schema);
@@ -10837,7 +11269,7 @@ function redactParamsValue(value, schema, name) {
10837
11269
  return "<redacted>";
10838
11270
  }
10839
11271
  const unwrapped = unwrapOptional(schema);
10840
- if (unwrapped.kind === "object" && isPlainObject2(value)) {
11272
+ if (unwrapped.kind === "object" && isPlainObject3(value)) {
10841
11273
  return Object.fromEntries(
10842
11274
  Object.entries(value).map(([key2, childValue]) => {
10843
11275
  const childSchema = unwrapped.shape[key2];
@@ -10859,6 +11291,52 @@ function redactParams(params17, command) {
10859
11291
  }
10860
11292
  return redactParamsValue(params17, command.params, "");
10861
11293
  }
11294
+ function collectSensitiveParamValues(value, schema, name, output) {
11295
+ if (shouldRedactParam(name, schema)) {
11296
+ collectStringLeaves(value, output);
11297
+ return;
11298
+ }
11299
+ const unwrapped = unwrapOptional(schema);
11300
+ if (unwrapped.kind === "object" && isPlainObject3(value)) {
11301
+ for (const [key2, childValue] of Object.entries(value)) {
11302
+ const childSchema = unwrapped.shape[key2];
11303
+ if (childSchema !== void 0) {
11304
+ collectSensitiveParamValues(childValue, childSchema, key2, output);
11305
+ }
11306
+ }
11307
+ return;
11308
+ }
11309
+ if (unwrapped.kind === "array" && Array.isArray(value)) {
11310
+ for (const entry of value) {
11311
+ collectSensitiveParamValues(entry, unwrapped.item, name, output);
11312
+ }
11313
+ }
11314
+ }
11315
+ function createReportStringRedactor(context, env) {
11316
+ const values = /* @__PURE__ */ new Set();
11317
+ for (const value of Object.values(context.secrets ?? {})) {
11318
+ if (value !== void 0 && value.length > 0) {
11319
+ values.add(value);
11320
+ }
11321
+ }
11322
+ for (const [name, secret] of Object.entries(context.command?.secrets ?? {})) {
11323
+ const value = context.secrets?.[name] ?? env[secret.env];
11324
+ if (value !== void 0 && value.length > 0) {
11325
+ values.add(value);
11326
+ }
11327
+ }
11328
+ if (context.command !== void 0) {
11329
+ collectSensitiveParamValues(context.params, context.command.params, "", values);
11330
+ }
11331
+ const orderedValues = [...values].sort((left, right) => right.length - left.length);
11332
+ return (value) => {
11333
+ let redacted = value;
11334
+ for (const secretValue of orderedValues) {
11335
+ redacted = redacted.split(secretValue).join("<redacted>");
11336
+ }
11337
+ return redacted;
11338
+ };
11339
+ }
10862
11340
  function commandSecretEnvNames(secrets) {
10863
11341
  if (secrets === void 0) {
10864
11342
  return [];
@@ -10912,28 +11390,42 @@ function redactArgv(argv, options) {
10912
11390
  function stableJson(value) {
10913
11391
  return JSON.stringify(value, null, 2) ?? "undefined";
10914
11392
  }
10915
- function redactStructuredErrorField(name, value) {
10916
- if (typeof value === "string" && name.toLowerCase() === "authorization") {
10917
- return "Bearer ****";
11393
+ function redactStructuredErrorField(name, value, redactString) {
11394
+ if (typeof value === "string") {
11395
+ const redactedHeaderValue = redactHttpHeaderValue(name, value);
11396
+ if (redactedHeaderValue !== value) {
11397
+ return redactedHeaderValue;
11398
+ }
11399
+ if (isSensitiveName(name)) {
11400
+ return "<redacted>";
11401
+ }
11402
+ return redactString(value);
10918
11403
  }
10919
11404
  if (Array.isArray(value)) {
10920
- return value.map((entry) => redactStructuredErrorField(name, entry));
11405
+ return value.map((entry) => redactStructuredErrorField(name, entry, redactString));
10921
11406
  }
10922
- if (isPlainObject2(value)) {
11407
+ if (isPlainObject3(value)) {
10923
11408
  return Object.fromEntries(
10924
- Object.entries(value).map(([key2, entry]) => [key2, redactStructuredErrorField(key2, entry)])
11409
+ Object.entries(value).map(([key2, entry]) => [
11410
+ key2,
11411
+ redactStructuredErrorField(key2, entry, redactString)
11412
+ ])
10925
11413
  );
10926
11414
  }
10927
11415
  return value;
10928
11416
  }
10929
- function ownStructuredFields(error3) {
11417
+ function ownStructuredFields(error3, redactString) {
10930
11418
  const fields = {};
10931
11419
  for (const key2 of Object.keys(error3)) {
10932
11420
  if (key2 === "name" || key2 === "message" || key2 === "stack" || key2 === "cause") {
10933
11421
  continue;
10934
11422
  }
10935
11423
  Object.defineProperty(fields, key2, {
10936
- value: redactStructuredErrorField(key2, error3[key2]),
11424
+ value: redactStructuredErrorField(
11425
+ key2,
11426
+ error3[key2],
11427
+ redactString
11428
+ ),
10937
11429
  enumerable: true,
10938
11430
  configurable: true,
10939
11431
  writable: true
@@ -10941,43 +11433,44 @@ function ownStructuredFields(error3) {
10941
11433
  }
10942
11434
  return fields;
10943
11435
  }
10944
- function formatStackChain(error3) {
11436
+ function formatStackChain(error3, redactString) {
10945
11437
  const lines = [];
10946
11438
  let current = error3;
10947
11439
  let index = 0;
10948
11440
  while (current !== void 0) {
10949
11441
  if (current instanceof Error) {
10950
- lines.push(
10951
- index === 0 ? current.stack ?? String(current) : `Caused by: ${current.stack ?? String(current)}`
10952
- );
11442
+ const stack = current.stack ?? String(current);
11443
+ lines.push(redactString(index === 0 ? stack : `Caused by: ${stack}`));
10953
11444
  current = current.cause;
10954
11445
  } else {
10955
- lines.push(index === 0 ? String(current) : `Caused by: ${String(current)}`);
11446
+ const message2 = String(current);
11447
+ lines.push(redactString(index === 0 ? message2 : `Caused by: ${message2}`));
10956
11448
  current = void 0;
10957
11449
  }
10958
11450
  index += 1;
10959
11451
  }
10960
11452
  return lines.join("\n");
10961
11453
  }
10962
- function formatHeaderValue(name, value) {
10963
- return name.toLowerCase() === "authorization" ? "Bearer ****" : value;
11454
+ function formatHeaderValue(name, value, redactString) {
11455
+ return redactString(redactHttpHeaderValue(name, value));
10964
11456
  }
10965
- function formatHeaders(headers) {
10966
- return Object.entries(headers).map(([name, value]) => `${name}: ${formatHeaderValue(name, value)}`).join("\n");
11457
+ function formatHeaders(headers, redactString) {
11458
+ return Object.entries(headers).map(([name, value]) => `${name}: ${formatHeaderValue(name, value, redactString)}`).join("\n");
10967
11459
  }
10968
- function formatBody(body) {
10969
- if (typeof body === "string") {
10970
- return body;
11460
+ function formatBody(body, redactString) {
11461
+ const redactedBody = redactHttpBody(body);
11462
+ if (typeof redactedBody === "string") {
11463
+ return redactString(redactedBody);
10971
11464
  }
10972
- return stableJson(body);
11465
+ return redactString(stableJson(redactedBody));
10973
11466
  }
10974
- function formatHttpTranscript(error3) {
11467
+ function formatHttpTranscript(error3, redactString) {
10975
11468
  const requestLines = [
10976
11469
  `${error3.request.method} ${error3.request.url}`,
10977
- formatHeaders(error3.request.headers)
11470
+ formatHeaders(error3.request.headers, redactString)
10978
11471
  ].filter((line) => line.length > 0);
10979
11472
  if (error3.request.body !== void 0) {
10980
- requestLines.push("", formatBody(error3.request.body));
11473
+ requestLines.push("", formatBody(error3.request.body, redactString));
10981
11474
  }
10982
11475
  return [
10983
11476
  "Request:",
@@ -10985,9 +11478,9 @@ function formatHttpTranscript(error3) {
10985
11478
  "",
10986
11479
  "Response:",
10987
11480
  `${error3.response.status} ${error3.response.statusText}`,
10988
- formatHeaders(error3.response.headers),
11481
+ formatHeaders(error3.response.headers, redactString),
10989
11482
  "",
10990
- formatBody(error3.response.body)
11483
+ formatBody(error3.response.body, redactString)
10991
11484
  ].join("\n");
10992
11485
  }
10993
11486
  function resolveToolcraftVersion(version) {
@@ -10996,9 +11489,10 @@ function resolveToolcraftVersion(version) {
10996
11489
  function buildReport(context) {
10997
11490
  const env = context.env ?? process.env;
10998
11491
  const error3 = context.error;
11492
+ const redactString = createReportStringRedactor(context, env);
10999
11493
  const errorName = error3 instanceof Error ? error3.name : typeof error3;
11000
- const errorMessage = error3 instanceof Error ? error3.message : String(error3);
11001
- const structuredFields = error3 instanceof Error ? ownStructuredFields(error3) : {};
11494
+ const errorMessage = redactString(error3 instanceof Error ? error3.message : String(error3));
11495
+ const structuredFields = error3 instanceof Error ? ownStructuredFields(error3, redactString) : {};
11002
11496
  const secretLines = Object.entries(context.command?.secrets ?? {}).map(([name, secret]) => {
11003
11497
  const value = context.secrets?.[name] ?? env[secret.env];
11004
11498
  return `${secret.env}=${redactValue(value)}`;
@@ -11012,7 +11506,9 @@ function buildReport(context) {
11012
11506
  `platform: ${process.platform} ${process.arch}`,
11013
11507
  "",
11014
11508
  "Argv",
11015
- stableJson(redactArgv(context.argv, { command: context.command, secrets: context.secrets })),
11509
+ redactString(
11510
+ stableJson(redactArgv(context.argv, { command: context.command, secrets: context.secrets }))
11511
+ ),
11016
11512
  "",
11017
11513
  "Resolved Secrets",
11018
11514
  ...secretLines.length === 0 ? ["<none>"] : secretLines,
@@ -11021,19 +11517,19 @@ function buildReport(context) {
11021
11517
  context.commandPath === void 0 || context.commandPath.length === 0 ? "root" : context.commandPath,
11022
11518
  "",
11023
11519
  "Parsed Params",
11024
- stableJson(redactParams(context.params, context.command)),
11520
+ redactString(stableJson(redactParams(context.params, context.command))),
11025
11521
  "",
11026
11522
  "Error",
11027
11523
  `name: ${errorName}`,
11028
11524
  `message: ${errorMessage}`,
11029
11525
  "structured fields:",
11030
- stableJson(structuredFields),
11526
+ redactString(stableJson(structuredFields)),
11031
11527
  "",
11032
11528
  "Stack",
11033
- formatStackChain(error3)
11529
+ formatStackChain(error3, redactString)
11034
11530
  ];
11035
11531
  if (hasHttpContext(error3)) {
11036
- lines.push("", "HTTP Transcript", formatHttpTranscript(error3));
11532
+ lines.push("", "HTTP Transcript", formatHttpTranscript(error3, redactString));
11037
11533
  }
11038
11534
  return `${lines.join("\n")}
11039
11535
  `;
@@ -11045,8 +11541,8 @@ async function writeErrorReport(context) {
11045
11541
  }
11046
11542
  const projectRoot = resolveProjectRoot(context.projectRoot);
11047
11543
  const reportDir = resolveReportDir(context.errorReports, projectRoot);
11048
- const fileName = `${formatTimestamp(/* @__PURE__ */ new Date())}-${slugifyCommandPath(context.commandPath)}-${randomUUID2()}.log`;
11049
- const absolutePath = path12.join(reportDir, fileName);
11544
+ const fileName = `${formatTimestamp(/* @__PURE__ */ new Date())}-${slugifyCommandPath(context.commandPath)}-${randomUUID3()}.log`;
11545
+ const absolutePath = path13.join(reportDir, fileName);
11050
11546
  await mkdir2(reportDir, { recursive: true });
11051
11547
  if (reportDirMustStayWithinProject(context.errorReports)) {
11052
11548
  await assertReportDirWithinProject(projectRoot, reportDir);
@@ -11493,6 +11989,7 @@ ${rendered.join("\n")}`);
11493
11989
  }
11494
11990
 
11495
11991
  // ../toolcraft/src/cli.ts
11992
+ configureTheme({ brand: "blue", label: "Toolcraft" });
11496
11993
  var RESERVED_SERVICE_NAMES = /* @__PURE__ */ new Set([
11497
11994
  "params",
11498
11995
  "secrets",
@@ -11510,7 +12007,7 @@ function inferProgramName(argv) {
11510
12007
  if (typeof entrypoint !== "string" || entrypoint.length === 0) {
11511
12008
  return "toolcraft";
11512
12009
  }
11513
- const parsed = path13.parse(entrypoint);
12010
+ const parsed = path14.parse(entrypoint);
11514
12011
  return parsed.name.length > 0 ? parsed.name : "toolcraft";
11515
12012
  }
11516
12013
  function normalizeRoots(roots, argv) {
@@ -11568,11 +12065,11 @@ function formatSegment(segment, casing) {
11568
12065
  const separator = casing === "snake" ? "_" : "-";
11569
12066
  return splitWords3(segment).join(separator);
11570
12067
  }
11571
- function toOptionFlag(path23, casing) {
11572
- return `--${path23.map((segment) => formatSegment(segment, casing)).join(".")}`;
12068
+ function toOptionFlag(path24, casing) {
12069
+ return `--${path24.map((segment) => formatSegment(segment, casing)).join(".")}`;
11573
12070
  }
11574
- function toOptionAttribute(path23, casing) {
11575
- return path23.map((segment) => {
12071
+ function toOptionAttribute(path24, casing) {
12072
+ return path24.map((segment) => {
11576
12073
  const formatted = formatSegment(segment, casing);
11577
12074
  if (casing === "snake") {
11578
12075
  return formatted;
@@ -11583,23 +12080,23 @@ function toOptionAttribute(path23, casing) {
11583
12080
  ).join("");
11584
12081
  }).join(".");
11585
12082
  }
11586
- function toDisplayPath(path23) {
11587
- return path23.join(".");
12083
+ function toDisplayPath(path24) {
12084
+ return path24.join(".");
11588
12085
  }
11589
- function toUnionKindControlPath(path23) {
11590
- if (path23.length === 0) {
12086
+ function toUnionKindControlPath(path24) {
12087
+ if (path24.length === 0) {
11591
12088
  return ["kind"];
11592
12089
  }
11593
- const head = path23.slice(0, -1);
11594
- const tail = path23[path23.length - 1] ?? "";
12090
+ const head = path24.slice(0, -1);
12091
+ const tail = path24[path24.length - 1] ?? "";
11595
12092
  return [...head, `${tail}Kind`];
11596
12093
  }
11597
- function toUnionKindDisplayPath(path23) {
11598
- if (path23.length === 0) {
12094
+ function toUnionKindDisplayPath(path24) {
12095
+ if (path24.length === 0) {
11599
12096
  return "kind";
11600
12097
  }
11601
- const head = path23.slice(0, -1);
11602
- const tail = path23[path23.length - 1] ?? "";
12098
+ const head = path24.slice(0, -1);
12099
+ const tail = path24[path24.length - 1] ?? "";
11603
12100
  return [...head, `${tail}-kind`].join(".");
11604
12101
  }
11605
12102
  function createSyntheticEnumSchema(values) {
@@ -11615,14 +12112,14 @@ function getRequiredBranchFingerprint(branch, casing) {
11615
12112
  const requiredKeys = Object.entries(branch.shape).filter(([, schema]) => schema.kind !== "optional").map(([key2]) => formatSegment(key2, casing)).sort();
11616
12113
  return requiredKeys.join("+");
11617
12114
  }
11618
- function collectFields(schema, casing, globalLongOptionFlags, path23 = [], inheritedOptional = false, variantContext) {
12115
+ function collectFields(schema, casing, globalLongOptionFlags, path24 = [], inheritedOptional = false, variantContext) {
11619
12116
  const collected = {
11620
12117
  dynamicFields: [],
11621
12118
  fields: [],
11622
12119
  variants: []
11623
12120
  };
11624
12121
  for (const [key2, rawChildSchema] of Object.entries(schema.shape)) {
11625
- const nextPath = [...path23, key2];
12122
+ const nextPath = [...path24, key2];
11626
12123
  const runtimeOptional = inheritedOptional || rawChildSchema.kind === "optional";
11627
12124
  const childSchema = unwrapOptional2(rawChildSchema);
11628
12125
  const requiredWhenActive = rawChildSchema.kind !== "optional" && childSchema.default === void 0;
@@ -11803,9 +12300,9 @@ function collectFields(schema, casing, globalLongOptionFlags, path23 = [], inher
11803
12300
  }
11804
12301
  return collected;
11805
12302
  }
11806
- function toCommanderOptionAttribute(path23, casing, globalLongOptionFlags) {
11807
- const optionAttribute = toOptionAttribute(path23, casing);
11808
- const optionFlag = toOptionFlag(path23, casing);
12303
+ function toCommanderOptionAttribute(path24, casing, globalLongOptionFlags) {
12304
+ const optionAttribute = toOptionAttribute(path24, casing);
12305
+ const optionFlag = toOptionFlag(path24, casing);
11809
12306
  if (!globalLongOptionFlags.has(optionFlag)) {
11810
12307
  return optionAttribute;
11811
12308
  }
@@ -12774,10 +13271,10 @@ function parseDebugStackMode(value) {
12774
13271
  formatInvalidEnumMessage("--debug", String(value), ["raw"], { candidates: ["raw"] })
12775
13272
  );
12776
13273
  }
12777
- function setNestedValue(target, path23, value) {
13274
+ function setNestedValue(target, path24, value) {
12778
13275
  let cursor = target;
12779
- for (let index = 0; index < path23.length - 1; index += 1) {
12780
- const segment = path23[index] ?? "";
13276
+ for (let index = 0; index < path24.length - 1; index += 1) {
13277
+ const segment = path24[index] ?? "";
12781
13278
  const existing = Object.prototype.hasOwnProperty.call(cursor, segment) ? cursor[segment] : void 0;
12782
13279
  if (typeof existing === "object" && existing !== null) {
12783
13280
  cursor = existing;
@@ -12792,7 +13289,7 @@ function setNestedValue(target, path23, value) {
12792
13289
  });
12793
13290
  cursor = next;
12794
13291
  }
12795
- const leaf = path23[path23.length - 1];
13292
+ const leaf = path24[path24.length - 1];
12796
13293
  if (leaf !== void 0) {
12797
13294
  Object.defineProperty(cursor, leaf, {
12798
13295
  value,
@@ -12902,21 +13399,21 @@ async function withOutputFormat2(output, fn) {
12902
13399
  }
12903
13400
  function createFs2() {
12904
13401
  return {
12905
- readFile: async (path23, encoding = "utf8") => readFile4(path23, { encoding }),
12906
- writeFile: async (path23, contents) => {
12907
- await writeFile5(path23, contents);
13402
+ readFile: async (path24, encoding = "utf8") => readFile4(path24, { encoding }),
13403
+ writeFile: async (path24, contents) => {
13404
+ await writeFile5(path24, contents);
12908
13405
  },
12909
- exists: async (path23) => {
13406
+ exists: async (path24) => {
12910
13407
  try {
12911
- await access2(path23);
13408
+ await access2(path24);
12912
13409
  return true;
12913
13410
  } catch {
12914
13411
  return false;
12915
13412
  }
12916
13413
  },
12917
- lstat: async (path23) => lstat3(path23),
13414
+ lstat: async (path24) => lstat3(path24),
12918
13415
  rename: async (fromPath, toPath) => rename3(fromPath, toPath),
12919
- unlink: async (path23) => unlink2(path23)
13416
+ unlink: async (path24) => unlink2(path24)
12920
13417
  };
12921
13418
  }
12922
13419
  function createEnv2(values = process.env) {
@@ -12926,15 +13423,15 @@ function createEnv2(values = process.env) {
12926
13423
  }
12927
13424
  };
12928
13425
  }
12929
- function isPlainObject3(value) {
13426
+ function isPlainObject4(value) {
12930
13427
  return typeof value === "object" && value !== null && !Array.isArray(value);
12931
13428
  }
12932
13429
  function hasFieldValue(value) {
12933
13430
  return value !== void 0;
12934
13431
  }
12935
- function hasNestedField(fields, path23) {
13432
+ function hasNestedField(fields, path24) {
12936
13433
  return fields.some(
12937
- (field) => path23.length < field.path.length && path23.every((segment, index) => field.path[index] === segment)
13434
+ (field) => path24.length < field.path.length && path24.every((segment, index) => field.path[index] === segment)
12938
13435
  );
12939
13436
  }
12940
13437
  function describeExpectedPresetValue(schema) {
@@ -13035,14 +13532,14 @@ async function loadPresetValues(fields, presetPath) {
13035
13532
  { cause: error3 }
13036
13533
  );
13037
13534
  }
13038
- if (!isPlainObject3(parsedPreset)) {
13535
+ if (!isPlainObject4(parsedPreset)) {
13039
13536
  throw new UserError(`Preset file "${presetPath}" must contain a JSON object.`);
13040
13537
  }
13041
13538
  const fieldByPath = new Map(fields.map((field) => [field.displayPath, field]));
13042
13539
  const presetValues = {};
13043
- function visitObject(current, path23) {
13540
+ function visitObject(current, path24) {
13044
13541
  for (const [key2, value] of Object.entries(current)) {
13045
- const nextPath = [...path23, key2];
13542
+ const nextPath = [...path24, key2];
13046
13543
  const displayPath = toDisplayPath(nextPath);
13047
13544
  const field = fieldByPath.get(displayPath);
13048
13545
  if (field !== void 0) {
@@ -13054,7 +13551,7 @@ async function loadPresetValues(fields, presetPath) {
13054
13551
  `Preset file "${presetPath}" contains unknown parameter "${displayPath}".`
13055
13552
  );
13056
13553
  }
13057
- if (!isPlainObject3(value)) {
13554
+ if (!isPlainObject4(value)) {
13058
13555
  throw new UserError(
13059
13556
  `Preset file "${presetPath}" has an invalid value for "${displayPath}". Expected an object, got ${describeReceived(value)}.`
13060
13557
  );
@@ -13097,8 +13594,8 @@ function matchesFixtureValue(expected, actual) {
13097
13594
  }
13098
13595
  return expected.every((item, index) => matchesFixtureValue(item, actual[index]));
13099
13596
  }
13100
- if (isPlainObject3(expected)) {
13101
- if (!isPlainObject3(actual)) {
13597
+ if (isPlainObject4(expected)) {
13598
+ if (!isPlainObject4(actual)) {
13102
13599
  return false;
13103
13600
  }
13104
13601
  return Object.entries(expected).every(
@@ -13161,9 +13658,9 @@ function createFixtureFetch(entries) {
13161
13658
  };
13162
13659
  }
13163
13660
  function createFixtureFs(definition) {
13164
- const fsDefinition = isPlainObject3(definition) ? definition : {};
13165
- const readFileEntries = isPlainObject3(fsDefinition.readFile) ? fsDefinition.readFile : {};
13166
- const existsEntries = isPlainObject3(fsDefinition.exists) ? fsDefinition.exists : {};
13661
+ const fsDefinition = isPlainObject4(definition) ? definition : {};
13662
+ const readFileEntries = isPlainObject4(fsDefinition.readFile) ? fsDefinition.readFile : {};
13663
+ const existsEntries = isPlainObject4(fsDefinition.exists) ? fsDefinition.exists : {};
13167
13664
  return {
13168
13665
  readFile: async (filePath) => {
13169
13666
  if (Object.prototype.hasOwnProperty.call(readFileEntries, filePath)) {
@@ -13186,10 +13683,10 @@ function createFixtureFs(definition) {
13186
13683
  function resolveFixtureMethodResult(methodName, definition, args) {
13187
13684
  if (Array.isArray(definition)) {
13188
13685
  for (const entry of definition) {
13189
- if (!isPlainObject3(entry)) {
13686
+ if (!isPlainObject4(entry)) {
13190
13687
  continue;
13191
13688
  }
13192
- const explicitMatcher = isPlainObject3(entry.request) ? entry.request : void 0;
13689
+ const explicitMatcher = isPlainObject4(entry.request) ? entry.request : void 0;
13193
13690
  const matcher = explicitMatcher ?? Object.fromEntries(
13194
13691
  Object.entries(entry).filter(
13195
13692
  ([key2]) => key2 !== "result" && key2 !== "response" && key2 !== "error"
@@ -13201,7 +13698,7 @@ function resolveFixtureMethodResult(methodName, definition, args) {
13201
13698
  matched = matchesFixtureValue(matcher.args, args);
13202
13699
  } else if (Object.keys(matcher).length === 0) {
13203
13700
  matched = true;
13204
- } else if (isPlainObject3(firstArg)) {
13701
+ } else if (isPlainObject4(firstArg)) {
13205
13702
  matched = matchesFixtureValue(matcher, firstArg);
13206
13703
  } else if (args.length === 1 && Object.keys(matcher).length === 1) {
13207
13704
  const [[, expectedValue]] = Object.entries(matcher);
@@ -13222,7 +13719,7 @@ function resolveFixtureMethodResult(methodName, definition, args) {
13222
13719
  return Promise.resolve(null);
13223
13720
  }
13224
13721
  }
13225
- if (isPlainObject3(definition)) {
13722
+ if (isPlainObject4(definition)) {
13226
13723
  const firstArg = args[0];
13227
13724
  if (typeof firstArg === "string" && Object.prototype.hasOwnProperty.call(definition, firstArg)) {
13228
13725
  return Promise.resolve(definition[firstArg]);
@@ -13234,7 +13731,7 @@ function resolveFixtureMethodResult(methodName, definition, args) {
13234
13731
  return Promise.resolve(null);
13235
13732
  }
13236
13733
  function createFixtureService(definition) {
13237
- const methods = isPlainObject3(definition) ? definition : {};
13734
+ const methods = isPlainObject4(definition) ? definition : {};
13238
13735
  return new Proxy(
13239
13736
  {},
13240
13737
  {
@@ -13249,8 +13746,8 @@ function createFixtureService(definition) {
13249
13746
  );
13250
13747
  }
13251
13748
  function resolveFixturePath(commandPath) {
13252
- const parsed = path13.parse(commandPath);
13253
- return path13.join(parsed.dir, `${parsed.name}.fixture.json`);
13749
+ const parsed = path14.parse(commandPath);
13750
+ return path14.join(parsed.dir, `${parsed.name}.fixture.json`);
13254
13751
  }
13255
13752
  function selectFixtureScenario(scenarios, selector, fixturePath) {
13256
13753
  if (isNumericFixtureSelector(selector)) {
@@ -13332,7 +13829,7 @@ async function resolveFixtureRuntime(command, services, requirementOptions) {
13332
13829
  };
13333
13830
  }
13334
13831
  const scenario = await loadFixtureScenario(command, selector);
13335
- const scenarioServices = isPlainObject3(scenario.services) ? scenario.services : {};
13832
+ const scenarioServices = isPlainObject4(scenario.services) ? scenario.services : {};
13336
13833
  const customServiceNames = /* @__PURE__ */ new Set([
13337
13834
  ...Object.keys(services),
13338
13835
  ...Object.keys(scenarioServices).filter((name) => !RESERVED_SERVICE_NAMES.has(name))
@@ -13384,8 +13881,8 @@ function validateServices(services) {
13384
13881
  }
13385
13882
  }
13386
13883
  }
13387
- function getNestedValue(target, path23) {
13388
- return path23.reduce(
13884
+ function getNestedValue(target, path24) {
13885
+ return path24.reduce(
13389
13886
  (current, segment) => current !== null && typeof current === "object" ? current[segment] : void 0,
13390
13887
  target
13391
13888
  );
@@ -13614,7 +14111,7 @@ function finalizeDynamicValue(schema, value, displayPath, errors2) {
13614
14111
  if (itemSchema.kind !== "object") {
13615
14112
  return value;
13616
14113
  }
13617
- if (!isPlainObject3(value)) {
14114
+ if (!isPlainObject4(value)) {
13618
14115
  errors2.push({
13619
14116
  path: displayPath,
13620
14117
  message: `Invalid value for "${displayPath}". Expected indexed object entries, got ${describeReceived(value)}.`
@@ -13649,7 +14146,7 @@ function finalizeDynamicValue(schema, value, displayPath, errors2) {
13649
14146
  );
13650
14147
  }
13651
14148
  case "object": {
13652
- if (!isPlainObject3(value)) {
14149
+ if (!isPlainObject4(value)) {
13653
14150
  errors2.push({
13654
14151
  path: displayPath,
13655
14152
  message: `Invalid value for "${displayPath}". Expected an object, got ${describeReceived(value)}.`
@@ -13680,7 +14177,7 @@ function finalizeDynamicValue(schema, value, displayPath, errors2) {
13680
14177
  return result;
13681
14178
  }
13682
14179
  case "record": {
13683
- if (!isPlainObject3(value)) {
14180
+ if (!isPlainObject4(value)) {
13684
14181
  errors2.push({
13685
14182
  path: displayPath,
13686
14183
  message: `Invalid value for "${displayPath}". Expected an object, got ${describeReceived(value)}.`
@@ -14106,10 +14603,10 @@ async function executeCommand(state, services, requirementOptions, runtimeOption
14106
14603
  }
14107
14604
  }
14108
14605
  function isStringRecord(value) {
14109
- return isPlainObject3(value) && Object.values(value).every((entry) => typeof entry === "string");
14606
+ return isPlainObject4(value) && Object.values(value).every((entry) => typeof entry === "string");
14110
14607
  }
14111
14608
  function isHttpErrorLike(error3) {
14112
- if (!isPlainObject3(error3)) {
14609
+ if (!isPlainObject4(error3)) {
14113
14610
  return false;
14114
14611
  }
14115
14612
  if (error3.name !== "HttpError" || typeof error3.message !== "string") {
@@ -14117,7 +14614,7 @@ function isHttpErrorLike(error3) {
14117
14614
  }
14118
14615
  const request = error3.request;
14119
14616
  const response = error3.response;
14120
- return isPlainObject3(request) && typeof request.method === "string" && typeof request.url === "string" && isStringRecord(request.headers) && isPlainObject3(response) && typeof response.status === "number" && typeof response.statusText === "string" && isStringRecord(response.headers) && "body" in response;
14617
+ return isPlainObject4(request) && typeof request.method === "string" && typeof request.url === "string" && isStringRecord(request.headers) && isPlainObject4(response) && typeof response.status === "number" && typeof response.statusText === "string" && isStringRecord(response.headers) && "body" in response;
14121
14618
  }
14122
14619
  function hasTypedOptionalField(value, field, type2) {
14123
14620
  return !(field in value) || typeof value[field] === type2;
@@ -14126,7 +14623,7 @@ function isNonEmptyString(value) {
14126
14623
  return typeof value === "string" && value.trim().length > 0;
14127
14624
  }
14128
14625
  function isProblemDetailsLike(body) {
14129
- if (!isPlainObject3(body)) {
14626
+ if (!isPlainObject4(body)) {
14130
14627
  return false;
14131
14628
  }
14132
14629
  if (!hasTypedOptionalField(body, "type", "string")) {
@@ -14147,11 +14644,11 @@ function isProblemDetailsLike(body) {
14147
14644
  return isNonEmptyString(body.title) || isNonEmptyString(body.detail);
14148
14645
  }
14149
14646
  function isGraphQLErrorEnvelopeLike(body) {
14150
- if (!isPlainObject3(body) || !Array.isArray(body.errors) || body.errors.length === 0) {
14647
+ if (!isPlainObject4(body) || !Array.isArray(body.errors) || body.errors.length === 0) {
14151
14648
  return false;
14152
14649
  }
14153
14650
  return body.errors.every((error3) => {
14154
- if (!isPlainObject3(error3) || typeof error3.message !== "string") {
14651
+ if (!isPlainObject4(error3) || typeof error3.message !== "string") {
14155
14652
  return false;
14156
14653
  }
14157
14654
  if ("path" in error3) {
@@ -14161,7 +14658,7 @@ function isGraphQLErrorEnvelopeLike(body) {
14161
14658
  }
14162
14659
  }
14163
14660
  if ("extensions" in error3) {
14164
- if (!isPlainObject3(error3.extensions)) {
14661
+ if (!isPlainObject4(error3.extensions)) {
14165
14662
  return false;
14166
14663
  }
14167
14664
  if ("code" in error3.extensions && typeof error3.extensions.code !== "string") {
@@ -14209,23 +14706,24 @@ function formatGraphQLErrorEnvelopeBody(body) {
14209
14706
  }).join("\n\n");
14210
14707
  }
14211
14708
  function formatHttpErrorBody(body) {
14212
- if (typeof body === "string") {
14213
- return body;
14709
+ const redactedBody = redactHttpBody(body);
14710
+ if (typeof redactedBody === "string") {
14711
+ return redactedBody;
14214
14712
  }
14215
- if (isProblemDetailsLike(body)) {
14216
- return formatProblemDetailsBody(body);
14713
+ if (isProblemDetailsLike(redactedBody)) {
14714
+ return formatProblemDetailsBody(redactedBody);
14217
14715
  }
14218
- if (isGraphQLErrorEnvelopeLike(body)) {
14219
- return formatGraphQLErrorEnvelopeBody(body);
14716
+ if (isGraphQLErrorEnvelopeLike(redactedBody)) {
14717
+ return formatGraphQLErrorEnvelopeBody(redactedBody);
14220
14718
  }
14221
- const serialized = JSON.stringify(body, null, 2);
14222
- return serialized === void 0 ? String(body) : serialized;
14719
+ const serialized = JSON.stringify(redactedBody, null, 2);
14720
+ return serialized === void 0 ? String(redactedBody) : serialized;
14223
14721
  }
14224
14722
  function indentHttpErrorBlock(value) {
14225
14723
  return value.split("\n").map((line) => ` ${line}`).join("\n");
14226
14724
  }
14227
14725
  function formatHttpHeaderValue(name, value) {
14228
- return name.toLowerCase() === "authorization" ? "Bearer ****" : value;
14726
+ return redactHttpHeaderValue(name, value);
14229
14727
  }
14230
14728
  function formatHttpErrorHeaders(headers) {
14231
14729
  return Object.entries(headers).map(
@@ -14243,7 +14741,11 @@ function renderHttpError(error3, options) {
14243
14741
  if (detailed) {
14244
14742
  lines.push("", "Request headers:", ...formatHttpErrorHeaders(error3.request.headers), "");
14245
14743
  if (error3.request.body !== void 0) {
14246
- lines.push("Request body:", indentHttpErrorBlock(formatHttpErrorBody(error3.request.body)), "");
14744
+ lines.push(
14745
+ "Request body:",
14746
+ indentHttpErrorBlock(formatHttpErrorBody(error3.request.body)),
14747
+ ""
14748
+ );
14247
14749
  }
14248
14750
  }
14249
14751
  lines.push(
@@ -14536,7 +15038,8 @@ function configureCommanderSuggestionOutput(command) {
14536
15038
  }
14537
15039
  async function runCLI(roots, options = {}) {
14538
15040
  enableSourceMaps();
14539
- const root = mergeApprovalsGroup(normalizeRoots(roots, process.argv));
15041
+ const normalizedRoot = normalizeRoots(roots, process.argv);
15042
+ const root = options.approvals === false ? normalizedRoot : mergeApprovalsGroup(normalizedRoot);
14540
15043
  await resolveMcpProxies(root, { projectRoot: options.projectRoot });
14541
15044
  const casing = options.casing ?? "kebab";
14542
15045
  const services = options.services ?? {};
@@ -14648,7 +15151,7 @@ async function runCLI(roots, options = {}) {
14648
15151
  }
14649
15152
 
14650
15153
  // src/terminal-pilot.ts
14651
- import { randomUUID as randomUUID3 } from "node:crypto";
15154
+ import { randomUUID as randomUUID4 } from "node:crypto";
14652
15155
 
14653
15156
  // src/terminal-session.ts
14654
15157
  import { EventEmitter } from "node:events";
@@ -14667,7 +15170,7 @@ var DCS = 144;
14667
15170
  var SOS = 152;
14668
15171
  var PM = 158;
14669
15172
  var APC = 159;
14670
- function stripAnsi3(input) {
15173
+ function stripAnsi4(input) {
14671
15174
  let output = "";
14672
15175
  for (let index = 0; index < input.length; index += 1) {
14673
15176
  const code = input.charCodeAt(index);
@@ -15547,7 +16050,7 @@ var TerminalScreen = class {
15547
16050
  cursor,
15548
16051
  size
15549
16052
  }) {
15550
- this.lines = Object.freeze(lines.map((line) => stripAnsi3(line)));
16053
+ this.lines = Object.freeze(lines.map((line) => stripAnsi4(line)));
15551
16054
  this.rawLines = Object.freeze([...rawLines]);
15552
16055
  this.cursor = Object.freeze({ ...cursor });
15553
16056
  this.size = Object.freeze({ ...size });
@@ -15702,7 +16205,7 @@ var TerminalSession = class {
15702
16205
  });
15703
16206
  }
15704
16207
  async history(opts) {
15705
- const normalized = normalizeHistoryBuffer(stripAnsi3(this.rawBuffer));
16208
+ const normalized = normalizeHistoryBuffer(stripAnsi4(this.rawBuffer));
15706
16209
  const lines = splitHistoryLines(normalized);
15707
16210
  if (opts?.last === void 0) {
15708
16211
  return lines;
@@ -15835,7 +16338,7 @@ function isMissingFileError(error3) {
15835
16338
  return "code" in error3 && error3.code === "ENOENT";
15836
16339
  }
15837
16340
  function matchPattern(buffer, pattern) {
15838
- const clean = normalizeHistoryBuffer(stripAnsi3(buffer));
16341
+ const clean = normalizeHistoryBuffer(stripAnsi4(buffer));
15839
16342
  for (const line of clean.split("\n")) {
15840
16343
  if (typeof pattern === "string") {
15841
16344
  if (line.includes(pattern)) return line;
@@ -15923,7 +16426,7 @@ var TerminalPilot = class _TerminalPilot {
15923
16426
  }
15924
16427
  async newSession(opts) {
15925
16428
  const session = new TerminalSession({
15926
- id: randomUUID3(),
16429
+ id: randomUUID4(),
15927
16430
  command: opts.command,
15928
16431
  args: opts.args,
15929
16432
  cwd: opts.cwd,
@@ -16191,7 +16694,7 @@ var getSession = defineCommand({
16191
16694
 
16192
16695
  // ../agent-skill-config/src/configs.ts
16193
16696
  import os2 from "node:os";
16194
- import path14 from "node:path";
16697
+ import path15 from "node:path";
16195
16698
 
16196
16699
  // ../agent-defs/src/agents/claude-code.ts
16197
16700
  var claudeCodeAgent = {
@@ -16397,11 +16900,11 @@ function resolveAgentSupport(input, registry = agentSkillConfigs) {
16397
16900
  if (!resolvedId) {
16398
16901
  return { status: "unknown", input };
16399
16902
  }
16400
- const config = registry[resolvedId];
16401
- if (!config) {
16903
+ const config2 = registry[resolvedId];
16904
+ if (!config2) {
16402
16905
  return { status: "unsupported", input, id: resolvedId };
16403
16906
  }
16404
- return { status: "supported", input, id: resolvedId, config: { ...config } };
16907
+ return { status: "supported", input, id: resolvedId, config: { ...config2 } };
16405
16908
  }
16406
16909
  function getAgentConfig(agentId) {
16407
16910
  const support = resolveAgentSupport(agentId);
@@ -16500,7 +17003,7 @@ var templateMutation = {
16500
17003
  };
16501
17004
 
16502
17005
  // ../config-mutations/src/execution/apply-mutation.ts
16503
- import path16 from "node:path";
17006
+ import path17 from "node:path";
16504
17007
 
16505
17008
  // ../config-mutations/src/formats/json.ts
16506
17009
  import * as jsonc from "jsonc-parser";
@@ -16538,6 +17041,13 @@ function cloneConfigValue(value) {
16538
17041
  function isConfigObject(value) {
16539
17042
  return typeof value === "object" && value !== null && !Array.isArray(value);
16540
17043
  }
17044
+ function detectIndent(content) {
17045
+ const match = content.match(/^[\t ]+/m);
17046
+ if (match) {
17047
+ return match[0];
17048
+ }
17049
+ return " ";
17050
+ }
16541
17051
  function parse3(content) {
16542
17052
  if (!content || content.trim() === "") {
16543
17053
  return {};
@@ -16577,6 +17087,9 @@ function merge(base, patch) {
16577
17087
  }
16578
17088
  return result;
16579
17089
  }
17090
+ function configValuesEqual(left, right) {
17091
+ return JSON.stringify(left) === JSON.stringify(right);
17092
+ }
16580
17093
  function prune(obj, shape) {
16581
17094
  let changed = false;
16582
17095
  const result = cloneConfigObject(obj);
@@ -16613,9 +17126,56 @@ function prune(obj, shape) {
16613
17126
  }
16614
17127
  return { changed, result };
16615
17128
  }
17129
+ function modifyAtPath(content, path24, value) {
17130
+ const indent = detectIndent(content);
17131
+ const formattingOptions = {
17132
+ tabSize: indent === " " ? 1 : indent.length,
17133
+ insertSpaces: indent !== " ",
17134
+ eol: "\n"
17135
+ };
17136
+ const edits = jsonc.modify(content, path24, value, { formattingOptions });
17137
+ let result = jsonc.applyEdits(content, edits);
17138
+ if (!result.endsWith("\n")) {
17139
+ result += "\n";
17140
+ }
17141
+ return result;
17142
+ }
17143
+ function removeAtPath(content, path24) {
17144
+ return modifyAtPath(content, path24, void 0);
17145
+ }
17146
+ function serializeUpdate(content, current, next) {
17147
+ let result = content || "{}";
17148
+ result = applyObjectUpdate(result, [], current, next);
17149
+ if (!result.endsWith("\n")) {
17150
+ result += "\n";
17151
+ }
17152
+ return result;
17153
+ }
17154
+ function applyObjectUpdate(content, path24, current, next) {
17155
+ let result = content;
17156
+ for (const key2 of Object.keys(current)) {
17157
+ if (!hasConfigEntry(next, key2)) {
17158
+ result = removeAtPath(result, [...path24, key2]);
17159
+ }
17160
+ }
17161
+ for (const [key2, nextValue] of Object.entries(next)) {
17162
+ const nextPath = [...path24, key2];
17163
+ const hasCurrent = hasConfigEntry(current, key2);
17164
+ const currentValue = hasCurrent ? current[key2] : void 0;
17165
+ if (hasCurrent && isConfigObject(currentValue) && isConfigObject(nextValue)) {
17166
+ result = applyObjectUpdate(result, nextPath, currentValue, nextValue);
17167
+ continue;
17168
+ }
17169
+ if (!hasCurrent || !configValuesEqual(currentValue, nextValue)) {
17170
+ result = modifyAtPath(result, nextPath, nextValue);
17171
+ }
17172
+ }
17173
+ return result;
17174
+ }
16616
17175
  var jsonFormat = {
16617
17176
  parse: parse3,
16618
17177
  serialize,
17178
+ serializeUpdate,
16619
17179
  merge,
16620
17180
  prune
16621
17181
  };
@@ -16799,20 +17359,20 @@ function getConfigFormat(pathOrFormat) {
16799
17359
  }
16800
17360
  return formatRegistry[formatName];
16801
17361
  }
16802
- function detectFormat(path23) {
16803
- const ext = getExtension(path23);
17362
+ function detectFormat(path24) {
17363
+ const ext = getExtension(path24);
16804
17364
  return extensionMap[ext];
16805
17365
  }
16806
- function getExtension(path23) {
16807
- const lastDot = path23.lastIndexOf(".");
17366
+ function getExtension(path24) {
17367
+ const lastDot = path24.lastIndexOf(".");
16808
17368
  if (lastDot === -1) {
16809
17369
  return "";
16810
17370
  }
16811
- return path23.slice(lastDot).toLowerCase();
17371
+ return path24.slice(lastDot).toLowerCase();
16812
17372
  }
16813
17373
 
16814
17374
  // ../config-mutations/src/execution/path-utils.ts
16815
- import path15 from "node:path";
17375
+ import path16 from "node:path";
16816
17376
  function expandHome(targetPath, homeDir) {
16817
17377
  if (!targetPath?.startsWith("~")) {
16818
17378
  return targetPath;
@@ -16829,7 +17389,7 @@ function expandHome(targetPath, homeDir) {
16829
17389
  remainder = remainder.slice(1);
16830
17390
  }
16831
17391
  }
16832
- return remainder.length === 0 ? homeDir : path15.join(homeDir, remainder);
17392
+ return remainder.length === 0 ? homeDir : path16.join(homeDir, remainder);
16833
17393
  }
16834
17394
  function validateHomePath(targetPath) {
16835
17395
  if (typeof targetPath !== "string" || targetPath.length === 0) {
@@ -16844,21 +17404,21 @@ function validateHomePath(targetPath) {
16844
17404
  function resolvePath(rawPath, homeDir, pathMapper) {
16845
17405
  validateHomePath(rawPath);
16846
17406
  const expanded = expandHome(rawPath, homeDir);
16847
- const canonicalHome = path15.resolve(homeDir);
16848
- const canonicalExpanded = path15.resolve(expanded);
16849
- const relative = path15.relative(canonicalHome, canonicalExpanded);
16850
- if (relative === ".." || relative.startsWith(`..${path15.sep}`) || path15.isAbsolute(relative)) {
17407
+ const canonicalHome = path16.resolve(homeDir);
17408
+ const canonicalExpanded = path16.resolve(expanded);
17409
+ const relative = path16.relative(canonicalHome, canonicalExpanded);
17410
+ if (relative === ".." || relative.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative)) {
16851
17411
  throw new Error(`Target path resolves outside home directory: "${rawPath}"`);
16852
17412
  }
16853
17413
  if (!pathMapper) {
16854
17414
  return canonicalExpanded;
16855
17415
  }
16856
- const rawDirectory = path15.dirname(expanded);
17416
+ const rawDirectory = path16.dirname(expanded);
16857
17417
  const mappedDirectory = pathMapper.mapTargetDirectory({
16858
17418
  targetDirectory: rawDirectory
16859
17419
  });
16860
- const filename = path15.basename(expanded);
16861
- return filename.length === 0 ? mappedDirectory : path15.join(mappedDirectory, filename);
17420
+ const filename = path16.basename(expanded);
17421
+ return filename.length === 0 ? mappedDirectory : path16.join(mappedDirectory, filename);
16862
17422
  }
16863
17423
 
16864
17424
  // ../config-mutations/src/fs-utils.ts
@@ -16922,8 +17482,8 @@ function isAlreadyExists(error3) {
16922
17482
  return Boolean(error3 && typeof error3 === "object" && "code" in error3 && error3.code === "EEXIST");
16923
17483
  }
16924
17484
  async function assertRegularWriteTarget(context, targetPath) {
16925
- const boundary = path16.dirname(path16.resolve(context.homeDir));
16926
- let currentPath = path16.resolve(targetPath);
17485
+ const boundary = path17.dirname(path17.resolve(context.homeDir));
17486
+ let currentPath = path17.resolve(targetPath);
16927
17487
  while (currentPath !== boundary) {
16928
17488
  try {
16929
17489
  if ((await context.fs.lstat(currentPath)).isSymbolicLink()) {
@@ -16934,7 +17494,7 @@ async function assertRegularWriteTarget(context, targetPath) {
16934
17494
  throw error3;
16935
17495
  }
16936
17496
  }
16937
- const parentPath = path16.dirname(currentPath);
17497
+ const parentPath = path17.dirname(currentPath);
16938
17498
  if (parentPath === currentPath) {
16939
17499
  return;
16940
17500
  }
@@ -16997,7 +17557,7 @@ function pruneKeysByPrefix(table, prefix) {
16997
17557
  const result = {};
16998
17558
  for (const [key2, value] of Object.entries(table)) {
16999
17559
  if (!key2.startsWith(prefix)) {
17000
- result[key2] = value;
17560
+ setConfigEntry(result, key2, value);
17001
17561
  }
17002
17562
  }
17003
17563
  return result;
@@ -17006,28 +17566,43 @@ function isConfigObject4(value) {
17006
17566
  return typeof value === "object" && value !== null && !Array.isArray(value);
17007
17567
  }
17008
17568
  function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
17009
- const result = { ...base };
17569
+ const result = cloneConfigObject(base);
17010
17570
  const prefixMap = pruneByPrefix ?? {};
17011
17571
  for (const [key2, value] of Object.entries(patch)) {
17572
+ if (value === void 0) {
17573
+ continue;
17574
+ }
17012
17575
  const current = result[key2];
17013
17576
  const prefix = prefixMap[key2];
17014
17577
  if (isConfigObject4(current) && isConfigObject4(value)) {
17015
17578
  if (prefix) {
17016
17579
  const pruned = pruneKeysByPrefix(current, prefix);
17017
- result[key2] = { ...pruned, ...value };
17580
+ setConfigEntry(result, key2, mergePrunedConfigObject(pruned, value));
17018
17581
  } else {
17019
- result[key2] = mergeWithPruneByPrefix(
17020
- current,
17021
- value,
17022
- prefixMap
17023
- );
17582
+ setConfigEntry(result, key2, mergeWithPruneByPrefix(current, value, prefixMap));
17024
17583
  }
17025
17584
  continue;
17026
17585
  }
17027
- result[key2] = value;
17586
+ setConfigEntry(result, key2, value);
17028
17587
  }
17029
17588
  return result;
17030
17589
  }
17590
+ function mergePrunedConfigObject(base, patch) {
17591
+ const result = cloneConfigObject(base);
17592
+ for (const [key2, value] of Object.entries(patch)) {
17593
+ if (value === void 0) {
17594
+ continue;
17595
+ }
17596
+ setConfigEntry(result, key2, value);
17597
+ }
17598
+ return result;
17599
+ }
17600
+ function serializeConfigUpdate(format, rawContent, current, next) {
17601
+ if (rawContent !== null && format.serializeUpdate) {
17602
+ return format.serializeUpdate(rawContent, current, next);
17603
+ }
17604
+ return format.serialize(next);
17605
+ }
17031
17606
  async function applyMutation(mutation, context, options) {
17032
17607
  switch (mutation.kind) {
17033
17608
  case "ensureDirectory":
@@ -17340,6 +17915,7 @@ async function applyConfigMerge(mutation, context, options) {
17340
17915
  }
17341
17916
  const format = getConfigFormat(formatName);
17342
17917
  const rawContent = await readFileIfExists(context.fs, targetPath);
17918
+ let preserveContent = rawContent;
17343
17919
  let current;
17344
17920
  try {
17345
17921
  current = rawContent === null ? {} : format.parse(rawContent);
@@ -17348,6 +17924,7 @@ async function applyConfigMerge(mutation, context, options) {
17348
17924
  await backupInvalidDocument(context, targetPath, rawContent);
17349
17925
  }
17350
17926
  current = {};
17927
+ preserveContent = null;
17351
17928
  }
17352
17929
  const value = resolveValue(mutation.value, options);
17353
17930
  let merged;
@@ -17356,7 +17933,7 @@ async function applyConfigMerge(mutation, context, options) {
17356
17933
  } else {
17357
17934
  merged = format.merge(current, value);
17358
17935
  }
17359
- const serialized = format.serialize(merged);
17936
+ const serialized = serializeConfigUpdate(format, preserveContent, current, merged);
17360
17937
  const changed = serialized !== rawContent;
17361
17938
  if (changed && !context.dryRun) {
17362
17939
  await writeAtomically2(context, targetPath, serialized);
@@ -17424,7 +18001,7 @@ async function applyConfigPrune(mutation, context, options) {
17424
18001
  details
17425
18002
  };
17426
18003
  }
17427
- const serialized = format.serialize(result);
18004
+ const serialized = serializeConfigUpdate(format, rawContent, current, result);
17428
18005
  if (!context.dryRun) {
17429
18006
  await writeAtomically2(context, targetPath, serialized);
17430
18007
  }
@@ -17449,6 +18026,7 @@ async function applyConfigTransform(mutation, context, options) {
17449
18026
  }
17450
18027
  const format = getConfigFormat(formatName);
17451
18028
  const rawContent = await readFileIfExists(context.fs, targetPath);
18029
+ let preserveContent = rawContent;
17452
18030
  let current;
17453
18031
  try {
17454
18032
  current = rawContent === null ? {} : format.parse(rawContent);
@@ -17457,6 +18035,7 @@ async function applyConfigTransform(mutation, context, options) {
17457
18035
  await backupInvalidDocument(context, targetPath, rawContent);
17458
18036
  }
17459
18037
  current = {};
18038
+ preserveContent = null;
17460
18039
  }
17461
18040
  const { content: transformed, changed } = mutation.transform(current, options);
17462
18041
  if (!changed) {
@@ -17480,7 +18059,7 @@ async function applyConfigTransform(mutation, context, options) {
17480
18059
  details
17481
18060
  };
17482
18061
  }
17483
- const serialized = format.serialize(transformed);
18062
+ const serialized = serializeConfigUpdate(format, preserveContent, current, transformed);
17484
18063
  if (!context.dryRun) {
17485
18064
  await writeAtomically2(context, targetPath, serialized);
17486
18065
  }
@@ -17622,7 +18201,7 @@ async function executeMutation(mutation, context, options) {
17622
18201
 
17623
18202
  // ../agent-skill-config/src/templates.ts
17624
18203
  import { readFile as readFile5, stat } from "node:fs/promises";
17625
- import path17 from "node:path";
18204
+ import path18 from "node:path";
17626
18205
  import { fileURLToPath as fileURLToPath3 } from "node:url";
17627
18206
 
17628
18207
  // ../agent-skill-config/src/apply.ts
@@ -17657,14 +18236,14 @@ async function installSkill(agentId, skill, options) {
17657
18236
  throw new UnsupportedAgentError(agentId);
17658
18237
  }
17659
18238
  const scope = options.scope ?? "local";
17660
- const config = support.config;
18239
+ const config2 = support.config;
17661
18240
  if (skill.name.length === 0 || skill.name === "." || skill.name === ".." || skill.name.includes("/") || skill.name.includes("\\") || skill.name.includes("\n") || skill.name.includes("\r")) {
17662
18241
  throw new Error(`Invalid skill name: ${skill.name}`);
17663
18242
  }
17664
- const skillDir = scope === "global" ? config.globalSkillDir : toHomeRelative(config.localSkillDir);
18243
+ const skillDir = scope === "global" ? config2.globalSkillDir : toHomeRelative(config2.localSkillDir);
17665
18244
  const skillFolderPath = `${skillDir}/${skill.name}`;
17666
18245
  const skillFilePath = `${skillFolderPath}/SKILL.md`;
17667
- const displayPath = `${scope === "global" ? config.globalSkillDir : config.localSkillDir}/${skill.name}/SKILL.md`;
18246
+ const displayPath = `${scope === "global" ? config2.globalSkillDir : config2.localSkillDir}/${skill.name}/SKILL.md`;
17668
18247
  const absoluteSkillPath = `${scope === "global" ? options.homeDir : options.cwd}/${skillFilePath.slice(2)}`;
17669
18248
  if (await pathExists2(options.fs, absoluteSkillPath)) {
17670
18249
  throw new Error(`Skill already exists: ${displayPath}`);
@@ -17699,21 +18278,21 @@ async function installSkill(agentId, skill, options) {
17699
18278
 
17700
18279
  // ../agent-skill-config/src/resolve-skill-reference.ts
17701
18280
  import { statSync as statSync2 } from "node:fs";
17702
- import path18 from "node:path";
18281
+ import path19 from "node:path";
17703
18282
 
17704
18283
  // ../agent-skill-config/src/git-exclude.ts
17705
18284
  import { execFileSync } from "node:child_process";
17706
18285
  import * as fs2 from "node:fs";
17707
- import path19 from "node:path";
18286
+ import path20 from "node:path";
17708
18287
 
17709
18288
  // ../agent-skill-config/src/bridge-active-skills.ts
17710
18289
  import * as fs3 from "node:fs";
17711
- import { createHash as createHash4, randomUUID as randomUUID4 } from "node:crypto";
17712
- import path20 from "node:path";
18290
+ import { createHash as createHash4, randomUUID as randomUUID5 } from "node:crypto";
18291
+ import path21 from "node:path";
17713
18292
 
17714
18293
  // src/commands/installer.ts
17715
18294
  import os3 from "node:os";
17716
- import path21 from "node:path";
18295
+ import path22 from "node:path";
17717
18296
  import * as nodeFs2 from "node:fs/promises";
17718
18297
  import { readFile as readFile6 } from "node:fs/promises";
17719
18298
  var DEFAULT_INSTALL_AGENT = "claude-code";
@@ -17780,28 +18359,28 @@ function resolveHomeRelativePath(targetPath, homeDir) {
17780
18359
  return homeDir;
17781
18360
  }
17782
18361
  if (targetPath.startsWith("~/")) {
17783
- return path21.join(homeDir, targetPath.slice(2));
18362
+ return path22.join(homeDir, targetPath.slice(2));
17784
18363
  }
17785
18364
  return targetPath;
17786
18365
  }
17787
18366
  function getSkillFolderWithHome(agent, scope, cwd, homeDir) {
17788
- const config = getAgentConfig(agent);
17789
- if (!config) {
18367
+ const config2 = getAgentConfig(agent);
18368
+ if (!config2) {
17790
18369
  throwUnsupportedAgent(agent);
17791
18370
  }
17792
18371
  return {
17793
- displayPath: path21.join(
17794
- scope === "global" ? config.globalSkillDir : config.localSkillDir,
18372
+ displayPath: path22.join(
18373
+ scope === "global" ? config2.globalSkillDir : config2.localSkillDir,
17795
18374
  TERMINAL_PILOT_SKILL_NAME
17796
18375
  ),
17797
- fullPath: path21.join(
17798
- scope === "global" ? resolveHomeRelativePath(config.globalSkillDir, homeDir) : path21.resolve(cwd, config.localSkillDir),
18376
+ fullPath: path22.join(
18377
+ scope === "global" ? resolveHomeRelativePath(config2.globalSkillDir, homeDir) : path22.resolve(cwd, config2.localSkillDir),
17799
18378
  TERMINAL_PILOT_SKILL_NAME
17800
18379
  )
17801
18380
  };
17802
18381
  }
17803
18382
  async function assertNoSymbolicLinkPath(fs4, targetPath) {
17804
- const rootPath = path21.parse(targetPath).root;
18383
+ const rootPath = path22.parse(targetPath).root;
17805
18384
  let currentPath = targetPath;
17806
18385
  while (currentPath !== rootPath) {
17807
18386
  try {
@@ -17813,7 +18392,7 @@ async function assertNoSymbolicLinkPath(fs4, targetPath) {
17813
18392
  throw error3;
17814
18393
  }
17815
18394
  }
17816
- currentPath = path21.dirname(currentPath);
18395
+ currentPath = path22.dirname(currentPath);
17817
18396
  }
17818
18397
  }
17819
18398
 
@@ -17952,7 +18531,7 @@ var resize = defineCommand({
17952
18531
  });
17953
18532
 
17954
18533
  // ../terminal-png/src/index.ts
17955
- import { randomUUID as randomUUID5 } from "node:crypto";
18534
+ import { randomUUID as randomUUID6 } from "node:crypto";
17956
18535
  import { rename as rename4, rm, writeFile as writeFile6 } from "node:fs/promises";
17957
18536
 
17958
18537
  // ../terminal-png/src/ansi-parser.ts
@@ -18892,7 +19471,7 @@ async function renderTerminalPng(ansiText, options = {}) {
18892
19471
  });
18893
19472
  const png = renderPng(svg);
18894
19473
  if (options.output) {
18895
- const temporaryPath = `${options.output}.${randomUUID5()}.tmp`;
19474
+ const temporaryPath = `${options.output}.${randomUUID6()}.tmp`;
18896
19475
  try {
18897
19476
  await writeFile6(temporaryPath, png, { flag: "wx" });
18898
19477
  await rename4(temporaryPath, options.output);
@@ -18969,7 +19548,7 @@ var type = defineCommand({
18969
19548
  });
18970
19549
 
18971
19550
  // src/commands/uninstall.ts
18972
- import { randomUUID as randomUUID6 } from "node:crypto";
19551
+ import { randomUUID as randomUUID7 } from "node:crypto";
18973
19552
  var params14 = S.Object({
18974
19553
  agent: S.Enum(installableAgents, {
18975
19554
  description: "Agent to uninstall terminal-pilot from",
@@ -19005,7 +19584,7 @@ var uninstall = defineCommand({
19005
19584
  if (!await folderExists(services.fs, skill.fullPath)) {
19006
19585
  continue;
19007
19586
  }
19008
- const stagingPath = `${skill.fullPath}.removing-${randomUUID6()}`;
19587
+ const stagingPath = `${skill.fullPath}.removing-${randomUUID7()}`;
19009
19588
  await services.fs.rename(skill.fullPath, stagingPath);
19010
19589
  staged.push({ ...skill, stagingPath });
19011
19590
  }
@@ -19112,6 +19691,7 @@ function createTerminalPilotGroup() {
19112
19691
  var terminalPilotGroup = Object.freeze(createTerminalPilotGroup());
19113
19692
 
19114
19693
  // src/cli.ts
19694
+ configureTheme({ brand: "green", label: "Terminal Pilot" });
19115
19695
  function normalizeArgv(argv) {
19116
19696
  if (argv.includes("--json") && !argv.some((argument) => argument === "--output" || argument.startsWith("--output="))) {
19117
19697
  return argv.flatMap((argument) => argument === "--json" ? ["--output", "json"] : [argument]);
@@ -19135,8 +19715,8 @@ async function isDirectExecution(argv) {
19135
19715
  try {
19136
19716
  const modulePath = fileURLToPath5(import.meta.url);
19137
19717
  const [resolvedEntryPoint, resolvedModulePath] = await Promise.all([
19138
- realpath2(path22.resolve(entryPoint)),
19139
- realpath2(modulePath)
19718
+ realpath3(path23.resolve(entryPoint)),
19719
+ realpath3(modulePath)
19140
19720
  ]);
19141
19721
  return resolvedEntryPoint === resolvedModulePath;
19142
19722
  } catch {