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.
- package/dist/cli.js +537 -242
- package/dist/cli.js.map +4 -4
- package/dist/commands/index.js +261 -37
- package/dist/commands/index.js.map +3 -3
- package/dist/commands/install.js +261 -37
- package/dist/commands/install.js.map +3 -3
- package/dist/commands/installer.js.map +1 -1
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/testing/cli-repl.js +533 -238
- package/dist/testing/cli-repl.js.map +4 -4
- package/dist/testing/qa-cli.js +543 -248
- package/dist/testing/qa-cli.js.map +4 -4
- package/node_modules/@poe-code/agent-skill-config/README.md +103 -0
- package/node_modules/@poe-code/agent-skill-config/dist/apply.d.ts +25 -0
- package/node_modules/@poe-code/agent-skill-config/dist/apply.js +159 -0
- package/node_modules/@poe-code/agent-skill-config/dist/bridge-active-skills.d.ts +23 -0
- package/node_modules/@poe-code/agent-skill-config/dist/bridge-active-skills.js +341 -0
- package/node_modules/@poe-code/agent-skill-config/dist/configs.d.ts +16 -0
- package/node_modules/@poe-code/agent-skill-config/dist/configs.js +70 -0
- package/node_modules/@poe-code/agent-skill-config/dist/exports.compile-check.d.ts +1 -0
- package/node_modules/@poe-code/agent-skill-config/dist/exports.compile-check.js +1 -0
- package/node_modules/@poe-code/agent-skill-config/dist/git-exclude.d.ts +8 -0
- package/node_modules/@poe-code/agent-skill-config/dist/git-exclude.js +159 -0
- package/node_modules/@poe-code/agent-skill-config/dist/index.d.ts +11 -0
- package/node_modules/@poe-code/agent-skill-config/dist/index.js +6 -0
- package/node_modules/@poe-code/agent-skill-config/dist/resolve-skill-reference.d.ts +22 -0
- package/node_modules/@poe-code/agent-skill-config/dist/resolve-skill-reference.js +87 -0
- package/node_modules/@poe-code/agent-skill-config/dist/templates/poe-generate.md +47 -0
- package/node_modules/@poe-code/agent-skill-config/dist/templates/terminal-pilot.md +45 -0
- package/node_modules/@poe-code/agent-skill-config/dist/templates.d.ts +3 -0
- package/node_modules/@poe-code/agent-skill-config/dist/templates.js +63 -0
- package/node_modules/@poe-code/agent-skill-config/dist/types.d.ts +16 -0
- package/node_modules/@poe-code/agent-skill-config/dist/types.js +1 -0
- package/node_modules/@poe-code/agent-skill-config/package.json +24 -0
- package/package.json +3 -2
package/dist/commands/index.js
CHANGED
|
@@ -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
|
"<": "<",
|
|
@@ -2910,14 +2911,27 @@ var HTML_ESCAPE = {
|
|
|
2910
2911
|
"=": "="
|
|
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
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
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,
|
|
2920
|
-
return renderTokens(parseTemplate(template), context, template,
|
|
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,
|
|
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
|
|
3043
|
-
if (value == null) {
|
|
3044
|
-
if (
|
|
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
|
|
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,
|
|
3102
|
+
output += renderTokens(token.children, context, template, state);
|
|
3057
3103
|
}
|
|
3058
3104
|
continue;
|
|
3059
3105
|
}
|
|
3060
3106
|
case "section": {
|
|
3061
|
-
const
|
|
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,
|
|
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,
|
|
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,
|
|
3134
|
+
output += renderTokens(token.children, pushContext(context, value), template, state);
|
|
3085
3135
|
continue;
|
|
3086
3136
|
}
|
|
3087
|
-
output += renderTokens(token.children, context, template,
|
|
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
|
|
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 =
|
|
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
|
|
3918
|
+
setConfigEntry(result, key, mergePrunedConfigObject(pruned, value));
|
|
3711
3919
|
} else {
|
|
3712
|
-
result
|
|
3713
|
-
current,
|
|
3714
|
-
value,
|
|
3715
|
-
prefixMap
|
|
3716
|
-
);
|
|
3920
|
+
setConfigEntry(result, key, mergeWithPruneByPrefix(current, value, prefixMap));
|
|
3717
3921
|
}
|
|
3718
3922
|
continue;
|
|
3719
3923
|
}
|
|
3720
|
-
result
|
|
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
|
|
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
|
|
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
|
|
4400
|
+
const serialized = serializeConfigUpdate(format, preserveContent, current, transformed);
|
|
4177
4401
|
if (!context.dryRun) {
|
|
4178
4402
|
await writeAtomically(context, targetPath, serialized);
|
|
4179
4403
|
}
|