terminal-pilot 0.0.19 → 0.0.20

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 (35) hide show
  1. package/dist/cli.js +537 -242
  2. package/dist/cli.js.map +4 -4
  3. package/dist/commands/index.js +261 -37
  4. package/dist/commands/index.js.map +3 -3
  5. package/dist/commands/install.js +261 -37
  6. package/dist/commands/install.js.map +3 -3
  7. package/dist/commands/installer.js.map +1 -1
  8. package/dist/commands/uninstall.js.map +1 -1
  9. package/dist/testing/cli-repl.js +533 -238
  10. package/dist/testing/cli-repl.js.map +4 -4
  11. package/dist/testing/qa-cli.js +543 -248
  12. package/dist/testing/qa-cli.js.map +4 -4
  13. package/node_modules/@poe-code/agent-skill-config/README.md +103 -0
  14. package/node_modules/@poe-code/agent-skill-config/dist/apply.d.ts +25 -0
  15. package/node_modules/@poe-code/agent-skill-config/dist/apply.js +159 -0
  16. package/node_modules/@poe-code/agent-skill-config/dist/bridge-active-skills.d.ts +23 -0
  17. package/node_modules/@poe-code/agent-skill-config/dist/bridge-active-skills.js +341 -0
  18. package/node_modules/@poe-code/agent-skill-config/dist/configs.d.ts +16 -0
  19. package/node_modules/@poe-code/agent-skill-config/dist/configs.js +70 -0
  20. package/node_modules/@poe-code/agent-skill-config/dist/exports.compile-check.d.ts +1 -0
  21. package/node_modules/@poe-code/agent-skill-config/dist/exports.compile-check.js +1 -0
  22. package/node_modules/@poe-code/agent-skill-config/dist/git-exclude.d.ts +8 -0
  23. package/node_modules/@poe-code/agent-skill-config/dist/git-exclude.js +159 -0
  24. package/node_modules/@poe-code/agent-skill-config/dist/index.d.ts +11 -0
  25. package/node_modules/@poe-code/agent-skill-config/dist/index.js +6 -0
  26. package/node_modules/@poe-code/agent-skill-config/dist/resolve-skill-reference.d.ts +22 -0
  27. package/node_modules/@poe-code/agent-skill-config/dist/resolve-skill-reference.js +87 -0
  28. package/node_modules/@poe-code/agent-skill-config/dist/templates/poe-generate.md +47 -0
  29. package/node_modules/@poe-code/agent-skill-config/dist/templates/terminal-pilot.md +45 -0
  30. package/node_modules/@poe-code/agent-skill-config/dist/templates.d.ts +3 -0
  31. package/node_modules/@poe-code/agent-skill-config/dist/templates.js +63 -0
  32. package/node_modules/@poe-code/agent-skill-config/dist/types.d.ts +16 -0
  33. package/node_modules/@poe-code/agent-skill-config/dist/types.js +1 -0
  34. package/node_modules/@poe-code/agent-skill-config/package.json +24 -0
  35. package/package.json +3 -2
@@ -776,6 +776,7 @@ var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
776
776
  var graphemeSegmenter2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
777
777
 
778
778
  // ../design-system/src/components/template.ts
