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
@@ -2899,6 +2899,7 @@ var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
2899
2899
  var graphemeSegmenter2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
2900
2900
 
2901
2901
  // ../design-system/src/components/template.ts
2902
+ var MAX_PARTIAL_DEPTH = 100;
2902
2903
  var HTML_ESCAPE = {
2903
2904
  "&": "&",
2904
2905
  "<": "&lt;",
@@ -2910,14 +2911,27 @@ var HTML_ESCAPE = {
2910
2911
  "=": "&#x3D;"
2911
2912
  };
2912
2913
  function renderTemplate(template, view, options = {}) {
2913
- const prepared = options.yield === void 0 ? template : template.split("{{yield}}").join(options.yield);
2914
+ const prepared = options.yield === void 0 ? template : resolveTemplatePartials(template, options.partials ?? {}).split("{{yield}}").join(options.yield);
2914
2915
  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);
2916
+ validatePartialReferences(tokens, options.partials ?? {}, []);
2917
+ if (options.validate === true) {
2918
+ const expanded = resolveTemplatePartials(prepared, options.partials ?? {});
2919
+ validateVariables(parseTemplate(expanded), { view });
2920
+ }
2921
+ const state = {
2922
+ escape: options.escape === "none" ? String : escapeHtml,
2923
+ partials: options.partials ?? {},
2924
+ partialStack: [],
2925
+ preserveMissing: options.yield !== void 0 && options.escape === "none",
2926
+ validate: options.validate === true
2927
+ };
2928
+ return renderTokens(tokens, { view }, prepared, state);
2929
+ }
2930
+ function resolveTemplatePartials(template, partials) {
2931
+ return expandTemplatePartials(template, partials, []);
2918
2932
  }
