pakhale 0.2.0 → 0.4.0

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 CHANGED
@@ -6,6 +6,7 @@ import { homedir } from "node:os";
6
6
  import { parseArgs, styleText } from "node:util";
7
7
  import process$1, { stdin, stdout } from "node:process";
8
8
  import l__default from "node:readline";
9
+ import { createHash } from "node:crypto";
9
10
  import { spawn } from "node:child_process";
10
11
 
11
12
  //#region rolldown:runtime
@@ -104,7 +105,7 @@ var require_picocolors = /* @__PURE__ */ __commonJS({ "node_modules/picocolors/p
104
105
 
105
106
  //#endregion
106
107
  //#region config.ts
107
- var import_picocolors$3 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
108
+ var import_picocolors$4 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
108
109
  const config = {
109
110
  agents: ["claude-code", "opencode"],
110
111
  marketplaces: {
@@ -195,6 +196,14 @@ const config = {
195
196
  repo: "github/gh-stack",
196
197
  skills: ["gh-stack"]
197
198
  } }
199
+ },
200
+ {
201
+ name: "sitedrop",
202
+ deliver: { all: {
203
+ via: "skills",
204
+ repo: "pratikpakhale/sitedrop",
205
+ skills: ["sitedrop"]
206
+ } }
198
207
  }
199
208
  ],
200
209
  authoredSkillsDir: "skills",
@@ -218,14 +227,14 @@ const config = {
218
227
  //#endregion
219
228
  //#region src/util/log.ts
220
229
  const log = {
221
- agent: (s) => console.log(`\n${import_picocolors$3.default.bold(import_picocolors$3.default.magenta(`■ ${s}`))}`),
222
- section: (s) => console.log(`\n${import_picocolors$3.default.bold(import_picocolors$3.default.cyan(`▸ ${s}`))}`),
223
- ok: (s) => console.log(` ${import_picocolors$3.default.green("✓")} ${s}`),
224
- skip: (s) => console.log(` ${import_picocolors$3.default.dim("·")} ${import_picocolors$3.default.dim(s)}`),
225
- warn: (s) => console.log(` ${import_picocolors$3.default.yellow("!")} ${s}`),
226
- fail: (s) => console.log(` ${import_picocolors$3.default.red("✗")} ${s}`),
227
- plan: (s) => console.log(` ${import_picocolors$3.default.yellow("would")} ${s}`),
228
- info: (s) => console.log(` ${import_picocolors$3.default.dim(s)}`)
230
+ agent: (s) => console.log(`\n${import_picocolors$4.default.bold(import_picocolors$4.default.magenta(`■ ${s}`))}`),
231
+ section: (s) => console.log(`\n${import_picocolors$4.default.bold(import_picocolors$4.default.cyan(`▸ ${s}`))}`),
232
+ ok: (s) => console.log(` ${import_picocolors$4.default.green("✓")} ${s}`),
233
+ skip: (s) => console.log(` ${import_picocolors$4.default.dim("·")} ${import_picocolors$4.default.dim(s)}`),
234
+ warn: (s) => console.log(` ${import_picocolors$4.default.yellow("!")} ${s}`),
235
+ fail: (s) => console.log(` ${import_picocolors$4.default.red("✗")} ${s}`),
236
+ plan: (s) => console.log(` ${import_picocolors$4.default.yellow("would")} ${s}`),
237
+ info: (s) => console.log(` ${import_picocolors$4.default.dim(s)}`)
229
238
  };
230
239
 
231
240
  //#endregion
@@ -633,7 +642,7 @@ function isActionKey(n$2, e) {
633
642
  for (const s of n$2) if (s !== void 0 && isActionKey(s, e)) return true;
634
643
  return false;
635
644
  }
636
- function diffLines(i$1, s) {
645
+ function diffLines$1(i$1, s) {
637
646
  if (i$1 === s) return;
638
647
  const e = i$1.split(`
639
648
  `), t = s.split(`
@@ -661,8 +670,8 @@ function wrapTextWithPrefix(e, r$1, o, t = o, s = o, n$2) {
661
670
  hard: true,
662
671
  trim: false
663
672
  }).split(`
664
- `).map((c, i$1, m$1) => {
665
- const d = n$2 ? n$2(c, i$1) : c;
673
+ `).map((c$1, i$1, m$1) => {
674
+ const d = n$2 ? n$2(c$1, i$1) : c$1;
666
675
  return i$1 === 0 ? `${t}${d}` : i$1 === m$1.length - 1 ? `${s}${d}` : `${o}${d}`;
667
676
  }).join(`
668
677
  `);
@@ -813,7 +822,7 @@ var V = class {
813
822
  if (t !== this._prevFrame) {
814
823
  if (this.state === "initial") this.output.write(import_src$1.cursor.hide);
815
824
  else {
816
- const e = diffLines(this._prevFrame, t), i$1 = getRows(this.output);
825
+ const e = diffLines$1(this._prevFrame, t), i$1 = getRows(this.output);
817
826
  if (this.restoreCursor(), e) {
818
827
  const n$2 = Math.max(0, e.numLinesAfter - i$1), s = Math.max(0, e.numLinesBefore - i$1);
819
828
  let r$1 = e.lines.find((o) => o >= n$2);
@@ -891,6 +900,34 @@ var a = class extends V {
891
900
  });
892
901
  }
893
902
  };
903
+ let n$1 = class n$2 extends V {
904
+ options;
905
+ cursor = 0;
906
+ get _selectedValue() {
907
+ return this.options[this.cursor];
908
+ }
909
+ changeValue() {
910
+ const e = this._selectedValue;
911
+ this.value = e === void 0 ? void 0 : e.value;
912
+ }
913
+ constructor(e) {
914
+ super(e, false), this.options = e.options;
915
+ const o = this.options.findIndex(({ value: s }) => s === e.initialValue), t = o === -1 ? 0 : o;
916
+ this.cursor = this.options[t]?.disabled ? findCursor(t, 1, this.options) : t, this.changeValue(), this.on("cursor", (s) => {
917
+ switch (s) {
918
+ case "left":
919
+ case "up":
920
+ this.cursor = findCursor(this.cursor, -1, this.options);
921
+ break;
922
+ case "down":
923
+ case "right":
924
+ this.cursor = findCursor(this.cursor, 1, this.options);
925
+ break;
926
+ }
927
+ this.changeValue();
928
+ });
929
+ }
930
+ };
894
931
 
895
932
  //#endregion
896
933
  //#region node_modules/@clack/prompts/dist/index.mjs
@@ -939,11 +976,11 @@ const limitOptions = ({ cursor: l, options: e, style: w, output: p$1 = process.s
939
976
  const i$1 = getColumns(p$1) - C, m$1 = getRows(p$1), M = styleText("dim", "..."), v = Math.max(m$1 - r$1, 0), a$1 = Math.max(Math.min(b, v), 5);
940
977
  let f = 0;
941
978
  l >= a$1 - 3 && (f = Math.max(Math.min(l - a$1 + 3, e.length - a$1), 0));
942
- let d = a$1 < e.length && f > 0, c = a$1 < e.length && f + a$1 < e.length;
979
+ let d = a$1 < e.length && f > 0, c$1 = a$1 < e.length && f + a$1 < e.length;
943
980
  const W = Math.min(f + a$1, e.length), s = [];
944
981
  let g = 0;
945
- d && g++, c && g++;
946
- const T = f + (d ? 1 : 0), y = W - (c ? 1 : 0);
982
+ d && g++, c$1 && g++;
983
+ const T = f + (d ? 1 : 0), y = W - (c$1 ? 1 : 0);
947
984
  for (let t = T; t < y; t++) {
948
985
  const n$2 = e[t], h$1 = wrapAnsi(n$2 ? w(n$2, t === l) : "", i$1, {
949
986
  hard: true,
@@ -957,12 +994,12 @@ const limitOptions = ({ cursor: l, options: e, style: w, output: p$1 = process.s
957
994
  const h$1 = l - T;
958
995
  let u$4 = v;
959
996
  const L = () => I(s, o, 0, h$1, u$4), E = () => I(s, o, h$1 + 1, s.length, u$4, true);
960
- d ? ({lineCount: o, removals: t} = L(), o > u$4 && (c || (u$4 -= 1), {lineCount: o, removals: n$2} = E())) : (c || (u$4 -= 1), {lineCount: o, removals: n$2} = E(), o > u$4 && (u$4 -= 1, {lineCount: o, removals: t} = L())), t > 0 && (d = true, s.splice(0, t)), n$2 > 0 && (c = true, s.splice(s.length - n$2, n$2));
997
+ d ? ({lineCount: o, removals: t} = L(), o > u$4 && (c$1 || (u$4 -= 1), {lineCount: o, removals: n$2} = E())) : (c$1 || (u$4 -= 1), {lineCount: o, removals: n$2} = E(), o > u$4 && (u$4 -= 1, {lineCount: o, removals: t} = L())), t > 0 && (d = true, s.splice(0, t)), n$2 > 0 && (c$1 = true, s.splice(s.length - n$2, n$2));
961
998
  }
962
999
  const x = [];
963
1000
  d && x.push(M);
964
1001
  for (const t of s) for (const n$2 of t) x.push(n$2);
965
- return c && x.push(M), x;
1002
+ return c$1 && x.push(M), x;
966
1003
  };
967
1004
  const MULTISELECT_INSTRUCTIONS = [
968
1005
  `${styleText("dim", "↑/↓")} to navigate`,
@@ -1074,6 +1111,63 @@ const u$3 = {
1074
1111
  block: unicodeOr("█", "#")
1075
1112
  };
1076
1113
  const SELECT_INSTRUCTIONS = [`${styleText("dim", "↑/↓")} to navigate`, `${styleText("dim", "Enter:")} confirm`];
1114
+ const c = (t, o) => t.includes(`
1115
+ `) ? t.split(`
1116
+ `).map((d) => o(d)).join(`
1117
+ `) : o(t);
1118
+ const select$1 = (t) => {
1119
+ const o = (n$2, m$1) => {
1120
+ if (n$2 === void 0) return "";
1121
+ const s = n$2.label ?? String(n$2.value);
1122
+ switch (m$1) {
1123
+ case "disabled": return `${styleText("gray", S_RADIO_INACTIVE)} ${c(s, (i$1) => styleText("gray", i$1))}${n$2.hint ? ` ${styleText("dim", `(${n$2.hint ?? "disabled"})`)}` : ""}`;
1124
+ case "selected": return `${c(s, (i$1) => styleText("dim", i$1))}`;
1125
+ case "active": return `${styleText("green", S_RADIO_ACTIVE)} ${s}${n$2.hint ? ` ${styleText("dim", `(${n$2.hint})`)}` : ""}`;
1126
+ case "cancelled": return `${c(s, (i$1) => styleText(["strikethrough", "dim"], i$1))}`;
1127
+ default: return `${styleText("dim", S_RADIO_INACTIVE)} ${c(s, (i$1) => styleText("dim", i$1))}`;
1128
+ }
1129
+ }, d = t.showInstructions ?? true;
1130
+ return new n$1({
1131
+ options: t.options,
1132
+ signal: t.signal,
1133
+ input: t.input,
1134
+ output: t.output,
1135
+ initialValue: t.initialValue,
1136
+ render() {
1137
+ const n$2 = t.withGuide ?? settings.withGuide, m$1 = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, i$1 = wrapTextWithPrefix(t.output, t.message, s, m$1), u$4 = `${n$2 ? `${styleText("gray", S_BAR)}
1138
+ ` : ""}${i$1}
1139
+ `;
1140
+ switch (this.state) {
1141
+ case "submit": {
1142
+ const r$1 = n$2 ? `${styleText("gray", S_BAR)} ` : "";
1143
+ return `${u$4}${wrapTextWithPrefix(t.output, o(this.options[this.cursor], "selected"), r$1)}`;
1144
+ }
1145
+ case "cancel": {
1146
+ const r$1 = n$2 ? `${styleText("gray", S_BAR)} ` : "";
1147
+ return `${u$4}${wrapTextWithPrefix(t.output, o(this.options[this.cursor], "cancelled"), r$1)}${n$2 ? `
1148
+ ${styleText("gray", S_BAR)}` : ""}`;
1149
+ }
1150
+ default: {
1151
+ const r$1 = n$2 ? `${styleText("cyan", S_BAR)} ` : "", a$1 = u$4.split(`
1152
+ `).length, p$1 = d ? formatInstructionFooter(SELECT_INSTRUCTIONS, n$2) : n$2 ? [styleText("cyan", S_BAR_END)] : [], b = p$1.join(`
1153
+ `), f = p$1.length + 1;
1154
+ return `${u$4}${r$1}${limitOptions({
1155
+ output: t.output,
1156
+ cursor: this.cursor,
1157
+ options: this.options,
1158
+ maxItems: t.maxItems,
1159
+ columnPadding: r$1.length,
1160
+ rowPadding: a$1 + f,
1161
+ style: (g, x) => o(g, g.disabled ? "disabled" : x ? "active" : "inactive")
1162
+ }).join(`
1163
+ ${r$1}`)}
1164
+ ${b}
1165
+ `;
1166
+ }
1167
+ }
1168
+ }
1169
+ }).prompt();
1170
+ };
1077
1171
  const i = `${styleText("gray", S_BAR)} `;
1078
1172
 
1079
1173
  //#endregion
@@ -1083,12 +1177,12 @@ async function multiSelect(message, choices) {
1083
1177
  if (!choices.length) return [];
1084
1178
  const picked = await multiselect({
1085
1179
  message,
1086
- options: choices.map((c) => ({
1087
- value: c.value,
1088
- label: c.label,
1089
- hint: c.hint
1180
+ options: choices.map((c$1) => ({
1181
+ value: c$1.value,
1182
+ label: c$1.label,
1183
+ hint: c$1.hint
1090
1184
  })),
1091
- initialValues: choices.map((c) => c.value),
1185
+ initialValues: choices.map((c$1) => c$1.value),
1092
1186
  required: false
1093
1187
  });
1094
1188
  if (isCancel(picked)) {
@@ -1097,6 +1191,22 @@ async function multiSelect(message, choices) {
1097
1191
  }
1098
1192
  return picked;
1099
1193
  }
1194
+ /** One answer from a list. Cancelling returns null — the caller decides what safe means. */
1195
+ async function select(message, choices) {
1196
+ const picked = await select$1({
1197
+ message,
1198
+ options: choices.map((c$1) => ({
1199
+ value: c$1.value,
1200
+ label: c$1.label,
1201
+ hint: c$1.hint
1202
+ }))
1203
+ });
1204
+ if (isCancel(picked)) {
1205
+ cancel("cancelled");
1206
+ return null;
1207
+ }
1208
+ return picked;
1209
+ }
1100
1210
  function isInteractive() {
1101
1211
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
1102
1212
  }
@@ -1210,7 +1320,7 @@ function logCoverage(config$1) {
1210
1320
  const nameWidth = Math.max(...rows.map((r$1) => r$1.name.length));
1211
1321
  const cellWidths = config$1.agents.map((_, i$1) => Math.max(...rows.map((r$1) => r$1.cells[i$1].length)));
1212
1322
  for (const { name: name$1, cells } of rows) {
1213
- const line = cells.map((c, i$1) => c.padEnd(cellWidths[i$1])).join(" ");
1323
+ const line = cells.map((c$1, i$1) => c$1.padEnd(cellWidths[i$1])).join(" ");
1214
1324
  log.info(`${name$1.padEnd(nameWidth)} ${line.trimEnd()}`);
1215
1325
  }
1216
1326
  }
@@ -1268,8 +1378,10 @@ function claudePluginInstalls(config$1, home) {
1268
1378
  return out;
1269
1379
  }
1270
1380
  /**
1271
- * skills.sh installs into the shared `~/.agents` store. `linkDir` is where the agent expects
1272
- * to find each skill afterwards — omit it for agents that read the store directly.
1381
+ * `linkDir` is where *this* agent expects to find each skill afterwards — the shared store
1382
+ * itself for agents that read it directly. Always pass it: `.skill-lock.json` is one
1383
+ * agent-agnostic file, so a name-only probe is satisfied by whichever agent installed first
1384
+ * and every later agent's install is skipped forever, silently reporting `unchanged`.
1273
1385
  */
1274
1386
  function skillInstalls(config$1, agent, home, linkDir) {
1275
1387
  return skillDeliveries(config$1, agent).map((delivery) => {
@@ -1300,6 +1412,36 @@ function skillInstalls(config$1, agent, home, linkDir) {
1300
1412
  });
1301
1413
  }
1302
1414
 
1415
+ //#endregion
1416
+ //#region src/commands/setup/agents/state.ts
1417
+ /** Machine-local bookkeeping, never a source of truth — do not sync or vendor it. */
1418
+ function stateFileFor(home) {
1419
+ return join(home, ".agents", ".setup-state.json");
1420
+ }
1421
+ function hashContent(content) {
1422
+ return createHash("sha256").update(content).digest("hex");
1423
+ }
1424
+ function hashValue(value) {
1425
+ return hashContent(stable(value));
1426
+ }
1427
+ async function loadState(file) {
1428
+ try {
1429
+ const parsed = JSON.parse(await readFile(file, "utf8"));
1430
+ return {
1431
+ files: parsed.files ?? {},
1432
+ keys: parsed.keys ?? {}
1433
+ };
1434
+ } catch {
1435
+ return {
1436
+ files: {},
1437
+ keys: {}
1438
+ };
1439
+ }
1440
+ }
1441
+ async function saveState(file, state) {
1442
+ await writeJson(file, state);
1443
+ }
1444
+
1303
1445
  //#endregion
1304
1446
  //#region src/commands/setup/agents/store.ts
1305
1447
  const AGENTS_STORE = ".agents/skills";
@@ -1312,6 +1454,7 @@ async function buildStorePlan(ctx) {
1312
1454
  const archiveDir = join(ctx.home, ".agents/.setup-backups", stamp);
1313
1455
  return {
1314
1456
  archiveDir,
1457
+ stateFile: stateFileFor(ctx.home),
1315
1458
  groups: [{
1316
1459
  section: "Skills store (~/.agents/skills)",
1317
1460
  artifacts: (await authoredSkillNames(ctx)).map((name$1) => ({
@@ -1398,6 +1541,7 @@ async function buildPlan$1(ctx) {
1398
1541
  });
1399
1542
  return {
1400
1543
  archiveDir,
1544
+ stateFile: stateFileFor(ctx.home),
1401
1545
  groups
1402
1546
  };
1403
1547
  }
@@ -1423,8 +1567,8 @@ function claudeMcp(server) {
1423
1567
  ...server.headers && { headers: server.headers }
1424
1568
  };
1425
1569
  }
1426
- function mapValues(record, fn) {
1427
- return Object.fromEntries(Object.entries(record).map(([k, v]) => [k, fn(v)]));
1570
+ function mapValues(record$1, fn) {
1571
+ return Object.fromEntries(Object.entries(record$1).map(([k, v]) => [k, fn(v)]));
1428
1572
  }
1429
1573
  async function authoredSkillLinks(ctx, root, archiveDir) {
1430
1574
  return (await authoredSkillNames(ctx)).map((name$1) => ({
@@ -1473,7 +1617,7 @@ async function buildPlan(ctx) {
1473
1617
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1474
1618
  const groups = [{
1475
1619
  section: "Skills (remote)",
1476
- artifacts: skillInstalls(ctx.config, "opencode", ctx.home)
1620
+ artifacts: skillInstalls(ctx.config, "opencode", ctx.home, join(ctx.home, AGENTS_STORE))
1477
1621
  }, {
1478
1622
  section: "Instructions",
1479
1623
  artifacts: [{
@@ -1494,6 +1638,7 @@ async function buildPlan(ctx) {
1494
1638
  });
1495
1639
  return {
1496
1640
  archiveDir: join(root, ".setup-backups", stamp),
1641
+ stateFile: stateFileFor(ctx.home),
1497
1642
  groups
1498
1643
  };
1499
1644
  }
@@ -1561,14 +1706,631 @@ async function which(bin) {
1561
1706
  ])).ok;
1562
1707
  }
1563
1708
 
1709
+ //#endregion
1710
+ //#region node_modules/diff/libesm/diff/base.js
1711
+ var Diff = class {
1712
+ diff(oldStr, newStr, options = {}) {
1713
+ let callback;
1714
+ if (typeof options === "function") {
1715
+ callback = options;
1716
+ options = {};
1717
+ } else if ("callback" in options) callback = options.callback;
1718
+ const oldString = this.castInput(oldStr, options);
1719
+ const newString = this.castInput(newStr, options);
1720
+ const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
1721
+ const newTokens = this.removeEmpty(this.tokenize(newString, options));
1722
+ return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
1723
+ }
1724
+ diffWithOptionsObj(oldTokens, newTokens, options, callback) {
1725
+ var _a;
1726
+ const done = (value) => {
1727
+ value = this.postProcess(value, options);
1728
+ if (callback) {
1729
+ setTimeout(function() {
1730
+ callback(value);
1731
+ }, 0);
1732
+ return;
1733
+ } else return value;
1734
+ };
1735
+ const newLen = newTokens.length, oldLen = oldTokens.length;
1736
+ let editLength = 1;
1737
+ let maxEditLength = newLen + oldLen;
1738
+ if (options.maxEditLength != null) maxEditLength = Math.min(maxEditLength, options.maxEditLength);
1739
+ const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
1740
+ const abortAfterTimestamp = Date.now() + maxExecutionTime;
1741
+ const bestPath = [{
1742
+ oldPos: -1,
1743
+ lastComponent: void 0
1744
+ }];
1745
+ let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
1746
+ if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
1747
+ let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
1748
+ const execEditLength = () => {
1749
+ for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
1750
+ let basePath;
1751
+ const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
1752
+ if (removePath) bestPath[diagonalPath - 1] = void 0;
1753
+ let canAdd = false;
1754
+ if (addPath) {
1755
+ const addPathNewPos = addPath.oldPos - diagonalPath;
1756
+ canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
1757
+ }
1758
+ const canRemove = removePath && removePath.oldPos + 1 < oldLen;
1759
+ if (!canAdd && !canRemove) {
1760
+ bestPath[diagonalPath] = void 0;
1761
+ continue;
1762
+ }
1763
+ if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) basePath = this.addToPath(addPath, true, false, 0, options);
1764
+ else basePath = this.addToPath(removePath, false, true, 1, options);
1765
+ newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
1766
+ if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
1767
+ else {
1768
+ bestPath[diagonalPath] = basePath;
1769
+ if (basePath.oldPos + 1 >= oldLen) maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
1770
+ if (newPos + 1 >= newLen) minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
1771
+ }
1772
+ }
1773
+ editLength++;
1774
+ };
1775
+ if (callback) (function exec$1() {
1776
+ setTimeout(function() {
1777
+ if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) return callback(void 0);
1778
+ if (!execEditLength()) exec$1();
1779
+ }, 0);
1780
+ })();
1781
+ else while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
1782
+ const ret = execEditLength();
1783
+ if (ret) return ret;
1784
+ }
1785
+ }
1786
+ addToPath(path, added, removed, oldPosInc, options) {
1787
+ const last = path.lastComponent;
1788
+ if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) return {
1789
+ oldPos: path.oldPos + oldPosInc,
1790
+ lastComponent: {
1791
+ count: last.count + 1,
1792
+ added,
1793
+ removed,
1794
+ previousComponent: last.previousComponent
1795
+ }
1796
+ };
1797
+ else return {
1798
+ oldPos: path.oldPos + oldPosInc,
1799
+ lastComponent: {
1800
+ count: 1,
1801
+ added,
1802
+ removed,
1803
+ previousComponent: last
1804
+ }
1805
+ };
1806
+ }
1807
+ extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
1808
+ const newLen = newTokens.length, oldLen = oldTokens.length;
1809
+ let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
1810
+ while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
1811
+ newPos++;
1812
+ oldPos++;
1813
+ commonCount++;
1814
+ if (options.oneChangePerToken) basePath.lastComponent = {
1815
+ count: 1,
1816
+ previousComponent: basePath.lastComponent,
1817
+ added: false,
1818
+ removed: false
1819
+ };
1820
+ }
1821
+ if (commonCount && !options.oneChangePerToken) basePath.lastComponent = {
1822
+ count: commonCount,
1823
+ previousComponent: basePath.lastComponent,
1824
+ added: false,
1825
+ removed: false
1826
+ };
1827
+ basePath.oldPos = oldPos;
1828
+ return newPos;
1829
+ }
1830
+ equals(left, right, options) {
1831
+ if (options.comparator) return options.comparator(left, right);
1832
+ else return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
1833
+ }
1834
+ removeEmpty(array) {
1835
+ const ret = [];
1836
+ for (let i$1 = 0; i$1 < array.length; i$1++) if (array[i$1]) ret.push(array[i$1]);
1837
+ return ret;
1838
+ }
1839
+ castInput(value, options) {
1840
+ return value;
1841
+ }
1842
+ tokenize(value, options) {
1843
+ return Array.from(value);
1844
+ }
1845
+ join(chars) {
1846
+ return chars.join("");
1847
+ }
1848
+ postProcess(changeObjects, options) {
1849
+ return changeObjects;
1850
+ }
1851
+ get useLongestToken() {
1852
+ return false;
1853
+ }
1854
+ buildValues(lastComponent, newTokens, oldTokens) {
1855
+ const components = [];
1856
+ let nextComponent;
1857
+ while (lastComponent) {
1858
+ components.push(lastComponent);
1859
+ nextComponent = lastComponent.previousComponent;
1860
+ delete lastComponent.previousComponent;
1861
+ lastComponent = nextComponent;
1862
+ }
1863
+ components.reverse();
1864
+ const componentLen = components.length;
1865
+ let componentPos = 0, newPos = 0, oldPos = 0;
1866
+ for (; componentPos < componentLen; componentPos++) {
1867
+ const component = components[componentPos];
1868
+ if (!component.removed) {
1869
+ if (!component.added && this.useLongestToken) {
1870
+ let value = newTokens.slice(newPos, newPos + component.count);
1871
+ value = value.map(function(value$1, i$1) {
1872
+ const oldValue = oldTokens[oldPos + i$1];
1873
+ return oldValue.length > value$1.length ? oldValue : value$1;
1874
+ });
1875
+ component.value = this.join(value);
1876
+ } else component.value = this.join(newTokens.slice(newPos, newPos + component.count));
1877
+ newPos += component.count;
1878
+ if (!component.added) oldPos += component.count;
1879
+ } else {
1880
+ component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
1881
+ oldPos += component.count;
1882
+ }
1883
+ }
1884
+ return components;
1885
+ }
1886
+ };
1887
+
1888
+ //#endregion
1889
+ //#region node_modules/diff/libesm/diff/character.js
1890
+ var CharacterDiff = class extends Diff {};
1891
+ const characterDiff = new CharacterDiff();
1892
+
1893
+ //#endregion
1894
+ //#region node_modules/diff/libesm/util/string.js
1895
+ function longestCommonPrefix(str1, str2) {
1896
+ let i$1;
1897
+ for (i$1 = 0; i$1 < str1.length && i$1 < str2.length; i$1++) if (str1[i$1] != str2[i$1]) return str1.slice(0, i$1);
1898
+ return str1.slice(0, i$1);
1899
+ }
1900
+ function longestCommonSuffix(str1, str2) {
1901
+ let i$1;
1902
+ if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) return "";
1903
+ for (i$1 = 0; i$1 < str1.length && i$1 < str2.length; i$1++) if (str1[str1.length - (i$1 + 1)] != str2[str2.length - (i$1 + 1)]) return str1.slice(-i$1);
1904
+ return str1.slice(-i$1);
1905
+ }
1906
+ function replacePrefix(string, oldPrefix, newPrefix) {
1907
+ if (string.slice(0, oldPrefix.length) != oldPrefix) throw Error(`string ${JSON.stringify(string)} doesn't start with prefix ${JSON.stringify(oldPrefix)}; this is a bug`);
1908
+ return newPrefix + string.slice(oldPrefix.length);
1909
+ }
1910
+ function replaceSuffix(string, oldSuffix, newSuffix) {
1911
+ if (!oldSuffix) return string + newSuffix;
1912
+ if (string.slice(-oldSuffix.length) != oldSuffix) throw Error(`string ${JSON.stringify(string)} doesn't end with suffix ${JSON.stringify(oldSuffix)}; this is a bug`);
1913
+ return string.slice(0, -oldSuffix.length) + newSuffix;
1914
+ }
1915
+ function removePrefix(string, oldPrefix) {
1916
+ return replacePrefix(string, oldPrefix, "");
1917
+ }
1918
+ function removeSuffix(string, oldSuffix) {
1919
+ return replaceSuffix(string, oldSuffix, "");
1920
+ }
1921
+ function maximumOverlap(string1, string2) {
1922
+ return string2.slice(0, overlapCount(string1, string2));
1923
+ }
1924
+ function overlapCount(a$1, b) {
1925
+ let startA = 0;
1926
+ if (a$1.length > b.length) startA = a$1.length - b.length;
1927
+ let endB = b.length;
1928
+ if (a$1.length < b.length) endB = a$1.length;
1929
+ const map = Array(endB);
1930
+ let k = 0;
1931
+ map[0] = 0;
1932
+ for (let j = 1; j < endB; j++) {
1933
+ if (b[j] == b[k]) map[j] = map[k];
1934
+ else map[j] = k;
1935
+ while (k > 0 && b[j] != b[k]) k = map[k];
1936
+ if (b[j] == b[k]) k++;
1937
+ }
1938
+ k = 0;
1939
+ for (let i$1 = startA; i$1 < a$1.length; i$1++) {
1940
+ while (k > 0 && a$1[i$1] != b[k]) k = map[k];
1941
+ if (a$1[i$1] == b[k]) k++;
1942
+ }
1943
+ return k;
1944
+ }
1945
+ /**
1946
+ * Split a string into segments using a word segmenter, merging consecutive
1947
+ * segments if they are both whitespace segments. Whitespace segments can
1948
+ * appear adjacent to one another for two reasons:
1949
+ * - newlines always get their own segment
1950
+ * - where a diacritic is attached to a whitespace character in the text, the
1951
+ * segment ends after the diacritic, so e.g. " \u0300 " becomes two segments.
1952
+ * This function therefore runs the segmenter's .segment() method and then
1953
+ * merges consecutive segments of whitespace into a single part.
1954
+ */
1955
+ function segment(string, segmenter) {
1956
+ const parts = [];
1957
+ for (const segmentObj of Array.from(segmenter.segment(string))) {
1958
+ const segment$1 = segmentObj.segment;
1959
+ if (parts.length && /\s/.test(parts[parts.length - 1]) && /\s/.test(segment$1)) parts[parts.length - 1] += segment$1;
1960
+ else parts.push(segment$1);
1961
+ }
1962
+ return parts;
1963
+ }
1964
+ function trailingWs(string, segmenter) {
1965
+ if (segmenter) return leadingAndTrailingWs(string, segmenter)[1];
1966
+ let i$1;
1967
+ for (i$1 = string.length - 1; i$1 >= 0; i$1--) if (!string[i$1].match(/\s/)) break;
1968
+ return string.substring(i$1 + 1);
1969
+ }
1970
+ function leadingWs(string, segmenter) {
1971
+ if (segmenter) return leadingAndTrailingWs(string, segmenter)[0];
1972
+ const match = string.match(/^\s*/);
1973
+ return match ? match[0] : "";
1974
+ }
1975
+ function leadingAndTrailingWs(string, segmenter) {
1976
+ if (!segmenter) return [leadingWs(string), trailingWs(string)];
1977
+ if (segmenter.resolvedOptions().granularity != "word") throw new Error("The segmenter passed must have a granularity of \"word\"");
1978
+ const segments = segment(string, segmenter);
1979
+ const firstSeg = segments[0];
1980
+ const lastSeg = segments[segments.length - 1];
1981
+ return [/\s/.test(firstSeg) ? firstSeg : "", /\s/.test(lastSeg) ? lastSeg : ""];
1982
+ }
1983
+
1984
+ //#endregion
1985
+ //#region node_modules/diff/libesm/diff/word.js
1986
+ const extendedWordChars = "a-zA-Z0-9_\\u{AD}\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}";
1987
+ const tokenizeIncludingWhitespace = new RegExp(`[${extendedWordChars}]+|\\s+|[^${extendedWordChars}]`, "ug");
1988
+ var WordDiff = class extends Diff {
1989
+ equals(left, right, options) {
1990
+ if (options.ignoreCase) {
1991
+ left = left.toLowerCase();
1992
+ right = right.toLowerCase();
1993
+ }
1994
+ return left.trim() === right.trim();
1995
+ }
1996
+ tokenize(value, options = {}) {
1997
+ let parts;
1998
+ if (options.intlSegmenter) {
1999
+ const segmenter = options.intlSegmenter;
2000
+ if (segmenter.resolvedOptions().granularity != "word") throw new Error("The segmenter passed must have a granularity of \"word\"");
2001
+ parts = segment(value, segmenter);
2002
+ } else parts = value.match(tokenizeIncludingWhitespace) || [];
2003
+ const tokens = [];
2004
+ let prevPart = null;
2005
+ parts.forEach((part) => {
2006
+ if (/\s/.test(part)) if (prevPart == null) tokens.push(part);
2007
+ else tokens.push(tokens.pop() + part);
2008
+ else if (prevPart != null && /\s/.test(prevPart)) if (tokens[tokens.length - 1] == prevPart) tokens.push(tokens.pop() + part);
2009
+ else tokens.push(prevPart + part);
2010
+ else tokens.push(part);
2011
+ prevPart = part;
2012
+ });
2013
+ return tokens;
2014
+ }
2015
+ join(tokens) {
2016
+ return tokens.map((token, i$1) => {
2017
+ if (i$1 == 0) return token;
2018
+ else return token.replace(/^\s+/, "");
2019
+ }).join("");
2020
+ }
2021
+ postProcess(changes, options) {
2022
+ if (!changes || options.oneChangePerToken) return changes;
2023
+ let lastKeep = null;
2024
+ let insertion = null;
2025
+ let deletion = null;
2026
+ changes.forEach((change) => {
2027
+ if (change.added) insertion = change;
2028
+ else if (change.removed) deletion = change;
2029
+ else {
2030
+ if (insertion || deletion) dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, change, options.intlSegmenter);
2031
+ lastKeep = change;
2032
+ insertion = null;
2033
+ deletion = null;
2034
+ }
2035
+ });
2036
+ if (insertion || deletion) dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, null, options.intlSegmenter);
2037
+ return changes;
2038
+ }
2039
+ };
2040
+ const wordDiff = new WordDiff();
2041
+ function dedupeWhitespaceInChangeObjects(startKeep, deletion, insertion, endKeep, segmenter) {
2042
+ if (deletion && insertion) {
2043
+ const [oldWsPrefix, oldWsSuffix] = leadingAndTrailingWs(deletion.value, segmenter);
2044
+ const [newWsPrefix, newWsSuffix] = leadingAndTrailingWs(insertion.value, segmenter);
2045
+ if (startKeep) {
2046
+ const commonWsPrefix = longestCommonPrefix(oldWsPrefix, newWsPrefix);
2047
+ startKeep.value = replaceSuffix(startKeep.value, newWsPrefix, commonWsPrefix);
2048
+ deletion.value = removePrefix(deletion.value, commonWsPrefix);
2049
+ insertion.value = removePrefix(insertion.value, commonWsPrefix);
2050
+ }
2051
+ if (endKeep) {
2052
+ const commonWsSuffix = longestCommonSuffix(oldWsSuffix, newWsSuffix);
2053
+ endKeep.value = replacePrefix(endKeep.value, newWsSuffix, commonWsSuffix);
2054
+ deletion.value = removeSuffix(deletion.value, commonWsSuffix);
2055
+ insertion.value = removeSuffix(insertion.value, commonWsSuffix);
2056
+ }
2057
+ } else if (insertion) {
2058
+ if (startKeep) {
2059
+ const ws = leadingWs(insertion.value, segmenter);
2060
+ insertion.value = insertion.value.substring(ws.length);
2061
+ }
2062
+ if (endKeep) {
2063
+ const ws = leadingWs(endKeep.value, segmenter);
2064
+ endKeep.value = endKeep.value.substring(ws.length);
2065
+ }
2066
+ } else if (startKeep && endKeep) {
2067
+ const newWsFull = leadingWs(endKeep.value, segmenter), [delWsStart, delWsEnd] = leadingAndTrailingWs(deletion.value, segmenter);
2068
+ const newWsStart = longestCommonPrefix(newWsFull, delWsStart);
2069
+ deletion.value = removePrefix(deletion.value, newWsStart);
2070
+ const newWsEnd = longestCommonSuffix(removePrefix(newWsFull, newWsStart), delWsEnd);
2071
+ deletion.value = removeSuffix(deletion.value, newWsEnd);
2072
+ endKeep.value = replacePrefix(endKeep.value, newWsFull, newWsEnd);
2073
+ startKeep.value = replaceSuffix(startKeep.value, newWsFull, newWsFull.slice(0, newWsFull.length - newWsEnd.length));
2074
+ } else if (endKeep) {
2075
+ const endKeepWsPrefix = leadingWs(endKeep.value, segmenter);
2076
+ const overlap = maximumOverlap(trailingWs(deletion.value, segmenter), endKeepWsPrefix);
2077
+ deletion.value = removeSuffix(deletion.value, overlap);
2078
+ } else if (startKeep) {
2079
+ const overlap = maximumOverlap(trailingWs(startKeep.value, segmenter), leadingWs(deletion.value, segmenter));
2080
+ deletion.value = removePrefix(deletion.value, overlap);
2081
+ }
2082
+ }
2083
+ var WordsWithSpaceDiff = class extends Diff {
2084
+ tokenize(value) {
2085
+ const regex = new RegExp(`(\\r?\\n)|[${extendedWordChars}]+|[^\\S\\n\\r]+|[^${extendedWordChars}]`, "ug");
2086
+ return value.match(regex) || [];
2087
+ }
2088
+ };
2089
+ const wordsWithSpaceDiff = new WordsWithSpaceDiff();
2090
+
2091
+ //#endregion
2092
+ //#region node_modules/diff/libesm/diff/line.js
2093
+ var LineDiff = class extends Diff {
2094
+ constructor() {
2095
+ super(...arguments);
2096
+ this.tokenize = tokenize;
2097
+ }
2098
+ equals(left, right, options) {
2099
+ if (options.ignoreWhitespace) {
2100
+ if (!options.newlineIsToken || !left.includes("\n")) left = left.trim();
2101
+ if (!options.newlineIsToken || !right.includes("\n")) right = right.trim();
2102
+ } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
2103
+ if (left.endsWith("\n")) left = left.slice(0, -1);
2104
+ if (right.endsWith("\n")) right = right.slice(0, -1);
2105
+ }
2106
+ return super.equals(left, right, options);
2107
+ }
2108
+ };
2109
+ const lineDiff = new LineDiff();
2110
+ function diffLines(oldStr, newStr, options) {
2111
+ return lineDiff.diff(oldStr, newStr, options);
2112
+ }
2113
+ function tokenize(value, options) {
2114
+ if (options.stripTrailingCr) value = value.replace(/\r\n/g, "\n");
2115
+ const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
2116
+ if (!linesAndNewlines[linesAndNewlines.length - 1]) linesAndNewlines.pop();
2117
+ for (let i$1 = 0; i$1 < linesAndNewlines.length; i$1++) {
2118
+ const line = linesAndNewlines[i$1];
2119
+ if (i$1 % 2 && !options.newlineIsToken) retLines[retLines.length - 1] += line;
2120
+ else retLines.push(line);
2121
+ }
2122
+ return retLines;
2123
+ }
2124
+
2125
+ //#endregion
2126
+ //#region node_modules/diff/libesm/diff/sentence.js
2127
+ function isSentenceEndPunct(char) {
2128
+ return char == "." || char == "!" || char == "?";
2129
+ }
2130
+ var SentenceDiff = class extends Diff {
2131
+ tokenize(value) {
2132
+ var _a;
2133
+ const result = [];
2134
+ let tokenStartI = 0;
2135
+ for (let i$1 = 0; i$1 < value.length; i$1++) {
2136
+ if (i$1 == value.length - 1) {
2137
+ result.push(value.slice(tokenStartI));
2138
+ break;
2139
+ }
2140
+ if (isSentenceEndPunct(value[i$1]) && value[i$1 + 1].match(/\s/)) {
2141
+ result.push(value.slice(tokenStartI, i$1 + 1));
2142
+ i$1 = tokenStartI = i$1 + 1;
2143
+ while ((_a = value[i$1 + 1]) === null || _a === void 0 ? void 0 : _a.match(/\s/)) i$1++;
2144
+ result.push(value.slice(tokenStartI, i$1 + 1));
2145
+ tokenStartI = i$1 + 1;
2146
+ }
2147
+ }
2148
+ return result;
2149
+ }
2150
+ };
2151
+ const sentenceDiff = new SentenceDiff();
2152
+
2153
+ //#endregion
2154
+ //#region node_modules/diff/libesm/diff/css.js
2155
+ var CssDiff = class extends Diff {
2156
+ tokenize(value) {
2157
+ return value.split(/([{}:;,]|\s+)/);
2158
+ }
2159
+ };
2160
+ const cssDiff = new CssDiff();
2161
+
2162
+ //#endregion
2163
+ //#region node_modules/diff/libesm/diff/json.js
2164
+ var JsonDiff = class extends Diff {
2165
+ constructor() {
2166
+ super(...arguments);
2167
+ this.tokenize = tokenize;
2168
+ }
2169
+ get useLongestToken() {
2170
+ return true;
2171
+ }
2172
+ castInput(value, options) {
2173
+ const { undefinedReplacement, stringifyReplacer = (k, v) => typeof v === "undefined" ? undefinedReplacement : v } = options;
2174
+ return typeof value === "string" ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), null, " ");
2175
+ }
2176
+ equals(left, right, options) {
2177
+ return super.equals(left.replace(/,([\r\n])/g, "$1"), right.replace(/,([\r\n])/g, "$1"), options);
2178
+ }
2179
+ };
2180
+ const jsonDiff = new JsonDiff();
2181
+ function canonicalize(obj, stack, replacementStack, replacer, key) {
2182
+ stack = stack || [];
2183
+ replacementStack = replacementStack || [];
2184
+ if (replacer) obj = replacer(key === void 0 ? "" : key, obj);
2185
+ let i$1;
2186
+ for (i$1 = 0; i$1 < stack.length; i$1 += 1) if (stack[i$1] === obj) return replacementStack[i$1];
2187
+ let canonicalizedObj;
2188
+ if ("[object Array]" === Object.prototype.toString.call(obj)) {
2189
+ stack.push(obj);
2190
+ canonicalizedObj = new Array(obj.length);
2191
+ replacementStack.push(canonicalizedObj);
2192
+ for (i$1 = 0; i$1 < obj.length; i$1 += 1) canonicalizedObj[i$1] = canonicalize(obj[i$1], stack, replacementStack, replacer, String(i$1));
2193
+ stack.pop();
2194
+ replacementStack.pop();
2195
+ return canonicalizedObj;
2196
+ }
2197
+ if (obj && obj.toJSON) obj = obj.toJSON();
2198
+ if (typeof obj === "object" && obj !== null) {
2199
+ stack.push(obj);
2200
+ canonicalizedObj = {};
2201
+ replacementStack.push(canonicalizedObj);
2202
+ const sortedKeys = [];
2203
+ let key$1;
2204
+ for (key$1 in obj)
2205
+ /* istanbul ignore else */
2206
+ if (Object.prototype.hasOwnProperty.call(obj, key$1)) sortedKeys.push(key$1);
2207
+ sortedKeys.sort();
2208
+ for (i$1 = 0; i$1 < sortedKeys.length; i$1 += 1) {
2209
+ key$1 = sortedKeys[i$1];
2210
+ canonicalizedObj[key$1] = canonicalize(obj[key$1], stack, replacementStack, replacer, key$1);
2211
+ }
2212
+ stack.pop();
2213
+ replacementStack.pop();
2214
+ } else canonicalizedObj = obj;
2215
+ return canonicalizedObj;
2216
+ }
2217
+
2218
+ //#endregion
2219
+ //#region node_modules/diff/libesm/diff/array.js
2220
+ var ArrayDiff = class extends Diff {
2221
+ tokenize(value) {
2222
+ return value.slice();
2223
+ }
2224
+ join(value) {
2225
+ return value;
2226
+ }
2227
+ removeEmpty(value) {
2228
+ return value;
2229
+ }
2230
+ };
2231
+ const arrayDiff = new ArrayDiff();
2232
+
2233
+ //#endregion
2234
+ //#region node_modules/diff/libesm/patch/create.js
2235
+ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
2236
+ let optionsObj;
2237
+ if (!options) optionsObj = {};
2238
+ else if (typeof options === "function") optionsObj = { callback: options };
2239
+ else optionsObj = options;
2240
+ if (typeof optionsObj.context === "undefined") optionsObj.context = 4;
2241
+ const context = optionsObj.context;
2242
+ if (optionsObj.newlineIsToken) throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
2243
+ if (!optionsObj.callback) return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
2244
+ else {
2245
+ const { callback } = optionsObj;
2246
+ diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff$1) => {
2247
+ callback(diffLinesResultToPatch(diff$1));
2248
+ } }));
2249
+ }
2250
+ function diffLinesResultToPatch(diff$1) {
2251
+ if (!diff$1) return;
2252
+ diff$1.push({
2253
+ value: "",
2254
+ lines: []
2255
+ });
2256
+ function contextLines(lines) {
2257
+ return lines.map(function(entry) {
2258
+ return " " + entry;
2259
+ });
2260
+ }
2261
+ const hunks = [];
2262
+ let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
2263
+ for (let i$1 = 0; i$1 < diff$1.length; i$1++) {
2264
+ const current = diff$1[i$1], lines = current.lines || splitLines(current.value);
2265
+ current.lines = lines;
2266
+ if (current.added || current.removed) {
2267
+ if (!oldRangeStart) {
2268
+ const prev = diff$1[i$1 - 1];
2269
+ oldRangeStart = oldLine;
2270
+ newRangeStart = newLine;
2271
+ if (prev) {
2272
+ curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
2273
+ oldRangeStart -= curRange.length;
2274
+ newRangeStart -= curRange.length;
2275
+ }
2276
+ }
2277
+ for (const line of lines) curRange.push((current.added ? "+" : "-") + line);
2278
+ if (current.added) newLine += lines.length;
2279
+ else oldLine += lines.length;
2280
+ } else {
2281
+ if (oldRangeStart) if (lines.length <= context * 2 && i$1 < diff$1.length - 2) for (const line of contextLines(lines)) curRange.push(line);
2282
+ else {
2283
+ const contextSize = Math.min(lines.length, context);
2284
+ for (const line of contextLines(lines.slice(0, contextSize))) curRange.push(line);
2285
+ const hunk = {
2286
+ oldStart: oldRangeStart,
2287
+ oldLines: oldLine - oldRangeStart + contextSize,
2288
+ newStart: newRangeStart,
2289
+ newLines: newLine - newRangeStart + contextSize,
2290
+ lines: curRange
2291
+ };
2292
+ hunks.push(hunk);
2293
+ oldRangeStart = 0;
2294
+ newRangeStart = 0;
2295
+ curRange = [];
2296
+ }
2297
+ oldLine += lines.length;
2298
+ newLine += lines.length;
2299
+ }
2300
+ }
2301
+ for (const hunk of hunks) for (let i$1 = 0; i$1 < hunk.lines.length; i$1++) if (hunk.lines[i$1].endsWith("\n")) hunk.lines[i$1] = hunk.lines[i$1].slice(0, -1);
2302
+ else {
2303
+ hunk.lines.splice(i$1 + 1, 0, "\");
2304
+ i$1++;
2305
+ }
2306
+ return {
2307
+ oldFileName,
2308
+ newFileName,
2309
+ oldHeader,
2310
+ newHeader,
2311
+ hunks
2312
+ };
2313
+ }
2314
+ }
2315
+ /**
2316
+ * Split `text` into an array of lines, including the trailing newline character (where present)
2317
+ */
2318
+ function splitLines(text) {
2319
+ const hasTrailingNl = text.endsWith("\n");
2320
+ const result = text.split("\n").map((line) => line + "\n");
2321
+ if (hasTrailingNl) result.pop();
2322
+ else result.push(result.pop().slice(0, -1));
2323
+ return result;
2324
+ }
2325
+
1564
2326
  //#endregion
1565
2327
  //#region src/commands/setup/agents/diff.ts
1566
- async function diff(plan) {
2328
+ async function diff(plan, state) {
1567
2329
  const probes = new Probes();
1568
2330
  const sections = [];
1569
2331
  for (const group of plan.groups) {
1570
2332
  const changes = [];
1571
- for (const artifact of group.artifacts) changes.push(await changeFor(artifact, plan.archiveDir, probes));
2333
+ for (const artifact of group.artifacts) changes.push(await changeFor(artifact, plan.archiveDir, state, probes));
1572
2334
  sections.push({
1573
2335
  title: group.section,
1574
2336
  changes
@@ -1576,29 +2338,62 @@ async function diff(plan) {
1576
2338
  }
1577
2339
  return sections;
1578
2340
  }
1579
- function changeFor(a$1, archiveDir, probes) {
2341
+ function changeFor(a$1, archiveDir, state, probes) {
1580
2342
  switch (a$1.kind) {
1581
- case "file": return fileChange(a$1);
2343
+ case "file": return fileChange(a$1, archiveDir, state);
1582
2344
  case "symlink": return symlinkChange(a$1);
1583
- case "jsonMerge": return jsonMergeChange(a$1, archiveDir);
2345
+ case "jsonMerge": return jsonMergeChange(a$1, archiveDir, state);
1584
2346
  case "install": return installChange(a$1, probes);
1585
2347
  }
1586
2348
  }
1587
- async function fileChange(a$1) {
2349
+ async function fileChange(a$1, archiveDir, state) {
1588
2350
  const next = await readFile(a$1.src);
1589
- if ((await readFile(a$1.dest).catch(() => null))?.equals(next)) return {
2351
+ const current = await readFile(a$1.dest).catch(() => null);
2352
+ const record$1 = {
2353
+ file: a$1.dest,
2354
+ hash: hashContent(next)
2355
+ };
2356
+ if (current?.equals(next)) return {
1590
2357
  status: "unchanged",
1591
- label: a$1.dest
2358
+ label: a$1.dest,
2359
+ record: record$1
2360
+ };
2361
+ const action = {
2362
+ do: "write-file",
2363
+ src: a$1.src,
2364
+ dest: a$1.dest,
2365
+ ...a$1.mode && { mode: a$1.mode }
2366
+ };
2367
+ if (current === null && a$1.dest in state.files) return {
2368
+ status: "conflict",
2369
+ label: a$1.dest,
2370
+ summary: "was deleted on this machine",
2371
+ diffText: unified(basename(a$1.dest), "", next.toString()),
2372
+ apply: {
2373
+ action,
2374
+ record: record$1
2375
+ },
2376
+ keep: null
2377
+ };
2378
+ if (current !== null && state.files[a$1.dest] !== hashContent(current)) return {
2379
+ status: "conflict",
2380
+ label: a$1.dest,
2381
+ summary: "has content setup did not write",
2382
+ diffText: unified(basename(a$1.dest), current.toString(), next.toString()),
2383
+ apply: {
2384
+ action: {
2385
+ ...action,
2386
+ backupTo: archiveDir
2387
+ },
2388
+ record: record$1
2389
+ },
2390
+ keep: null
1592
2391
  };
1593
2392
  return {
1594
2393
  status: "pending",
1595
2394
  label: a$1.dest,
1596
- action: {
1597
- do: "write-file",
1598
- src: a$1.src,
1599
- dest: a$1.dest,
1600
- ...a$1.mode && { mode: a$1.mode }
1601
- }
2395
+ action,
2396
+ record: record$1
1602
2397
  };
1603
2398
  }
1604
2399
  async function symlinkChange(a$1) {
@@ -1613,7 +2408,8 @@ async function symlinkChange(a$1) {
1613
2408
  label,
1614
2409
  action: link
1615
2410
  };
1616
- if ((await lstat(a$1.linkPath)).isSymbolicLink()) {
2411
+ const st = await lstat(a$1.linkPath);
2412
+ if (st.isSymbolicLink()) {
1617
2413
  if (await readlink(a$1.linkPath) === a$1.target) return {
1618
2414
  status: "unchanged",
1619
2415
  label
@@ -1634,49 +2430,106 @@ async function symlinkChange(a$1) {
1634
2430
  };
1635
2431
  const archiveTo = join(a$1.archiveDisplaced, basename(a$1.linkPath));
1636
2432
  return {
1637
- status: "pending",
2433
+ status: "conflict",
1638
2434
  label,
1639
- action: {
2435
+ summary: `a real ${st.isDirectory() ? "directory" : "file"} sits where the link goes`,
2436
+ diffText: "",
2437
+ apply: { action: {
1640
2438
  ...link,
1641
2439
  archiveTo
1642
- }
2440
+ } },
2441
+ keep: null
1643
2442
  };
1644
2443
  }
1645
- async function jsonMergeChange(a$1, archiveDir) {
2444
+ async function jsonMergeChange(a$1, archiveDir, state) {
1646
2445
  const label = basename(a$1.dest);
1647
2446
  const raw = await readFile(a$1.dest, "utf8").catch(() => null);
1648
2447
  let current = {};
1649
- if (raw !== null) try {
1650
- current = JSON.parse(raw);
1651
- } catch {
1652
- return {
2448
+ if (raw !== null) {
2449
+ let parsed;
2450
+ try {
2451
+ parsed = JSON.parse(raw);
2452
+ } catch {
2453
+ return {
2454
+ status: "blocked",
2455
+ label,
2456
+ reason: "not parseable JSON (comments?)"
2457
+ };
2458
+ }
2459
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {
1653
2460
  status: "blocked",
1654
2461
  label,
1655
- reason: "not parseable JSON (comments?)"
2462
+ reason: "not a JSON object"
1656
2463
  };
2464
+ current = parsed;
1657
2465
  }
1658
- const value = {
1659
- ...current,
2466
+ const desired = {
1660
2467
  ...a$1.managed,
1661
2468
  ...a$1.derived
1662
2469
  };
2470
+ const value = {
2471
+ ...current,
2472
+ ...desired
2473
+ };
2474
+ const record$1 = {
2475
+ file: a$1.dest,
2476
+ keys: Object.fromEntries(Object.keys(desired).map((k) => [k, hashValue(desired[k])]))
2477
+ };
1663
2478
  if (stable(current) === stable(value)) return {
1664
2479
  status: "unchanged",
1665
- label
2480
+ label,
2481
+ record: record$1
1666
2482
  };
1667
2483
  const notes = Object.keys(value).filter((k) => stable(current[k]) !== stable(value[k])).map((k) => `set ${k}${k in a$1.derived ? " (derived)" : ""}`);
1668
2484
  const unmanaged = Object.keys(current).filter((k) => !(k in a$1.managed) && !(k in a$1.derived));
1669
2485
  if (unmanaged.length) notes.push(`preserved unmanaged keys: ${unmanaged.join(", ")}`);
1670
- return {
2486
+ const action = {
2487
+ do: "write-json",
2488
+ dest: a$1.dest,
2489
+ value,
2490
+ backupTo: archiveDir
2491
+ };
2492
+ const ours = state.keys[a$1.dest] ?? {};
2493
+ const contested = Object.keys(desired).filter((k) => {
2494
+ if (stable(current[k]) === stable(desired[k])) return false;
2495
+ if (k in current) return ours[k] !== hashValue(current[k]);
2496
+ return k in ours;
2497
+ });
2498
+ if (!contested.length) return {
1671
2499
  status: "pending",
1672
2500
  label,
2501
+ action,
2502
+ notes,
2503
+ record: record$1
2504
+ };
2505
+ const keepValue = { ...value };
2506
+ for (const k of contested) if (k in current) keepValue[k] = current[k];
2507
+ else delete keepValue[k];
2508
+ const keepRecord = {
2509
+ file: a$1.dest,
2510
+ keys: Object.fromEntries(Object.entries(record$1.keys).filter(([k]) => !contested.includes(k)))
2511
+ };
2512
+ const keep = stable(keepValue) === stable(current) ? null : {
1673
2513
  action: {
1674
2514
  do: "write-json",
1675
2515
  dest: a$1.dest,
1676
- value,
2516
+ value: keepValue,
1677
2517
  backupTo: archiveDir
1678
2518
  },
1679
- notes
2519
+ record: keepRecord,
2520
+ notes: [`kept your values: ${contested.join(", ")}`]
2521
+ };
2522
+ return {
2523
+ status: "conflict",
2524
+ label,
2525
+ summary: `set outside setup: ${contested.join(", ")}`,
2526
+ diffText: contested.map((k) => unified(k, k in current ? pretty(current[k]) : "", pretty(desired[k]))).join("\n"),
2527
+ apply: {
2528
+ action,
2529
+ record: record$1,
2530
+ notes
2531
+ },
2532
+ keep
1680
2533
  };
1681
2534
  }
1682
2535
  async function installChange(a$1, probes) {
@@ -1698,6 +2551,18 @@ async function installChange(a$1, probes) {
1698
2551
  }
1699
2552
  };
1700
2553
  }
2554
+ function pretty(value) {
2555
+ return `${JSON.stringify(value, null, 2) ?? "null"}\n`;
2556
+ }
2557
+ function unified(name$1, before, after) {
2558
+ const patch = structuredPatch(name$1, name$1, before, after);
2559
+ const lines = [`── ${name$1}`];
2560
+ for (const hunk of patch.hunks) {
2561
+ lines.push(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`);
2562
+ lines.push(...hunk.lines);
2563
+ }
2564
+ return lines.join("\n");
2565
+ }
1701
2566
  /** Memoised machine lookups — `claude plugin list` costs a process spawn, so it runs once. */
1702
2567
  var Probes = class {
1703
2568
  onPathCache = /* @__PURE__ */ new Map();
@@ -1753,8 +2618,11 @@ var Probes = class {
1753
2618
  //#endregion
1754
2619
  //#region src/commands/setup/agents/executor.ts
1755
2620
  /** The one place `dryRun` is consulted: the same diff is either printed or committed. */
1756
- async function runPlan(plan, dryRun) {
1757
- await walk(await diff(plan), dryRun ? render : commit);
2621
+ async function runPlan(plan, dryRun, resolve$1) {
2622
+ const state = await loadState(plan.stateFile);
2623
+ const before = stable(state);
2624
+ await walk(await diff(plan, state), dryRun ? render : (change) => commit(change, state, resolve$1));
2625
+ if (!dryRun && stable(state) !== before) await saveState(plan.stateFile, state);
1758
2626
  }
1759
2627
  async function walk(sections, handle) {
1760
2628
  for (const { title, changes } of sections) {
@@ -1769,29 +2637,59 @@ function render(change) {
1769
2637
  case "pending":
1770
2638
  log.plan(phrase(change.action));
1771
2639
  for (const note of change.notes ?? []) log.info(note);
2640
+ return;
2641
+ case "conflict":
2642
+ log.warn(`conflict: ${change.label} — ${change.summary}`);
2643
+ log.info("will ask keep / use config / show diff (piped runs keep; --force uses config)");
1772
2644
  }
1773
2645
  }
1774
- async function commit(change) {
2646
+ async function commit(change, state, resolve$1) {
1775
2647
  switch (change.status) {
1776
- case "unchanged": return log.skip(`${change.label} (unchanged)`);
2648
+ case "unchanged":
2649
+ record(state, change.record);
2650
+ return log.skip(`${change.label} (unchanged)`);
1777
2651
  case "blocked": return log.fail(`${change.label} — ${change.reason}, leaving it alone`);
1778
2652
  case "pending": {
1779
2653
  const { ok, notes } = await perform(change.action);
1780
2654
  if (!ok) return log.fail(`${change.label}: ${notes.join(" ")}`);
2655
+ record(state, change.record);
1781
2656
  log.ok(change.label);
1782
2657
  for (const note of [...notes, ...change.notes ?? []]) log.info(note);
2658
+ return;
2659
+ }
2660
+ case "conflict": {
2661
+ const outcome = await resolve$1(change) === "apply" ? change.apply : change.keep;
2662
+ if (!outcome) return log.warn(`${change.label} — ${change.summary}; kept as-is (resolve interactively or with --force)`);
2663
+ const { ok, notes } = await perform(outcome.action);
2664
+ if (!ok) return log.fail(`${change.label}: ${notes.join(" ")}`);
2665
+ record(state, outcome.record);
2666
+ log.ok(change.label);
2667
+ for (const note of [...notes, ...outcome.notes ?? []]) log.info(note);
1783
2668
  }
1784
2669
  }
1785
2670
  }
2671
+ function record(state, entry) {
2672
+ if (!entry) return;
2673
+ if ("hash" in entry) state.files[entry.file] = entry.hash;
2674
+ else state.keys[entry.file] = {
2675
+ ...state.keys[entry.file],
2676
+ ...entry.keys
2677
+ };
2678
+ }
1786
2679
  async function perform(action) {
1787
2680
  switch (action.do) {
1788
2681
  case "write-file": {
2682
+ const notes = [];
2683
+ if (action.backupTo) {
2684
+ const bak = await backup(action.dest, action.backupTo);
2685
+ if (bak) notes.push(`backed up → ${bak}`);
2686
+ }
1789
2687
  const bytes = await readFile(action.src);
1790
2688
  await ensureDir(dirname(action.dest));
1791
2689
  await writeFile(action.dest, bytes, action.mode ? { mode: action.mode } : void 0);
1792
2690
  return {
1793
2691
  ok: true,
1794
- notes: []
2692
+ notes
1795
2693
  };
1796
2694
  }
1797
2695
  case "link": {
@@ -1848,16 +2746,72 @@ function phrase(action) {
1848
2746
  }
1849
2747
  }
1850
2748
 
2749
+ //#endregion
2750
+ //#region src/commands/setup/agents/resolve.ts
2751
+ var import_picocolors$3 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
2752
+ /** `--force`: the config wins every conflict, exactly as if each prompt answered "use config". */
2753
+ const forceResolver = async () => "apply";
2754
+ /** No one to ask — keep what the machine has, dpkg's default for the same reason. */
2755
+ const keepResolver = async () => "keep";
2756
+ function defaultResolver() {
2757
+ return isInteractive() ? interactiveResolver() : keepResolver;
2758
+ }
2759
+ function interactiveResolver(ask = select) {
2760
+ let rest$1 = null;
2761
+ return async (conflict) => {
2762
+ if (rest$1) return rest$1;
2763
+ for (;;) switch (await ask(`${conflict.label} — ${conflict.summary}`, [
2764
+ {
2765
+ value: "keep",
2766
+ label: "Keep what this machine has",
2767
+ hint: "asks again next run"
2768
+ },
2769
+ {
2770
+ value: "apply",
2771
+ label: "Use the config's version",
2772
+ hint: "existing is backed up"
2773
+ },
2774
+ ...conflict.diffText ? [{
2775
+ value: "diff",
2776
+ label: "Show the diff first"
2777
+ }] : [],
2778
+ {
2779
+ value: "keep-all",
2780
+ label: "Keep — here and for every remaining conflict"
2781
+ },
2782
+ {
2783
+ value: "apply-all",
2784
+ label: "Use config — here and for every remaining conflict"
2785
+ }
2786
+ ])) {
2787
+ case "diff":
2788
+ printDiff(conflict.diffText);
2789
+ continue;
2790
+ case "apply": return "apply";
2791
+ case "apply-all": return rest$1 = "apply";
2792
+ case "keep-all":
2793
+ case null: return rest$1 = "keep";
2794
+ default: return "keep";
2795
+ }
2796
+ };
2797
+ }
2798
+ function printDiff(text) {
2799
+ for (const line of text.split("\n")) if (line.startsWith("+")) console.log(` ${import_picocolors$3.default.green(line)}`);
2800
+ else if (line.startsWith("-")) console.log(` ${import_picocolors$3.default.red(line)}`);
2801
+ else if (line.startsWith("@@")) console.log(` ${import_picocolors$3.default.cyan(line)}`);
2802
+ else console.log(` ${import_picocolors$3.default.dim(line)}`);
2803
+ }
2804
+
1851
2805
  //#endregion
1852
2806
  //#region src/commands/setup/agents/apply.ts
1853
2807
  /** An agent with no config directory is not on this machine — skip it, never create it. */
1854
2808
  function isInstalled(home, emitter) {
1855
2809
  return exists(join(home, emitter.configDir));
1856
2810
  }
1857
- async function applyAgents(ctx, ids, dryRun) {
2811
+ async function applyAgents(ctx, ids, dryRun, resolve$1 = defaultResolver()) {
1858
2812
  validateExtensions(ctx.config);
1859
2813
  logCoverage(ctx.config);
1860
- await runPlan(await buildStorePlan(ctx), dryRun);
2814
+ await runPlan(await buildStorePlan(ctx), dryRun, resolve$1);
1861
2815
  for (const id of ids) {
1862
2816
  const emitter = emitters[id];
1863
2817
  if (!emitter) continue;
@@ -1866,7 +2820,7 @@ async function applyAgents(ctx, ids, dryRun) {
1866
2820
  log.warn(`${id} not installed on this machine — skipping`);
1867
2821
  continue;
1868
2822
  }
1869
- await runPlan(await emitter.buildPlan(ctx), dryRun);
2823
+ await runPlan(await emitter.buildPlan(ctx), dryRun, resolve$1);
1870
2824
  }
1871
2825
  }
1872
2826
 
@@ -1881,10 +2835,14 @@ scripted runs apply to every configured agent.
1881
2835
 
1882
2836
  plan dry run — show every change, write nothing
1883
2837
 
2838
+ Anything on the machine that setup did not write is a conflict: interactively you choose
2839
+ keep / use config / see the diff; piped runs keep the machine's version.
2840
+
1884
2841
  Options:
1885
2842
  -n, --dry-run same as plan
1886
2843
  -a, --agent <id> target one agent (repeatable) — skips the prompt
1887
2844
  --all every configured agent — skips the prompt
2845
+ --force resolve every conflict in the config's favour, no prompts
1888
2846
  --home <dir> apply against a different home directory
1889
2847
  -h, --help show this help`;
1890
2848
  const agents = {
@@ -1908,6 +2866,10 @@ const agents = {
1908
2866
  type: "boolean",
1909
2867
  default: false
1910
2868
  },
2869
+ force: {
2870
+ type: "boolean",
2871
+ default: false
2872
+ },
1911
2873
  home: { type: "string" },
1912
2874
  help: {
1913
2875
  type: "boolean",
@@ -1945,7 +2907,7 @@ const agents = {
1945
2907
  home,
1946
2908
  repoRoot: packageRoot$1,
1947
2909
  config
1948
- }, selected, dryRun);
2910
+ }, selected, dryRun, values.force ? forceResolver : defaultResolver());
1949
2911
  console.log(dryRun ? `\n${import_picocolors$2.default.yellow("dry run complete — nothing written")}\n` : `\n${import_picocolors$2.default.green("done")}\n`);
1950
2912
  return 0;
1951
2913
  }
@@ -2009,7 +2971,7 @@ async function version() {
2009
2971
  }
2010
2972
  function usage() {
2011
2973
  console.log(`Usage: pakhale <command> [options]\n\nCommands:`);
2012
- for (const c of Object.values(commands)) console.log(` ${c.name.padEnd(10)} ${c.describe}`);
2974
+ for (const c$1 of Object.values(commands)) console.log(` ${c$1.name.padEnd(10)} ${c$1.describe}`);
2013
2975
  console.log(`\nOptions:\n -h, --help show this help\n -v, --version show version`);
2014
2976
  }
2015
2977
  if (!name || name === "--help" || name === "-h") {