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
@@ -16,19 +16,19 @@ var ToolcraftBugError = class extends Error {
16
16
  };
17
17
 
18
18
  // ../toolcraft/src/human-in-loop/config.ts
19
- function validateHumanInLoopOnDefine(config) {
20
- const label = Array.isArray(config.children) ? "group" : "command";
21
- if (config.confirm === true && config.humanInLoop !== void 0 && config.humanInLoop !== null) {
22
- throw new Error(`${label} '${config.name}': use either confirm or humanInLoop, not both`);
19
+ function validateHumanInLoopOnDefine(config2) {
20
+ const label = Array.isArray(config2.children) ? "group" : "command";
21
+ if (config2.confirm === true && config2.humanInLoop !== void 0 && config2.humanInLoop !== null) {
22
+ throw new Error(`${label} '${config2.name}': use either confirm or humanInLoop, not both`);
23
23
  }
24
- if (config.humanInLoop === void 0 || config.humanInLoop === null) {
24
+ if (config2.humanInLoop === void 0 || config2.humanInLoop === null) {
25
25
  return;
26
26
  }
27
- if (config.humanInLoop.mode !== "sync" && config.humanInLoop.mode !== "async") {
28
- throw new Error(`${label} '${config.name}': humanInLoop.mode must be "sync" or "async"`);
27
+ if (config2.humanInLoop.mode !== "sync" && config2.humanInLoop.mode !== "async") {
28
+ throw new Error(`${label} '${config2.name}': humanInLoop.mode must be "sync" or "async"`);
29
29
  }
30
- if (typeof config.humanInLoop.message !== "function") {
31
- throw new Error(`${label} '${config.name}': humanInLoop.message must be a function`);
30
+ if (typeof config2.humanInLoop.message !== "function") {
31
+ throw new Error(`${label} '${config2.name}': humanInLoop.message must be a function`);
32
32
  }
33
33
  }
34
34
  function mergeHumanInLoopFromGroup(groupHumanInLoop, childHumanInLoop) {
@@ -58,12 +58,12 @@ function assertValidBranches(branches, discriminator) {
58
58
  }
59
59
  }
60
60
  }
61
- function OneOf(config) {
62
- assertValidBranches(config.branches, config.discriminator);
61
+ function OneOf(config2) {
62
+ assertValidBranches(config2.branches, config2.discriminator);
63
63
  return {
64
64
  kind: "oneOf",
65
- discriminator: config.discriminator,
66
- branches: config.branches
65
+ discriminator: config2.discriminator,
66
+ branches: config2.branches
67
67
  };
68
68
  }
69
69
 
@@ -262,22 +262,22 @@ function cloneStringArray(values) {
262
262
  function cloneStringRecord(values) {
263
263
  return values === void 0 ? void 0 : { ...values };
264
264
  }
265
- function cloneMcpServerConfig(config) {
266
- if (config === void 0) {
265
+ function cloneMcpServerConfig(config2) {
266
+ if (config2 === void 0) {
267
267
  return void 0;
268
268
  }
269
- if (config.transport === "stdio") {
269
+ if (config2.transport === "stdio") {
270
270
  return {
271
271
  transport: "stdio",
272
- command: config.command,
273
- args: cloneStringArray(config.args),
274
- env: cloneStringRecord(config.env)
272
+ command: config2.command,
273
+ args: cloneStringArray(config2.args),
274
+ env: cloneStringRecord(config2.env)
275
275
  };
276
276
  }
277
277
  return {
278
278
  transport: "http",
279
- url: config.url,
280
- headers: cloneStringRecord(config.headers)
279
+ url: config2.url,
280
+ headers: cloneStringRecord(config2.headers)
281
281
  };
282
282
  }
283
283
  function cloneRenameMap(rename2) {
@@ -399,57 +399,57 @@ function resolveCommandScope(ownScope, inheritedScope) {
399
399
  function resolveGroupScope(ownScope, inheritedScope) {
400
400
  return cloneScope(ownScope ?? inheritedScope);
401
401
  }
402
- function createBaseCommand(config) {
402
+ function createBaseCommand(config2) {
403
403
  const command = {
404
404
  kind: "command",
405
- name: config.name,
406
- description: config.description,
407
- aliases: [...config.aliases ?? []],
408
- positional: [...config.positional ?? []],
409
- params: config.params,
410
- secrets: cloneSecrets(config.secrets),
411
- scope: resolveCommandScope(config.scope, void 0),
412
- confirm: config.confirm ?? false,
413
- humanInLoop: config.humanInLoop,
414
- requires: cloneRequires(config.requires),
415
- handler: config.handler,
416
- render: config.render
405
+ name: config2.name,
406
+ description: config2.description,
407
+ aliases: [...config2.aliases ?? []],
408
+ positional: [...config2.positional ?? []],
409
+ params: config2.params,
410
+ secrets: cloneSecrets(config2.secrets),
411
+ scope: resolveCommandScope(config2.scope, void 0),
412
+ confirm: config2.confirm ?? false,
413
+ humanInLoop: config2.humanInLoop,
414
+ requires: cloneRequires(config2.requires),
415
+ handler: config2.handler,
416
+ render: config2.render
417
417
  };
418
418
  Object.defineProperty(command, commandConfigSymbol, {
419
419
  value: {
420
- scope: cloneScope(config.scope),
421
- humanInLoop: config.humanInLoop,
422
- secrets: cloneSecrets(config.secrets),
423
- requires: cloneRequires(config.requires),
420
+ scope: cloneScope(config2.scope),
421
+ humanInLoop: config2.humanInLoop,
422
+ secrets: cloneSecrets(config2.secrets),
423
+ requires: cloneRequires(config2.requires),
424
424
  sourcePath: inferCommandSourcePath()
425
425
  }
426
426
  });
427
427
  return command;
428
428
  }
429
- function createBaseGroup(config) {
429
+ function createBaseGroup(config2) {
430
430
  const group = {
431
431
  kind: "group",
432
- name: config.name,
433
- description: config.description,
434
- aliases: [...config.aliases ?? []],
435
- scope: resolveGroupScope(config.scope, void 0),
436
- humanInLoop: config.humanInLoop,
437
- secrets: cloneSecrets(config.secrets),
438
- requires: cloneRequires(config.requires),
432
+ name: config2.name,
433
+ description: config2.description,
434
+ aliases: [...config2.aliases ?? []],
435
+ scope: resolveGroupScope(config2.scope, void 0),
436
+ humanInLoop: config2.humanInLoop,
437
+ secrets: cloneSecrets(config2.secrets),
438
+ requires: cloneRequires(config2.requires),
439
439
  children: [],
440
440
  default: void 0
441
441
  };
442
442
  Object.defineProperty(group, groupConfigSymbol, {
443
443
  value: {
444
- mcp: cloneMcpServerConfig(config.mcp),
445
- scope: cloneScope(config.scope),
446
- humanInLoop: config.humanInLoop,
447
- secrets: cloneSecrets(config.secrets),
448
- tools: cloneStringArray(config.tools),
449
- rename: cloneRenameMap(config.rename),
450
- requires: cloneRequires(config.requires),
451
- children: [...config.children],
452
- default: config.default
444
+ mcp: cloneMcpServerConfig(config2.mcp),
445
+ scope: cloneScope(config2.scope),
446
+ humanInLoop: config2.humanInLoop,
447
+ secrets: cloneSecrets(config2.secrets),
448
+ tools: cloneStringArray(config2.tools),
449
+ rename: cloneRenameMap(config2.rename),
450
+ requires: cloneRequires(config2.requires),
451
+ children: [...config2.children],
452
+ default: config2.default
453
453
  }
454
454
  });
455
455
  return group;
@@ -552,10 +552,10 @@ function materializeNode(node, inherited) {
552
552
  }
553
553
  return materializeGroup(node, inherited);
554
554
  }
555
- function defineCommand(config) {
556
- validateHumanInLoopOnDefine(config);
555
+ function defineCommand(config2) {
556
+ validateHumanInLoopOnDefine(config2);
557
557
  return materializeCommand(
558
- createBaseCommand(config),
558
+ createBaseCommand(config2),
559
559
  {
560
560
  scope: void 0,
561
561
  humanInLoop: void 0,
@@ -564,10 +564,10 @@ function defineCommand(config) {
564
564
  }
565
565
  );
566
566
  }
567
- function defineGroup(config) {
568
- validateRenameMap(config.rename);
569
- validateHumanInLoopOnDefine(config);
570
- return materializeGroup(createBaseGroup(config), {
567
+ function defineGroup(config2) {
568
+ validateRenameMap(config2.rename);
569
+ validateHumanInLoopOnDefine(config2);
570
+ return materializeGroup(createBaseGroup(config2), {
571
571
  scope: void 0,
572
572
  humanInLoop: void 0,
573
573
  secrets: {},
@@ -2325,11 +2325,11 @@ function resolveAgentSupport(input, registry = agentSkillConfigs) {
2325
2325
  if (!resolvedId) {
2326
2326
  return { status: "unknown", input };
2327
2327
  }
2328
- const config = registry[resolvedId];
2329
- if (!config) {
2328
+ const config2 = registry[resolvedId];
2329
+ if (!config2) {
2330
2330
  return { status: "unsupported", input, id: resolvedId };
2331
2331
  }
2332
- return { status: "supported", input, id: resolvedId, config: { ...config } };
2332
+ return { status: "supported", input, id: resolvedId, config: { ...config2 } };
2333
2333
  }
2334
2334
  function getAgentConfig(agentId) {
2335
2335
  const support = resolveAgentSupport(agentId);
@@ -2430,7 +2430,7 @@ var templateMutation = {
2430
2430
  // ../config-mutations/src/execution/apply-mutation.ts
2431
2431
  import path4 from "node:path";
2432
2432
 
2433
- // ../design-system/src/internal/color-support.ts
2433
+ // ../toolcraft-design/src/internal/color-support.ts
2434
2434
  function supportsColor(env = process.env, stream = process.stdout) {
2435
2435
  if (env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "0") {
2436
2436
  return true;
@@ -2444,7 +2444,7 @@ function supportsColor(env = process.env, stream = process.stdout) {
2444
2444
  return typeof env.TERM === "string" && env.TERM.length > 0 && env.TERM !== "dumb";
2445
2445
  }
2446
2446
 
2447
- // ../design-system/src/components/color.ts
2447
+ // ../toolcraft-design/src/components/color.ts
2448
2448
  var reset = "\x1B[0m";
2449
2449
  var ansiStyles = {
2450
2450
  reset: { open: reset },
@@ -2546,41 +2546,122 @@ function createColor(styles = []) {
2546
2546
  }
2547
2547
  var color = createColor();
2548
2548
 
2549
- // ../design-system/src/tokens/colors.ts
2550
- var dark = {
2551
- header: (text4) => color.magentaBright.bold(text4),
2552
- divider: (text4) => color.dim(text4),
2553
- prompt: (text4) => color.cyan(text4),
2554
- number: (text4) => color.cyanBright(text4),
2555
- intro: (text4) => color.bgMagenta.white(` Poe - ${text4} `),
2556
- resolvedSymbol: color.magenta("\u25C7"),
2557
- errorSymbol: color.red("\u25A0"),
2558
- accent: (text4) => color.cyan(text4),
2559
- muted: (text4) => color.dim(text4),
2560
- success: (text4) => color.green(text4),
2561
- warning: (text4) => color.yellow(text4),
2562
- error: (text4) => color.red(text4),
2563
- info: (text4) => color.magenta(text4),
2564
- badge: (text4) => color.bgYellow.black(` ${text4} `)
2549
+ // ../toolcraft-design/src/tokens/brand.ts
2550
+ var brands = {
2551
+ purple: { name: "purple", primary: "#a200ff" },
2552
+ blue: { name: "blue", primary: "#2f6fed" },
2553
+ green: { name: "green", primary: "#1f9d57" }
2565
2554
  };
2566
- var light = {
2567
- header: (text4) => color.hex("#a200ff").bold(text4),
2568
- divider: (text4) => color.hex("#666666")(text4),
2569
- prompt: (text4) => color.hex("#006699").bold(text4),
2570
- number: (text4) => color.hex("#0077cc").bold(text4),
2571
- intro: (text4) => color.bgHex("#a200ff").white(` Poe - ${text4} `),
2572
- resolvedSymbol: color.hex("#a200ff")("\u25C7"),
2573
- errorSymbol: color.hex("#cc0000")("\u25A0"),
2574
- accent: (text4) => color.hex("#006699").bold(text4),
2575
- muted: (text4) => color.hex("#666666")(text4),
2576
- success: (text4) => color.hex("#008800")(text4),
2577
- warning: (text4) => color.hex("#cc6600")(text4),
2578
- error: (text4) => color.hex("#cc0000")(text4),
2579
- info: (text4) => color.hex("#a200ff")(text4),
2580
- badge: (text4) => color.bgHex("#cc6600").white(` ${text4} `)
2555
+
2556
+ // ../toolcraft-design/src/internal/theme-state.ts
2557
+ var defaults = {
2558
+ brand: "purple",
2559
+ label: "Poe"
2581
2560
  };
2561
+ var config = { ...defaults };
2562
+ var revision = 0;
2563
+ var brandConfigured = false;
2564
+ function getThemeConfig() {
2565
+ return { ...config };
2566
+ }
2567
+ function getThemeRevision() {
2568
+ return revision;
2569
+ }
2570
+ function isThemeBrandConfigured() {
2571
+ return brandConfigured;
2572
+ }
2582
2573
 
2583
- // ../design-system/src/internal/output-format.ts
2574
+ // ../toolcraft-design/src/tokens/colors.ts
2575
+ var brand = brands.purple.primary;
2576
+ function withStyles(palette, styles) {
2577
+ return Object.defineProperty(palette, "styles", {
2578
+ value: styles,
2579
+ enumerable: false
2580
+ });
2581
+ }
2582
+ function brandColor(activeBrand, purple) {
2583
+ return activeBrand.name === "purple" ? purple : color.hex(activeBrand.primary);
2584
+ }
2585
+ function brandBackground(activeBrand, purple) {
2586
+ return activeBrand.name === "purple" ? purple : color.bgHex(activeBrand.primary);
2587
+ }
2588
+ function createPalette(activeBrand, mode) {
2589
+ const isPurple = activeBrand.name === "purple";
2590
+ if (mode === "light") {
2591
+ const active2 = color.hex(activeBrand.primary);
2592
+ const prompt2 = isPurple ? color.hex("#006699") : active2;
2593
+ const number2 = isPurple ? color.hex("#0077cc") : active2;
2594
+ return withStyles(
2595
+ {
2596
+ header: (text4) => active2.bold(text4),
2597
+ divider: (text4) => color.hex("#666666")(text4),
2598
+ prompt: (text4) => prompt2.bold(text4),
2599
+ number: (text4) => number2.bold(text4),
2600
+ intro: (text4) => color.bgHex(activeBrand.primary).white(` ${getThemeConfig().label} - ${text4} `),
2601
+ get resolvedSymbol() {
2602
+ return active2("\u25C7");
2603
+ },
2604
+ get errorSymbol() {
2605
+ return color.hex("#cc0000")("\u25A0");
2606
+ },
2607
+ accent: (text4) => prompt2.bold(text4),
2608
+ muted: (text4) => color.hex("#666666")(text4),
2609
+ success: (text4) => color.hex("#008800")(text4),
2610
+ warning: (text4) => color.hex("#cc6600")(text4),
2611
+ error: (text4) => color.hex("#cc0000")(text4),
2612
+ info: (text4) => active2(text4),
2613
+ badge: (text4) => color.bgHex("#cc6600").white(` ${text4} `)
2614
+ },
2615
+ {
2616
+ accent: { fg: isPurple ? "#006699" : activeBrand.primary, bold: true },
2617
+ muted: { fg: "#666666" },
2618
+ success: { fg: "#008800" },
2619
+ warning: { fg: "#cc6600" },
2620
+ error: { fg: "#cc0000" },
2621
+ info: { fg: activeBrand.primary }
2622
+ }
2623
+ );
2624
+ }
2625
+ const active = brandColor(activeBrand, color.magenta);
2626
+ const activeBright = brandColor(activeBrand, color.magentaBright);
2627
+ const activeBackground = brandBackground(activeBrand, color.bgMagenta);
2628
+ const prompt = isPurple ? color.cyan : active;
2629
+ const number = isPurple ? color.cyanBright : active;
2630
+ return withStyles(
2631
+ {
2632
+ header: (text4) => activeBright.bold(text4),
2633
+ divider: (text4) => color.dim(text4),
2634
+ prompt: (text4) => prompt(text4),
2635
+ number: (text4) => number(text4),
2636
+ intro: (text4) => activeBackground.white(` ${getThemeConfig().label} - ${text4} `),
2637
+ get resolvedSymbol() {
2638
+ return active("\u25C7");
2639
+ },
2640
+ get errorSymbol() {
2641
+ return color.red("\u25A0");
2642
+ },
2643
+ accent: (text4) => prompt(text4),
2644
+ muted: (text4) => color.dim(text4),
2645
+ success: (text4) => color.green(text4),
2646
+ warning: (text4) => color.yellow(text4),
2647
+ error: (text4) => color.red(text4),
2648
+ info: (text4) => active(text4),
2649
+ badge: (text4) => color.bgYellow.black(` ${text4} `)
2650
+ },
2651
+ {
2652
+ accent: { fg: isPurple ? "cyan" : activeBrand.primary, bold: true },
2653
+ muted: { dim: true },
2654
+ success: { fg: "green" },
2655
+ warning: { fg: "yellow" },
2656
+ error: { fg: "red" },
2657
+ info: { fg: isPurple ? "magenta" : activeBrand.primary }
2658
+ }
2659
+ );
2660
+ }
2661
+ var dark = createPalette(brands.purple, "dark");
2662
+ var light = createPalette(brands.purple, "light");
2663
+
2664
+ // ../toolcraft-design/src/internal/output-format.ts
2584
2665
  import { AsyncLocalStorage } from "node:async_hooks";
2585
2666
  var VALID_FORMATS = /* @__PURE__ */ new Set(["terminal", "markdown", "json"]);
2586
2667
  var formatStorage = new AsyncLocalStorage();
@@ -2598,7 +2679,7 @@ function resolveOutputFormat(env = process.env) {
2598
2679
  return cached;
2599
2680
  }
2600
2681
 
2601
- // ../design-system/src/internal/theme-detect.ts
2682
+ // ../toolcraft-design/src/internal/theme-detect.ts
2602
2683
  function detectThemeFromEnv(env) {
2603
2684
  const apple = env.APPLE_INTERFACE_STYLE;
2604
2685
  if (typeof apple === "string") {
@@ -2635,17 +2716,30 @@ function resolveThemeName(env = process.env) {
2635
2716
  }
2636
2717
  return "dark";
2637
2718
  }
2638
- var cachedTheme;
2719
+ var themeCache = /* @__PURE__ */ new Map();
2720
+ var cachedRevision = -1;
2639
2721
  function getTheme(env) {
2722
+ const themeName = resolveThemeName(env);
2723
+ const config2 = getThemeConfig();
2724
+ const requestedBrand = env?.POE_BRAND?.toLowerCase();
2725
+ const activeBrandName = !isThemeBrandConfigured() && requestedBrand && Object.hasOwn(brands, requestedBrand) ? requestedBrand : config2.brand;
2726
+ const revision2 = getThemeRevision();
2727
+ if (revision2 !== cachedRevision) {
2728
+ themeCache.clear();
2729
+ cachedRevision = revision2;
2730
+ }
2731
+ const cacheKey = `${activeBrandName}:${themeName}`;
2732
+ const cachedTheme = themeCache.get(cacheKey);
2640
2733
  if (cachedTheme) {
2641
2734
  return cachedTheme;
2642
2735
  }
2643
- const themeName = resolveThemeName(env);
2644
- cachedTheme = themeName === "light" ? light : dark;
2645
- return cachedTheme;
2736
+ const activeBrand = brands[activeBrandName];
2737
+ const theme = activeBrandName === "purple" ? themeName === "light" ? light : dark : createPalette(activeBrand, themeName);
2738
+ themeCache.set(cacheKey, theme);
2739
+ return theme;
2646
2740
  }
2647
2741
 
2648
- // ../design-system/src/components/symbols.ts
2742
+ // ../toolcraft-design/src/components/symbols.ts
2649
2743
  var symbols = {
2650
2744
  get info() {
2651
2745
  const format = resolveOutputFormat();
@@ -2699,12 +2793,12 @@ var symbols = {
2699
2793
  }
2700
2794
  };
2701
2795
 
2702
- // ../design-system/src/internal/strip-ansi.ts
2796
+ // ../toolcraft-design/src/internal/strip-ansi.ts
2703
2797
  function stripAnsi2(value) {
2704
2798
  return value.replace(/\u001b\[[0-9;]*m/g, "");
2705
2799
  }
2706
2800
 
2707
- // ../design-system/src/prompts/primitives/log.ts
2801
+ // ../toolcraft-design/src/prompts/primitives/log.ts
2708
2802
  function renderMarkdownInline(value) {
2709
2803
  return stripAnsi2(value).replaceAll("\r\n", " ").replaceAll("\n", " ").replaceAll("\r", " ");
2710
2804
  }
@@ -2831,7 +2925,7 @@ var log = {
2831
2925
  error
2832
2926
  };
2833
2927
 
2834
- // ../design-system/src/components/logger.ts
2928
+ // ../toolcraft-design/src/components/logger.ts
2835
2929
  function createLogger(emitter) {
2836
2930
  const emit = (level, message2) => {
2837
2931
  if (emitter) {
@@ -2892,13 +2986,14 @@ function createLogger(emitter) {
2892
2986
  }
2893
2987
  var logger = createLogger();
2894
2988
 
2895
- // ../design-system/src/components/help-formatter.ts
2989
+ // ../toolcraft-design/src/components/help-formatter.ts
2896
2990
  var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
2897
2991
 
2898
- // ../design-system/src/components/table.ts
2992
+ // ../toolcraft-design/src/components/table.ts
2899
2993
  var graphemeSegmenter2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
2900
2994
 
2901
- // ../design-system/src/components/template.ts
2995
+ // ../toolcraft-design/src/components/template.ts
2996
+ var MAX_PARTIAL_DEPTH = 100;
2902
2997
  var HTML_ESCAPE = {
2903
2998
  "&": "&",
2904
2999
  "<": "&lt;",
@@ -2910,14 +3005,27 @@ var HTML_ESCAPE = {
2910
3005
  "=": "&#x3D;"
2911
3006
  };
2912
3007
  function renderTemplate(template, view, options = {}) {
2913
- const prepared = options.yield === void 0 ? template : template.split("{{yield}}").join(options.yield);
3008
+ const prepared = options.yield === void 0 ? template : resolveTemplatePartials(template, options.partials ?? {}).split("{{yield}}").join(options.yield);
2914
3009
  const tokens = parseTemplate(prepared);
2915
- const escape = options.escape === "none" ? String : escapeHtml;
2916
- const preserveMissing = options.yield !== void 0 && options.escape === "none";
2917
- return renderTokens(tokens, { view }, prepared, escape, preserveMissing);
3010
+ validatePartialReferences(tokens, options.partials ?? {}, []);
3011
+ if (options.validate === true) {
3012
+ const expanded = resolveTemplatePartials(prepared, options.partials ?? {});
3013
+ validateVariables(parseTemplate(expanded), { view });
3014
+ }
3015
+ const state = {
3016
+ escape: options.escape === "none" ? String : escapeHtml,
3017
+ partials: options.partials ?? {},
3018
+ partialStack: [],
3019
+ preserveMissing: options.yield !== void 0 && options.escape === "none",
3020
+ validate: options.validate === true
3021
+ };
3022
+ return renderTokens(tokens, { view }, prepared, state);
3023
+ }
3024
+ function resolveTemplatePartials(template, partials) {
3025
+ return expandTemplatePartials(template, partials, []);
2918
3026
  }
2919
- function renderTemplateInContext(template, context, escape, preserveMissing) {
2920
- return renderTokens(parseTemplate(template), context, template, escape, preserveMissing);
3027
+ function renderTemplateInContext(template, context, state) {
3028
+ return renderTokens(parseTemplate(template), context, template, state);
2921
3029
  }
2922
3030
  function parseTemplate(template) {
2923
3031
  const root = [];
@@ -2940,9 +3048,6 @@ function parseTemplate(template) {
2940
3048
  index = standalone?.nextIndex ?? parsed.end;
2941
3049
  continue;
2942
3050
  }
2943
- if (parsed.kind === "partial") {
2944
- throw new Error(`Partials are not supported: "${parsed.name}"`);
2945
- }
2946
3051
  if (parsed.kind === "delimiter") {
2947
3052
  throw new Error("Custom delimiters are not supported");
2948
3053
  }
@@ -2960,6 +3065,15 @@ function parseTemplate(template) {
2960
3065
  index = standalone?.nextIndex ?? parsed.end;
2961
3066
  continue;
2962
3067
  }
3068
+ if (parsed.kind === "partial") {
3069
+ tokens.push({
3070
+ type: "partial",
3071
+ name: parsed.name,
3072
+ indent: standalone === void 0 ? "" : template.slice(standalone.lineStart, open)
3073
+ });
3074
+ index = standalone?.nextIndex ?? parsed.end;
3075
+ continue;
3076
+ }
2963
3077
  if (parsed.kind === "close") {
2964
3078
  const frame2 = stack.pop();
2965
3079
  if (frame2 === void 0) {
@@ -3030,7 +3144,7 @@ function getStandalone(template, tagStart, tagEnd, kind) {
3030
3144
  }
3031
3145
  return void 0;
3032
3146
  }
3033
- function renderTokens(tokens, context, template, escape, preserveMissing) {
3147
+ function renderTokens(tokens, context, template, state) {
3034
3148
  let output = "";
3035
3149
  for (const token of tokens) {
3036
3150
  switch (token.type) {
@@ -3039,32 +3153,62 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
3039
3153
  continue;
3040
3154
  case "name":
3041
3155
  case "unescaped": {
3042
- const value = lookup2(context, token.name);
3043
- if (value == null) {
3044
- if (preserveMissing) {
3156
+ const result = lookup2(context, token.name);
3157
+ if (!result.hit || result.value == null) {
3158
+ if (state.validate) {
3159
+ throw new Error(`Template variable "${token.name}" not found.`);
3160
+ }
3161
+ if (state.preserveMissing) {
3045
3162
  output += token.raw;
3046
3163
  }
3047
3164
  continue;
3048
3165
  }
3049
- const rendered = String(value);
3050
- output += token.type === "name" ? escape(rendered) : rendered;
3166
+ const rendered = String(result.value);
3167
+ output += token.type === "name" ? state.escape(rendered) : rendered;
3168
+ continue;
3169
+ }
3170
+ case "partial": {
3171
+ if (!Object.hasOwn(state.partials, token.name)) {
3172
+ throw new Error(`Partial "${token.name}" not found.`);
3173
+ }
3174
+ if (state.partialStack.includes(token.name)) {
3175
+ throw new Error(
3176
+ `Circular partial reference detected: ${[...state.partialStack, token.name].join(" -> ")}.`
3177
+ );
3178
+ }
3179
+ if (state.partialStack.length >= MAX_PARTIAL_DEPTH) {
3180
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
3181
+ }
3182
+ const partial = indentPartial(state.partials[token.name], token.indent);
3183
+ output += renderTokens(parseTemplate(partial), context, partial, {
3184
+ ...state,
3185
+ partialStack: [...state.partialStack, token.name]
3186
+ });
3051
3187
  continue;
3052
3188
  }
3053
3189
  case "inverted": {
3054
- const value = lookup2(context, token.name);
3190
+ const result = lookup2(context, token.name);
3191
+ if (!result.hit && state.validate) {
3192
+ throw new Error(`Template variable "${token.name}" not found.`);
3193
+ }
3194
+ const value = result.value;
3055
3195
  if (!value || Array.isArray(value) && value.length === 0) {
3056
- output += renderTokens(token.children, context, template, escape, preserveMissing);
3196
+ output += renderTokens(token.children, context, template, state);
3057
3197
  }
3058
3198
  continue;
3059
3199
  }
3060
3200
  case "section": {
3061
- const value = lookup2(context, token.name);
3201
+ const result = lookup2(context, token.name);
3202
+ if (!result.hit && state.validate) {
3203
+ throw new Error(`Template variable "${token.name}" not found.`);
3204
+ }
3205
+ const value = result.value;
3062
3206
  if (!value) {
3063
3207
  continue;
3064
3208
  }
3065
3209
  if (Array.isArray(value)) {
3066
3210
  for (const item of value) {
3067
- output += renderTokens(token.children, pushContext(context, item), template, escape, preserveMissing);
3211
+ output += renderTokens(token.children, pushContext(context, item), template, state);
3068
3212
  }
3069
3213
  continue;
3070
3214
  }
@@ -3073,7 +3217,7 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
3073
3217
  const rendered = value.call(
3074
3218
  context.view,
3075
3219
  raw,
3076
- (nextTemplate) => renderTemplateInContext(nextTemplate, context, escape, preserveMissing)
3220
+ (nextTemplate) => renderTemplateInContext(nextTemplate, context, state)
3077
3221
  );
3078
3222
  if (rendered != null) {
3079
3223
  output += String(rendered);
@@ -3081,10 +3225,10 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
3081
3225
  continue;
3082
3226
  }
3083
3227
  if (typeof value === "object" || typeof value === "string" || typeof value === "number") {
3084
- output += renderTokens(token.children, pushContext(context, value), template, escape, preserveMissing);
3228
+ output += renderTokens(token.children, pushContext(context, value), template, state);
3085
3229
  continue;
3086
3230
  }
3087
- output += renderTokens(token.children, context, template, escape, preserveMissing);
3231
+ output += renderTokens(token.children, context, template, state);
3088
3232
  }
3089
3233
  }
3090
3234
  }
@@ -3092,17 +3236,115 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
3092
3236
  }
3093
3237
  function lookup2(context, name) {
3094
3238
  if (name === ".") {
3095
- return callLambda(context.view, context.view);
3239
+ return { hit: true, value: callLambda(context.view, context.view) };
3096
3240
  }
3097
3241
  let cursor = context;
3098
3242
  while (cursor !== void 0) {
3099
3243
  const result = name.includes(".") ? lookupDotted(cursor.view, name) : lookupName(cursor.view, name);
3100
3244
  if (result.hit) {
3101
- return callLambda(result.value, cursor.view);
3245
+ return { hit: true, value: callLambda(result.value, cursor.view) };
3102
3246
  }
3103
3247
  cursor = cursor.parent;
3104
3248
  }
3105
- return void 0;
3249
+ return { hit: false, value: void 0 };
3250
+ }
3251
+ function validateVariables(tokens, context) {
3252
+ for (const token of tokens) {
3253
+ if (token.type === "text" || token.type === "partial") {
3254
+ continue;
3255
+ }
3256
+ if (token.type === "name" || token.type === "unescaped") {
3257
+ if (!lookup2(context, token.name).hit) {
3258
+ throw new Error(`Template variable "${token.name}" not found.`);
3259
+ }
3260
+ continue;
3261
+ }
3262
+ if (token.type !== "section" && token.type !== "inverted") {
3263
+ continue;
3264
+ }
3265
+ const result = lookup2(context, token.name);
3266
+ if (!result.hit) {
3267
+ throw new Error(`Template variable "${token.name}" not found.`);
3268
+ }
3269
+ if (Array.isArray(result.value) && result.value.length > 0) {
3270
+ for (const item of result.value) {
3271
+ validateVariables(token.children, pushContext(context, item));
3272
+ }
3273
+ continue;
3274
+ }
3275
+ if (typeof result.value === "object" && result.value !== null || typeof result.value === "string" || typeof result.value === "number") {
3276
+ validateVariables(token.children, pushContext(context, result.value));
3277
+ continue;
3278
+ }
3279
+ validateVariables(token.children, context);
3280
+ }
3281
+ }
3282
+ function validatePartialReferences(tokens, partials, partialStack) {
3283
+ for (const token of tokens) {
3284
+ if (token.type === "section" || token.type === "inverted") {
3285
+ validatePartialReferences(token.children, partials, partialStack);
3286
+ continue;
3287
+ }
3288
+ if (token.type !== "partial") {
3289
+ continue;
3290
+ }
3291
+ if (!Object.hasOwn(partials, token.name)) {
3292
+ throw new Error(`Partial "${token.name}" not found.`);
3293
+ }
3294
+ if (partialStack.includes(token.name)) {
3295
+ throw new Error(
3296
+ `Circular partial reference detected: ${[...partialStack, token.name].join(" -> ")}.`
3297
+ );
3298
+ }
3299
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
3300
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
3301
+ }
3302
+ validatePartialReferences(parseTemplate(partials[token.name]), partials, [
3303
+ ...partialStack,
3304
+ token.name
3305
+ ]);
3306
+ }
3307
+ }
3308
+ function indentPartial(partial, indent) {
3309
+ if (indent === "") {
3310
+ return partial;
3311
+ }
3312
+ return partial.split("\n").map((line) => line === "" ? "" : `${indent}${line}`).join("\n");
3313
+ }
3314
+ function expandTemplatePartials(template, partials, partialStack) {
3315
+ let output = "";
3316
+ let index = 0;
3317
+ while (index < template.length) {
3318
+ const open = template.indexOf("{{", index);
3319
+ if (open === -1) {
3320
+ output += template.slice(index);
3321
+ break;
3322
+ }
3323
+ const parsed = parseTag(template, open);
3324
+ if (parsed.kind !== "partial") {
3325
+ output += template.slice(index, parsed.end);
3326
+ index = parsed.end;
3327
+ continue;
3328
+ }
3329
+ if (!Object.hasOwn(partials, parsed.name)) {
3330
+ throw new Error(`Partial "${parsed.name}" not found.`);
3331
+ }
3332
+ if (partialStack.includes(parsed.name)) {
3333
+ throw new Error(
3334
+ `Circular partial reference detected: ${[...partialStack, parsed.name].join(" -> ")}.`
3335
+ );
3336
+ }
3337
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
3338
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
3339
+ }
3340
+ const standalone = getStandalone(template, open, parsed.end, parsed.kind);
3341
+ const beforePartial = standalone === void 0 ? template.slice(index, open) : template.slice(index, standalone.lineStart);
3342
+ const indent = standalone === void 0 ? "" : template.slice(standalone.lineStart, open);
3343
+ const partial = indentPartial(partials[parsed.name], indent);
3344
+ output += beforePartial + expandTemplatePartials(partial, partials, [...partialStack, parsed.name]);
3345
+ index = standalone?.nextIndex ?? parsed.end;
3346
+ }
3347
+ return output;
3106
3348
  }
3107
3349
  function lookupName(view, name) {
3108
3350
  if (!isPropertyContainer(view) || !hasProperty(view, name)) {
@@ -3162,22 +3404,22 @@ function hasProperty(value, key) {
3162
3404
  return Object.prototype.hasOwnProperty.call(value, key);
3163
3405
  }
3164
3406
 
3165
- // ../design-system/src/components/browser.ts
3407
+ // ../toolcraft-design/src/components/browser.ts
3166
3408
  import { spawn as spawn2 } from "node:child_process";
3167
3409
  import process2 from "node:process";
3168
3410
 
3169
- // ../design-system/src/acp/writer.ts
3411
+ // ../toolcraft-design/src/acp/writer.ts
3170
3412
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
3171
3413
  var storage = new AsyncLocalStorage2();
3172
3414
 
3173
- // ../design-system/src/dashboard/terminal-width.ts
3415
+ // ../toolcraft-design/src/dashboard/terminal-width.ts
3174
3416
  var graphemeSegmenter3 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
3175
3417
 
3176
- // ../design-system/src/dashboard/terminal.ts
3418
+ // ../toolcraft-design/src/dashboard/terminal.ts
3177
3419
  import readline from "node:readline";
3178
3420
  import { PassThrough } from "node:stream";
3179
3421
 
3180
- // ../design-system/src/explorer/state.ts
3422
+ // ../toolcraft-design/src/explorer/state.ts
3181
3423
  var REGION_HEADER = 1 << 0;
3182
3424
  var REGION_LIST = 1 << 1;
3183
3425
  var REGION_DETAIL = 1 << 2;
@@ -3186,13 +3428,13 @@ var REGION_MODAL = 1 << 4;
3186
3428
  var REGION_TOAST = 1 << 5;
3187
3429
  var REGION_ALL = REGION_HEADER | REGION_LIST | REGION_DETAIL | REGION_FOOTER | REGION_MODAL | REGION_TOAST;
3188
3430
 
3189
- // ../design-system/src/prompts/index.ts
3431
+ // ../toolcraft-design/src/prompts/index.ts
3190
3432
  import * as clack from "@clack/prompts";
3191
3433
 
3192
- // ../design-system/src/prompts/primitives/cancel.ts
3434
+ // ../toolcraft-design/src/prompts/primitives/cancel.ts
3193
3435
  import { isCancel } from "@clack/prompts";
3194
3436
 
3195
- // ../design-system/src/static/spinner.ts
3437
+ // ../toolcraft-design/src/static/spinner.ts
3196
3438
  var SPINNER_FRAMES = Object.freeze(["\u25D2", "\u25D0", "\u25D3", "\u25D1"]);
3197
3439
 
3198
3440
  // ../config-mutations/src/formats/json.ts
@@ -3231,6 +3473,13 @@ function cloneConfigValue(value) {
3231
3473
  function isConfigObject(value) {
3232
3474
  return typeof value === "object" && value !== null && !Array.isArray(value);
3233
3475
  }
3476
+ function detectIndent(content) {
3477
+ const match = content.match(/^[\t ]+/m);
3478
+ if (match) {
3479
+ return match[0];
3480
+ }
3481
+ return " ";
3482
+ }
3234
3483
  function parse3(content) {
3235
3484
  if (!content || content.trim() === "") {
3236
3485
  return {};
@@ -3270,6 +3519,9 @@ function merge(base, patch) {
3270
3519
  }
3271
3520
  return result;
3272
3521
  }
3522
+ function configValuesEqual(left, right) {
3523
+ return JSON.stringify(left) === JSON.stringify(right);
3524
+ }
3273
3525
  function prune(obj, shape) {
3274
3526
  let changed = false;
3275
3527
  const result = cloneConfigObject(obj);
@@ -3306,9 +3558,56 @@ function prune(obj, shape) {
3306
3558
  }
3307
3559
  return { changed, result };
3308
3560
  }
3561
+ function modifyAtPath(content, path10, value) {
3562
+ const indent = detectIndent(content);
3563
+ const formattingOptions = {
3564
+ tabSize: indent === " " ? 1 : indent.length,
3565
+ insertSpaces: indent !== " ",
3566
+ eol: "\n"
3567
+ };
3568
+ const edits = jsonc.modify(content, path10, value, { formattingOptions });
3569
+ let result = jsonc.applyEdits(content, edits);
3570
+ if (!result.endsWith("\n")) {
3571
+ result += "\n";
3572
+ }
3573
+ return result;
3574
+ }
3575
+ function removeAtPath(content, path10) {
3576
+ return modifyAtPath(content, path10, void 0);
3577
+ }
3578
+ function serializeUpdate(content, current, next) {
3579
+ let result = content || "{}";
3580
+ result = applyObjectUpdate(result, [], current, next);
3581
+ if (!result.endsWith("\n")) {
3582
+ result += "\n";
3583
+ }
3584
+ return result;
3585
+ }
3586
+ function applyObjectUpdate(content, path10, current, next) {
3587
+ let result = content;
3588
+ for (const key of Object.keys(current)) {
3589
+ if (!hasConfigEntry(next, key)) {
3590
+ result = removeAtPath(result, [...path10, key]);
3591
+ }
3592
+ }
3593
+ for (const [key, nextValue] of Object.entries(next)) {
3594
+ const nextPath = [...path10, key];
3595
+ const hasCurrent = hasConfigEntry(current, key);
3596
+ const currentValue = hasCurrent ? current[key] : void 0;
3597
+ if (hasCurrent && isConfigObject(currentValue) && isConfigObject(nextValue)) {
3598
+ result = applyObjectUpdate(result, nextPath, currentValue, nextValue);
3599
+ continue;
3600
+ }
3601
+ if (!hasCurrent || !configValuesEqual(currentValue, nextValue)) {
3602
+ result = modifyAtPath(result, nextPath, nextValue);
3603
+ }
3604
+ }
3605
+ return result;
3606
+ }
3309
3607
  var jsonFormat = {
3310
3608
  parse: parse3,
3311
3609
  serialize,
3610
+ serializeUpdate,
3312
3611
  merge,
3313
3612
  prune
3314
3613
  };
@@ -3690,7 +3989,7 @@ function pruneKeysByPrefix(table, prefix) {
3690
3989
  const result = {};
3691
3990
  for (const [key, value] of Object.entries(table)) {
3692
3991
  if (!key.startsWith(prefix)) {
3693
- result[key] = value;
3992
+ setConfigEntry(result, key, value);
3694
3993
  }
3695
3994
  }
3696
3995
  return result;
@@ -3699,28 +3998,43 @@ function isConfigObject4(value) {
3699
3998
  return typeof value === "object" && value !== null && !Array.isArray(value);
3700
3999
  }
3701
4000
  function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
3702
- const result = { ...base };
4001
+ const result = cloneConfigObject(base);
3703
4002
  const prefixMap = pruneByPrefix ?? {};
3704
4003
  for (const [key, value] of Object.entries(patch)) {
4004
+ if (value === void 0) {
4005
+ continue;
4006
+ }
3705
4007
  const current = result[key];
3706
4008
  const prefix = prefixMap[key];
3707
4009
  if (isConfigObject4(current) && isConfigObject4(value)) {
3708
4010
  if (prefix) {
3709
4011
  const pruned = pruneKeysByPrefix(current, prefix);
3710
- result[key] = { ...pruned, ...value };
4012
+ setConfigEntry(result, key, mergePrunedConfigObject(pruned, value));
3711
4013
  } else {
3712
- result[key] = mergeWithPruneByPrefix(
3713
- current,
3714
- value,
3715
- prefixMap
3716
- );
4014
+ setConfigEntry(result, key, mergeWithPruneByPrefix(current, value, prefixMap));
3717
4015
  }
3718
4016
  continue;
3719
4017
  }
3720
- result[key] = value;
4018
+ setConfigEntry(result, key, value);
4019
+ }
4020
+ return result;
4021
+ }
4022
+ function mergePrunedConfigObject(base, patch) {
4023
+ const result = cloneConfigObject(base);
4024
+ for (const [key, value] of Object.entries(patch)) {
4025
+ if (value === void 0) {
4026
+ continue;
4027
+ }
4028
+ setConfigEntry(result, key, value);
3721
4029
  }
3722
4030
  return result;
3723
4031
  }
4032
+ function serializeConfigUpdate(format, rawContent, current, next) {
4033
+ if (rawContent !== null && format.serializeUpdate) {
4034
+ return format.serializeUpdate(rawContent, current, next);
4035
+ }
4036
+ return format.serialize(next);
4037
+ }
3724
4038
  async function applyMutation(mutation, context, options) {
3725
4039
  switch (mutation.kind) {
3726
4040
  case "ensureDirectory":
@@ -4033,6 +4347,7 @@ async function applyConfigMerge(mutation, context, options) {
4033
4347
  }
4034
4348
  const format = getConfigFormat(formatName);
4035
4349
  const rawContent = await readFileIfExists(context.fs, targetPath);
4350
+ let preserveContent = rawContent;
4036
4351
  let current;
4037
4352
  try {
4038
4353
  current = rawContent === null ? {} : format.parse(rawContent);
@@ -4041,6 +4356,7 @@ async function applyConfigMerge(mutation, context, options) {
4041
4356
  await backupInvalidDocument(context, targetPath, rawContent);
4042
4357
  }
4043
4358
  current = {};
4359
+ preserveContent = null;
4044
4360
  }
4045
4361
  const value = resolveValue(mutation.value, options);
4046
4362
  let merged;
@@ -4049,7 +4365,7 @@ async function applyConfigMerge(mutation, context, options) {
4049
4365
  } else {
4050
4366
  merged = format.merge(current, value);
4051
4367
  }
4052
- const serialized = format.serialize(merged);
4368
+ const serialized = serializeConfigUpdate(format, preserveContent, current, merged);
4053
4369
  const changed = serialized !== rawContent;
4054
4370
  if (changed && !context.dryRun) {
4055
4371
  await writeAtomically(context, targetPath, serialized);
@@ -4117,7 +4433,7 @@ async function applyConfigPrune(mutation, context, options) {
4117
4433
  details
4118
4434
  };
4119
4435
  }
4120
- const serialized = format.serialize(result);
4436
+ const serialized = serializeConfigUpdate(format, rawContent, current, result);
4121
4437
  if (!context.dryRun) {
4122
4438
  await writeAtomically(context, targetPath, serialized);
4123
4439
  }
@@ -4142,6 +4458,7 @@ async function applyConfigTransform(mutation, context, options) {
4142
4458
  }
4143
4459
  const format = getConfigFormat(formatName);
4144
4460
  const rawContent = await readFileIfExists(context.fs, targetPath);
4461
+ let preserveContent = rawContent;
4145
4462
  let current;
4146
4463
  try {
4147
4464
  current = rawContent === null ? {} : format.parse(rawContent);
@@ -4150,6 +4467,7 @@ async function applyConfigTransform(mutation, context, options) {
4150
4467
  await backupInvalidDocument(context, targetPath, rawContent);
4151
4468
  }
4152
4469
  current = {};
4470
+ preserveContent = null;
4153
4471
  }
4154
4472
  const { content: transformed, changed } = mutation.transform(current, options);
4155
4473
  if (!changed) {
@@ -4173,7 +4491,7 @@ async function applyConfigTransform(mutation, context, options) {
4173
4491
  details
4174
4492
  };
4175
4493
  }
4176
- const serialized = format.serialize(transformed);
4494
+ const serialized = serializeConfigUpdate(format, preserveContent, current, transformed);
4177
4495
  if (!context.dryRun) {
4178
4496
  await writeAtomically(context, targetPath, serialized);
4179
4497
  }
@@ -4350,14 +4668,14 @@ async function installSkill(agentId, skill, options) {
4350
4668
  throw new UnsupportedAgentError(agentId);
4351
4669
  }
4352
4670
  const scope = options.scope ?? "local";
4353
- const config = support.config;
4671
+ const config2 = support.config;
4354
4672
  if (skill.name.length === 0 || skill.name === "." || skill.name === ".." || skill.name.includes("/") || skill.name.includes("\\") || skill.name.includes("\n") || skill.name.includes("\r")) {
4355
4673
  throw new Error(`Invalid skill name: ${skill.name}`);
4356
4674
  }
4357
- const skillDir = scope === "global" ? config.globalSkillDir : toHomeRelative(config.localSkillDir);
4675
+ const skillDir = scope === "global" ? config2.globalSkillDir : toHomeRelative(config2.localSkillDir);
4358
4676
  const skillFolderPath = `${skillDir}/${skill.name}`;
4359
4677
  const skillFilePath = `${skillFolderPath}/SKILL.md`;
4360
- const displayPath = `${scope === "global" ? config.globalSkillDir : config.localSkillDir}/${skill.name}/SKILL.md`;
4678
+ const displayPath = `${scope === "global" ? config2.globalSkillDir : config2.localSkillDir}/${skill.name}/SKILL.md`;
4361
4679
  const absoluteSkillPath = `${scope === "global" ? options.homeDir : options.cwd}/${skillFilePath.slice(2)}`;
4362
4680
  if (await pathExists2(options.fs, absoluteSkillPath)) {
4363
4681
  throw new Error(`Skill already exists: ${displayPath}`);
@@ -4478,17 +4796,17 @@ function resolveHomeRelativePath(targetPath, homeDir) {
4478
4796
  return targetPath;
4479
4797
  }
4480
4798
  function getSkillFolderWithHome(agent, scope, cwd, homeDir) {
4481
- const config = getAgentConfig(agent);
4482
- if (!config) {
4799
+ const config2 = getAgentConfig(agent);
4800
+ if (!config2) {
4483
4801
  throwUnsupportedAgent(agent);
4484
4802
  }
4485
4803
  return {
4486
4804
  displayPath: path9.join(
4487
- scope === "global" ? config.globalSkillDir : config.localSkillDir,
4805
+ scope === "global" ? config2.globalSkillDir : config2.localSkillDir,
4488
4806
  TERMINAL_PILOT_SKILL_NAME
4489
4807
  ),
4490
4808
  fullPath: path9.join(
4491
- scope === "global" ? resolveHomeRelativePath(config.globalSkillDir, homeDir) : path9.resolve(cwd, config.localSkillDir),
4809
+ scope === "global" ? resolveHomeRelativePath(config2.globalSkillDir, homeDir) : path9.resolve(cwd, config2.localSkillDir),
4492
4810
  TERMINAL_PILOT_SKILL_NAME
4493
4811
  )
4494
4812
  };