2919
- function renderTemplateInContext(template, context, escape, preserveMissing) {
2920
- return renderTokens(parseTemplate(template), context, template, escape, preserveMissing);
2933
+ function renderTemplateInContext(template, context, state) {
2934
+ return renderTokens(parseTemplate(template), context, template, state);
2921
2935
  }
2922
2936
  function parseTemplate(template) {
2923
2937
  const root = [];
@@ -2940,9 +2954,6 @@ function parseTemplate(template) {
2940
2954
  index = standalone?.nextIndex ?? parsed.end;
2941
2955
  continue;
2942
2956
  }
2943
- if (parsed.kind === "partial") {
2944
- throw new Error(`Partials are not supported: "${parsed.name}"`);
2945
- }
2946
2957
  if (parsed.kind === "delimiter") {
2947
2958
  throw new Error("Custom delimiters are not supported");
2948
2959
  }
@@ -2960,6 +2971,15 @@ function parseTemplate(template) {
2960
2971
  index = standalone?.nextIndex ?? parsed.end;
2961
2972
  continue;
2962
2973
  }
2974
+ if (parsed.kind === "partial") {
2975
+ tokens.push({
2976
+ type: "partial",
2977
+ name: parsed.name,
2978
+ indent: standalone === void 0 ? "" : template.slice(standalone.lineStart, open)
2979
+ });
2980
+ index = standalone?.nextIndex ?? parsed.end;
2981
+ continue;
2982
+ }
2963
2983
  if (parsed.kind === "close") {
2964
2984
  const frame2 = stack.pop();
2965
2985
  if (frame2 === void 0) {
@@ -3030,7 +3050,7 @@ function getStandalone(template, tagStart, tagEnd, kind) {
3030
3050
  }
3031
3051
  return void 0;
3032
3052
  }
3033
- function renderTokens(tokens, context, template, escape, preserveMissing) {
3053
+ function renderTokens(tokens, context, template, state) {
3034
3054
  let output = "";
3035
3055
  for (const token of tokens) {
3036
3056
  switch (token.type) {
@@ -3039,32 +3059,62 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
3039
3059
  continue;
3040
3060
  case "name":
3041
3061
  case "unescaped": {
3042
- const value = lookup2(context, token.name);
3043
- if (value == null) {
3044
- if (preserveMissing) {
3062
+ const result = lookup2(context, token.name);
3063
+ if (!result.hit || result.value == null) {
3064
+ if (state.validate) {
3065
+ throw new Error(`Template variable "${token.name}" not found.`);
3066
+ }
3067
+ if (state.preserveMissing) {
3045
3068
  output += token.raw;
3046
3069
  }
3047
3070
  continue;
3048
3071
  }
3049
- const rendered = String(value);
3050
- output += token.type === "name" ? escape(rendered) : rendered;
3072
+ const rendered = String(result.value);
3073
+ output += token.type === "name" ? state.escape(rendered) : rendered;
3074
+ continue;
3075
+ }
3076
+ case "partial": {
3077
+ if (!Object.hasOwn(state.partials, token.name)) {
3078
+ throw new Error(`Partial "${token.name}" not found.`);
3079
+ }
3080
+ if (state.partialStack.includes(token.name)) {
3081
+ throw new Error(
3082
+ `Circular partial reference detected: ${[...state.partialStack, token.name].join(" -> ")}.`
3083
+ );
3084
+ }
3085
+ if (state.partialStack.length >= MAX_PARTIAL_DEPTH) {
3086
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
3087
+ }
3088
+ const partial = indentPartial(state.partials[token.name], token.indent);
3089
+ output += renderTokens(parseTemplate(partial), context, partial, {
3090
+ ...state,
3091
+ partialStack: [...state.partialStack, token.name]
3092
+ });
3051
3093
  continue;
3052
3094
  }
3053
3095
  case "inverted": {
3054
- const value = lookup2(context, token.name);
3096
+ const result = lookup2(context, token.name);
3097
+ if (!result.hit && state.validate) {
3098
+ throw new Error(`Template variable "${token.name}" not found.`);
3099
+ }
3100
+ const value = result.value;
3055
3101
  if (!value || Array.isArray(value) && value.length === 0) {
3056
- output += renderTokens(token.children, context, template, escape, preserveMissing);
3102
+ output += renderTokens(token.children, context, template, state);
3057
3103
  }
3058
3104
  continue;
3059
3105
  }
3060
3106
  case "section": {
3061
- const value = lookup2(context, token.name);
3107
+ const result = lookup2(context, token.name);
3108
+ if (!result.hit && state.validate) {
3109
+ throw new Error(`Template variable "${token.name}" not found.`);
3110
+ }
3111
+ const value = result.value;
3062
3112
  if (!value) {
3063
3113
  continue;
3064
3114
  }
3065
3115
  if (Array.isArray(value)) {
3066
3116
  for (const item of value) {
3067
- output += renderTokens(token.children, pushContext(context, item), template, escape, preserveMissing);
3117
+ output += renderTokens(token.children, pushContext(context, item), template, state);
3068
3118
  }
3069
3119
  continue;
3070
3120
  }
@@ -3073,7 +3123,7 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
3073
3123
  const rendered = value.call(
3074
3124
  context.view,
3075
3125
  raw,
3076
- (nextTemplate) => renderTemplateInContext(nextTemplate, context, escape, preserveMissing)
3126
+ (nextTemplate) => renderTemplateInContext(nextTemplate, context, state)
3077
3127
  );
3078
3128
  if (rendered != null) {
3079
3129
  output += String(rendered);
@@ -3081,10 +3131,10 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
3081
3131
  continue;
3082
3132
  }
3083
3133
  if (typeof value === "object" || typeof value === "string" || typeof value === "number") {
3084
- output += renderTokens(token.children, pushContext(context, value), template, escape, preserveMissing);
3134
+ output += renderTokens(token.children, pushContext(context, value), template, state);
3085
3135
  continue;
3086
3136
  }
3087
- output += renderTokens(token.children, context, template, escape, preserveMissing);
3137
+ output += renderTokens(token.children, context, template, state);
3088
3138
  }
3089
3139
  }
3090
3140
  }
@@ -3092,17 +3142,115 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
3092
3142
  }
3093
3143
  function lookup2(context, name) {
3094
3144
  if (name === ".") {
3095
- return callLambda(context.view, context.view);
3145
+ return { hit: true, value: callLambda(context.view, context.view) };
3096
3146
  }
3097
3147
  let cursor = context;
3098
3148
  while (cursor !== void 0) {
3099
3149
  const result = name.includes(".") ? lookupDotted(cursor.view, name) : lookupName(cursor.view, name);
3100
3150
  if (result.hit) {
3101
- return callLambda(result.value, cursor.view);
3151
+ return { hit: true, value: callLambda(result.value, cursor.view) };
3102
3152
  }
3103
3153
  cursor = cursor.parent;
3104
3154
  }
3105
- return void 0;
3155
+ return { hit: false, value: void 0 };
3156
+ }
3157
+ function validateVariables(tokens, context) {
3158
+ for (const token of tokens) {
3159
+ if (token.type === "text" || token.type === "partial") {
3160
+ continue;
3161
+ }
3162
+ if (token.type === "name" || token.type === "unescaped") {
3163
+ if (!lookup2(context, token.name).hit) {
3164
+ throw new Error(`Template variable "${token.name}" not found.`);
3165
+ }
3166
+ continue;
3167
+ }
3168
+ if (token.type !== "section" && token.type !== "inverted") {
3169
+ continue;
3170
+ }
3171
+ const result = lookup2(context, token.name);
3172
+ if (!result.hit) {
3173
+ throw new Error(`Template variable "${token.name}" not found.`);
3174
+ }
3175
+ if (Array.isArray(result.value) && result.value.length > 0) {
3176
+ for (const item of result.value) {
3177
+ validateVariables(token.children, pushContext(context, item));
3178
+ }
3179
+ continue;
3180
+ }
3181
+ if (typeof result.value === "object" && result.value !== null || typeof result.value === "string" || typeof result.value === "number") {
3182
+ validateVariables(token.children, pushContext(context, result.value));
3183
+ continue;
3184
+ }
3185
+ validateVariables(token.children, context);
3186
+ }
3187
+ }
3188
+ function validatePartialReferences(tokens, partials, partialStack) {
3189
+ for (const token of tokens) {
3190
+ if (token.type === "section" || token.type === "inverted") {
3191
+ validatePartialReferences(token.children, partials, partialStack);
3192
+ continue;
3193
+ }
3194
+ if (token.type !== "partial") {
3195
+ continue;
3196
+ }
3197
+ if (!Object.hasOwn(partials, token.name)) {
3198
+ throw new Error(`Partial "${token.name}" not found.`);
3199
+ }
3200
+ if (partialStack.includes(token.name)) {
3201
+ throw new Error(
3202
+ `Circular partial reference detected: ${[...partialStack, token.name].join(" -> ")}.`
3203
+ );
3204
+ }
3205
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
3206
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
3207
+ }
3208
+ validatePartialReferences(parseTemplate(partials[token.name]), partials, [
3209
+ ...partialStack,
3210
+ token.name
3211
+ ]);
3212
+ }
3213
+ }
3214
+ function indentPartial(partial, indent) {
3215
+ if (indent === "") {
3216
+ return partial;
3217
+ }
3218
+ return partial.split("\n").map((line) => line === "" ? "" : `${indent}${line}`).join("\n");
3219
+ }
3220
+ function expandTemplatePartials(template, partials, partialStack) {
3221
+ let output = "";
3222
+ let index = 0;
3223
+ while (index < template.length) {
3224
+ const open = template.indexOf("{{", index);
3225
+ if (open === -1) {
3226
+ output += template.slice(index);
3227
+ break;
3228
+ }
3229
+ const parsed = parseTag(template, open);
3230
+ if (parsed.kind !== "partial") {
3231
+ output += template.slice(index, parsed.end);
3232
+ index = parsed.end;
3233
+ continue;
3234
+ }
3235
+ if (!Object.hasOwn(partials, parsed.name)) {
3236
+ throw new Error(`Partial "${parsed.name}" not found.`);
3237
+ }
3238
+ if (partialStack.includes(parsed.name)) {
3239
+ throw new Error(
3240
+ `Circular partial reference detected: ${[...partialStack, parsed.name].join(" -> ")}.`
3241
+ );
3242
+ }
3243
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
3244
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
3245
+ }
3246
+ const standalone = getStandalone(template, open, parsed.end, parsed.kind);
3247
+ const beforePartial = standalone === void 0 ? template.slice(index, open) : template.slice(index, standalone.lineStart);
3248
+ const indent = standalone === void 0 ? "" : template.slice(standalone.lineStart, open);
3249
+ const partial = indentPartial(partials[parsed.name], indent);
3250
+ output += beforePartial + expandTemplatePartials(partial, partials, [...partialStack, parsed.name]);
3251
+ index = standalone?.nextIndex ?? parsed.end;
3252
+ }
3253
+ return output;
3106
3254
  }
3107
3255
  function lookupName(view, name) {
3108
3256
  if (!isPropertyContainer(view) || !hasProperty(view, name)) {
@@ -3231,6 +3379,13 @@ function cloneConfigValue(value) {
3231
3379
  function isConfigObject(value) {
3232
3380
  return typeof value === "object" && value !== null && !Array.isArray(value);
3233
3381
  }
3382
+ function detectIndent(content) {
3383
+ const match = content.match(/^[\t ]+/m);
3384
+ if (match) {
3385
+ return match[0];
3386
+ }
3387
+ return " ";
3388
+ }
3234
3389
  function parse3(content) {
3235
3390
  if (!content || content.trim() === "") {
3236
3391
  return {};
@@ -3270,6 +3425,9 @@ function merge(base, patch) {
3270
3425
  }
3271
3426
  return result;
3272
3427
  }
3428
+ function configValuesEqual(left, right) {
3429
+ return JSON.stringify(left) === JSON.stringify(right);
3430
+ }
3273
3431
  function prune(obj, shape) {
3274
3432
  let changed = false;
3275
3433
  const result = cloneConfigObject(obj);
@@ -3306,9 +3464,56 @@ function prune(obj, shape) {
3306
3464
  }
3307
3465
  return { changed, result };
3308
3466
  }
3467
+ function modifyAtPath(content, path10, value) {
3468
+ const indent = detectIndent(content);
3469
+ const formattingOptions = {
3470
+ tabSize: indent === " " ? 1 : indent.length,
3471
+ insertSpaces: indent !== " ",
3472
+ eol: "\n"
3473
+ };
3474
+ const edits = jsonc.modify(content, path10, value, { formattingOptions });
3475
+ let result = jsonc.applyEdits(content, edits);
3476
+ if (!result.endsWith("\n")) {
3477
+ result += "\n";
3478
+ }
3479
+ return result;
3480
+ }
3481
+ function removeAtPath(content, path10) {
3482
+ return modifyAtPath(content, path10, void 0);
3483
+ }
3484
+ function serializeUpdate(content, current, next) {
3485
+ let result = content || "{}";
3486
+ result = applyObjectUpdate(result, [], current, next);
3487
+ if (!result.endsWith("\n")) {
3488
+ result += "\n";
3489
+ }
3490
+ return result;
3491
+ }
3492
+ function applyObjectUpdate(content, path10, current, next) {
3493
+ let result = content;
3494
+ for (const key of Object.keys(current)) {
3495
+ if (!hasConfigEntry(next, key)) {
3496
+ result = removeAtPath(result, [...path10, key]);
3497
+ }
3498
+ }
3499
+ for (const [key, nextValue] of Object.entries(next)) {
3500
+ const nextPath = [...path10, key];
3501
+ const hasCurrent = hasConfigEntry(current, key);
3502
+ const currentValue = hasCurrent ? current[key] : void 0;
3503
+ if (hasCurrent && isConfigObject(currentValue) && isConfigObject(nextValue)) {
3504
+ result = applyObjectUpdate(result, nextPath, currentValue, nextValue);
3505
+ continue;
3506
+ }
3507
+ if (!hasCurrent || !configValuesEqual(currentValue, nextValue)) {
3508
+ result = modifyAtPath(result, nextPath, nextValue);
3509
+ }
3510
+ }
3511
+ return result;
3512
+ }
3309
3513
  var jsonFormat = {
3310
3514
  parse: parse3,
3311
3515
  serialize,
3516
+ serializeUpdate,
3312
3517
  merge,
3313
3518
  prune
3314
3519
  };
@@ -3690,7 +3895,7 @@ function pruneKeysByPrefix(table, prefix) {
3690
3895
  const result = {};
3691
3896
  for (const [key, value] of Object.entries(table)) {
3692
3897
  if (!key.startsWith(prefix)) {
3693
- result[key] = value;
3898
+ setConfigEntry(result, key, value);
3694
3899
  }
3695
3900
  }
3696
3901
  return result;
@@ -3699,28 +3904,43 @@ function isConfigObject4(value) {
3699
3904
  return typeof value === "object" && value !== null && !Array.isArray(value);
3700
3905
  }
3701
3906
  function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
3702
- const result = { ...base };
3907
+ const result = cloneConfigObject(base);
3703
3908
  const prefixMap = pruneByPrefix ?? {};
3704
3909
  for (const [key, value] of Object.entries(patch)) {
3910
+ if (value === void 0) {
3911
+ continue;
3912
+ }
3705
3913
  const current = result[key];
3706
3914
  const prefix = prefixMap[key];
3707
3915
  if (isConfigObject4(current) && isConfigObject4(value)) {
3708
3916
  if (prefix) {
3709
3917
  const pruned = pruneKeysByPrefix(current, prefix);
3710
- result[key] = { ...pruned, ...value };
3918
+ setConfigEntry(result, key, mergePrunedConfigObject(pruned, value));
3711
3919
  } else {
3712
- result[key] = mergeWithPruneByPrefix(
3713
- current,
3714
- value,
3715
- prefixMap
3716
- );
3920
+ setConfigEntry(result, key, mergeWithPruneByPrefix(current, value, prefixMap));
3717
3921
  }
3718
3922
  continue;
3719
3923
  }
3720
- result[key] = value;
3924
+ setConfigEntry(result, key, value);
3721
3925
  }
3722
3926
  return result;
3723
3927
  }
3928
+ function mergePrunedConfigObject(base, patch) {
3929
+ const result = cloneConfigObject(base);
3930
+ for (const [key, value] of Object.entries(patch)) {
3931
+ if (value === void 0) {
3932
+ continue;
3933
+ }
3934
+ setConfigEntry(result, key, value);
3935
+ }
3936
+ return result;
3937
+ }
3938
+ function serializeConfigUpdate(format, rawContent, current, next) {
3939
+ if (rawContent !== null && format.serializeUpdate) {
3940
+ return format.serializeUpdate(rawContent, current, next);
3941
+ }
3942
+ return format.serialize(next);
3943
+ }
3724
3944
  async function applyMutation(mutation, context, options) {
3725
3945
  switch (mutation.kind) {
3726
3946
  case "ensureDirectory":
@@ -4033,6 +4253,7 @@ async function applyConfigMerge(mutation, context, options) {
4033
4253
  }
4034
4254
  const format = getConfigFormat(formatName);
4035
4255
  const rawContent = await readFileIfExists(context.fs, targetPath);
4256
+ let preserveContent = rawContent;
4036
4257
  let current;
4037
4258
  try {
4038
4259
  current = rawContent === null ? {} : format.parse(rawContent);
@@ -4041,6 +4262,7 @@ async function applyConfigMerge(mutation, context, options) {
4041
4262
  await backupInvalidDocument(context, targetPath, rawContent);
4042
4263
  }
4043
4264
  current = {};
4265
+ preserveContent = null;
4044
4266
  }
4045
4267
  const value = resolveValue(mutation.value, options);
4046
4268
  let merged;
@@ -4049,7 +4271,7 @@ async function applyConfigMerge(mutation, context, options) {
4049
4271
  } else {
4050
4272
  merged = format.merge(current, value);
4051
4273
  }
4052
- const serialized = format.serialize(merged);
4274
+ const serialized = serializeConfigUpdate(format, preserveContent, current, merged);
4053
4275
  const changed = serialized !== rawContent;
4054
4276
  if (changed && !context.dryRun) {
4055
4277
  await writeAtomically(context, targetPath, serialized);
@@ -4117,7 +4339,7 @@ async function applyConfigPrune(mutation, context, options) {
4117
4339
  details
4118
4340
  };
4119
4341
  }
4120
- const serialized = format.serialize(result);
4342
+ const serialized = serializeConfigUpdate(format, rawContent, current, result);
4121
4343
  if (!context.dryRun) {
4122
4344
  await writeAtomically(context, targetPath, serialized);
4123
4345
  }
@@ -4142,6 +4364,7 @@ async function applyConfigTransform(mutation, context, options) {
4142
4364
  }
4143
4365
  const format = getConfigFormat(formatName);
4144
4366
  const rawContent = await readFileIfExists(context.fs, targetPath);
4367
+ let preserveContent = rawContent;
4145
4368
  let current;
4146
4369
  try {
4147
4370
  current = rawContent === null ? {} : format.parse(rawContent);
@@ -4150,6 +4373,7 @@ async function applyConfigTransform(mutation, context, options) {
4150
4373
  await backupInvalidDocument(context, targetPath, rawContent);
4151
4374
  }
4152
4375
  current = {};
4376
+ preserveContent = null;
4153
4377
  }
4154
4378
  const { content: transformed, changed } = mutation.transform(current, options);
4155
4379
  if (!changed) {
@@ -4173,7 +4397,7 @@ async function applyConfigTransform(mutation, context, options) {
4173
4397
  details
4174
4398
  };
4175
4399
  }
4176
- const serialized = format.serialize(transformed);
4400
+ const serialized = serializeConfigUpdate(format, preserveContent, current, transformed);
4177
4401
  if (!context.dryRun) {
4178
4402
  await writeAtomically(context, targetPath, serialized);
4179
4403
  }