@fern-api/generator-cli 0.6.9 → 0.6.11

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 (4) hide show
  1. package/dist/api.d.ts +15 -19
  2. package/dist/api.js +124 -356
  3. package/dist/cli.js +612 -414
  4. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -258,10 +258,10 @@ var init_esm2 = __esm({
258
258
  }
259
259
  });
260
260
 
261
- // ../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/assert-valid-pattern.js
261
+ // ../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/assert-valid-pattern.js
262
262
  var MAX_PATTERN_LENGTH, assertValidPattern;
263
263
  var init_assert_valid_pattern = __esm({
264
- "../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/assert-valid-pattern.js"() {
264
+ "../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/assert-valid-pattern.js"() {
265
265
  "use strict";
266
266
  MAX_PATTERN_LENGTH = 1024 * 64;
267
267
  assertValidPattern = (pattern) => {
@@ -275,10 +275,10 @@ var init_assert_valid_pattern = __esm({
275
275
  }
276
276
  });
277
277
 
278
- // ../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/brace-expressions.js
278
+ // ../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/brace-expressions.js
279
279
  var posixClasses, braceEscape, regexpEscape, rangesToString, parseClass;
280
280
  var init_brace_expressions = __esm({
281
- "../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/brace-expressions.js"() {
281
+ "../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/brace-expressions.js"() {
282
282
  "use strict";
283
283
  posixClasses = {
284
284
  "[:alnum:]": ["\\p{L}\\p{Nl}\\p{Nd}", true],
@@ -390,10 +390,10 @@ var init_brace_expressions = __esm({
390
390
  }
391
391
  });
392
392
 
393
- // ../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/unescape.js
393
+ // ../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/unescape.js
394
394
  var unescape;
395
395
  var init_unescape = __esm({
396
- "../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/unescape.js"() {
396
+ "../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/unescape.js"() {
397
397
  "use strict";
398
398
  unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => {
399
399
  if (magicalBraces) {
@@ -404,15 +404,62 @@ var init_unescape = __esm({
404
404
  }
405
405
  });
406
406
 
407
- // ../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/ast.js
408
- var types, isExtglobType, startNoTraversal, startNoDot, addPatternStart, justDots, reSpecials, regExpEscape, qmark, star, starNoEmpty, AST;
407
+ // ../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/ast.js
408
+ var _a, types, isExtglobType, isExtglobAST, adoptionMap, adoptionWithSpaceMap, adoptionAnyMap, usurpMap, startNoTraversal, startNoDot, addPatternStart, justDots, reSpecials, regExpEscape, qmark, star, starNoEmpty, ID, AST;
409
409
  var init_ast = __esm({
410
- "../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/ast.js"() {
410
+ "../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/ast.js"() {
411
411
  "use strict";
412
412
  init_brace_expressions();
413
413
  init_unescape();
414
414
  types = /* @__PURE__ */ new Set(["!", "?", "+", "*", "@"]);
415
415
  isExtglobType = (c) => types.has(c);
416
+ isExtglobAST = (c) => isExtglobType(c.type);
417
+ adoptionMap = /* @__PURE__ */ new Map([
418
+ ["!", ["@"]],
419
+ ["?", ["?", "@"]],
420
+ ["@", ["@"]],
421
+ ["*", ["*", "+", "?", "@"]],
422
+ ["+", ["+", "@"]]
423
+ ]);
424
+ adoptionWithSpaceMap = /* @__PURE__ */ new Map([
425
+ ["!", ["?"]],
426
+ ["@", ["?"]],
427
+ ["+", ["?", "*"]]
428
+ ]);
429
+ adoptionAnyMap = /* @__PURE__ */ new Map([
430
+ ["!", ["?", "@"]],
431
+ ["?", ["?", "@"]],
432
+ ["@", ["?", "@"]],
433
+ ["*", ["*", "+", "?", "@"]],
434
+ ["+", ["+", "@", "?", "*"]]
435
+ ]);
436
+ usurpMap = /* @__PURE__ */ new Map([
437
+ ["!", /* @__PURE__ */ new Map([["!", "@"]])],
438
+ [
439
+ "?",
440
+ /* @__PURE__ */ new Map([
441
+ ["*", "*"],
442
+ ["+", "*"]
443
+ ])
444
+ ],
445
+ [
446
+ "@",
447
+ /* @__PURE__ */ new Map([
448
+ ["!", "!"],
449
+ ["?", "?"],
450
+ ["@", "@"],
451
+ ["*", "*"],
452
+ ["+", "+"]
453
+ ])
454
+ ],
455
+ [
456
+ "+",
457
+ /* @__PURE__ */ new Map([
458
+ ["?", "*"],
459
+ ["*", "*"]
460
+ ])
461
+ ]
462
+ ]);
416
463
  startNoTraversal = "(?!(?:^|/)\\.\\.?(?:$|/))";
417
464
  startNoDot = "(?!\\.)";
418
465
  addPatternStart = /* @__PURE__ */ new Set(["[", "."]);
@@ -422,7 +469,8 @@ var init_ast = __esm({
422
469
  qmark = "[^/]";
423
470
  star = qmark + "*?";
424
471
  starNoEmpty = qmark + "+?";
425
- AST = class _AST {
472
+ ID = 0;
473
+ AST = class {
426
474
  type;
427
475
  #root;
428
476
  #hasMagic;
@@ -437,6 +485,22 @@ var init_ast = __esm({
437
485
  // set to true if it's an extglob with no children
438
486
  // (which really means one child of '')
439
487
  #emptyExt = false;
488
+ id = ++ID;
489
+ get depth() {
490
+ return (this.#parent?.depth ?? -1) + 1;
491
+ }
492
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
493
+ return {
494
+ "@@type": "AST",
495
+ id: this.id,
496
+ type: this.type,
497
+ root: this.#root.id,
498
+ parent: this.#parent?.id,
499
+ depth: this.depth,
500
+ partsLength: this.#parts.length,
501
+ parts: this.#parts
502
+ };
503
+ }
440
504
  constructor(type, parent, options = {}) {
441
505
  this.type = type;
442
506
  if (type)
@@ -502,7 +566,7 @@ var init_ast = __esm({
502
566
  for (const p of parts) {
503
567
  if (p === "")
504
568
  continue;
505
- if (typeof p !== "string" && !(p instanceof _AST && p.#parent === this)) {
569
+ if (typeof p !== "string" && !(p instanceof _a && p.#parent === this)) {
506
570
  throw new Error("invalid part: " + p);
507
571
  }
508
572
  this.#parts.push(p);
@@ -527,7 +591,7 @@ var init_ast = __esm({
527
591
  const p = this.#parent;
528
592
  for (let i = 0; i < this.#parentIndex; i++) {
529
593
  const pp = p.#parts[i];
530
- if (!(pp instanceof _AST && pp.type === "!")) {
594
+ if (!(pp instanceof _a && pp.type === "!")) {
531
595
  return false;
532
596
  }
533
597
  }
@@ -552,13 +616,14 @@ var init_ast = __esm({
552
616
  this.push(part.clone(this));
553
617
  }
554
618
  clone(parent) {
555
- const c = new _AST(this.type, parent);
619
+ const c = new _a(this.type, parent);
556
620
  for (const p of this.#parts) {
557
621
  c.copyIn(p);
558
622
  }
559
623
  return c;
560
624
  }
561
- static #parseAST(str, ast, pos, opt) {
625
+ static #parseAST(str, ast, pos, opt, extDepth) {
626
+ const maxDepth = opt.maxExtglobRecursion ?? 2;
562
627
  let escaping = false;
563
628
  let inBrace = false;
564
629
  let braceStart = -1;
@@ -590,11 +655,12 @@ var init_ast = __esm({
590
655
  acc2 += c;
591
656
  continue;
592
657
  }
593
- if (!opt.noext && isExtglobType(c) && str.charAt(i2) === "(") {
658
+ const doRecurse = !opt.noext && isExtglobType(c) && str.charAt(i2) === "(" && extDepth <= maxDepth;
659
+ if (doRecurse) {
594
660
  ast.push(acc2);
595
661
  acc2 = "";
596
- const ext2 = new _AST(c, ast);
597
- i2 = _AST.#parseAST(str, ext2, i2, opt);
662
+ const ext2 = new _a(c, ast);
663
+ i2 = _a.#parseAST(str, ext2, i2, opt, extDepth + 1);
598
664
  ast.push(ext2);
599
665
  continue;
600
666
  }
@@ -604,7 +670,7 @@ var init_ast = __esm({
604
670
  return i2;
605
671
  }
606
672
  let i = pos + 1;
607
- let part = new _AST(null, ast);
673
+ let part = new _a(null, ast);
608
674
  const parts = [];
609
675
  let acc = "";
610
676
  while (i < str.length) {
@@ -631,19 +697,22 @@ var init_ast = __esm({
631
697
  acc += c;
632
698
  continue;
633
699
  }
634
- if (isExtglobType(c) && str.charAt(i) === "(") {
700
+ const doRecurse = !opt.noext && isExtglobType(c) && str.charAt(i) === "(" && /* c8 ignore start - the maxDepth is sufficient here */
701
+ (extDepth <= maxDepth || ast && ast.#canAdoptType(c));
702
+ if (doRecurse) {
703
+ const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;
635
704
  part.push(acc);
636
705
  acc = "";
637
- const ext2 = new _AST(c, part);
706
+ const ext2 = new _a(c, part);
638
707
  part.push(ext2);
639
- i = _AST.#parseAST(str, ext2, i, opt);
708
+ i = _a.#parseAST(str, ext2, i, opt, extDepth + depthAdd);
640
709
  continue;
641
710
  }
642
711
  if (c === "|") {
643
712
  part.push(acc);
644
713
  acc = "";
645
714
  parts.push(part);
646
- part = new _AST(null, ast);
715
+ part = new _a(null, ast);
647
716
  continue;
648
717
  }
649
718
  if (c === ")") {
@@ -662,9 +731,71 @@ var init_ast = __esm({
662
731
  ast.#parts = [str.substring(pos - 1)];
663
732
  return i;
664
733
  }
734
+ #canAdoptWithSpace(child) {
735
+ return this.#canAdopt(child, adoptionWithSpaceMap);
736
+ }
737
+ #canAdopt(child, map = adoptionMap) {
738
+ if (!child || typeof child !== "object" || child.type !== null || child.#parts.length !== 1 || this.type === null) {
739
+ return false;
740
+ }
741
+ const gc = child.#parts[0];
742
+ if (!gc || typeof gc !== "object" || gc.type === null) {
743
+ return false;
744
+ }
745
+ return this.#canAdoptType(gc.type, map);
746
+ }
747
+ #canAdoptType(c, map = adoptionAnyMap) {
748
+ return !!map.get(this.type)?.includes(c);
749
+ }
750
+ #adoptWithSpace(child, index) {
751
+ const gc = child.#parts[0];
752
+ const blank = new _a(null, gc, this.options);
753
+ blank.#parts.push("");
754
+ gc.push(blank);
755
+ this.#adopt(child, index);
756
+ }
757
+ #adopt(child, index) {
758
+ const gc = child.#parts[0];
759
+ this.#parts.splice(index, 1, ...gc.#parts);
760
+ for (const p of gc.#parts) {
761
+ if (typeof p === "object")
762
+ p.#parent = this;
763
+ }
764
+ this.#toString = void 0;
765
+ }
766
+ #canUsurpType(c) {
767
+ const m = usurpMap.get(this.type);
768
+ return !!m?.has(c);
769
+ }
770
+ #canUsurp(child) {
771
+ if (!child || typeof child !== "object" || child.type !== null || child.#parts.length !== 1 || this.type === null || this.#parts.length !== 1) {
772
+ return false;
773
+ }
774
+ const gc = child.#parts[0];
775
+ if (!gc || typeof gc !== "object" || gc.type === null) {
776
+ return false;
777
+ }
778
+ return this.#canUsurpType(gc.type);
779
+ }
780
+ #usurp(child) {
781
+ const m = usurpMap.get(this.type);
782
+ const gc = child.#parts[0];
783
+ const nt = m?.get(gc.type);
784
+ if (!nt)
785
+ return false;
786
+ this.#parts = gc.#parts;
787
+ for (const p of this.#parts) {
788
+ if (typeof p === "object") {
789
+ p.#parent = this;
790
+ }
791
+ }
792
+ this.type = nt;
793
+ this.#toString = void 0;
794
+ this.#emptyExt = false;
795
+ }
665
796
  static fromGlob(pattern, options = {}) {
666
- const ast = new _AST(null, void 0, options);
667
- _AST.#parseAST(pattern, ast, 0, options);
797
+ const ast = new _a(null, void 0, options);
798
+ _a.#parseAST(pattern, ast, 0, options, 0);
668
799
  return ast;
669
800
  }
670
801
  // returns the regular expression if there's magic, or the unescaped
@@ -758,12 +889,14 @@ var init_ast = __esm({
758
889
  // or start or whatever) and prepend ^ or / at the Regexp construction.
759
890
  toRegExpSource(allowDot) {
760
891
  const dot = allowDot ?? !!this.#options.dot;
761
- if (this.#root === this)
892
+ if (this.#root === this) {
893
+ this.#flatten();
762
894
  this.#fillNegs();
763
- if (!this.type) {
895
+ }
896
+ if (!isExtglobAST(this)) {
764
897
  const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string");
765
898
  const src = this.#parts.map((p) => {
766
- const [re, _, hasMagic, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
899
+ const [re, _, hasMagic, uflag] = typeof p === "string" ? _a.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
767
900
  this.#hasMagic = this.#hasMagic || hasMagic;
768
901
  this.#uflag = this.#uflag || uflag;
769
902
  return re;
@@ -802,9 +935,10 @@ var init_ast = __esm({
802
935
  let body = this.#partsToRegExp(dot);
803
936
  if (this.isStart() && this.isEnd() && !body && this.type !== "!") {
804
937
  const s = this.toString();
805
- this.#parts = [s];
806
- this.type = null;
807
- this.#hasMagic = void 0;
938
+ const me = this;
939
+ me.#parts = [s];
940
+ me.type = null;
941
+ me.#hasMagic = void 0;
808
942
  return [s, unescape(this.toString()), false, false];
809
943
  }
810
944
  let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ? "" : this.#partsToRegExp(true);
@@ -831,6 +965,38 @@ var init_ast = __esm({
831
965
  this.#uflag
832
966
  ];
833
967
  }
968
+ #flatten() {
969
+ if (!isExtglobAST(this)) {
970
+ for (const p of this.#parts) {
971
+ if (typeof p === "object") {
972
+ p.#flatten();
973
+ }
974
+ }
975
+ } else {
976
+ let iterations = 0;
977
+ let done = false;
978
+ do {
979
+ done = true;
980
+ for (let i = 0; i < this.#parts.length; i++) {
981
+ const c = this.#parts[i];
982
+ if (typeof c === "object") {
983
+ c.#flatten();
984
+ if (this.#canAdopt(c)) {
985
+ done = false;
986
+ this.#adopt(c, i);
987
+ } else if (this.#canAdoptWithSpace(c)) {
988
+ done = false;
989
+ this.#adoptWithSpace(c, i);
990
+ } else if (this.#canUsurp(c)) {
991
+ done = false;
992
+ this.#usurp(c);
993
+ }
994
+ }
995
+ }
996
+ } while (!done && ++iterations < 10);
997
+ }
998
+ this.#toString = void 0;
999
+ }
834
1000
  #partsToRegExp(dot) {
835
1001
  return this.#parts.map((p) => {
836
1002
  if (typeof p === "string") {
@@ -891,13 +1057,14 @@ var init_ast = __esm({
891
1057
  return [re, unescape(glob), !!hasMagic, uflag];
892
1058
  }
893
1059
  };
1060
+ _a = AST;
894
1061
  }
895
1062
  });
896
1063
 
897
- // ../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/escape.js
1064
+ // ../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/escape.js
898
1065
  var escape;
899
1066
  var init_escape = __esm({
900
- "../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/escape.js"() {
1067
+ "../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/escape.js"() {
901
1068
  "use strict";
902
1069
  escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => {
903
1070
  if (magicalBraces) {
@@ -908,10 +1075,10 @@ var init_escape = __esm({
908
1075
  }
909
1076
  });
910
1077
 
911
- // ../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/index.js
1078
+ // ../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js
912
1079
  var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path, sep, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
913
1080
  var init_esm3 = __esm({
914
- "../../node_modules/.pnpm/minimatch@10.2.2/node_modules/minimatch/dist/esm/index.js"() {
1081
+ "../../node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js"() {
915
1082
  "use strict";
916
1083
  init_esm2();
917
1084
  init_assert_valid_pattern();
@@ -1069,11 +1236,13 @@ var init_esm3 = __esm({
1069
1236
  isWindows;
1070
1237
  platform;
1071
1238
  windowsNoMagicRoot;
1239
+ maxGlobstarRecursion;
1072
1240
  regexp;
1073
1241
  constructor(pattern, options = {}) {
1074
1242
  assertValidPattern(pattern);
1075
1243
  options = options || {};
1076
1244
  this.options = options;
1245
+ this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200;
1077
1246
  this.pattern = pattern;
1078
1247
  this.platform = options.platform || defaultPlatform;
1079
1248
  this.isWindows = this.platform === "win32";
@@ -1410,7 +1579,8 @@ var init_esm3 = __esm({
1410
1579
  // out of pattern, then that's fine, as long as all
1411
1580
  // the parts match.
1412
1581
  matchOne(file, pattern, partial = false) {
1413
- const options = this.options;
1582
+ let fileStartIndex = 0;
1583
+ let patternStartIndex = 0;
1414
1584
  if (this.isWindows) {
1415
1585
  const fileDrive = typeof file[0] === "string" && /^[a-z]:$/i.test(file[0]);
1416
1586
  const fileUNC = !fileDrive && file[0] === "" && file[1] === "" && file[2] === "?" && /^[a-z]:$/i.test(file[3]);
@@ -1425,11 +1595,8 @@ var init_esm3 = __esm({
1425
1595
  ];
1426
1596
  if (fd.toLowerCase() === pd.toLowerCase()) {
1427
1597
  pattern[pdi] = fd;
1428
- if (pdi > fdi) {
1429
- pattern = pattern.slice(pdi);
1430
- } else if (fdi > pdi) {
1431
- file = file.slice(fdi);
1432
- }
1598
+ patternStartIndex = pdi;
1599
+ fileStartIndex = fdi;
1433
1600
  }
1434
1601
  }
1435
1602
  }
@@ -1437,49 +1604,123 @@ var init_esm3 = __esm({
1437
1604
  if (optimizationLevel >= 2) {
1438
1605
  file = this.levelTwoFileOptimize(file);
1439
1606
  }
1440
- this.debug("matchOne", this, { file, pattern });
1441
- this.debug("matchOne", file.length, pattern.length);
1442
- for (var fi = 0, pi = 0, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
1443
- this.debug("matchOne loop");
1444
- var p = pattern[pi];
1445
- var f = file[fi];
1446
- this.debug(pattern, p, f);
1447
- if (p === false) {
1607
+ if (pattern.includes(GLOBSTAR)) {
1608
+ return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
1609
+ }
1610
+ return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
1611
+ }
1612
+ #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
1613
+ const firstgs = pattern.indexOf(GLOBSTAR, patternIndex);
1614
+ const lastgs = pattern.lastIndexOf(GLOBSTAR);
1615
+ const [head, body, tail] = partial ? [
1616
+ pattern.slice(patternIndex, firstgs),
1617
+ pattern.slice(firstgs + 1),
1618
+ []
1619
+ ] : [
1620
+ pattern.slice(patternIndex, firstgs),
1621
+ pattern.slice(firstgs + 1, lastgs),
1622
+ pattern.slice(lastgs + 1)
1623
+ ];
1624
+ if (head.length) {
1625
+ const fileHead = file.slice(fileIndex, fileIndex + head.length);
1626
+ if (!this.#matchOne(fileHead, head, partial, 0, 0)) {
1448
1627
  return false;
1449
1628
  }
1450
- if (p === GLOBSTAR) {
1451
- this.debug("GLOBSTAR", [pattern, p, f]);
1452
- var fr = fi;
1453
- var pr = pi + 1;
1454
- if (pr === pl) {
1455
- this.debug("** at the end");
1456
- for (; fi < fl; fi++) {
1457
- if (file[fi] === "." || file[fi] === ".." || !options.dot && file[fi].charAt(0) === ".")
1458
- return false;
1459
- }
1460
- return true;
1629
+ fileIndex += head.length;
1630
+ patternIndex += head.length;
1631
+ }
1632
+ let fileTailMatch = 0;
1633
+ if (tail.length) {
1634
+ if (tail.length + fileIndex > file.length)
1635
+ return false;
1636
+ let tailStart = file.length - tail.length;
1637
+ if (this.#matchOne(file, tail, partial, tailStart, 0)) {
1638
+ fileTailMatch = tail.length;
1639
+ } else {
1640
+ if (file[file.length - 1] !== "" || fileIndex + tail.length === file.length) {
1641
+ return false;
1461
1642
  }
1462
- while (fr < fl) {
1463
- var swallowee = file[fr];
1464
- this.debug("\nglobstar while", file, fr, pattern, pr, swallowee);
1465
- if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
1466
- this.debug("globstar found match!", fr, fl, swallowee);
1467
- return true;
1468
- } else {
1469
- if (swallowee === "." || swallowee === ".." || !options.dot && swallowee.charAt(0) === ".") {
1470
- this.debug("dot detected!", file, fr, pattern, pr);
1471
- break;
1472
- }
1473
- this.debug("globstar swallow a segment, and continue");
1474
- fr++;
1475
- }
1643
+ tailStart--;
1644
+ if (!this.#matchOne(file, tail, partial, tailStart, 0)) {
1645
+ return false;
1476
1646
  }
1477
- if (partial) {
1478
- this.debug("\n>>> no match, partial?", file, fr, pattern, pr);
1479
- if (fr === fl) {
1480
- return true;
1481
- }
1647
+ fileTailMatch = tail.length + 1;
1648
+ }
1649
+ }
1650
+ if (!body.length) {
1651
+ let sawSome = !!fileTailMatch;
1652
+ for (let i2 = fileIndex; i2 < file.length - fileTailMatch; i2++) {
1653
+ const f = String(file[i2]);
1654
+ sawSome = true;
1655
+ if (f === "." || f === ".." || !this.options.dot && f.startsWith(".")) {
1656
+ return false;
1657
+ }
1658
+ }
1659
+ return partial || sawSome;
1660
+ }
1661
+ const bodySegments = [[[], 0]];
1662
+ let currentBody = bodySegments[0];
1663
+ let nonGsParts = 0;
1664
+ const nonGsPartsSums = [0];
1665
+ for (const b of body) {
1666
+ if (b === GLOBSTAR) {
1667
+ nonGsPartsSums.push(nonGsParts);
1668
+ currentBody = [[], 0];
1669
+ bodySegments.push(currentBody);
1670
+ } else {
1671
+ currentBody[0].push(b);
1672
+ nonGsParts++;
1673
+ }
1674
+ }
1675
+ let i = bodySegments.length - 1;
1676
+ const fileLength = file.length - fileTailMatch;
1677
+ for (const b of bodySegments) {
1678
+ b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
1679
+ }
1680
+ return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
1681
+ }
1682
+ // return false for "nope, not matching"
1683
+ // return null for "not matching, cannot keep trying"
1684
+ #matchGlobStarBodySections(file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
1685
+ const bs = bodySegments[bodyIndex];
1686
+ if (!bs) {
1687
+ for (let i = fileIndex; i < file.length; i++) {
1688
+ sawTail = true;
1689
+ const f = file[i];
1690
+ if (f === "." || f === ".." || !this.options.dot && f.startsWith(".")) {
1691
+ return false;
1482
1692
  }
1693
+ }
1694
+ return sawTail;
1695
+ }
1696
+ const [body, after] = bs;
1697
+ while (fileIndex <= after) {
1698
+ const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
1699
+ if (m && globStarDepth < this.maxGlobstarRecursion) {
1700
+ const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
1701
+ if (sub !== false) {
1702
+ return sub;
1703
+ }
1704
+ }
1705
+ const f = file[fileIndex];
1706
+ if (f === "." || f === ".." || !this.options.dot && f.startsWith(".")) {
1707
+ return false;
1708
+ }
1709
+ fileIndex++;
1710
+ }
1711
+ return partial || null;
1712
+ }
1713
+ #matchOne(file, pattern, partial, fileIndex, patternIndex) {
1714
+ let fi;
1715
+ let pi;
1716
+ let pl;
1717
+ let fl;
1718
+ for (fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
1719
+ this.debug("matchOne loop");
1720
+ let p = pattern[pi];
1721
+ let f = file[fi];
1722
+ this.debug(pattern, p, f);
1723
+ if (p === false || p === GLOBSTAR) {
1483
1724
  return false;
1484
1725
  }
1485
1726
  let hit;
@@ -3181,11 +3422,11 @@ var require_build2 = __commonJS({
3181
3422
  function stripQuotes2(val) {
3182
3423
  return typeof val === "string" && (val[0] === "'" || val[0] === '"') && val[val.length - 1] === val[0] ? val.substring(1, val.length - 1) : val;
3183
3424
  }
3184
- var _a3;
3425
+ var _a4;
3185
3426
  var _b2;
3186
3427
  var _c2;
3187
3428
  var minNodeVersion2 = process && process.env && process.env.YARGS_MIN_NODE_VERSION ? Number(process.env.YARGS_MIN_NODE_VERSION) : 12;
3188
- var nodeVersion2 = (_b2 = (_a3 = process === null || process === void 0 ? void 0 : process.versions) === null || _a3 === void 0 ? void 0 : _a3.node) !== null && _b2 !== void 0 ? _b2 : (_c2 = process === null || process === void 0 ? void 0 : process.version) === null || _c2 === void 0 ? void 0 : _c2.slice(1);
3429
+ var nodeVersion2 = (_b2 = (_a4 = process === null || process === void 0 ? void 0 : process.versions) === null || _a4 === void 0 ? void 0 : _a4.node) !== null && _b2 !== void 0 ? _b2 : (_c2 = process === null || process === void 0 ? void 0 : process.version) === null || _c2 === void 0 ? void 0 : _c2.slice(1);
3189
3430
  if (nodeVersion2) {
3190
3431
  const major = Number(nodeVersion2.match(/^([^.]+)/)[1]);
3191
3432
  if (major < minNodeVersion2) {
@@ -4605,9 +4846,9 @@ var require_build3 = __commonJS({
4605
4846
  var left2 = 3;
4606
4847
  var UI2 = class {
4607
4848
  constructor(opts) {
4608
- var _a3;
4849
+ var _a4;
4609
4850
  this.width = opts.width;
4610
- this.wrap = (_a3 = opts.wrap) !== null && _a3 !== void 0 ? _a3 : true;
4851
+ this.wrap = (_a4 = opts.wrap) !== null && _a4 !== void 0 ? _a4 : true;
4611
4852
  this.rows = [];
4612
4853
  }
4613
4854
  span(...args) {
@@ -9292,7 +9533,7 @@ function gitInstanceFactory(baseDir, options) {
9292
9533
  customBinaryPlugin(plugins, config.binary, config.unsafe?.allowUnsafeCustomBinary);
9293
9534
  return new Git(config, plugins);
9294
9535
  }
9295
- var import_node_buffer, import_file_exists, import_debug, import_child_process, import_promise_deferred, import_node_path, import_promise_deferred2, import_node_events, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __esm2, __commonJS2, __export2, __copyProps2, __toCommonJS, cache, init_pathspec, GitError, init_git_error, GitResponseError, init_git_response_error, TaskConfigurationError, init_task_configuration_error, NULL, NOOP, objectToString, init_util, filterArray, filterNumber, filterString, filterStringOrStringArray, filterHasLength, init_argument_filters, ExitCodes, init_exit_codes, GitOutputStreams, init_git_output_streams, LineParser, RemoteLineParser, init_line_parser, defaultOptions, init_simple_git_options, init_task_options, init_task_parser, utils_exports, init_utils, check_is_repo_exports, CheckRepoActions, onError, parser2, init_check_is_repo, CleanResponse, removalRegexp, dryRunRemovalRegexp, isFolderRegexp, init_CleanSummary, task_exports, EMPTY_COMMANDS, init_task, clean_exports, CONFIG_ERROR_INTERACTIVE_MODE, CONFIG_ERROR_MODE_REQUIRED, CONFIG_ERROR_UNKNOWN_OPTION, CleanOptions, CleanOptionValues, init_clean, ConfigList, init_ConfigList, GitConfigScope, init_config, DiffNameStatus, diffNameStatus, init_diff_name_status, disallowedOptions, Query, _a2, GrepQuery, init_grep, reset_exports, ResetMode, validResetModes, init_reset, init_git_logger, TasksPendingQueue, init_tasks_pending_queue, GitExecutorChain, init_git_executor_chain, git_executor_exports, GitExecutor, init_git_executor, init_task_callback, init_change_working_directory, init_checkout, parser22, init_count_objects, parsers, init_parse_commit, init_commit, init_first_commit, init_hash_object, InitSummary, initResponseRegex, reInitResponseRegex, init_InitSummary, bareCommand, init_init, logFormatRegex, init_log_format, DiffSummary, init_DiffSummary, statParser, numStatParser, nameOnlyParser, nameStatusParser, diffSummaryParsers, init_parse_diff_summary, START_BOUNDARY, COMMIT_BOUNDARY, SPLITTER, defaultFieldNames, init_parse_list_log_summary, diff_exports, init_diff, excludeOptions, init_log, MergeSummaryConflict, MergeSummaryDetail, init_MergeSummary, PullSummary, PullFailedSummary, init_PullSummary, remoteMessagesObjectParsers, init_parse_remote_objects, parsers2, RemoteMessageSummary, init_parse_remote_messages, FILE_UPDATE_REGEX, SUMMARY_REGEX, ACTION_REGEX, parsers3, errorParsers, parsePullDetail, parsePullResult, init_parse_pull, parsers4, parseMergeResult, parseMergeDetail, init_parse_merge, init_merge, parsers5, parsePushResult, parsePushDetail, init_parse_push, push_exports, init_push, init_show, fromPathRegex, FileStatusSummary, init_FileStatusSummary, StatusSummary, parsers6, parseStatusSummary, init_StatusSummary, ignoredOptions, init_status, NOT_INSTALLED, parsers7, init_version, simple_git_api_exports, SimpleGitApi, init_simple_git_api, scheduler_exports, createScheduledTask, Scheduler, init_scheduler, apply_patch_exports, init_apply_patch, BranchDeletionBatch, init_BranchDeleteSummary, deleteSuccessRegex, deleteErrorRegex, parsers8, parseBranchDeletions, init_parse_branch_delete, BranchSummaryResult, init_BranchSummary, parsers9, currentBranchParser, init_parse_branch, branch_exports, init_branch, parseCheckIgnore, init_CheckIgnore, check_ignore_exports, init_check_ignore, clone_exports, init_clone, parsers10, init_parse_fetch, fetch_exports, init_fetch, parsers11, init_parse_move, move_exports, init_move, pull_exports, init_pull, init_GetRemoteSummary, remote_exports, init_remote, stash_list_exports, init_stash_list, sub_module_exports, init_sub_module, TagList, parseTagList, init_TagList, tag_exports, init_tag, require_git, GitConstructError, GitPluginError, never, WRONG_NUMBER_ERR, WRONG_CHARS_ERR, PluginStore, Git, simpleGit;
9536
+ var import_node_buffer, import_file_exists, import_debug, import_child_process, import_promise_deferred, import_node_path, import_promise_deferred2, import_node_events, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __hasOwnProp2, __esm2, __commonJS2, __export2, __copyProps2, __toCommonJS, cache, init_pathspec, GitError, init_git_error, GitResponseError, init_git_response_error, TaskConfigurationError, init_task_configuration_error, NULL, NOOP, objectToString, init_util, filterArray, filterNumber, filterString, filterStringOrStringArray, filterHasLength, init_argument_filters, ExitCodes, init_exit_codes, GitOutputStreams, init_git_output_streams, LineParser, RemoteLineParser, init_line_parser, defaultOptions, init_simple_git_options, init_task_options, init_task_parser, utils_exports, init_utils, check_is_repo_exports, CheckRepoActions, onError, parser2, init_check_is_repo, CleanResponse, removalRegexp, dryRunRemovalRegexp, isFolderRegexp, init_CleanSummary, task_exports, EMPTY_COMMANDS, init_task, clean_exports, CONFIG_ERROR_INTERACTIVE_MODE, CONFIG_ERROR_MODE_REQUIRED, CONFIG_ERROR_UNKNOWN_OPTION, CleanOptions, CleanOptionValues, init_clean, ConfigList, init_ConfigList, GitConfigScope, init_config, DiffNameStatus, diffNameStatus, init_diff_name_status, disallowedOptions, Query, _a3, GrepQuery, init_grep, reset_exports, ResetMode, validResetModes, init_reset, init_git_logger, TasksPendingQueue, init_tasks_pending_queue, GitExecutorChain, init_git_executor_chain, git_executor_exports, GitExecutor, init_git_executor, init_task_callback, init_change_working_directory, init_checkout, parser22, init_count_objects, parsers, init_parse_commit, init_commit, init_first_commit, init_hash_object, InitSummary, initResponseRegex, reInitResponseRegex, init_InitSummary, bareCommand, init_init, logFormatRegex, init_log_format, DiffSummary, init_DiffSummary, statParser, numStatParser, nameOnlyParser, nameStatusParser, diffSummaryParsers, init_parse_diff_summary, START_BOUNDARY, COMMIT_BOUNDARY, SPLITTER, defaultFieldNames, init_parse_list_log_summary, diff_exports, init_diff, excludeOptions, init_log, MergeSummaryConflict, MergeSummaryDetail, init_MergeSummary, PullSummary, PullFailedSummary, init_PullSummary, remoteMessagesObjectParsers, init_parse_remote_objects, parsers2, RemoteMessageSummary, init_parse_remote_messages, FILE_UPDATE_REGEX, SUMMARY_REGEX, ACTION_REGEX, parsers3, errorParsers, parsePullDetail, parsePullResult, init_parse_pull, parsers4, parseMergeResult, parseMergeDetail, init_parse_merge, init_merge, parsers5, parsePushResult, parsePushDetail, init_parse_push, push_exports, init_push, init_show, fromPathRegex, FileStatusSummary, init_FileStatusSummary, StatusSummary, parsers6, parseStatusSummary, init_StatusSummary, ignoredOptions, init_status, NOT_INSTALLED, parsers7, init_version, simple_git_api_exports, SimpleGitApi, init_simple_git_api, scheduler_exports, createScheduledTask, Scheduler, init_scheduler, apply_patch_exports, init_apply_patch, BranchDeletionBatch, init_BranchDeleteSummary, deleteSuccessRegex, deleteErrorRegex, parsers8, parseBranchDeletions, init_parse_branch_delete, BranchSummaryResult, init_BranchSummary, parsers9, currentBranchParser, init_parse_branch, branch_exports, init_branch, parseCheckIgnore, init_CheckIgnore, check_ignore_exports, init_check_ignore, clone_exports, init_clone, parsers10, init_parse_fetch, fetch_exports, init_fetch, parsers11, init_parse_move, move_exports, init_move, pull_exports, init_pull, init_GetRemoteSummary, remote_exports, init_remote, stash_list_exports, init_stash_list, sub_module_exports, init_sub_module, TagList, parseTagList, init_TagList, tag_exports, init_tag, require_git, GitConstructError, GitPluginError, never, WRONG_NUMBER_ERR, WRONG_CHARS_ERR, PluginStore, Git, simpleGit;
9296
9537
  var init_esm4 = __esm({
9297
9538
  "../../node_modules/.pnpm/simple-git@3.30.0/node_modules/simple-git/dist/esm/index.js"() {
9298
9539
  "use strict";
@@ -9738,9 +9979,9 @@ var init_esm4 = __esm({
9738
9979
  Query = /* @__PURE__ */ Symbol("grepQuery");
9739
9980
  GrepQuery = class {
9740
9981
  constructor() {
9741
- this[_a2] = [];
9982
+ this[_a3] = [];
9742
9983
  }
9743
- *[(_a2 = Query, Symbol.iterator)]() {
9984
+ *[(_a3 = Query, Symbol.iterator)]() {
9744
9985
  for (const query of this[Query]) {
9745
9986
  yield query;
9746
9987
  }
@@ -19327,7 +19568,7 @@ var init_diff3 = __esm({
19327
19568
  }
19328
19569
  });
19329
19570
 
19330
- // ../../node_modules/.pnpm/@fern-api+replay@0.6.2/node_modules/@fern-api/replay/dist/index.js
19571
+ // ../../node_modules/.pnpm/@fern-api+replay@0.7.0/node_modules/@fern-api/replay/dist/index.js
19331
19572
  var dist_exports = {};
19332
19573
  __export(dist_exports, {
19333
19574
  FERN_BOT_EMAIL: () => FERN_BOT_EMAIL2,
@@ -19650,17 +19891,82 @@ async function resolve7(outputDir, options) {
19650
19891
  return { success: false, reason: "no-patches" };
19651
19892
  }
19652
19893
  const git = new GitClient(outputDir);
19894
+ const unresolvedPatches = lockManager.getUnresolvedPatches();
19895
+ const resolvingPatches = lockManager.getResolvingPatches();
19896
+ if (unresolvedPatches.length > 0) {
19897
+ const applicator = new ReplayApplicator(git, lockManager, outputDir);
19898
+ await applicator.applyPatches(unresolvedPatches);
19899
+ const markerFiles = await findConflictMarkerFiles(git);
19900
+ if (markerFiles.length > 0) {
19901
+ for (const patch of unresolvedPatches) {
19902
+ lockManager.updatePatch(patch.id, { status: "resolving" });
19903
+ }
19904
+ lockManager.save();
19905
+ return {
19906
+ success: false,
19907
+ reason: "conflicts-applied",
19908
+ unresolvedFiles: markerFiles,
19909
+ phase: "applied",
19910
+ patchesApplied: unresolvedPatches.length
19911
+ };
19912
+ }
19913
+ }
19653
19914
  if (options?.checkMarkers !== false) {
19654
- const markerFiles = await git.exec(["grep", "-l", "<<<<<<<", "--", "."]).catch(() => "");
19655
- if (markerFiles.trim()) {
19656
- const files = markerFiles.trim().split("\n").filter(Boolean);
19657
- return { success: false, reason: "unresolved-conflicts", unresolvedFiles: files };
19915
+ const currentMarkerFiles = await findConflictMarkerFiles(git);
19916
+ if (currentMarkerFiles.length > 0) {
19917
+ return { success: false, reason: "unresolved-conflicts", unresolvedFiles: currentMarkerFiles };
19918
+ }
19919
+ }
19920
+ const patchesToCommit = [...resolvingPatches, ...unresolvedPatches];
19921
+ if (patchesToCommit.length > 0) {
19922
+ const currentGen = lock.current_generation;
19923
+ const detector = new ReplayDetector(git, lockManager, outputDir);
19924
+ let patchesResolved = 0;
19925
+ for (const patch of patchesToCommit) {
19926
+ const diff = await git.exec(["diff", currentGen, "--", ...patch.files]).catch(() => null);
19927
+ if (!diff || !diff.trim()) {
19928
+ lockManager.removePatch(patch.id);
19929
+ continue;
19930
+ }
19931
+ const newContentHash = detector.computeContentHash(diff);
19932
+ const changedFiles = await getChangedFiles(git, currentGen, patch.files);
19933
+ lockManager.markPatchResolved(patch.id, {
19934
+ patch_content: diff,
19935
+ content_hash: newContentHash,
19936
+ base_generation: currentGen,
19937
+ files: changedFiles
19938
+ });
19939
+ patchesResolved++;
19658
19940
  }
19941
+ lockManager.save();
19942
+ const committer2 = new ReplayCommitter(git, outputDir);
19943
+ await committer2.stageAll();
19944
+ const commitSha2 = await committer2.commitReplay(
19945
+ lock.patches.length,
19946
+ lock.patches,
19947
+ "[fern-replay] Resolved conflicts"
19948
+ );
19949
+ return {
19950
+ success: true,
19951
+ commitSha: commitSha2,
19952
+ phase: "committed",
19953
+ patchesResolved
19954
+ };
19659
19955
  }
19660
19956
  const committer = new ReplayCommitter(git, outputDir);
19661
19957
  await committer.stageAll();
19662
19958
  const commitSha = await committer.commitReplay(lock.patches.length, lock.patches);
19663
- return { success: true, commitSha };
19959
+ return { success: true, commitSha, phase: "committed" };
19960
+ }
19961
+ async function findConflictMarkerFiles(git) {
19962
+ const output = await git.exec(["grep", "-l", "<<<<<<<", "--", "."]).catch(() => "");
19963
+ return output.trim() ? output.trim().split("\n").filter(Boolean) : [];
19964
+ }
19965
+ async function getChangedFiles(git, currentGen, files) {
19966
+ const filesOutput = await git.exec(["diff", "--name-only", currentGen, "--", ...files]).catch(() => null);
19967
+ if (!filesOutput || !filesOutput.trim()) return files;
19968
+ const changed = filesOutput.trim().split("\n").filter(Boolean).filter((f) => !f.startsWith(".fern/"));
19969
+ return changed.length > 0 ? changed : files;
19664
19970
  }
19665
19971
  function status(outputDir) {
19666
19972
  const lockManager = new LockfileManager(outputDir);
@@ -19686,7 +19992,7 @@ function status(outputDir) {
19686
19992
  }
19687
19993
  var import_fs6, import_path8, import_yaml, import_crypto, import_promises4, import_os, import_path9, import_fs7, import_path10, import_crypto2, import_fs8, import_path11, import_yaml2, import_crypto3, import_fs9, import_path12, import_fs10, __defProp3, __getOwnPropNames3, __esm3, __export3, GitClient_exports, GitClient, init_GitClient, FERN_BOT_NAME2, FERN_BOT_EMAIL2, FERN_BOT_LOGIN2, FERN_SUPPORT_NAMES, LOCKFILE_HEADER, LockfileManager, INFRASTRUCTURE_FILES, ReplayDetector, BINARY_EXTENSIONS, ReplayApplicator, ReplayCommitter, ReplayService, FernignoreMigrator, REPLAY_FERNIGNORE_ENTRIES;
19688
19994
  var init_dist = __esm({
19689
- "../../node_modules/.pnpm/@fern-api+replay@0.6.2/node_modules/@fern-api/replay/dist/index.js"() {
19995
+ "../../node_modules/.pnpm/@fern-api+replay@0.7.0/node_modules/@fern-api/replay/dist/index.js"() {
19690
19996
  "use strict";
19691
19997
  init_esm4();
19692
19998
  import_fs6 = require("fs");
@@ -19917,6 +20223,26 @@ var init_dist = __esm({
19917
20223
  this.ensureLoaded();
19918
20224
  this.lock.patches = [];
19919
20225
  }
20226
+ getUnresolvedPatches() {
20227
+ this.ensureLoaded();
20228
+ return this.lock.patches.filter((p) => p.status === "unresolved");
20229
+ }
20230
+ getResolvingPatches() {
20231
+ this.ensureLoaded();
20232
+ return this.lock.patches.filter((p) => p.status === "resolving");
20233
+ }
20234
+ markPatchUnresolved(patchId) {
20235
+ this.updatePatch(patchId, { status: "unresolved" });
20236
+ }
20237
+ markPatchResolved(patchId, updates) {
20238
+ this.ensureLoaded();
20239
+ const patch = this.lock.patches.find((p) => p.id === patchId);
20240
+ if (!patch) {
20241
+ throw new Error(`Patch not found: ${patchId}`);
20242
+ }
20243
+ delete patch.status;
20244
+ Object.assign(patch, updates);
20245
+ }
19920
20246
  getPatches() {
19921
20247
  this.ensureLoaded();
19922
20248
  return this.lock.patches;
@@ -20560,12 +20886,12 @@ CLI Version: ${options.cliVersion}`;
20560
20886
  await this.git.exec(["commit", "-m", fullMessage]);
20561
20887
  return (await this.git.exec(["rev-parse", "HEAD"])).trim();
20562
20888
  }
20563
- async commitReplay(_patchCount, patches) {
20889
+ async commitReplay(_patchCount, patches, message) {
20564
20890
  await this.stageAll();
20565
20891
  if (!await this.hasStagedChanges()) {
20566
20892
  return (await this.git.exec(["rev-parse", "HEAD"])).trim();
20567
20893
  }
20568
- let fullMessage = `[fern-replay] Applied customizations`;
20894
+ let fullMessage = message ?? `[fern-replay] Applied customizations`;
20569
20895
  if (patches && patches.length > 0) {
20570
20896
  fullMessage += "\n\nPatches replayed:";
20571
20897
  for (const patch of patches) {
@@ -20655,13 +20981,27 @@ CLI Version: ${options.cliVersion}`;
20655
20981
  this.lockManager.initializeInMemory(record2);
20656
20982
  } else {
20657
20983
  this.lockManager.read();
20984
+ const unresolvedPatches = [
20985
+ ...this.lockManager.getUnresolvedPatches(),
20986
+ ...this.lockManager.getResolvingPatches()
20987
+ ];
20658
20988
  this.lockManager.addGeneration(record2);
20659
20989
  this.lockManager.clearPatches();
20990
+ for (const patch of unresolvedPatches) {
20991
+ this.lockManager.addPatch(patch);
20992
+ }
20660
20993
  }
20661
20994
  this.lockManager.save();
20662
20995
  try {
20663
20996
  const redetectedPatches = await this.detector.detectNewPatches();
20664
20997
  if (redetectedPatches.length > 0) {
20998
+ const redetectedFiles = new Set(redetectedPatches.flatMap((p) => p.files));
20999
+ const currentPatches = this.lockManager.getPatches();
21000
+ for (const patch of currentPatches) {
21001
+ if (patch.status != null && patch.files.some((f) => redetectedFiles.has(f))) {
21002
+ this.lockManager.removePatch(patch.id);
21003
+ }
21004
+ }
20665
21005
  for (const patch of redetectedPatches) {
20666
21006
  this.lockManager.addPatch(patch);
20667
21007
  }
@@ -20769,6 +21109,12 @@ CLI Version: ${options.cliVersion}`;
20769
21109
  let results = [];
20770
21110
  if (newPatches.length > 0) {
20771
21111
  results = await this.applicator.applyPatches(newPatches);
21112
+ this.revertConflictingFiles(results);
21113
+ for (const result of results) {
21114
+ if (result.status === "conflict") {
21115
+ result.patch.status = "unresolved";
21116
+ }
21117
+ }
20772
21118
  }
20773
21119
  const rebaseCounts = await this.rebasePatches(results, genRecord.commit_sha);
20774
21120
  for (const patch of newPatches) {
@@ -20779,10 +21125,8 @@ CLI Version: ${options.cliVersion}`;
20779
21125
  this.lockManager.save();
20780
21126
  if (newPatches.length > 0) {
20781
21127
  if (!options?.stageOnly) {
20782
- const appliedCount = results.filter((r) => r.status === "applied" || r.status === "conflict").length;
20783
- if (appliedCount > 0) {
20784
- await this.committer.commitReplay(appliedCount, newPatches);
20785
- }
21128
+ const appliedCount = results.filter((r) => r.status === "applied").length;
21129
+ await this.committer.commitReplay(appliedCount, newPatches);
20786
21130
  } else {
20787
21131
  await this.committer.stageAll();
20788
21132
  }
@@ -20830,20 +21174,32 @@ CLI Version: ${options.cliVersion}`;
20830
21174
  const genRecord = await this.committer.createGenerationRecord(commitOpts);
20831
21175
  this.lockManager.addGeneration(genRecord);
20832
21176
  const results = await this.applicator.applyPatches(allPatches);
21177
+ this.revertConflictingFiles(results);
21178
+ for (const result of results) {
21179
+ if (result.status === "conflict") {
21180
+ result.patch.status = "unresolved";
21181
+ }
21182
+ }
20833
21183
  const rebaseCounts = await this.rebasePatches(results, genRecord.commit_sha);
20834
21184
  for (const patch of newPatches) {
20835
21185
  if (!rebaseCounts.absorbedPatchIds.has(patch.id)) {
20836
21186
  this.lockManager.addPatch(patch);
20837
21187
  }
20838
21188
  }
21189
+ for (const result of results) {
21190
+ if (result.status === "conflict") {
21191
+ try {
21192
+ this.lockManager.markPatchUnresolved(result.patch.id);
21193
+ } catch {
21194
+ }
21195
+ }
21196
+ }
20839
21197
  this.lockManager.save();
20840
21198
  if (options?.stageOnly) {
20841
21199
  await this.committer.stageAll();
20842
21200
  } else {
20843
- const appliedCount = results.filter((r) => r.status === "applied" || r.status === "conflict").length;
20844
- if (appliedCount > 0) {
20845
- await this.committer.commitReplay(appliedCount, allPatches);
20846
- }
21201
+ const appliedCount = results.filter((r) => r.status === "applied").length;
21202
+ await this.committer.commitReplay(appliedCount, allPatches);
20847
21203
  }
20848
21204
  return this.buildReport(
20849
21205
  "normal-regeneration",
@@ -20991,6 +21347,10 @@ CLI Version: ${options.cliVersion}`;
20991
21347
  let conflictAbsorbed = 0;
20992
21348
  let contentRefreshed = 0;
20993
21349
  for (const patch of patches) {
21350
+ if (patch.status != null) {
21351
+ delete patch.status;
21352
+ continue;
21353
+ }
20994
21354
  if (patch.base_generation === currentGen) {
20995
21355
  try {
20996
21356
  const diff = await this.git.exec(["diff", currentGen, "HEAD", "--", ...patch.files]).catch(() => null);
@@ -21041,6 +21401,55 @@ CLI Version: ${options.cliVersion}`;
21041
21401
  }
21042
21402
  return { conflictResolved, conflictAbsorbed, contentRefreshed };
21043
21403
  }
21404
+ /**
21405
+ * Strip conflict markers from file content, keeping the OURS (Generated) side.
21406
+ * Preserves clean patches' non-conflicting changes on shared files.
21407
+ */
21408
+ stripConflictMarkers(content) {
21409
+ const lines = content.split("\n");
21410
+ const result = [];
21411
+ let inConflict = false;
21412
+ let inOurs = false;
21413
+ for (const line of lines) {
21414
+ if (line.startsWith("<<<<<<< ")) {
21415
+ inConflict = true;
21416
+ inOurs = true;
21417
+ continue;
21418
+ }
21419
+ if (inConflict && line === "=======") {
21420
+ inOurs = false;
21421
+ continue;
21422
+ }
21423
+ if (inConflict && line.startsWith(">>>>>>> ")) {
21424
+ inConflict = false;
21425
+ inOurs = false;
21426
+ continue;
21427
+ }
21428
+ if (!inConflict || inOurs) {
21429
+ result.push(line);
21430
+ }
21431
+ }
21432
+ return result.join("\n");
21433
+ }
21434
+ /**
21435
+ * After applyPatches(), strip conflict markers from conflicting files
21436
+ * so only clean content is committed. Keeps the Generated (OURS) side.
21437
+ */
21438
+ revertConflictingFiles(results) {
21439
+ for (const result of results) {
21440
+ if (result.status !== "conflict" || !result.fileResults) continue;
21441
+ for (const fileResult of result.fileResults) {
21442
+ if (fileResult.status !== "conflict") continue;
21443
+ const filePath = (0, import_path10.join)(this.outputDir, fileResult.file);
21444
+ try {
21445
+ const content = (0, import_fs7.readFileSync)(filePath, "utf-8");
21446
+ const stripped = this.stripConflictMarkers(content);
21447
+ (0, import_fs7.writeFileSync)(filePath, stripped);
21448
+ } catch {
21449
+ }
21450
+ }
21451
+ }
21452
+ }
21044
21453
  readFernignorePatterns() {
21045
21454
  const fernignorePath = (0, import_path10.join)(this.outputDir, ".fernignore");
21046
21455
  if (!(0, import_fs7.existsSync)(fernignorePath)) return [];
@@ -21075,6 +21484,12 @@ CLI Version: ${options.cliVersion}`;
21075
21484
  patchesRefreshed: preRebaseCounts && preRebaseCounts.contentRefreshed > 0 ? preRebaseCounts.contentRefreshed : void 0,
21076
21485
  conflicts: conflictResults.flatMap((r) => r.fileResults?.filter((f) => f.status === "conflict") ?? []),
21077
21486
  conflictDetails: conflictDetails.length > 0 ? conflictDetails : void 0,
21487
+ unresolvedPatches: conflictResults.length > 0 ? conflictResults.map((r) => ({
21488
+ patchId: r.patch.id,
21489
+ patchMessage: r.patch.original_message,
21490
+ files: r.patch.files,
21491
+ conflictDetails: r.fileResults?.filter((f) => f.status === "conflict") ?? []
21492
+ })) : void 0,
21078
21493
  wouldApply: options?.dryRun ? patches : void 0,
21079
21494
  warnings: warnings && warnings.length > 0 ? warnings : void 0
21080
21495
  };
@@ -22282,11 +22697,11 @@ function stripQuotes(val) {
22282
22697
 
22283
22698
  // ../../node_modules/.pnpm/yargs-parser@21.1.1/node_modules/yargs-parser/build/lib/index.js
22284
22699
  var import_fs = require("fs");
22285
- var _a;
22700
+ var _a2;
22286
22701
  var _b;
22287
22702
  var _c;
22288
22703
  var minNodeVersion = process && process.env && process.env.YARGS_MIN_NODE_VERSION ? Number(process.env.YARGS_MIN_NODE_VERSION) : 12;
22289
- var nodeVersion = (_b = (_a = process === null || process === void 0 ? void 0 : process.versions) === null || _a === void 0 ? void 0 : _a.node) !== null && _b !== void 0 ? _b : (_c = process === null || process === void 0 ? void 0 : process.version) === null || _c === void 0 ? void 0 : _c.slice(1);
22704
+ var nodeVersion = (_b = (_a2 = process === null || process === void 0 ? void 0 : process.versions) === null || _a2 === void 0 ? void 0 : _a2.node) !== null && _b !== void 0 ? _b : (_c = process === null || process === void 0 ? void 0 : process.version) === null || _c === void 0 ? void 0 : _c.slice(1);
22290
22705
  if (nodeVersion) {
22291
22706
  const major = Number(nodeVersion.match(/^([^.]+)/)[1]);
22292
22707
  if (major < minNodeVersion) {
@@ -22340,9 +22755,9 @@ var bottom = 2;
22340
22755
  var left = 3;
22341
22756
  var UI = class {
22342
22757
  constructor(opts) {
22343
- var _a3;
22758
+ var _a4;
22344
22759
  this.width = opts.width;
22345
- this.wrap = (_a3 = opts.wrap) !== null && _a3 !== void 0 ? _a3 : true;
22760
+ this.wrap = (_a4 = opts.wrap) !== null && _a4 !== void 0 ? _a4 : true;
22346
22761
  this.rows = [];
22347
22762
  }
22348
22763
  span(...args) {
@@ -26986,9 +27401,6 @@ async function githubRelease(params) {
26986
27401
  await github.release();
26987
27402
  }
26988
27403
 
26989
- // src/api/generate-readme.ts
26990
- var import_node_stream = require("stream");
26991
-
26992
27404
  // ../../node_modules/.pnpm/es-toolkit@1.44.0/node_modules/es-toolkit/dist/predicate/isLength.mjs
26993
27405
  function isLength(value) {
26994
27406
  return Number.isSafeInteger(value) && value >= 0;
@@ -29739,7 +30151,8 @@ var BlockList = class {
29739
30151
  }
29740
30152
  addBlock(block) {
29741
30153
  if (this.hasBlock(block.id)) {
29742
- throw new Error(`block with id "${block.id}" already exists`);
30154
+ console.warn(`[readme] Duplicate block with id "${block.id}" detected; overriding with latest content`);
30155
+ this.blocks = this.blocks.filter((b) => b.id !== block.id);
29743
30156
  }
29744
30157
  this.ids.add(block.id);
29745
30158
  this.blocks.push(block);
@@ -29753,7 +30166,9 @@ var BlockList = class {
29753
30166
  // }
29754
30167
  insertBlock(block, precedingBlockId) {
29755
30168
  if (this.hasBlock(block.id)) {
29756
- throw new Error(`block with id "${block.id}" already exists`);
30169
+ console.warn(`[readme] Duplicate block with id "${block.id}" detected; overriding with latest content`);
30170
+ this.blocks = this.blocks.filter((b) => b.id !== block.id);
30171
+ this.ids.delete(block.id);
29757
30172
  }
29758
30173
  if (precedingBlockId === void 0) {
29759
30174
  this.addBlock(block);
@@ -29804,16 +30219,25 @@ var ReadmeGenerator = class {
29804
30219
  this.apiName = this.readmeConfig.apiName ?? this.organizationPascalCase;
29805
30220
  }
29806
30221
  async generateReadme({ output }) {
30222
+ const writer = new StreamWriter(output);
30223
+ await this.writeReadmeContent({ writer });
30224
+ await writer.end();
30225
+ }
30226
+ async generateReadmeToString() {
30227
+ const writer = new StringWriter();
30228
+ await this.writeReadmeContent({ writer });
30229
+ await writer.end();
30230
+ return writer.toString();
30231
+ }
30232
+ async writeReadmeContent({ writer }) {
29807
30233
  const blocks = await this.generateBlocks();
29808
30234
  const mergedBlocks = await this.mergeBlocks({ blocks });
29809
- const writer = new StreamWriter(output);
29810
30235
  await this.writeHeader({ writer });
29811
30236
  await this.writeTableOfContents({ writer, blocks: mergedBlocks });
29812
30237
  await this.writeBlocks({
29813
30238
  writer,
29814
30239
  blocks: mergedBlocks
29815
30240
  });
29816
- await writer.end();
29817
30241
  }
29818
30242
  async generateBlocks() {
29819
30243
  const blocks = [];
@@ -30683,9 +31107,6 @@ async function generateReadmeToStream(params) {
30683
31107
  });
30684
31108
  }
30685
31109
 
30686
- // src/api/generate-reference.ts
30687
- var import_node_stream2 = require("stream");
30688
-
30689
31110
  // src/reference/ReferenceGenerator.ts
30690
31111
  var ReferenceGenerator = class {
30691
31112
  referenceConfig;
@@ -30696,6 +31117,16 @@ var ReferenceGenerator = class {
30696
31117
  }
30697
31118
  async generate({ output }) {
30698
31119
  const writer = new StreamWriter(output);
31120
+ await this.writeReferenceContent({ writer });
31121
+ await writer.end();
31122
+ }
31123
+ async generateToString() {
31124
+ const writer = new StringWriter();
31125
+ await this.writeReferenceContent({ writer });
31126
+ await writer.end();
31127
+ return writer.toString();
31128
+ }
31129
+ async writeReferenceContent({ writer }) {
30699
31130
  await writer.writeLine("# Reference");
30700
31131
  if (this.referenceConfig.rootSection != null) {
30701
31132
  await this.writeRootSection({
@@ -30706,7 +31137,6 @@ var ReferenceGenerator = class {
30706
31137
  for (const section of this.referenceConfig.sections) {
30707
31138
  await this.writeSection({ section, writer });
30708
31139
  }
30709
- await writer.end();
30710
31140
  }
30711
31141
  async writeRootSection({
30712
31142
  section,
@@ -30846,41 +31276,19 @@ var import_path7 = require("path");
30846
31276
 
30847
31277
  // src/pipeline/github/createReplayBranch.ts
30848
31278
  async function createReplayBranch(repository, branchName, commitMessage, replayConflictInfo, logger) {
30849
- if (replayConflictInfo?.hasConflicts) {
30850
- const parentSha = replayConflictInfo.previousGenerationSha;
30851
- const lockfileRestoreSha = replayConflictInfo.baseBranchHead ?? replayConflictInfo.previousGenerationSha;
30852
- logger.debug(`Creating divergent PR branch with generation tree (parent: ${parentSha.substring(0, 7)})`);
30853
- const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
30854
- const syntheticCommitSha = await repository.commitTree(
30855
- genTreeHash,
30856
- parentSha,
30857
- `[fern-generated] ${commitMessage ?? "Update SDK"}`
30858
- );
30859
- await repository.createBranchFromCommit(branchName, syntheticCommitSha);
30860
- try {
30861
- await repository.restoreFilesFromCommit(lockfileRestoreSha, ".fern/replay.lock");
30862
- await repository.commitAllChanges(`[fern-generated] ${commitMessage ?? "Update SDK"}`);
30863
- } catch (error) {
30864
- logger.debug(
30865
- `Could not restore lockfile from base branch: ${error instanceof Error ? error.message : String(error)}`
30866
- );
30867
- }
30868
- return syntheticCommitSha;
30869
- } else if (replayConflictInfo != null) {
30870
- const parentSha = replayConflictInfo.previousGenerationSha;
30871
- logger.debug(`Creating linear PR branch from HEAD (no conflicts)`);
30872
- await repository.createBranchFromHead(branchName);
30873
- const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
30874
- const tagCommitSha = await repository.commitTree(
30875
- genTreeHash,
30876
- parentSha,
30877
- `[fern-generated] ${commitMessage ?? "Update SDK"}`
30878
- );
30879
- return tagCommitSha;
30880
- } else {
31279
+ if (replayConflictInfo == null) {
30881
31280
  await repository.createBranchFromHead(branchName);
30882
31281
  return void 0;
30883
31282
  }
31283
+ const parentSha = replayConflictInfo.previousGenerationSha;
31284
+ await repository.createBranchFromHead(branchName);
31285
+ const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
31286
+ const tagCommitSha = await repository.commitTree(
31287
+ genTreeHash,
31288
+ parentSha,
31289
+ `[fern-generated] ${commitMessage ?? "Update SDK"}`
31290
+ );
31291
+ return tagCommitSha;
30884
31292
  }
30885
31293
 
30886
31294
  // src/pipeline/github/findExistingUpdatablePR.ts
@@ -30975,11 +31383,11 @@ function formatConflictReason(reason) {
30975
31383
  case "same-line-edit":
30976
31384
  return "The new generation changed the same lines you edited";
30977
31385
  case "new-file-both":
30978
- return "You and the generator both created this file";
31386
+ return "Both the generator and your customization created this file";
30979
31387
  case "base-generation-mismatch":
30980
- return "The generated code changed significantly around your edit";
31388
+ return "This customization was from a previous generation";
30981
31389
  case "patch-apply-failed":
30982
- return "Your change could not be applied to the new generated code";
31390
+ return "The customization could not be applied to the new code";
30983
31391
  default:
30984
31392
  return "Your edit overlaps with changes in the new generation";
30985
31393
  }
@@ -30988,7 +31396,7 @@ function patchDescription(detail) {
30988
31396
  if (detail.patchMessage && detail.patchMessage !== "update") {
30989
31397
  return detail.patchMessage;
30990
31398
  }
30991
- const firstFile = detail.files[0]?.file;
31399
+ const firstFile = detail.files[0];
30992
31400
  return firstFile != null ? `changes in ${firstFile}` : "customization";
30993
31401
  }
30994
31402
  function formatReplayPrBody(result, options) {
@@ -30997,70 +31405,66 @@ function formatReplayPrBody(result, options) {
30997
31405
  }
30998
31406
  const applied = result.patchesApplied ?? 0;
30999
31407
  const absorbed = result.patchesAbsorbed ?? 0;
31000
- const conflicts2 = result.patchesWithConflicts ?? 0;
31408
+ const unresolvedPatches = result.unresolvedPatches ?? [];
31409
+ const hasUnresolved = unresolvedPatches.length > 0;
31001
31410
  const preserved = applied - absorbed;
31002
- if (preserved === 0 && conflicts2 === 0) {
31411
+ if (preserved === 0 && !hasUnresolved) {
31003
31412
  return void 0;
31004
31413
  }
31005
31414
  const parts = [];
31006
- if (preserved > 0 && conflicts2 === 0) {
31415
+ if (preserved > 0 && !hasUnresolved) {
31007
31416
  parts.push(`\u2705 Customizations automatically preserved in this update.`);
31008
31417
  } else if (preserved > 0) {
31418
+ const totalFiles = unresolvedPatches.reduce((sum, patch) => sum + patch.conflictDetails.length, 0);
31009
31419
  parts.push(
31010
- `\u2705 Customizations automatically preserved, but ${plural(conflicts2, "file")} ${conflicts2 === 1 ? "needs" : "need"} your attention.`
31420
+ `\u2705 Customizations automatically preserved, but ${plural(totalFiles, "file")} ${totalFiles === 1 ? "needs" : "need"} your attention.`
31011
31421
  );
31012
31422
  }
31013
- if (conflicts2 > 0) {
31014
- const allFiles = (result.conflictDetails ?? []).flatMap((d) => d.files);
31015
- const totalFiles = allFiles.length;
31423
+ if (hasUnresolved) {
31424
+ const totalFiles = unresolvedPatches.reduce((sum, patch) => sum + patch.conflictDetails.length, 0);
31016
31425
  parts.push(`
31017
- ### \u26A0\uFE0F Action required: ${plural(totalFiles, "file")} with customization conflicts
31426
+ ### Action required: ${plural(totalFiles, "file")} with unresolved customization conflicts
31018
31427
  `);
31019
31428
  parts.push(
31020
- `You previously customized ${totalFiles === 1 ? "a file" : "files"} in this SDK. This generation changed the same code, so your ${totalFiles === 1 ? "edit" : "edits"} couldn't be applied automatically.
31021
- `
31022
- );
31023
- parts.push(
31024
- `> \u26A0\uFE0F **Label guide:** In GitHub's conflict editor, \`<<<<<<< HEAD\` ("Accept current") = new generated code. \`>>>>>>> main\` ("Accept incoming") = your customization.
31429
+ `The new generation changed code you previously customized. Non-conflicting customizations have been applied automatically. The following files need manual resolution:
31025
31430
  `
31026
31431
  );
31027
31432
  parts.push(`| File | Your customization | Why it conflicted |`);
31028
31433
  parts.push(`|------|-------------------|-------------------|`);
31029
- for (const detail of result.conflictDetails ?? []) {
31030
- const description = patchDescription(detail);
31031
- for (const f of detail.files) {
31032
- parts.push(`| \`${f.file}\` | ${description} | ${formatConflictReason(f.conflictReason)} |`);
31434
+ for (const patch of unresolvedPatches) {
31435
+ const description = patchDescription(patch);
31436
+ for (const file of patch.conflictDetails) {
31437
+ parts.push(`| \`${file.file}\` | ${description} | ${formatConflictReason(file.conflictReason)} |`);
31033
31438
  }
31034
31439
  }
31035
31440
  const branch = options?.branchName ?? "<branch-name>";
31441
+ const repoUri = options?.repoUri;
31442
+ const repoName = repoUri?.split("/").pop() ?? "<repo>";
31036
31443
  parts.push(`
31037
- #### How to fix
31444
+ #### How to resolve
31038
31445
  `);
31039
- parts.push(`**Option A: Resolve in GitHub (recommended for simple conflicts)**
31040
- `);
31041
- parts.push(`1. Click the **Resolve conflicts** button below`);
31042
- parts.push(`2. For each file, choose which code to keep:`);
31043
- parts.push(` - \`<<<<<<< HEAD\` / "Accept current changes" = **new generated code**`);
31044
- parts.push(` - \`>>>>>>> main\` / "Accept incoming changes" = **your customization**`);
31045
- parts.push(` - Or manually combine both, then delete the conflict markers`);
31046
- parts.push(`3. Click **Mark as resolved** for each file, then **Commit merge**
31047
- `);
31048
- parts.push(`**Option B: Resolve locally**
31049
- `);
31050
- parts.push(`1. Fetch and check out this branch:`);
31051
- parts.push(` \`\`\`sh`);
31052
- parts.push(` git fetch origin && git checkout ${branch}`);
31053
- parts.push(` \`\`\``);
31054
- parts.push(`2. Merge the base branch and resolve conflicts in your editor:`);
31446
+ parts.push(`1. Check out this branch:`);
31055
31447
  parts.push(` \`\`\`sh`);
31056
- parts.push(` git merge origin/main`);
31448
+ parts.push(` git fetch origin && git checkout -b ${branch} origin/${branch}`);
31057
31449
  parts.push(` \`\`\``);
31058
- parts.push(`3. Commit and push:`);
31059
- parts.push(` \`\`\`sh`);
31060
- parts.push(` git add -A && git commit -m "resolve conflicts" && git push`);
31061
- parts.push(` \`\`\`
31450
+ if (repoUri != null) {
31451
+ parts.push(` Or if you don't have the repo cloned:`);
31452
+ parts.push(` \`\`\`sh`);
31453
+ parts.push(
31454
+ ` git clone https://github.com/${repoUri}.git && cd ${repoName} && git checkout -b ${branch} origin/${branch}`
31455
+ );
31456
+ parts.push(` \`\`\``);
31457
+ }
31458
+ parts.push(`2. Run: \`fern replay resolve\``);
31459
+ parts.push(`3. Open the conflicting files in your editor \u2014 you'll see standard merge conflict markers`);
31460
+ parts.push(`4. Resolve using your editor's merge tools (VS Code, IntelliJ, etc.)`);
31461
+ parts.push(`5. Run: \`fern replay resolve\` again to finalize`);
31462
+ parts.push(`6. Push your changes
31062
31463
  `);
31063
- parts.push(`Your resolved changes will be remembered on future SDK generations.`);
31464
+ parts.push(`Your resolved customizations will be remembered on future SDK generations.`);
31465
+ parts.push(
31466
+ `If you merge this PR without resolving, your unresolved customizations will conflict again on the next generation.`
31467
+ );
31064
31468
  }
31065
31469
  return parts.join("\n");
31066
31470
  }
@@ -31196,16 +31600,9 @@ var GithubStep = class extends BaseStep {
31196
31600
  }
31197
31601
  }
31198
31602
  }
31199
- if (!this.config.previewMode) {
31200
- const headSha = await repository.getHeadSha();
31201
- await this.postReplayConflictStatus(octokit, owner, repo, headSha, replayConflictInfo, replayResult);
31202
- if (isUpdatingExistingPR && existingPR != null) {
31203
- await this.togglePrDraftState(octokit, existingPR, replayConflictInfo);
31204
- }
31205
- }
31206
31603
  const finalCommitMessage = this.config.commitMessage ?? "SDK Generation";
31207
31604
  const { prTitle, prBody } = parseCommitMessageForPR(finalCommitMessage);
31208
- const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch });
31605
+ const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch, repoUri: this.config.uri });
31209
31606
  const enrichedBody = replaySection != null ? prBody + "\n\n---\n\n" + replaySection : prBody;
31210
31607
  if (isUpdatingExistingPR && existingPR != null) {
31211
31608
  this.logger.info(`Updated existing pull request: ${existingPR.htmlUrl}`);
@@ -31227,7 +31624,6 @@ var GithubStep = class extends BaseStep {
31227
31624
  }
31228
31625
  } else {
31229
31626
  const head = `${owner}:${prBranch}`;
31230
- const hasConflicts = replayConflictInfo?.hasConflicts === true;
31231
31627
  try {
31232
31628
  const { data: pullRequest } = await octokit.pulls.create({
31233
31629
  owner,
@@ -31235,8 +31631,7 @@ var GithubStep = class extends BaseStep {
31235
31631
  title: prTitle,
31236
31632
  body: enrichedBody,
31237
31633
  head,
31238
- base: baseBranch,
31239
- draft: hasConflicts
31634
+ base: baseBranch
31240
31635
  });
31241
31636
  this.logger.info(`Created pull request: ${pullRequest.html_url}`);
31242
31637
  result.prUrl = pullRequest.html_url;
@@ -31250,17 +31645,6 @@ var GithubStep = class extends BaseStep {
31250
31645
  }
31251
31646
  }
31252
31647
  }
31253
- if (!this.config.previewMode && result.prNumber != null && replayConflictInfo?.hasConflicts === true && replayResult != null) {
31254
- await this.postWebEditorFallbackComment(
31255
- octokit,
31256
- owner,
31257
- repo,
31258
- result.prNumber,
31259
- prBranch,
31260
- baseBranch,
31261
- replayResult
31262
- );
31263
- }
31264
31648
  return result;
31265
31649
  }
31266
31650
  async executePushMode(repository) {
@@ -31306,198 +31690,17 @@ var GithubStep = class extends BaseStep {
31306
31690
  if (replayResult == null) {
31307
31691
  return void 0;
31308
31692
  }
31309
- const hasConflicts = (replayResult.patchesWithConflicts ?? 0) > 0;
31310
31693
  const previousGenerationSha = replayResult.previousGenerationSha;
31311
31694
  const currentGenerationSha = replayResult.currentGenerationSha;
31312
31695
  if (previousGenerationSha != null && currentGenerationSha != null) {
31313
31696
  return {
31314
31697
  previousGenerationSha,
31315
- currentGenerationSha,
31316
- hasConflicts,
31317
- baseBranchHead: replayResult.baseBranchHead
31698
+ currentGenerationSha
31318
31699
  };
31319
31700
  }
31320
31701
  return void 0;
31321
31702
  }
31322
- async togglePrDraftState(octokit, existingPR, replayConflictInfo) {
31323
- const hasConflicts = replayConflictInfo?.hasConflicts === true;
31324
- if (hasConflicts && !existingPR.isDraft) {
31325
- await this.convertPrToDraft(octokit, existingPR.nodeId, existingPR.number);
31326
- } else if (!hasConflicts && existingPR.isDraft) {
31327
- await this.markPrReady(octokit, existingPR.nodeId, existingPR.number);
31328
- }
31329
- }
31330
- async convertPrToDraft(octokit, nodeId, prNumber) {
31331
- try {
31332
- await octokit.graphql(
31333
- `mutation($id: ID!) {
31334
- convertPullRequestToDraft(input: {pullRequestId: $id}) {
31335
- pullRequest { isDraft }
31336
- }
31337
- }`,
31338
- { id: nodeId }
31339
- );
31340
- this.logger.info(`Converted PR #${prNumber} to draft due to replay conflicts`);
31341
- } catch (error) {
31342
- this.logger.debug(
31343
- `Could not convert PR to draft: ${error instanceof Error ? error.message : String(error)}`
31344
- );
31345
- }
31346
- }
31347
- async markPrReady(octokit, nodeId, prNumber) {
31348
- try {
31349
- await octokit.graphql(
31350
- `mutation($id: ID!) {
31351
- markPullRequestReadyForReview(input: {pullRequestId: $id}) {
31352
- pullRequest { isDraft }
31353
- }
31354
- }`,
31355
- { id: nodeId }
31356
- );
31357
- this.logger.info(`Marked PR #${prNumber} as ready (conflicts resolved)`);
31358
- } catch (error) {
31359
- this.logger.debug(`Could not mark PR as ready: ${error instanceof Error ? error.message : String(error)}`);
31360
- }
31361
- }
31362
- async postReplayConflictStatus(octokit, owner, repo, sha, replayConflictInfo, replayResult) {
31363
- if (replayResult == null || !replayResult.executed) {
31364
- return;
31365
- }
31366
- const hasConflicts = replayConflictInfo?.hasConflicts === true;
31367
- const conflictFileCount = (replayResult.conflictDetails ?? []).reduce(
31368
- (sum, detail) => sum + detail.files.length,
31369
- 0
31370
- );
31371
- const sanitizedName = this.config.generatorName?.replace(/\//g, "--");
31372
- const context = sanitizedName != null ? `fern / sdk customizations / ${sanitizedName}` : "fern / sdk customizations";
31373
- try {
31374
- await octokit.repos.createCommitStatus({
31375
- owner,
31376
- repo,
31377
- sha,
31378
- state: hasConflicts ? "failure" : "success",
31379
- context,
31380
- description: hasConflicts ? `${conflictFileCount} file(s) need manual conflict resolution \u2014 see PR description` : "All customizations applied"
31381
- });
31382
- this.logger.debug(`Posted ${hasConflicts ? "failing" : "passing"} commit status (${context})`);
31383
- } catch (error) {
31384
- this.logger.debug(
31385
- `Could not post commit status: ${error instanceof Error ? error.message : String(error)}`
31386
- );
31387
- }
31388
- }
31389
- async postWebEditorFallbackComment(octokit, owner, repo, prNumber, branchName, baseBranch, replayResult) {
31390
- try {
31391
- const webEditorLikelyDisabled = await this.isWebEditorLikelyDisabled(
31392
- octokit,
31393
- owner,
31394
- repo,
31395
- prNumber,
31396
- replayResult
31397
- );
31398
- if (!webEditorLikelyDisabled) {
31399
- this.logger.debug(`PR #${prNumber}: web conflict editor appears usable, skipping fallback comment`);
31400
- return;
31401
- }
31402
- const comment = buildWebEditorFallbackComment(branchName, baseBranch);
31403
- await octokit.issues.createComment({
31404
- owner,
31405
- repo,
31406
- issue_number: prNumber,
31407
- body: comment
31408
- });
31409
- this.logger.info(`Posted web-editor fallback comment on PR #${prNumber}`);
31410
- } catch (error) {
31411
- this.logger.debug(
31412
- `Could not post web-editor fallback comment: ${error instanceof Error ? error.message : String(error)}`
31413
- );
31414
- }
31415
- }
31416
- async isWebEditorLikelyDisabled(octokit, owner, repo, prNumber, replayResult) {
31417
- const conflictDetails = replayResult.conflictDetails ?? [];
31418
- const allConflictFiles = conflictDetails.flatMap((d) => d.files);
31419
- const totalConflictFiles = allConflictFiles.length;
31420
- const hasFileDeletionConflict = allConflictFiles.some((f) => {
31421
- const status2 = (f.status ?? "").toLowerCase();
31422
- const reason = (f.conflictReason ?? "").toLowerCase();
31423
- return status2 === "skipped" || reason.includes("delete") || reason.includes("removed");
31424
- });
31425
- if (hasFileDeletionConflict) {
31426
- this.logger.debug(`PR #${prNumber}: detected file deletion conflict, web editor likely disabled`);
31427
- return true;
31428
- }
31429
- if (totalConflictFiles >= WEB_EDITOR_FILE_THRESHOLD) {
31430
- this.logger.debug(
31431
- `PR #${prNumber}: ${totalConflictFiles} conflicting files exceeds threshold (${WEB_EDITOR_FILE_THRESHOLD}), web editor likely disabled`
31432
- );
31433
- return true;
31434
- }
31435
- const totalHunks = (replayResult.conflicts ?? []).reduce(
31436
- (sum, fileConflict) => sum + fileConflict.conflicts.length,
31437
- 0
31438
- );
31439
- if (totalHunks >= WEB_EDITOR_HUNK_THRESHOLD) {
31440
- this.logger.debug(
31441
- `PR #${prNumber}: ${totalHunks} conflict hunks exceeds threshold (${WEB_EDITOR_HUNK_THRESHOLD}), web editor likely disabled`
31442
- );
31443
- return true;
31444
- }
31445
- const mergeableState = await this.pollMergeableState(octokit, owner, repo, prNumber);
31446
- if (mergeableState.mergeable === null) {
31447
- this.logger.debug(`PR #${prNumber}: mergeable state still null after polling, web editor likely disabled`);
31448
- return true;
31449
- }
31450
- return false;
31451
- }
31452
- async pollMergeableState(octokit, owner, repo, prNumber) {
31453
- for (let attempt2 = 0; attempt2 < MERGEABLE_POLL_MAX_ATTEMPTS; attempt2++) {
31454
- const { data: pr } = await octokit.pulls.get({
31455
- owner,
31456
- repo,
31457
- pull_number: prNumber
31458
- });
31459
- if (pr.mergeable != null) {
31460
- return {
31461
- mergeable: pr.mergeable,
31462
- mergeableState: pr.mergeable_state
31463
- };
31464
- }
31465
- if (attempt2 < MERGEABLE_POLL_MAX_ATTEMPTS - 1) {
31466
- await sleep(MERGEABLE_POLL_DELAY_MS);
31467
- }
31468
- }
31469
- return { mergeable: null, mergeableState: "unknown" };
31470
- }
31471
31703
  };
31472
- var WEB_EDITOR_FILE_THRESHOLD = 20;
31473
- var WEB_EDITOR_HUNK_THRESHOLD = 50;
31474
- var MERGEABLE_POLL_MAX_ATTEMPTS = 3;
31475
- var MERGEABLE_POLL_DELAY_MS = 1500;
31476
- function buildWebEditorFallbackComment(branchName, baseBranch) {
31477
- const lines = [
31478
- `> **Note:** These conflicts may be too complex for GitHub's web editor. To resolve locally:`,
31479
- `>`,
31480
- `> 1. Check out this branch:`,
31481
- `> \`\`\`sh`,
31482
- `> git fetch origin && git checkout ${branchName}`,
31483
- `> \`\`\``,
31484
- `> 2. Merge the base branch:`,
31485
- `> \`\`\`sh`,
31486
- `> git merge origin/${baseBranch}`,
31487
- `> \`\`\``,
31488
- `> 3. Resolve conflicts in your editor (VS Code, IntelliJ, etc. have built-in merge tools)`,
31489
- `> 4. Commit and push:`,
31490
- `> \`\`\`sh`,
31491
- `> git add -A && git commit -m "resolve conflicts" && git push`,
31492
- `> \`\`\``,
31493
- `>`,
31494
- `> **Label guide:** In your editor's merge view, "current" = new generated code, "incoming" = your customization.`
31495
- ];
31496
- return lines.join("\n");
31497
- }
31498
- function sleep(ms) {
31499
- return new Promise((resolve8) => setTimeout(resolve8, ms));
31500
- }
31501
31704
 
31502
31705
  // src/replay/replay-run.ts
31503
31706
  init_dist();
@@ -31706,8 +31909,7 @@ var ReplayStep = class extends BaseStep {
31706
31909
  flow: "first-generation",
31707
31910
  patchesDetected: 0,
31708
31911
  patchesApplied: 0,
31709
- patchesWithConflicts: 0,
31710
- conflicts: []
31912
+ patchesWithConflicts: 0
31711
31913
  };
31712
31914
  }
31713
31915
  const report = result.report;
@@ -31725,22 +31927,12 @@ var ReplayStep = class extends BaseStep {
31725
31927
  patchesRepointed: report.patchesRepointed,
31726
31928
  patchesContentRebased: report.patchesContentRebased,
31727
31929
  patchesKeptAsUserOwned: report.patchesKeptAsUserOwned,
31728
- conflicts: report.conflicts.map((fileResult) => ({
31729
- filePath: fileResult.file,
31730
- conflicts: fileResult.conflicts?.map((conflict) => ({
31731
- startLine: conflict.startLine,
31732
- endLine: conflict.endLine,
31733
- ours: conflict.ours,
31734
- theirs: conflict.theirs
31735
- })) ?? []
31736
- })),
31737
- conflictDetails: report.conflictDetails?.map((detail) => ({
31738
- patchId: detail.patchId,
31739
- patchMessage: detail.patchMessage,
31740
- reason: detail.reason,
31741
- files: detail.files.map((f) => ({
31930
+ unresolvedPatches: report.unresolvedPatches?.map((info) => ({
31931
+ patchId: info.patchId,
31932
+ patchMessage: info.patchMessage,
31933
+ files: info.files,
31934
+ conflictDetails: info.conflictDetails.map((f) => ({
31742
31935
  file: f.file,
31743
- status: f.status,
31744
31936
  conflictReason: f.conflictReason
31745
31937
  }))
31746
31938
  })),
@@ -31864,7 +32056,10 @@ Base generation: ${bootstrapResult.generationCommit.sha.slice(0, 7)}`;
31864
32056
  `user.name=${FERN_BOT_NAME2}`,
31865
32057
  "-c",
31866
32058
  `user.email=${FERN_BOT_EMAIL2}`,
32059
+ "-c",
32060
+ "commit.gpgsign=false",
31867
32061
  "commit",
32062
+ "--no-verify",
31868
32063
  "-m",
31869
32064
  commitMessage
31870
32065
  ]);
@@ -32001,6 +32196,9 @@ function buildPrBody(result) {
32001
32196
  return lines.join("\n");
32002
32197
  }
32003
32198
 
32199
+ // src/replay/replay-resolve.ts
32200
+ init_dist();
32201
+
32004
32202
  // src/configuration/loadGitHubConfig.ts
32005
32203
  var import_promises6 = require("fs/promises");
32006
32204
  async function loadGitHubConfig({