@fern-api/generator-cli 0.6.10 → 0.6.14

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 +21 -19
  2. package/dist/api.js +282 -480
  3. package/dist/cli.js +444 -455
  4. package/package.json +2 -2
package/dist/api.js CHANGED
@@ -893,18 +893,16 @@ __export(api_exports2, {
893
893
  githubRelease: () => githubRelease,
894
894
  logReplaySummary: () => logReplaySummary,
895
895
  replayInit: () => replayInit,
896
+ replayResolve: () => replayResolve,
896
897
  replayRun: () => replayRun
897
898
  });
898
899
  module.exports = __toCommonJS(api_exports2);
899
900
 
900
- // src/api/generate-readme.ts
901
- var import_node_stream = require("stream");
902
-
903
901
  // ../commons/github/lib/ClonedRepository.js
904
902
  var import_promises = require("fs/promises");
905
903
  var import_path = __toESM(require("path"), 1);
906
904
 
907
- // ../../node_modules/.pnpm/simple-git@3.30.0/node_modules/simple-git/dist/esm/index.js
905
+ // ../../node_modules/.pnpm/simple-git@3.33.0/node_modules/simple-git/dist/esm/index.js
908
906
  var import_node_buffer = require("buffer");
909
907
  var import_file_exists = __toESM(require_dist(), 1);
910
908
  var import_debug = __toESM(require_src(), 1);