779
+ var MAX_PARTIAL_DEPTH = 100;
779
780
  var HTML_ESCAPE = {
780
781
  "&": "&",
781
782
  "<": "&lt;",
@@ -787,14 +788,27 @@ var HTML_ESCAPE = {
787
788
  "=": "&#x3D;"
788
789
  };
789
790
  function renderTemplate(template, view, options = {}) {
790
- const prepared = options.yield === void 0 ? template : template.split("{{yield}}").join(options.yield);
791
+ const prepared = options.yield === void 0 ? template : resolveTemplatePartials(template, options.partials ?? {}).split("{{yield}}").join(options.yield);
791
792
  const tokens = parseTemplate(prepared);
792
- const escape = options.escape === "none" ? String : escapeHtml;
793
- const preserveMissing = options.yield !== void 0 && options.escape === "none";
794
- return renderTokens(tokens, { view }, prepared, escape, preserveMissing);
793
+ validatePartialReferences(tokens, options.partials ?? {}, []);
794
+ if (options.validate === true) {
795
+ const expanded = resolveTemplatePartials(prepared, options.partials ?? {});
796
+ validateVariables(parseTemplate(expanded), { view });
797
+ }
798
+ const state = {
799
+ escape: options.escape === "none" ? String : escapeHtml,
800
+ partials: options.partials ?? {},
801
+ partialStack: [],
802
+ preserveMissing: options.yield !== void 0 && options.escape === "none",
803
+ validate: options.validate === true
804
+ };
805
+ return renderTokens(tokens, { view }, prepared, state);
806
+ }
807
+ function resolveTemplatePartials(template, partials) {
808
+ return expandTemplatePartials(template, partials, []);
795
809
  }
796
- function renderTemplateInContext(template, context, escape, preserveMissing) {
797
- return renderTokens(parseTemplate(template), context, template, escape, preserveMissing);
810
+ function renderTemplateInContext(template, context, state) {
811
+ return renderTokens(parseTemplate(template), context, template, state);
798
812
  }
799
813
  function parseTemplate(template) {
800
814
  const root = [];
@@ -817,9 +831,6 @@ function parseTemplate(template) {
817
831
  index = standalone?.nextIndex ?? parsed.end;
818
832
  continue;
819
833
  }
820
- if (parsed.kind === "partial") {
821
- throw new Error(`Partials are not supported: "${parsed.name}"`);
822
- }
823
834
  if (parsed.kind === "delimiter") {
824
835
  throw new Error("Custom delimiters are not supported");
825
836
  }
@@ -837,6 +848,15 @@ function parseTemplate(template) {
837
848
  index = standalone?.nextIndex ?? parsed.end;
838
849
  continue;
839
850
  }
851
+ if (parsed.kind === "partial") {
852
+ tokens.push({
853
+ type: "partial",
854
+ name: parsed.name,
855
+ indent: standalone === void 0 ? "" : template.slice(standalone.lineStart, open)
856
+ });
857
+ index = standalone?.nextIndex ?? parsed.end;
858
+ continue;
859
+ }
840
860
  if (parsed.kind === "close") {
841
861
  const frame2 = stack.pop();
842
862
  if (frame2 === void 0) {
@@ -907,7 +927,7 @@ function getStandalone(template, tagStart, tagEnd, kind) {
907
927
  }
908
928
  return void 0;
909
929
  }
910
- function renderTokens(tokens, context, template, escape, preserveMissing) {
930
+ function renderTokens(tokens, context, template, state) {
911
931
  let output = "";
912
932
  for (const token of tokens) {
913
933
  switch (token.type) {
@@ -916,32 +936,62 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
916
936
  continue;
917
937
  case "name":
918
938
  case "unescaped": {
919
- const value = lookup2(context, token.name);
920
- if (value == null) {
921
- if (preserveMissing) {
939
+ const result = lookup2(context, token.name);
940
+ if (!result.hit || result.value == null) {
941
+ if (state.validate) {
942
+ throw new Error(`Template variable "${token.name}" not found.`);
943
+ }
944
+ if (state.preserveMissing) {
922
945
  output += token.raw;
923
946
  }
924
947
  continue;
925
948
  }
926
- const rendered = String(value);
927
- output += token.type === "name" ? escape(rendered) : rendered;
949
+ const rendered = String(result.value);
950
+ output += token.type === "name" ? state.escape(rendered) : rendered;
951
+ continue;
952
+ }
953
+ case "partial": {
954
+ if (!Object.hasOwn(state.partials, token.name)) {
955
+ throw new Error(`Partial "${token.name}" not found.`);
956
+ }
957
+ if (state.partialStack.includes(token.name)) {
958
+ throw new Error(
959
+ `Circular partial reference detected: ${[...state.partialStack, token.name].join(" -> ")}.`
960
+ );
961
+ }
962
+ if (state.partialStack.length >= MAX_PARTIAL_DEPTH) {
963
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
964
+ }
965
+ const partial = indentPartial(state.partials[token.name], token.indent);
966
+ output += renderTokens(parseTemplate(partial), context, partial, {
967
+ ...state,
968
+ partialStack: [...state.partialStack, token.name]
969
+ });
928
970
  continue;
929
971
  }
930
972
  case "inverted": {
931
- const value = lookup2(context, token.name);
973
+ const result = lookup2(context, token.name);
974
+ if (!result.hit && state.validate) {
975
+ throw new Error(`Template variable "${token.name}" not found.`);
976
+ }
977
+ const value = result.value;
932
978
  if (!value || Array.isArray(value) && value.length === 0) {
933
- output += renderTokens(token.children, context, template, escape, preserveMissing);
979
+ output += renderTokens(token.children, context, template, state);
934
980
  }
935
981
  continue;
936
982
  }
937
983
  case "section": {
938
- const value = lookup2(context, token.name);
984
+ const result = lookup2(context, token.name);
985
+ if (!result.hit && state.validate) {
986
+ throw new Error(`Template variable "${token.name}" not found.`);
987
+ }
988
+ const value = result.value;
939
989
  if (!value) {
940
990
  continue;
941
991
  }
942
992
  if (Array.isArray(value)) {
943
993
  for (const item of value) {
944
- output += renderTokens(token.children, pushContext(context, item), template, escape, preserveMissing);
994
+ output += renderTokens(token.children, pushContext(context, item), template, state);
945
995
  }
946
996
  continue;
947
997
  }
@@ -950,7 +1000,7 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
950
1000
  const rendered = value.call(
951
1001
  context.view,
952
1002
  raw,
953
- (nextTemplate) => renderTemplateInContext(nextTemplate, context, escape, preserveMissing)
1003
+ (nextTemplate) => renderTemplateInContext(nextTemplate, context, state)
954
1004
  );
955
1005
  if (rendered != null) {
956
1006
  output += String(rendered);
@@ -958,10 +1008,10 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
958
1008
  continue;
959
1009
  }
960
1010
  if (typeof value === "object" || typeof value === "string" || typeof value === "number") {
961
- output += renderTokens(token.children, pushContext(context, value), template, escape, preserveMissing);
1011
+ output += renderTokens(token.children, pushContext(context, value), template, state);
962
1012
  continue;
963
1013
  }
964
- output += renderTokens(token.children, context, template, escape, preserveMissing);
1014
+ output += renderTokens(token.children, context, template, state);
965
1015
  }
966
1016
  }
967
1017
  }
@@ -969,17 +1019,115 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
969
1019
  }
970
1020
  function lookup2(context, name) {
971
1021
  if (name === ".") {
972
- return callLambda(context.view, context.view);
1022
+ return { hit: true, value: callLambda(context.view, context.view) };
973
1023
  }
974
1024
  let cursor = context;
975
1025
  while (cursor !== void 0) {
976
1026
  const result = name.includes(".") ? lookupDotted(cursor.view, name) : lookupName(cursor.view, name);
977
1027
  if (result.hit) {
978
- return callLambda(result.value, cursor.view);
1028
+ return { hit: true, value: callLambda(result.value, cursor.view) };
979
1029
  }
980
1030
  cursor = cursor.parent;
981
1031
  }
982
- return void 0;
1032
+ return { hit: false, value: void 0 };
1033
+ }
1034
+ function validateVariables(tokens, context) {
1035
+ for (const token of tokens) {
1036
+ if (token.type === "text" || token.type === "partial") {
1037
+ continue;
1038
+ }
1039
+ if (token.type === "name" || token.type === "unescaped") {
1040
+ if (!lookup2(context, token.name).hit) {
1041
+ throw new Error(`Template variable "${token.name}" not found.`);
1042
+ }
1043
+ continue;
1044
+ }
1045
+ if (token.type !== "section" && token.type !== "inverted") {
1046
+ continue;
1047
+ }
1048
+ const result = lookup2(context, token.name);
1049
+ if (!result.hit) {
1050
+ throw new Error(`Template variable "${token.name}" not found.`);
1051
+ }
1052
+ if (Array.isArray(result.value) && result.value.length > 0) {
1053
+ for (const item of result.value) {
1054
+ validateVariables(token.children, pushContext(context, item));
1055
+ }
1056
+ continue;
1057
+ }
1058
+ if (typeof result.value === "object" && result.value !== null || typeof result.value === "string" || typeof result.value === "number") {
1059
+ validateVariables(token.children, pushContext(context, result.value));
1060
+ continue;
1061
+ }
1062
+ validateVariables(token.children, context);
1063
+ }
1064
+ }
1065
+ function validatePartialReferences(tokens, partials, partialStack) {
1066
+ for (const token of tokens) {
1067
+ if (token.type === "section" || token.type === "inverted") {
1068
+ validatePartialReferences(token.children, partials, partialStack);
1069
+ continue;
1070
+ }
1071
+ if (token.type !== "partial") {
1072
+ continue;
1073
+ }
1074
+ if (!Object.hasOwn(partials, token.name)) {
1075
+ throw new Error(`Partial "${token.name}" not found.`);
1076
+ }
1077
+ if (partialStack.includes(token.name)) {
1078
+ throw new Error(
1079
+ `Circular partial reference detected: ${[...partialStack, token.name].join(" -> ")}.`
1080
+ );
1081
+ }
1082
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
1083
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
1084
+ }
1085
+ validatePartialReferences(parseTemplate(partials[token.name]), partials, [
1086
+ ...partialStack,
1087
+ token.name
1088
+ ]);
1089
+ }
1090
+ }
1091
+ function indentPartial(partial, indent) {
1092
+ if (indent === "") {
1093
+ return partial;
1094
+ }
1095
+ return partial.split("\n").map((line) => line === "" ? "" : `${indent}${line}`).join("\n");
1096
+ }
1097
+ function expandTemplatePartials(template, partials, partialStack) {
1098
+ let output = "";
1099
+ let index = 0;
1100
+ while (index < template.length) {
1101
+ const open = template.indexOf("{{", index);
1102
+ if (open === -1) {
1103
+ output += template.slice(index);
1104
+ break;
1105
+ }
1106
+ const parsed = parseTag(template, open);
1107
+ if (parsed.kind !== "partial") {
1108
+ output += template.slice(index, parsed.end);
1109
+ index = parsed.end;
1110
+ continue;
1111
+ }
1112
+ if (!Object.hasOwn(partials, parsed.name)) {
1113
+ throw new Error(`Partial "${parsed.name}" not found.`);
1114
+ }
1115
+ if (partialStack.includes(parsed.name)) {
1116
+ throw new Error(
1117
+ `Circular partial reference detected: ${[...partialStack, parsed.name].join(" -> ")}.`
1118
+ );
1119
+ }
1120
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
1121
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
1122
+ }
1123
+ const standalone = getStandalone(template, open, parsed.end, parsed.kind);
1124
+ const beforePartial = standalone === void 0 ? template.slice(index, open) : template.slice(index, standalone.lineStart);
1125
+ const indent = standalone === void 0 ? "" : template.slice(standalone.lineStart, open);
1126
+ const partial = indentPartial(partials[parsed.name], indent);
1127
+ output += beforePartial + expandTemplatePartials(partial, partials, [...partialStack, parsed.name]);
1128
+ index = standalone?.nextIndex ?? parsed.end;
1129
+ }
1130
+ return output;
983
1131
  }
984
1132
  function lookupName(view, name) {
985
1133
  if (!isPropertyContainer(view) || !hasProperty(view, name)) {
@@ -1108,6 +1256,13 @@ function cloneConfigValue(value) {
1108
1256
  function isConfigObject(value) {
1109
1257
  return typeof value === "object" && value !== null && !Array.isArray(value);
1110
1258
  }
1259
+ function detectIndent(content) {
1260
+ const match = content.match(/^[\t ]+/m);
1261
+ if (match) {
1262
+ return match[0];
1263
+ }
1264
+ return " ";
1265
+ }
1111
1266
  function parse3(content) {
1112
1267
  if (!content || content.trim() === "") {
1113
1268
  return {};
@@ -1147,6 +1302,9 @@ function merge(base, patch) {
1147
1302
  }
1148
1303
  return result;
1149
1304
  }
1305
+ function configValuesEqual(left, right) {
1306
+ return JSON.stringify(left) === JSON.stringify(right);
1307
+ }
1150
1308
  function prune(obj, shape) {
1151
1309
  let changed = false;
1152
1310
  const result = cloneConfigObject(obj);
@@ -1183,9 +1341,56 @@ function prune(obj, shape) {
1183
1341
  }
1184
1342
  return { changed, result };
1185
1343
  }
1344
+ function modifyAtPath(content, path10, value) {
1345
+ const indent = detectIndent(content);
1346
+ const formattingOptions = {
1347
+ tabSize: indent === " " ? 1 : indent.length,
1348
+ insertSpaces: indent !== " ",
1349
+ eol: "\n"
1350
+ };
1351
+ const edits = jsonc.modify(content, path10, value, { formattingOptions });
1352
+ let result = jsonc.applyEdits(content, edits);
1353
+ if (!result.endsWith("\n")) {
1354
+ result += "\n";
1355
+ }
1356
+ return result;
1357
+ }
1358
+ function removeAtPath(content, path10) {
1359
+ return modifyAtPath(content, path10, void 0);
1360
+ }
1361
+ function serializeUpdate(content, current, next) {
1362
+ let result = content || "{}";
1363
+ result = applyObjectUpdate(result, [], current, next);
1364
+ if (!result.endsWith("\n")) {
1365
+ result += "\n";
1366
+ }
1367
+ return result;
1368
+ }
1369
+ function applyObjectUpdate(content, path10, current, next) {
1370
+ let result = content;
1371
+ for (const key of Object.keys(current)) {
1372
+ if (!hasConfigEntry(next, key)) {
1373
+ result = removeAtPath(result, [...path10, key]);
1374
+ }
1375
+ }
1376
+ for (const [key, nextValue] of Object.entries(next)) {
1377
+ const nextPath = [...path10, key];
1378
+ const hasCurrent = hasConfigEntry(current, key);
1379
+ const currentValue = hasCurrent ? current[key] : void 0;
1380
+ if (hasCurrent && isConfigObject(currentValue) && isConfigObject(nextValue)) {
1381
+ result = applyObjectUpdate(result, nextPath, currentValue, nextValue);
1382
+ continue;
1383
+ }
1384
+ if (!hasCurrent || !configValuesEqual(currentValue, nextValue)) {
1385
+ result = modifyAtPath(result, nextPath, nextValue);
1386
+ }
1387
+ }
1388
+ return result;
1389
+ }
1186
1390
  var jsonFormat = {
1187
1391
  parse: parse3,
1188
1392
  serialize,
1393
+ serializeUpdate,
1189
1394
  merge,
1190
1395
  prune
1191
1396
  };
@@ -1567,7 +1772,7 @@ function pruneKeysByPrefix(table, prefix) {
1567
1772
  const result = {};
1568
1773
  for (const [key, value] of Object.entries(table)) {
1569
1774
  if (!key.startsWith(prefix)) {
1570
- result[key] = value;
1775
+ setConfigEntry(result, key, value);
1571
1776
  }
1572
1777
  }
1573
1778
  return result;
@@ -1576,28 +1781,43 @@ function isConfigObject4(value) {
1576
1781
  return typeof value === "object" && value !== null && !Array.isArray(value);
1577
1782
  }
1578
1783
  function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
1579
- const result = { ...base };
1784
+ const result = cloneConfigObject(base);
1580
1785
  const prefixMap = pruneByPrefix ?? {};
1581
1786
  for (const [key, value] of Object.entries(patch)) {
1787
+ if (value === void 0) {
1788
+ continue;
1789
+ }
1582
1790
  const current = result[key];
1583
1791
  const prefix = prefixMap[key];
1584
1792
  if (isConfigObject4(current) && isConfigObject4(value)) {
1585
1793
  if (prefix) {
1586
1794
  const pruned = pruneKeysByPrefix(current, prefix);
1587
- result[key] = { ...pruned, ...value };
1795
+ setConfigEntry(result, key, mergePrunedConfigObject(pruned, value));
1588
1796
  } else {
1589
- result[key] = mergeWithPruneByPrefix(
1590
- current,
1591
- value,
1592
- prefixMap
1593
- );
1797
+ setConfigEntry(result, key, mergeWithPruneByPrefix(current, value, prefixMap));
1594
1798
  }
1595
1799
  continue;
1596
1800
  }
1597
- result[key] = value;
1801
+ setConfigEntry(result, key, value);
1598
1802
  }
1599
1803
  return result;
1600
1804
  }
1805
+ function mergePrunedConfigObject(base, patch) {
1806
+ const result = cloneConfigObject(base);
1807
+ for (const [key, value] of Object.entries(patch)) {
1808
+ if (value === void 0) {
1809
+ continue;
1810
+ }
1811
+ setConfigEntry(result, key, value);
1812
+ }
1813
+ return result;
1814
+ }
1815
+ function serializeConfigUpdate(format, rawContent, current, next) {
1816
+ if (rawContent !== null && format.serializeUpdate) {
1817
+ return format.serializeUpdate(rawContent, current, next);
1818
+ }
1819
+ return format.serialize(next);
1820
+ }
1601
1821
  async function applyMutation(mutation, context, options) {
1602
1822
  switch (mutation.kind) {
1603
1823
  case "ensureDirectory":
@@ -1910,6 +2130,7 @@ async function applyConfigMerge(mutation, context, options) {
1910
2130
  }
1911
2131
  const format = getConfigFormat(formatName);
1912
2132
  const rawContent = await readFileIfExists(context.fs, targetPath);
2133
+ let preserveContent = rawContent;
1913
2134
  let current;
1914
2135
  try {
1915
2136
  current = rawContent === null ? {} : format.parse(rawContent);
@@ -1918,6 +2139,7 @@ async function applyConfigMerge(mutation, context, options) {
1918
2139
  await backupInvalidDocument(context, targetPath, rawContent);
1919
2140
  }
1920
2141
  current = {};
2142
+ preserveContent = null;
1921
2143
  }
1922
2144
  const value = resolveValue(mutation.value, options);
1923
2145
  let merged;
@@ -1926,7 +2148,7 @@ async function applyConfigMerge(mutation, context, options) {
1926
2148
  } else {
1927
2149
  merged = format.merge(current, value);
1928
2150
  }
1929
- const serialized = format.serialize(merged);
2151
+ const serialized = serializeConfigUpdate(format, preserveContent, current, merged);
1930
2152
  const changed = serialized !== rawContent;
1931
2153
  if (changed && !context.dryRun) {
1932
2154
  await writeAtomically(context, targetPath, serialized);
@@ -1994,7 +2216,7 @@ async function applyConfigPrune(mutation, context, options) {
1994
2216
  details
1995
2217
  };
1996
2218
  }
1997
- const serialized = format.serialize(result);
2219
+ const serialized = serializeConfigUpdate(format, rawContent, current, result);
1998
2220
  if (!context.dryRun) {
1999
2221
  await writeAtomically(context, targetPath, serialized);
2000
2222
  }
@@ -2019,6 +2241,7 @@ async function applyConfigTransform(mutation, context, options) {
2019
2241
  }
2020
2242
  const format = getConfigFormat(formatName);
2021
2243
  const rawContent = await readFileIfExists(context.fs, targetPath);
2244
+ let preserveContent = rawContent;
2022
2245
  let current;
2023
2246
  try {
2024
2247
  current = rawContent === null ? {} : format.parse(rawContent);
@@ -2027,6 +2250,7 @@ async function applyConfigTransform(mutation, context, options) {
2027
2250
  await backupInvalidDocument(context, targetPath, rawContent);
2028
2251
  }
2029
2252
  current = {};
2253
+ preserveContent = null;
2030
2254
  }
2031
2255
  const { content: transformed, changed } = mutation.transform(current, options);
2032
2256
  if (!changed) {
@@ -2050,7 +2274,7 @@ async function applyConfigTransform(mutation, context, options) {
2050
2274
  details
2051
2275
  };
2052
2276
  }
2053
- const serialized = format.serialize(transformed);
2277
+ const serialized = serializeConfigUpdate(format, preserveContent, current, transformed);
2054
2278
  if (!context.dryRun) {
2055
2279
  await writeAtomically(context, targetPath, serialized);
2056
2280
  }