@@ -1155,7 +1153,7 @@ var init_argument_filters = __esm({
1155
1153
  return typeof input === "number";
1156
1154
  };
1157
1155
  filterString = (input) => {
1158
- return typeof input === "string";
1156
+ return typeof input === "string" || isPathSpec(input);
1159
1157
  };
1160
1158
  filterStringOrStringArray = (input) => {
1161
1159
  return filterString(input) || Array.isArray(input) && input.every(filterString);
@@ -3530,15 +3528,15 @@ function parser3(indexX, indexY, handler) {
3530
3528
  return [`${indexX}${indexY}`, handler];
3531
3529
  }
3532
3530
  function conflicts(indexX, ...indexY) {
3533
- return indexY.map((y) => parser3(indexX, y, (result, file) => append(result.conflicted, file)));
3531
+ return indexY.map((y) => parser3(indexX, y, (result, file) => result.conflicted.push(file)));
3534
3532
  }
3535
3533
  function splitLine(result, lineStr) {
3536
3534
  const trimmed2 = lineStr.trim();
3537
3535
  switch (" ") {
3538
3536
  case trimmed2.charAt(2):
3539
- return data(trimmed2.charAt(0), trimmed2.charAt(1), trimmed2.substr(3));
3537
+ return data(trimmed2.charAt(0), trimmed2.charAt(1), trimmed2.slice(3));
3540
3538
  case trimmed2.charAt(1):
3541
- return data(" ", trimmed2.charAt(0), trimmed2.substr(2));
3539
+ return data(" ", trimmed2.charAt(0), trimmed2.slice(2));
3542
3540
  default:
3543
3541
  return;
3544
3542
  }
@@ -3586,58 +3584,54 @@ var init_StatusSummary = __esm({
3586
3584
  parser3(
3587
3585
  " ",
3588
3586
  "A",
3589
- (result, file) => append(result.created, file)
3587
+ (result, file) => result.created.push(file)
3590
3588
  ),
3591
3589
  parser3(
3592
3590
  " ",
3593
3591
  "D",
3594
- (result, file) => append(result.deleted, file)
3595
- ),
3596
- parser3(
3597
- " ",
3598
- "M",
3599
- (result, file) => append(result.modified, file)
3592
+ (result, file) => result.deleted.push(file)
3600
3593
  ),
3601
3594
  parser3(
3602
- "A",
3603
3595
  " ",
3604
- (result, file) => append(result.created, file) && append(result.staged, file)
3605
- ),
3606
- parser3(
3607
- "A",
3608
3596
  "M",
3609
- (result, file) => append(result.created, file) && append(result.staged, file) && append(result.modified, file)
3610
- ),
3611
- parser3(
3612
- "D",
3613
- " ",
3614
- (result, file) => append(result.deleted, file) && append(result.staged, file)
3615
- ),
3616
- parser3(
3617
- "M",
3618
- " ",
3619
- (result, file) => append(result.modified, file) && append(result.staged, file)
3620
- ),
3621
- parser3(
3622
- "M",
3623
- "M",
3624
- (result, file) => append(result.modified, file) && append(result.staged, file)
3597
+ (result, file) => result.modified.push(file)
3625
3598
  ),
3599
+ parser3("A", " ", (result, file) => {
3600
+ result.created.push(file);
3601
+ result.staged.push(file);
3602
+ }),
3603
+ parser3("A", "M", (result, file) => {
3604
+ result.created.push(file);
3605
+ result.staged.push(file);
3606
+ result.modified.push(file);
3607
+ }),
3608
+ parser3("D", " ", (result, file) => {
3609
+ result.deleted.push(file);
3610
+ result.staged.push(file);
3611
+ }),
3612
+ parser3("M", " ", (result, file) => {
3613
+ result.modified.push(file);
3614
+ result.staged.push(file);
3615
+ }),
3616
+ parser3("M", "M", (result, file) => {
3617
+ result.modified.push(file);
3618
+ result.staged.push(file);
3619
+ }),
3626
3620
  parser3("R", " ", (result, file) => {
3627
- append(result.renamed, renamedFile(file));
3621
+ result.renamed.push(renamedFile(file));
3628
3622
  }),
3629
3623
  parser3("R", "M", (result, file) => {
3630
3624
  const renamed = renamedFile(file);
3631
- append(result.renamed, renamed);
3632
- append(result.modified, renamed.to);
3625
+ result.renamed.push(renamed);
3626
+ result.modified.push(renamed.to);
3633
3627
  }),
3634
3628
  parser3("!", "!", (_result, _file) => {
3635
- append(_result.ignored = _result.ignored || [], _file);
3629
+ (_result.ignored = _result.ignored || []).push(_file);
3636
3630
  }),
3637
3631
  parser3(
3638
3632
  "?",
3639
3633
  "?",
3640
- (result, file) => append(result.not_added, file)
3634
+ (result, file) => result.not_added.push(file)
3641
3635
  ),
3642
3636
  ...conflicts(
3643
3637
  "A",
@@ -3795,6 +3789,48 @@ var init_version = __esm({
3795
3789
  ];
3796
3790
  }
3797
3791
  });
3792
+ function createCloneTask(api, task, repoPath, ...args) {
3793
+ if (!filterString(repoPath)) {
3794
+ return configurationErrorTask(`git.${api}() requires a string 'repoPath'`);
3795
+ }
3796
+ return task(repoPath, filterType(args[0], filterString), getTrailingOptions(arguments));
3797
+ }
3798
+ function clone_default() {
3799
+ return {
3800
+ clone(repo, ...rest) {
3801
+ return this._runTask(
3802
+ createCloneTask("clone", cloneTask, filterType(repo, filterString), ...rest),
3803
+ trailingFunctionArgument(arguments)
3804
+ );
3805
+ },
3806
+ mirror(repo, ...rest) {
3807
+ return this._runTask(
3808
+ createCloneTask("mirror", cloneMirrorTask, filterType(repo, filterString), ...rest),
3809
+ trailingFunctionArgument(arguments)
3810
+ );
3811
+ }
3812
+ };
3813
+ }
3814
+ var cloneTask;
3815
+ var cloneMirrorTask;
3816
+ var init_clone = __esm({
3817
+ "src/lib/tasks/clone.ts"() {
3818
+ "use strict";
3819
+ init_task();
3820
+ init_utils();
3821
+ init_pathspec();
3822
+ cloneTask = (repo, directory, customArgs) => {
3823
+ const commands = ["clone", ...customArgs];
3824
+ filterString(repo) && commands.push(pathspec(repo));
3825
+ filterString(directory) && commands.push(pathspec(directory));
3826
+ return straightThroughStringTask(commands);
3827
+ };
3828
+ cloneMirrorTask = (repo, directory, customArgs) => {
3829
+ append(customArgs, "--mirror");
3830
+ return cloneTask(repo, directory, customArgs);
3831
+ };
3832
+ }
3833
+ });
3798
3834
  var simple_git_api_exports = {};
3799
3835
  __export2(simple_git_api_exports, {
3800
3836
  SimpleGitApi: () => SimpleGitApi
@@ -3821,6 +3857,7 @@ var init_simple_git_api = __esm({
3821
3857
  init_task();
3822
3858
  init_version();
3823
3859
  init_utils();
3860
+ init_clone();
3824
3861
  SimpleGitApi = class {
3825
3862
  constructor(_executor) {
3826
3863
  this._executor = _executor;
@@ -3923,6 +3960,7 @@ var init_simple_git_api = __esm({
3923
3960
  Object.assign(
3924
3961
  SimpleGitApi.prototype,
3925
3962
  checkout_default(),
3963
+ clone_default(),
3926
3964
  commit_default(),
3927
3965
  config_default(),
3928
3966
  count_objects_default(),
@@ -4246,35 +4284,6 @@ var init_check_ignore = __esm({
4246
4284
  init_CheckIgnore();
4247
4285
  }
4248
4286
  });
4249
- var clone_exports = {};
4250
- __export2(clone_exports, {
4251
- cloneMirrorTask: () => cloneMirrorTask,
4252
- cloneTask: () => cloneTask
4253
- });
4254
- function disallowedCommand(command) {
4255
- return /^--upload-pack(=|$)/.test(command);
4256
- }
4257
- function cloneTask(repo, directory, customArgs) {
4258
- const commands = ["clone", ...customArgs];
4259
- filterString(repo) && commands.push(repo);
4260
- filterString(directory) && commands.push(directory);
4261
- const banned = commands.find(disallowedCommand);
4262
- if (banned) {
4263
- return configurationErrorTask(`git.fetch: potential exploit argument blocked.`);
4264
- }
4265
- return straightThroughStringTask(commands);
4266
- }
4267
- function cloneMirrorTask(repo, directory, customArgs) {
4268
- append(customArgs, "--mirror");
4269
- return cloneTask(repo, directory, customArgs);
4270
- }
4271
- var init_clone = __esm({
4272
- "src/lib/tasks/clone.ts"() {
4273
- "use strict";
4274
- init_task();
4275
- init_utils();
4276
- }
4277
- });
4278
4287
  function parseFetchResult(stdOut, stdErr) {
4279
4288
  const result = {
4280
4289
  raw: stdOut,
@@ -4330,7 +4339,7 @@ var fetch_exports = {};
4330
4339
  __export2(fetch_exports, {
4331
4340
  fetchTask: () => fetchTask
4332
4341
  });
4333
- function disallowedCommand2(command) {
4342
+ function disallowedCommand(command) {
4334
4343
  return /^--upload-pack(=|$)/.test(command);
4335
4344
  }
4336
4345
  function fetchTask(remote, branch, customArgs) {
@@ -4338,7 +4347,7 @@ function fetchTask(remote, branch, customArgs) {
4338
4347
  if (remote && branch) {
4339
4348
  commands.push(remote, branch);
4340
4349
  }
4341
- const banned = commands.find(disallowedCommand2);
4350
+ const banned = commands.find(disallowedCommand);
4342
4351
  if (banned) {
4343
4352
  return configurationErrorTask(`git.fetch: potential exploit argument blocked.`);
4344
4353
  }
@@ -4654,7 +4663,7 @@ var require_git = __commonJS2({
4654
4663
  var { GitExecutor: GitExecutor2 } = (init_git_executor(), __toCommonJS2(git_executor_exports));
4655
4664
  var { SimpleGitApi: SimpleGitApi2 } = (init_simple_git_api(), __toCommonJS2(simple_git_api_exports));
4656
4665
  var { Scheduler: Scheduler2 } = (init_scheduler(), __toCommonJS2(scheduler_exports));
4657
- var { configurationErrorTask: configurationErrorTask2 } = (init_task(), __toCommonJS2(task_exports));
4666
+ var { adhocExecTask: adhocExecTask2, configurationErrorTask: configurationErrorTask2 } = (init_task(), __toCommonJS2(task_exports));
4658
4667
  var {
4659
4668
  asArray: asArray2,
4660
4669
  filterArray: filterArray2,
@@ -4675,7 +4684,6 @@ var require_git = __commonJS2({
4675
4684
  } = (init_branch(), __toCommonJS2(branch_exports));
4676
4685
  var { checkIgnoreTask: checkIgnoreTask2 } = (init_check_ignore(), __toCommonJS2(check_ignore_exports));
4677
4686
  var { checkIsRepoTask: checkIsRepoTask2 } = (init_check_is_repo(), __toCommonJS2(check_is_repo_exports));
4678
- var { cloneTask: cloneTask2, cloneMirrorTask: cloneMirrorTask2 } = (init_clone(), __toCommonJS2(clone_exports));
4679
4687
  var { cleanWithOptionsTask: cleanWithOptionsTask2, isCleanOptionsArray: isCleanOptionsArray2 } = (init_clean(), __toCommonJS2(clean_exports));
4680
4688
  var { diffSummaryTask: diffSummaryTask2 } = (init_diff(), __toCommonJS2(diff_exports));
4681
4689
  var { fetchTask: fetchTask2 } = (init_fetch(), __toCommonJS2(fetch_exports));
@@ -4730,24 +4738,6 @@ var require_git = __commonJS2({
4730
4738
  trailingFunctionArgument2(arguments)
4731
4739
  );
4732
4740
  };
4733
- function createCloneTask(api, task, repoPath, localPath) {
4734
- if (typeof repoPath !== "string") {
4735
- return configurationErrorTask2(`git.${api}() requires a string 'repoPath'`);
4736
- }
4737
- return task(repoPath, filterType2(localPath, filterString2), getTrailingOptions2(arguments));
4738
- }
4739
- Git2.prototype.clone = function() {
4740
- return this._runTask(
4741
- createCloneTask("clone", cloneTask2, ...arguments),
4742
- trailingFunctionArgument2(arguments)
4743
- );
4744
- };
4745
- Git2.prototype.mirror = function() {
4746
- return this._runTask(
4747
- createCloneTask("mirror", cloneMirrorTask2, ...arguments),
4748
- trailingFunctionArgument2(arguments)
4749
- );
4750
- };
4751
4741
  Git2.prototype.mv = function(from, to) {
4752
4742
  return this._runTask(moveTask2(from, to), trailingFunctionArgument2(arguments));
4753
4743
  };
@@ -4780,10 +4770,13 @@ var require_git = __commonJS2({
4780
4770
  );
4781
4771
  };
4782
4772
  Git2.prototype.silent = function(silence) {
4783
- console.warn(
4784
- "simple-git deprecation notice: git.silent: logging should be configured using the `debug` library / `DEBUG` environment variable, this will be an error in version 3"
4773
+ return this._runTask(
4774
+ adhocExecTask2(
4775
+ () => console.warn(
4776
+ "simple-git deprecation notice: git.silent: logging should be configured using the `debug` library / `DEBUG` environment variable, this method will be removed."
4777
+ )
4778
+ )
4785
4779
  );
4786
- return this;
4787
4780
  };
4788
4781
  Git2.prototype.tags = function(options, then) {
4789
4782
  return this._runTask(
@@ -5009,7 +5002,13 @@ var require_git = __commonJS2({
5009
5002
  return this._runTask(task);
5010
5003
  };
5011
5004
  Git2.prototype.clearQueue = function() {
5012
- return this;
5005
+ return this._runTask(
5006
+ adhocExecTask2(
5007
+ () => console.warn(
5008
+ "simple-git deprecation notice: clearQueue() is deprecated and will be removed, switch to using the abortPlugin instead."
5009
+ )
5010
+ )
5011
+ );
5013
5012
  };
5014
5013
  Git2.prototype.checkIgnore = function(pathnames, then) {
5015
5014
  return this._runTask(
@@ -5079,19 +5078,36 @@ function abortPlugin(signal) {
5079
5078
  function isConfigSwitch(arg) {
5080
5079
  return typeof arg === "string" && arg.trim().toLowerCase() === "-c";
5081
5080
  }
5082
- function preventProtocolOverride(arg, next) {
5083
- if (!isConfigSwitch(arg)) {
5084
- return;
5085
- }
5086
- if (!/^\s*protocol(.[a-z]+)?.allow/.test(next)) {
5087
- return;
5081
+ function isCloneUploadPackSwitch(char, arg) {
5082
+ if (typeof arg !== "string" || !arg.includes(char)) {
5083
+ return false;
5088
5084
  }
5089
- throw new GitPluginError(
5090
- void 0,
5091
- "unsafe",
5092
- "Configuring protocol.allow is not permitted without enabling allowUnsafeExtProtocol"
5093
- );
5085
+ const cleaned = arg.trim().replace(/\0/g, "");
5086
+ return /^(--no)?-{1,2}[\dlsqvnobucj]+(\s|$)/.test(cleaned);
5087
+ }
5088
+ function preventConfigBuilder(config, setting, message = String(config)) {
5089
+ const regex = typeof config === "string" ? new RegExp(`\\s*${config}`, "i") : config;
5090
+ return function preventCommand(options, arg, next) {
5091
+ if (options[setting] !== true && isConfigSwitch(arg) && regex.test(next)) {
5092
+ throw new GitPluginError(
5093
+ void 0,
5094
+ "unsafe",
5095
+ `Configuring ${message} is not permitted without enabling ${setting}`
5096
+ );
5097
+ }
5098
+ };
5094
5099
  }
5100
+ var preventUnsafeConfig = [
5101
+ preventConfigBuilder(
5102
+ /^\s*protocol(.[a-z]+)?.allow/i,
5103
+ "allowUnsafeProtocolOverride",
5104
+ "protocol.allow"
5105
+ ),
5106
+ preventConfigBuilder("core.sshCommand", "allowUnsafeSshCommand"),
5107
+ preventConfigBuilder("core.gitProxy", "allowUnsafeGitProxy"),
5108
+ preventConfigBuilder("core.hooksPath", "allowUnsafeHooksPath"),
5109
+ preventConfigBuilder("diff.external", "allowUnsafeDiffExternal")
5110
+ ];
5095
5111
  function preventUploadPack(arg, method) {
5096
5112
  if (/^\s*--(upload|receive)-pack/.test(arg)) {
5097
5113
  throw new GitPluginError(
@@ -5100,7 +5116,7 @@ function preventUploadPack(arg, method) {
5100
5116
  `Use of --upload-pack or --receive-pack is not permitted without enabling allowUnsafePack`
5101
5117
  );
5102
5118
  }
5103
- if (method === "clone" && /^\s*-u\b/.test(arg)) {
5119
+ if (method === "clone" && isCloneUploadPackSwitch("u", arg)) {
5104
5120
  throw new GitPluginError(
5105
5121
  void 0,
5106
5122
  "unsafe",
@@ -5116,16 +5132,16 @@ function preventUploadPack(arg, method) {
5116
5132
  }
5117
5133
  }
5118
5134
  function blockUnsafeOperationsPlugin({
5119
- allowUnsafeProtocolOverride = false,
5120
- allowUnsafePack = false
5135
+ allowUnsafePack = false,
5136
+ ...options
5121
5137
  } = {}) {
5122
5138
  return {
5123
5139
  type: "spawn.args",
5124
5140
  action(args, context) {
5125
5141
  args.forEach((current, index) => {
5126
5142
  const next = index < args.length ? args[index + 1] : "";
5127
- allowUnsafeProtocolOverride || preventProtocolOverride(current, next);
5128
5143
  allowUnsafePack || preventUploadPack(current, context.method);
5144
+ preventUnsafeConfig.forEach((helper) => helper(options, current, next));
5129
5145
  });
5130
5146
  return args;
5131
5147
  }
@@ -5209,7 +5225,7 @@ init_utils();
5209
5225
  var WRONG_NUMBER_ERR = `Invalid value supplied for custom binary, requires a single string or an array containing either one or two strings`;
5210
5226
  var WRONG_CHARS_ERR = `Invalid value supplied for custom binary, restricted characters must be removed or supply the unsafe.allowUnsafeCustomBinary option`;
5211
5227
  function isBadArgument(arg) {
5212
- return !arg || !/^([a-z]:)?([a-z0-9/.\\_-]+)$/i.test(arg);
5228
+ return !arg || !/^([a-z]:)?([a-z0-9/.\\_~-]+)$/i.test(arg);
5213
5229
  }
5214
5230
  function toBinaryConfig(input, allowUnsafe) {
5215
5231
  if (input.length < 1 || input.length > 2) {
@@ -5437,12 +5453,12 @@ function gitInstanceFactory(baseDir, options) {
5437
5453
  plugins.add(commandConfigPrefixingPlugin(config.config));
5438
5454
  }
5439
5455
  plugins.add(blockUnsafeOperationsPlugin(config.unsafe));
5440
- plugins.add(suffixPathsPlugin());
5441
5456
  plugins.add(completionDetectionPlugin(config.completion));
5442
5457
  config.abort && plugins.add(abortPlugin(config.abort));
5443
5458
  config.progress && plugins.add(progressMonitorPlugin(config.progress));
5444
5459
  config.timeout && plugins.add(timeoutPlugin(config.timeout));
5445
5460
  config.spawnOptions && plugins.add(spawnOptionsPlugin(config.spawnOptions));
5461
+ plugins.add(suffixPathsPlugin());
5446
5462
  plugins.add(errorDetectionPlugin(errorDetectionHandler(true)));
5447
5463
  config.errors && plugins.add(errorDetectionPlugin(config.errors));
5448
5464
  customBinaryPlugin(plugins, config.binary, config.unsafe?.allowUnsafeCustomBinary);
@@ -5750,6 +5766,21 @@ async function doesPathExist(filepath) {
5750
5766
  }
5751
5767
  }
5752
5768
 
5769
+ // ../commons/core-utils/lib/extractErrorMessage.js
5770
+ function extractErrorMessage(e) {
5771
+ if (typeof e === "string") {
5772
+ return e.length > 0 ? e : "Unknown error";
5773
+ }
5774
+ if (e instanceof Error) {
5775
+ return e.message.length > 0 ? e.message : "Unknown error";
5776
+ }
5777
+ if (typeof e === "object" && e !== null && "message" in e && typeof e["message"] === "string") {
5778
+ return e["message"].length > 0 ? e["message"] : "Unknown error";
5779
+ }
5780
+ const stringified = String(e);
5781
+ return stringified.length > 0 ? stringified : "Unknown error";
5782
+ }
5783
+
5753
5784
  // ../commons/github/lib/cloneRepository.js
5754
5785
  var import_child_process2 = require("child_process");
5755
5786
  var import_tmp_promise = __toESM(require("tmp-promise"), 1);
@@ -5771,7 +5802,7 @@ function parseRepository(githubRepository) {
5771
5802
  } else if (parts.length === 3 && parts[0] != null && parts[1] != null && parts[2] != null) {
5772
5803
  [remote, owner, repo] = parts;
5773
5804
  } else {
5774
- throw new Error(`Failed to parse GitHub repostiory ${githubRepository}`);
5805
+ throw new Error(`Failed to parse GitHub repository ${githubRepository}`);
5775
5806
  }
5776
5807
  return newRepositoryReference({ remote, owner, repo });
5777
5808
  }
@@ -5852,7 +5883,7 @@ async function cloneRepository({ githubRepository, installationToken, targetDire
5852
5883
  await git.clone(cloneUrl, ".");
5853
5884
  } catch (error) {
5854
5885
  const elapsed = Date.now() - startTime;
5855
- const errorMessage = error instanceof Error ? error.message : String(error);
5886
+ const errorMessage = extractErrorMessage(error);
5856
5887
  if (errorMessage.includes("ENOENT") || errorMessage.includes("spawn git")) {
5857
5888
  const diagnostics = getGitDiagnostics();
5858
5889
  throw new Error(`Failed to clone repository: git command not found. Self-hosted GitHub mode requires git to be installed on the host machine where the Fern CLI runs. URL: ${sanitizedUrl}. Elapsed: ${elapsed}ms. Debug info: ${diagnostics}. Original error: ${errorMessage}`);
@@ -8256,7 +8287,7 @@ var StreamWriter = class extends Writer {
8256
8287
  this.stream = stream;
8257
8288
  }
8258
8289
  async write(content) {
8259
- return new Promise((resolve3, reject) => {
8290
+ return new Promise((resolve4, reject) => {
8260
8291
  const handleError = (err) => {
8261
8292
  this.stream.removeListener("error", handleError);
8262
8293
  reject(err);
@@ -8267,12 +8298,12 @@ var StreamWriter = class extends Writer {
8267
8298
  if (error) {
8268
8299
  reject(error);
8269
8300
  } else {
8270
- resolve3();
8301
+ resolve4();
8271
8302
  }
8272
8303
  });
8273
8304
  this.stream.once("error", handleError);
8274
8305
  if (!canWrite) {
8275
- this.stream.once("drain", resolve3);
8306
+ this.stream.once("drain", resolve4);
8276
8307
  }
8277
8308
  } else {
8278
8309
  const canWrite = this.stream.write(content);
@@ -8280,26 +8311,26 @@ var StreamWriter = class extends Writer {
8280
8311
  if (!canWrite) {
8281
8312
  this.stream.once("drain", () => {
8282
8313
  this.stream.removeListener("error", handleError);
8283
- resolve3();
8314
+ resolve4();
8284
8315
  });
8285
8316
  } else {
8286
8317
  this.stream.removeListener("error", handleError);
8287
- resolve3();
8318
+ resolve4();
8288
8319
  }
8289
8320
  }
8290
8321
  });
8291
8322
  }
8292
8323
  end() {
8293
- return new Promise((resolve3, reject) => {
8324
+ return new Promise((resolve4, reject) => {
8294
8325
  if (this.stream instanceof import_fs.default.WriteStream) {
8295
8326
  this.stream.end(() => {
8296
- resolve3();
8327
+ resolve4();
8297
8328
  });
8298
8329
  this.stream.once("error", reject);
8299
- this.stream.once("finish", resolve3);
8330
+ this.stream.once("finish", resolve4);
8300
8331
  } else {
8301
8332
  this.stream.write("", () => {
8302
- resolve3();
8333
+ resolve4();
8303
8334
  });
8304
8335
  }
8305
8336
  });
@@ -8456,16 +8487,25 @@ var ReadmeGenerator = class {
8456
8487
  this.apiName = this.readmeConfig.apiName ?? this.organizationPascalCase;
8457
8488
  }
8458
8489
  async generateReadme({ output }) {
8490
+ const writer = new StreamWriter(output);
8491
+ await this.writeReadmeContent({ writer });
8492
+ await writer.end();
8493
+ }
8494
+ async generateReadmeToString() {
8495
+ const writer = new StringWriter();
8496
+ await this.writeReadmeContent({ writer });
8497
+ await writer.end();
8498
+ return writer.toString();
8499
+ }
8500
+ async writeReadmeContent({ writer }) {
8459
8501
  const blocks = await this.generateBlocks();
8460
8502
  const mergedBlocks = await this.mergeBlocks({ blocks });
8461
- const writer = new StreamWriter(output);
8462
8503
  await this.writeHeader({ writer });
8463
8504
  await this.writeTableOfContents({ writer, blocks: mergedBlocks });
8464
8505
  await this.writeBlocks({
8465
8506
  writer,
8466
8507
  blocks: mergedBlocks
8467
8508
  });
8468
- await writer.end();
8469
8509
  }
8470
8510
  async generateBlocks() {
8471
8511
  const blocks = [];
@@ -8915,7 +8955,7 @@ var ReadmeGenerator = class {
8915
8955
  await writer.writeLine();
8916
8956
  await writer.writeLine("```groovy");
8917
8957
  await writer.writeLine("dependencies {");
8918
- await writer.writeLine(` implementation '${maven.group}:${maven.artifact}'`);
8958
+ await writer.writeLine(` implementation '${maven.group}:${maven.artifact}:${maven.version}'`);
8919
8959
  await writer.writeLine("}");
8920
8960
  await writer.writeLine("```");
8921
8961
  await writer.writeLine();
@@ -9337,28 +9377,14 @@ async function generateReadmeToStream(params) {
9337
9377
  }
9338
9378
  async function generateReadme(params) {
9339
9379
  const { originalReadmeContent, readmeConfig } = params;
9340
- const content = [];
9341
- const sink = new import_node_stream.Writable({
9342
- write(chunk, _encoding, callback) {
9343
- if (typeof chunk === "string") {
9344
- content.push(chunk);
9345
- } else {
9346
- content.push(Buffer.from(chunk).toString("utf8"));
9347
- }
9348
- callback();
9349
- }
9350
- });
9351
- await generateReadmeToStream({
9352
- originalReadmeContent,
9380
+ const generator = new ReadmeGenerator({
9381
+ readmeParser: new ReadmeParser(),
9353
9382
  readmeConfig,
9354
- outputStream: sink
9383
+ originalReadme: originalReadmeContent
9355
9384
  });
9356
- return content.join("");
9385
+ return generator.generateReadmeToString();
9357
9386
  }
9358
9387
 
9359
- // src/api/generate-reference.ts
9360
- var import_node_stream2 = require("stream");
9361
-
9362
9388
  // src/reference/ReferenceGenerator.ts
9363
9389
  var ReferenceGenerator = class {
9364
9390
  referenceConfig;
@@ -9369,6 +9395,16 @@ var ReferenceGenerator = class {
9369
9395
  }
9370
9396
  async generate({ output }) {
9371
9397
  const writer = new StreamWriter(output);
9398
+ await this.writeReferenceContent({ writer });
9399
+ await writer.end();
9400
+ }
9401
+ async generateToString() {
9402
+ const writer = new StringWriter();
9403
+ await this.writeReferenceContent({ writer });
9404
+ await writer.end();
9405
+ return writer.toString();
9406
+ }
9407
+ async writeReferenceContent({ writer }) {
9372
9408
  await writer.writeLine("# Reference");
9373
9409
  if (this.referenceConfig.rootSection != null) {
9374
9410
  await this.writeRootSection({
@@ -9379,7 +9415,6 @@ var ReferenceGenerator = class {
9379
9415
  for (const section of this.referenceConfig.sections) {
9380
9416
  await this.writeSection({ section, writer });
9381
9417
  }
9382
- await writer.end();
9383
9418
  }
9384
9419
  async writeRootSection({
9385
9420
  section,
@@ -9480,22 +9515,8 @@ async function generateReferenceToStream(params) {
9480
9515
  }
9481
9516
  async function generateReference(params) {
9482
9517
  const { referenceConfig } = params;
9483
- const content = [];
9484
- const sink = new import_node_stream2.Writable({
9485
- write(chunk, _encoding, callback) {
9486
- if (typeof chunk === "string") {
9487
- content.push(chunk);
9488
- } else {
9489
- content.push(Buffer.from(chunk).toString("utf8"));
9490
- }
9491
- callback();
9492
- }
9493
- });
9494
- await generateReferenceToStream({
9495
- referenceConfig,
9496
- outputStream: sink
9497
- });
9498
- return content.join("");
9518
+ const generator = new ReferenceGenerator({ referenceConfig });
9519
+ return generator.generateToString();
9499
9520
  }
9500
9521
 
9501
9522
  // ../commons/path-utils/lib/convertToOsPath.js
@@ -9689,41 +9710,19 @@ var import_path3 = require("path");
9689
9710
 
9690
9711
  // src/pipeline/github/createReplayBranch.ts
9691
9712
  async function createReplayBranch(repository, branchName, commitMessage, replayConflictInfo, logger) {
9692
- if (replayConflictInfo?.hasConflicts) {
9693
- const parentSha = replayConflictInfo.previousGenerationSha;
9694
- const lockfileRestoreSha = replayConflictInfo.baseBranchHead ?? replayConflictInfo.previousGenerationSha;
9695
- logger.debug(`Creating divergent PR branch with generation tree (parent: ${parentSha.substring(0, 7)})`);
9696
- const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
9697
- const syntheticCommitSha = await repository.commitTree(
9698
- genTreeHash,
9699
- parentSha,
9700
- `[fern-generated] ${commitMessage ?? "Update SDK"}`
9701
- );
9702
- await repository.createBranchFromCommit(branchName, syntheticCommitSha);
9703
- try {
9704
- await repository.restoreFilesFromCommit(lockfileRestoreSha, ".fern/replay.lock");
9705
- await repository.commitAllChanges(`[fern-generated] ${commitMessage ?? "Update SDK"}`);
9706
- } catch (error) {
9707
- logger.debug(
9708
- `Could not restore lockfile from base branch: ${error instanceof Error ? error.message : String(error)}`
9709
- );
9710
- }
9711
- return syntheticCommitSha;
9712
- } else if (replayConflictInfo != null) {
9713
- const parentSha = replayConflictInfo.previousGenerationSha;
9714
- logger.debug(`Creating linear PR branch from HEAD (no conflicts)`);
9715
- await repository.createBranchFromHead(branchName);
9716
- const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
9717
- const tagCommitSha = await repository.commitTree(
9718
- genTreeHash,
9719
- parentSha,
9720
- `[fern-generated] ${commitMessage ?? "Update SDK"}`
9721
- );
9722
- return tagCommitSha;
9723
- } else {
9713
+ if (replayConflictInfo == null) {
9724
9714
  await repository.createBranchFromHead(branchName);
9725
9715
  return void 0;
9726
9716
  }
9717
+ const parentSha = replayConflictInfo.previousGenerationSha;
9718
+ await repository.createBranchFromHead(branchName);
9719
+ const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha);
9720
+ const tagCommitSha = await repository.commitTree(
9721
+ genTreeHash,
9722
+ parentSha,
9723
+ `[fern-generated] ${commitMessage ?? "Update SDK"}`
9724
+ );
9725
+ return tagCommitSha;
9727
9726
  }
9728
9727
 
9729
9728
  // src/pipeline/github/findExistingUpdatablePR.ts
@@ -9769,7 +9768,7 @@ async function findExistingUpdatablePR(octokit, owner, repo, baseBranch, logger)
9769
9768
  }
9770
9769
  return void 0;
9771
9770
  } catch (error) {
9772
- logger.debug(`Error finding existing PRs: ${error instanceof Error ? error.message : String(error)}`);
9771
+ logger.debug(`Error finding existing PRs: ${extractErrorMessage(error)}`);
9773
9772
  return void 0;
9774
9773
  }
9775
9774
  }
@@ -9796,16 +9795,22 @@ async function checkPRHasOnlyGenerationCommits(octokit, owner, repo, pullNumber,
9796
9795
  }
9797
9796
  return true;
9798
9797
  } catch (error) {
9799
- logger.debug(`Error checking PR commits: ${error instanceof Error ? error.message : String(error)}`);
9798
+ logger.debug(`Error checking PR commits: ${extractErrorMessage(error)}`);
9800
9799
  return false;
9801
9800
  }
9802
9801
  }
9803
9802
 
9804
9803
  // src/pipeline/github/parseCommitMessage.ts
9805
- function parseCommitMessageForPR(commitMessage) {
9804
+ function parseCommitMessageForPR(commitMessage, changelogEntry, prDescription, versionBumpReason) {
9806
9805
  const lines = commitMessage.split("\n");
9807
9806
  const prTitle = lines[0]?.trim() || "SDK Generation";
9808
- const prBody = lines.slice(1).join("\n").trim() || "Automated SDK generation by Fern";
9807
+ const bodyFromCommit = lines.slice(1).join("\n").trim() || "Automated SDK generation by Fern";
9808
+ let prBody = prDescription?.trim() || changelogEntry?.trim() || bodyFromCommit;
9809
+ if (versionBumpReason?.trim()) {
9810
+ prBody = `**Version Bump:** ${versionBumpReason.trim()}
9811
+
9812
+ ${prBody}`;
9813
+ }
9809
9814
  return { prTitle, prBody };
9810
9815
  }
9811
9816
 
@@ -9818,11 +9823,11 @@ function formatConflictReason(reason) {
9818
9823
  case "same-line-edit":
9819
9824
  return "The new generation changed the same lines you edited";
9820
9825
  case "new-file-both":
9821
- return "You and the generator both created this file";
9826
+ return "Both the generator and your customization created this file";
9822
9827
  case "base-generation-mismatch":
9823
- return "The generated code changed significantly around your edit";
9828
+ return "This customization was from a previous generation";
9824
9829
  case "patch-apply-failed":
9825
- return "Your change could not be applied to the new generated code";
9830
+ return "The customization could not be applied to the new code";
9826
9831
  default:
9827
9832
  return "Your edit overlaps with changes in the new generation";
9828
9833
  }
@@ -9831,7 +9836,7 @@ function patchDescription(detail) {
9831
9836
  if (detail.patchMessage && detail.patchMessage !== "update") {
9832
9837
  return detail.patchMessage;
9833
9838
  }
9834
- const firstFile = detail.files[0]?.file;
9839
+ const firstFile = detail.files[0];
9835
9840
  return firstFile != null ? `changes in ${firstFile}` : "customization";
9836
9841
  }
9837
9842
  function logReplaySummary(result, logger) {
@@ -9840,10 +9845,10 @@ function logReplaySummary(result, logger) {
9840
9845
  }
9841
9846
  const applied = result.patchesApplied ?? 0;
9842
9847
  const absorbed = result.patchesAbsorbed ?? 0;
9843
- const conflicts2 = result.patchesWithConflicts ?? 0;
9848
+ const unresolvedCount = result.unresolvedPatches?.length ?? 0;
9844
9849
  const preserved = applied - absorbed;
9845
9850
  logger.debug(
9846
- `Replay: flow=${result.flow}, detected=${result.patchesDetected ?? 0}, applied=${applied}, absorbed=${absorbed}, conflicts=${conflicts2}`
9851
+ `Replay: flow=${result.flow}, detected=${result.patchesDetected ?? 0}, applied=${applied}, absorbed=${absorbed}, unresolved=${unresolvedCount}`
9847
9852
  );
9848
9853
  if (preserved > 0) {
9849
9854
  const absorbedNote = absorbed > 0 ? ` (some customizations now part of generated code)` : "";
@@ -9851,14 +9856,17 @@ function logReplaySummary(result, logger) {
9851
9856
  } else if (absorbed > 0) {
9852
9857
  logger.info(`Replay: customizations now part of generated code`);
9853
9858
  }
9854
- if (conflicts2 > 0) {
9855
- const totalFiles = (result.conflictDetails ?? []).reduce((sum, d) => sum + d.files.length, 0);
9859
+ if (unresolvedCount > 0) {
9860
+ const totalFiles = (result.unresolvedPatches ?? []).reduce(
9861
+ (sum, patch) => sum + patch.conflictDetails.length,
9862
+ 0
9863
+ );
9856
9864
  logger.warn(
9857
- `Replay: ${plural(totalFiles, "file")} ${totalFiles === 1 ? "has" : "have"} merge conflicts \u2014 resolve in the PR or locally`
9865
+ `Replay: ${plural(totalFiles, "file")} ${totalFiles === 1 ? "has" : "have"} unresolved conflicts \u2014 resolve via \`fern replay resolve\``
9858
9866
  );
9859
- for (const detail of result.conflictDetails ?? []) {
9860
- logger.warn(` "${patchDescription(detail)}":`);
9861
- for (const file of detail.files) {
9867
+ for (const patch of result.unresolvedPatches ?? []) {
9868
+ logger.warn(` "${patchDescription(patch)}":`);
9869
+ for (const file of patch.conflictDetails) {
9862
9870
  logger.warn(` ${file.file} \u2014 ${formatConflictReason(file.conflictReason)}`);
9863
9871
  }
9864
9872
  }
@@ -9873,70 +9881,66 @@ function formatReplayPrBody(result, options) {
9873
9881
  }
9874
9882
  const applied = result.patchesApplied ?? 0;
9875
9883
  const absorbed = result.patchesAbsorbed ?? 0;
9876
- const conflicts2 = result.patchesWithConflicts ?? 0;
9884
+ const unresolvedPatches = result.unresolvedPatches ?? [];
9885
+ const hasUnresolved = unresolvedPatches.length > 0;
9877
9886
  const preserved = applied - absorbed;
9878
- if (preserved === 0 && conflicts2 === 0) {
9887
+ if (preserved === 0 && !hasUnresolved) {
9879
9888
  return void 0;
9880
9889
  }
9881
9890
  const parts = [];
9882
- if (preserved > 0 && conflicts2 === 0) {
9891
+ if (preserved > 0 && !hasUnresolved) {
9883
9892
  parts.push(`\u2705 Customizations automatically preserved in this update.`);
9884
9893
  } else if (preserved > 0) {
9894
+ const totalFiles = unresolvedPatches.reduce((sum, patch) => sum + patch.conflictDetails.length, 0);
9885
9895
  parts.push(
9886
- `\u2705 Customizations automatically preserved, but ${plural(conflicts2, "file")} ${conflicts2 === 1 ? "needs" : "need"} your attention.`
9896
+ `\u2705 Customizations automatically preserved, but ${plural(totalFiles, "file")} ${totalFiles === 1 ? "needs" : "need"} your attention.`
9887
9897
  );
9888
9898
  }
9889
- if (conflicts2 > 0) {
9890
- const allFiles = (result.conflictDetails ?? []).flatMap((d) => d.files);
9891
- const totalFiles = allFiles.length;
9899
+ if (hasUnresolved) {
9900
+ const totalFiles = unresolvedPatches.reduce((sum, patch) => sum + patch.conflictDetails.length, 0);
9892
9901
  parts.push(`
9893
- ### \u26A0\uFE0F Action required: ${plural(totalFiles, "file")} with customization conflicts
9902
+ ### Action required: ${plural(totalFiles, "file")} with unresolved customization conflicts
9894
9903
  `);
9895
9904
  parts.push(
9896
- `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.
9897
- `
9898
- );
9899
- parts.push(
9900
- `> \u26A0\uFE0F **Label guide:** In GitHub's conflict editor, \`<<<<<<< HEAD\` ("Accept current") = new generated code. \`>>>>>>> main\` ("Accept incoming") = your customization.
9905
+ `The new generation changed code you previously customized. Non-conflicting customizations have been applied automatically. The following files need manual resolution:
9901
9906
  `
9902
9907
  );
9903
9908
  parts.push(`| File | Your customization | Why it conflicted |`);
9904
9909
  parts.push(`|------|-------------------|-------------------|`);
9905
- for (const detail of result.conflictDetails ?? []) {
9906
- const description = patchDescription(detail);
9907
- for (const f of detail.files) {
9908
- parts.push(`| \`${f.file}\` | ${description} | ${formatConflictReason(f.conflictReason)} |`);
9910
+ for (const patch of unresolvedPatches) {
9911
+ const description = patchDescription(patch);
9912
+ for (const file of patch.conflictDetails) {
9913
+ parts.push(`| \`${file.file}\` | ${description} | ${formatConflictReason(file.conflictReason)} |`);
9909
9914
  }
9910
9915
  }
9911
9916
  const branch = options?.branchName ?? "<branch-name>";
9917
+ const repoUri = options?.repoUri;
9918
+ const repoName = repoUri?.split("/").pop() ?? "<repo>";
9912
9919
  parts.push(`
9913
- #### How to fix
9914
- `);
9915
- parts.push(`**Option A: Resolve in GitHub (recommended for simple conflicts)**
9920
+ #### How to resolve
9916
9921
  `);
9917
- parts.push(`1. Click the **Resolve conflicts** button below`);
9918
- parts.push(`2. For each file, choose which code to keep:`);
9919
- parts.push(` - \`<<<<<<< HEAD\` / "Accept current changes" = **new generated code**`);
9920
- parts.push(` - \`>>>>>>> main\` / "Accept incoming changes" = **your customization**`);
9921
- parts.push(` - Or manually combine both, then delete the conflict markers`);
9922
- parts.push(`3. Click **Mark as resolved** for each file, then **Commit merge**
9923
- `);
9924
- parts.push(`**Option B: Resolve locally**
9925
- `);
9926
- parts.push(`1. Fetch and check out this branch:`);
9922
+ parts.push(`1. Check out this branch:`);
9927
9923
  parts.push(` \`\`\`sh`);
9928
- parts.push(` git fetch origin && git checkout ${branch}`);
9924
+ parts.push(` git fetch origin && git checkout -b ${branch} origin/${branch}`);
9929
9925
  parts.push(` \`\`\``);
9930
- parts.push(`2. Merge the base branch and resolve conflicts in your editor:`);
9931
- parts.push(` \`\`\`sh`);
9932
- parts.push(` git merge origin/main`);
9933
- parts.push(` \`\`\``);
9934
- parts.push(`3. Commit and push:`);
9935
- parts.push(` \`\`\`sh`);
9936
- parts.push(` git add -A && git commit -m "resolve conflicts" && git push`);
9937
- parts.push(` \`\`\`
9926
+ if (repoUri != null) {
9927
+ parts.push(` Or if you don't have the repo cloned:`);
9928
+ parts.push(` \`\`\`sh`);
9929
+ parts.push(
9930
+ ` git clone https://github.com/${repoUri}.git && cd ${repoName} && git checkout -b ${branch} origin/${branch}`
9931
+ );
9932
+ parts.push(` \`\`\``);
9933
+ }
9934
+ parts.push(`2. Run: \`fern replay resolve\``);
9935
+ parts.push(`3. Open the conflicting files in your editor \u2014 you'll see standard merge conflict markers`);
9936
+ parts.push(`4. Resolve using your editor's merge tools (VS Code, IntelliJ, etc.)`);
9937
+ parts.push(`5. Run: \`fern replay resolve\` again to finalize`);
9938
+ parts.push(`6. Push your changes
9938
9939
  `);
9939
- parts.push(`Your resolved changes will be remembered on future SDK generations.`);
9940
+ parts.push(`Your resolved customizations will be remembered on future SDK generations.`);
9941
+ parts.push(
9942
+ `If you merge this PR without resolving, your unresolved customizations will conflict again on the next generation.`
9943
+ );
9940
9944
  }
9941
9945
  return parts.join("\n");
9942
9946
  }
@@ -10066,22 +10070,18 @@ var GithubStep = class extends BaseStep {
10066
10070
  this.logger.debug(`Pushed ${tagName} tag for generation tracking`);
10067
10071
  result.generationBaseTagSha = generationBaseSha;
10068
10072
  } catch (error) {
10069
- this.logger.debug(
10070
- `Could not push generation tag: ${error instanceof Error ? error.message : String(error)}`
10071
- );
10073
+ this.logger.debug(`Could not push generation tag: ${extractErrorMessage(error)}`);
10072
10074
  }
10073
10075
  }
10074
10076
  }
10075
- if (!this.config.previewMode) {
10076
- const headSha = await repository.getHeadSha();
10077
- await this.postReplayConflictStatus(octokit, owner, repo, headSha, replayConflictInfo, replayResult);
10078
- if (isUpdatingExistingPR && existingPR != null) {
10079
- await this.togglePrDraftState(octokit, existingPR, replayConflictInfo);
10080
- }
10081
- }
10082
10077
  const finalCommitMessage = this.config.commitMessage ?? "SDK Generation";
10083
- const { prTitle, prBody } = parseCommitMessageForPR(finalCommitMessage);
10084
- const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch });
10078
+ const { prTitle, prBody } = parseCommitMessageForPR(
10079
+ finalCommitMessage,
10080
+ this.config.changelogEntry,
10081
+ this.config.prDescription,
10082
+ this.config.versionBumpReason
10083
+ );
10084
+ const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch, repoUri: this.config.uri });
10085
10085
  const enrichedBody = replaySection != null ? prBody + "\n\n---\n\n" + replaySection : prBody;
10086
10086
  if (isUpdatingExistingPR && existingPR != null) {
10087
10087
  this.logger.info(`Updated existing pull request: ${existingPR.htmlUrl}`);
@@ -10097,13 +10097,10 @@ var GithubStep = class extends BaseStep {
10097
10097
  });
10098
10098
  this.logger.debug(`Updated PR #${existingPR.number} title and body`);
10099
10099
  } catch (error) {
10100
- this.logger.debug(
10101
- `Failed to update PR title/body: ${error instanceof Error ? error.message : String(error)}`
10102
- );
10100
+ this.logger.debug(`Failed to update PR title/body: ${extractErrorMessage(error)}`);
10103
10101
  }
10104
10102
  } else {
10105
10103
  const head = `${owner}:${prBranch}`;
10106
- const hasConflicts = replayConflictInfo?.hasConflicts === true;
10107
10104
  try {
10108
10105
  const { data: pullRequest } = await octokit.pulls.create({
10109
10106
  owner,
@@ -10111,14 +10108,13 @@ var GithubStep = class extends BaseStep {
10111
10108
  title: prTitle,
10112
10109
  body: enrichedBody,
10113
10110
  head,
10114
- base: baseBranch,
10115
- draft: hasConflicts
10111
+ base: baseBranch
10116
10112
  });
10117
10113
  this.logger.info(`Created pull request: ${pullRequest.html_url}`);
10118
10114
  result.prUrl = pullRequest.html_url;
10119
10115
  result.prNumber = pullRequest.number;
10120
10116
  } catch (error) {
10121
- const message = error instanceof Error ? error.message : String(error);
10117
+ const message = extractErrorMessage(error);
10122
10118
  if (message.includes("A pull request already exists for")) {
10123
10119
  this.logger.warn(`A pull request already exists for ${head}`);
10124
10120
  } else {
@@ -10126,17 +10122,6 @@ var GithubStep = class extends BaseStep {
10126
10122
  }
10127
10123
  }
10128
10124
  }
10129
- if (!this.config.previewMode && result.prNumber != null && replayConflictInfo?.hasConflicts === true && replayResult != null) {
10130
- await this.postWebEditorFallbackComment(
10131
- octokit,
10132
- owner,
10133
- repo,
10134
- result.prNumber,
10135
- prBranch,
10136
- baseBranch,
10137
- replayResult
10138
- );
10139
- }
10140
10125
  return result;
10141
10126
  }
10142
10127
  async executePushMode(repository) {
@@ -10182,198 +10167,17 @@ var GithubStep = class extends BaseStep {
10182
10167
  if (replayResult == null) {
10183
10168
  return void 0;
10184
10169
  }
10185
- const hasConflicts = (replayResult.patchesWithConflicts ?? 0) > 0;
10186
10170
  const previousGenerationSha = replayResult.previousGenerationSha;
10187
10171
  const currentGenerationSha = replayResult.currentGenerationSha;
10188
10172
  if (previousGenerationSha != null && currentGenerationSha != null) {
10189
10173
  return {
10190
10174
  previousGenerationSha,
10191
- currentGenerationSha,
10192
- hasConflicts,
10193
- baseBranchHead: replayResult.baseBranchHead
10175
+ currentGenerationSha
10194
10176
  };
10195
10177
  }
10196
10178
  return void 0;
10197
10179
  }
10198
- async togglePrDraftState(octokit, existingPR, replayConflictInfo) {
10199
- const hasConflicts = replayConflictInfo?.hasConflicts === true;
10200
- if (hasConflicts && !existingPR.isDraft) {
10201
- await this.convertPrToDraft(octokit, existingPR.nodeId, existingPR.number);
10202
- } else if (!hasConflicts && existingPR.isDraft) {
10203
- await this.markPrReady(octokit, existingPR.nodeId, existingPR.number);
10204
- }
10205
- }
10206
- async convertPrToDraft(octokit, nodeId, prNumber) {
10207
- try {
10208
- await octokit.graphql(
10209
- `mutation($id: ID!) {
10210
- convertPullRequestToDraft(input: {pullRequestId: $id}) {
10211
- pullRequest { isDraft }
10212
- }
10213
- }`,
10214
- { id: nodeId }
10215
- );
10216
- this.logger.info(`Converted PR #${prNumber} to draft due to replay conflicts`);
10217
- } catch (error) {
10218
- this.logger.debug(
10219
- `Could not convert PR to draft: ${error instanceof Error ? error.message : String(error)}`
10220
- );
10221
- }
10222
- }
10223
- async markPrReady(octokit, nodeId, prNumber) {
10224
- try {
10225
- await octokit.graphql(
10226
- `mutation($id: ID!) {
10227
- markPullRequestReadyForReview(input: {pullRequestId: $id}) {
10228
- pullRequest { isDraft }
10229
- }
10230
- }`,
10231
- { id: nodeId }
10232
- );
10233
- this.logger.info(`Marked PR #${prNumber} as ready (conflicts resolved)`);
10234
- } catch (error) {
10235
- this.logger.debug(`Could not mark PR as ready: ${error instanceof Error ? error.message : String(error)}`);
10236
- }
10237
- }
10238
- async postReplayConflictStatus(octokit, owner, repo, sha, replayConflictInfo, replayResult) {
10239
- if (replayResult == null || !replayResult.executed) {
10240
- return;
10241
- }
10242
- const hasConflicts = replayConflictInfo?.hasConflicts === true;
10243
- const conflictFileCount = (replayResult.conflictDetails ?? []).reduce(
10244
- (sum, detail) => sum + detail.files.length,
10245
- 0
10246
- );
10247
- const sanitizedName = this.config.generatorName?.replace(/\//g, "--");
10248
- const context = sanitizedName != null ? `fern / sdk customizations / ${sanitizedName}` : "fern / sdk customizations";
10249
- try {
10250
- await octokit.repos.createCommitStatus({
10251
- owner,
10252
- repo,
10253
- sha,
10254
- state: hasConflicts ? "failure" : "success",
10255
- context,
10256
- description: hasConflicts ? `${conflictFileCount} file(s) need manual conflict resolution \u2014 see PR description` : "All customizations applied"
10257
- });
10258
- this.logger.debug(`Posted ${hasConflicts ? "failing" : "passing"} commit status (${context})`);
10259
- } catch (error) {
10260
- this.logger.debug(
10261
- `Could not post commit status: ${error instanceof Error ? error.message : String(error)}`
10262
- );
10263
- }
10264
- }
10265
- async postWebEditorFallbackComment(octokit, owner, repo, prNumber, branchName, baseBranch, replayResult) {
10266
- try {
10267
- const webEditorLikelyDisabled = await this.isWebEditorLikelyDisabled(
10268
- octokit,
10269
- owner,
10270
- repo,
10271
- prNumber,
10272
- replayResult
10273
- );
10274
- if (!webEditorLikelyDisabled) {
10275
- this.logger.debug(`PR #${prNumber}: web conflict editor appears usable, skipping fallback comment`);
10276
- return;
10277
- }
10278
- const comment = buildWebEditorFallbackComment(branchName, baseBranch);
10279
- await octokit.issues.createComment({
10280
- owner,
10281
- repo,
10282
- issue_number: prNumber,
10283
- body: comment
10284
- });
10285
- this.logger.info(`Posted web-editor fallback comment on PR #${prNumber}`);
10286
- } catch (error) {
10287
- this.logger.debug(
10288
- `Could not post web-editor fallback comment: ${error instanceof Error ? error.message : String(error)}`
10289
- );
10290
- }
10291
- }
10292
- async isWebEditorLikelyDisabled(octokit, owner, repo, prNumber, replayResult) {
10293
- const conflictDetails = replayResult.conflictDetails ?? [];
10294
- const allConflictFiles = conflictDetails.flatMap((d) => d.files);
10295
- const totalConflictFiles = allConflictFiles.length;
10296
- const hasFileDeletionConflict = allConflictFiles.some((f) => {
10297
- const status = (f.status ?? "").toLowerCase();
10298
- const reason = (f.conflictReason ?? "").toLowerCase();
10299
- return status === "skipped" || reason.includes("delete") || reason.includes("removed");
10300
- });
10301
- if (hasFileDeletionConflict) {
10302
- this.logger.debug(`PR #${prNumber}: detected file deletion conflict, web editor likely disabled`);
10303
- return true;
10304
- }
10305
- if (totalConflictFiles >= WEB_EDITOR_FILE_THRESHOLD) {
10306
- this.logger.debug(
10307
- `PR #${prNumber}: ${totalConflictFiles} conflicting files exceeds threshold (${WEB_EDITOR_FILE_THRESHOLD}), web editor likely disabled`
10308
- );
10309
- return true;
10310
- }
10311
- const totalHunks = (replayResult.conflicts ?? []).reduce(
10312
- (sum, fileConflict) => sum + fileConflict.conflicts.length,
10313
- 0
10314
- );
10315
- if (totalHunks >= WEB_EDITOR_HUNK_THRESHOLD) {
10316
- this.logger.debug(
10317
- `PR #${prNumber}: ${totalHunks} conflict hunks exceeds threshold (${WEB_EDITOR_HUNK_THRESHOLD}), web editor likely disabled`
10318
- );
10319
- return true;
10320
- }
10321
- const mergeableState = await this.pollMergeableState(octokit, owner, repo, prNumber);
10322
- if (mergeableState.mergeable === null) {
10323
- this.logger.debug(`PR #${prNumber}: mergeable state still null after polling, web editor likely disabled`);
10324
- return true;
10325
- }
10326
- return false;
10327
- }
10328
- async pollMergeableState(octokit, owner, repo, prNumber) {
10329
- for (let attempt = 0; attempt < MERGEABLE_POLL_MAX_ATTEMPTS; attempt++) {
10330
- const { data: pr } = await octokit.pulls.get({
10331
- owner,
10332
- repo,
10333
- pull_number: prNumber
10334
- });
10335
- if (pr.mergeable != null) {
10336
- return {
10337
- mergeable: pr.mergeable,
10338
- mergeableState: pr.mergeable_state
10339
- };
10340
- }
10341
- if (attempt < MERGEABLE_POLL_MAX_ATTEMPTS - 1) {
10342
- await sleep(MERGEABLE_POLL_DELAY_MS);
10343
- }
10344
- }
10345
- return { mergeable: null, mergeableState: "unknown" };
10346
- }
10347
10180
  };
10348
- var WEB_EDITOR_FILE_THRESHOLD = 20;
10349
- var WEB_EDITOR_HUNK_THRESHOLD = 50;
10350
- var MERGEABLE_POLL_MAX_ATTEMPTS = 3;
10351
- var MERGEABLE_POLL_DELAY_MS = 1500;
10352
- function buildWebEditorFallbackComment(branchName, baseBranch) {
10353
- const lines = [
10354
- `> **Note:** These conflicts may be too complex for GitHub's web editor. To resolve locally:`,
10355
- `>`,
10356
- `> 1. Check out this branch:`,
10357
- `> \`\`\`sh`,
10358
- `> git fetch origin && git checkout ${branchName}`,
10359
- `> \`\`\``,
10360
- `> 2. Merge the base branch:`,
10361
- `> \`\`\`sh`,
10362
- `> git merge origin/${baseBranch}`,
10363
- `> \`\`\``,
10364
- `> 3. Resolve conflicts in your editor (VS Code, IntelliJ, etc. have built-in merge tools)`,
10365
- `> 4. Commit and push:`,
10366
- `> \`\`\`sh`,
10367
- `> git add -A && git commit -m "resolve conflicts" && git push`,
10368
- `> \`\`\``,
10369
- `>`,
10370
- `> **Label guide:** In your editor's merge view, "current" = new generated code, "incoming" = your customization.`
10371
- ];
10372
- return lines.join("\n");
10373
- }
10374
- function sleep(ms) {
10375
- return new Promise((resolve3) => setTimeout(resolve3, ms));
10376
- }
10377
10181
 
10378
10182
  // src/replay/replay-run.ts
10379
10183
  var import_replay = require("@fern-api/replay");
@@ -10582,8 +10386,7 @@ var ReplayStep = class extends BaseStep {
10582
10386
  flow: "first-generation",
10583
10387
  patchesDetected: 0,
10584
10388
  patchesApplied: 0,
10585
- patchesWithConflicts: 0,
10586
- conflicts: []
10389
+ patchesWithConflicts: 0
10587
10390
  };
10588
10391
  }
10589
10392
  const report = result.report;
@@ -10601,22 +10404,12 @@ var ReplayStep = class extends BaseStep {
10601
10404
  patchesRepointed: report.patchesRepointed,
10602
10405
  patchesContentRebased: report.patchesContentRebased,
10603
10406
  patchesKeptAsUserOwned: report.patchesKeptAsUserOwned,
10604
- conflicts: report.conflicts.map((fileResult) => ({
10605
- filePath: fileResult.file,
10606
- conflicts: fileResult.conflicts?.map((conflict) => ({
10607
- startLine: conflict.startLine,
10608
- endLine: conflict.endLine,
10609
- ours: conflict.ours,
10610
- theirs: conflict.theirs
10611
- })) ?? []
10612
- })),
10613
- conflictDetails: report.conflictDetails?.map((detail) => ({
10614
- patchId: detail.patchId,
10615
- patchMessage: detail.patchMessage,
10616
- reason: detail.reason,
10617
- files: detail.files.map((f) => ({
10407
+ unresolvedPatches: report.unresolvedPatches?.map((info) => ({
10408
+ patchId: info.patchId,
10409
+ patchMessage: info.patchMessage,
10410
+ files: info.files,
10411
+ conflictDetails: info.conflictDetails.map((f) => ({
10618
10412
  file: f.file,
10619
- status: f.status,
10620
10413
  conflictReason: f.conflictReason
10621
10414
  }))
10622
10415
  })),
@@ -10687,7 +10480,7 @@ var PostGenerationPipeline = class {
10687
10480
  } catch (error) {
10688
10481
  result.success = false;
10689
10482
  result.errors = result.errors ?? [];
10690
- const errorMessage = error instanceof Error ? error.message : String(error);
10483
+ const errorMessage = extractErrorMessage(error);
10691
10484
  result.errors.push(`${step.name} step error: ${errorMessage}`);
10692
10485
  }
10693
10486
  }
@@ -10880,6 +10673,14 @@ function buildPrBody(result) {
10880
10673
  lines.push("Once merged, your customizations will be automatically preserved on future `fern generate` runs.");
10881
10674
  return lines.join("\n");
10882
10675
  }
10676
+
10677
+ // src/replay/replay-resolve.ts
10678
+ var import_replay3 = require("@fern-api/replay");
10679
+ async function replayResolve(params) {
10680
+ return (0, import_replay3.resolve)(params.outputDir, {
10681
+ checkMarkers: params.checkMarkers
10682
+ });
10683
+ }
10883
10684
  // Annotate the CommonJS export names for ESM import in node:
10884
10685
  0 && (module.exports = {
10885
10686
  PostGenerationPipeline,
@@ -10895,5 +10696,6 @@ function buildPrBody(result) {
10895
10696
  githubRelease,
10896
10697
  logReplaySummary,
10897
10698
  replayInit,
10699
+ replayResolve,
10898
10700
  replayRun
10899
10701
  });