@fern-api/generator-cli 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { BootstrapResult, ResolveResult, ReplayReport } from '@fern-api/replay';
2
- export { ConflictDetail, ReplayReport, ResolveResult } from '@fern-api/replay';
1
+ import { ForgetOptions, ForgetResult, BootstrapResult, ResolveResult, ReplayReport, StatusResult } from '@fern-api/replay';
2
+ export { ConflictDetail, DiffStat, ForgetOptions, ForgetResult, MatchedPatch, ReplayReport, ResolveResult, StatusGeneration, StatusPatch, StatusResult } from '@fern-api/replay';
3
3
  import fs from 'fs';
4
4
 
5
5
  /**
@@ -614,11 +614,19 @@ declare function formatReplayPrBody(result: ReplayStepResult | undefined, option
614
614
  repoUri?: string;
615
615
  }): string | undefined;
616
616
 
617
+ interface ReplayForgetParams {
618
+ /** Directory containing the SDK (where .fern/replay.lock lives) */
619
+ outputDir: string;
620
+ /** Options controlling which patches to forget */
621
+ options?: ForgetOptions;
622
+ }
623
+ declare function replayForget(params: ReplayForgetParams): ForgetResult;
624
+
617
625
  interface ReplayInitParams {
618
626
  /** GitHub repo URI (e.g., "fern-demo/fern-replay-testbed-java-sdk") */
619
627
  githubRepo: string;
620
- /** GitHub token for clone (read access) */
621
- token: string;
628
+ /** GitHub token for clone (read access). Optional for public repos. */
629
+ token?: string;
622
630
  /** Report what would happen but don't create PR */
623
631
  dryRun?: boolean;
624
632
  /** Max commits to scan for generation history */
@@ -699,4 +707,10 @@ interface ReplayRunResult {
699
707
  }
700
708
  declare function replayRun(params: ReplayRunParams): Promise<ReplayRunResult>;
701
709
 
702
- export { type BootstrapLogEntry, type GenerateReadmeParams, type GenerateReadmeToStreamParams, type GenerateReferenceParams, type GenerateReferenceToStreamParams, type GithubPrParams, type GithubPushParams, type GithubReleaseParams, type GithubStepConfig, type GithubStepResult, type PipelineConfig, type PipelineContext, type PipelineLogger, type PipelineResult, PostGenerationPipeline, type ReplayInitParams, type ReplayInitResult, type ReplayResolveParams, type ReplayRunParams, type ReplayRunResult, type ReplayStepResult, consolePipelineLogger, formatBootstrapSummary, formatReplayPrBody, generateReadme, generateReadmeToStream, generateReference, generateReferenceToStream, githubPr, githubPush, githubRelease, logReplaySummary, replayInit, replayResolve, replayRun };
710
+ interface ReplayStatusParams {
711
+ /** Directory containing the SDK (where .fern/replay.lock lives) */
712
+ outputDir: string;
713
+ }
714
+ declare function replayStatus(params: ReplayStatusParams): StatusResult;
715
+
716
+ export { type BootstrapLogEntry, type GenerateReadmeParams, type GenerateReadmeToStreamParams, type GenerateReferenceParams, type GenerateReferenceToStreamParams, type GithubPrParams, type GithubPushParams, type GithubReleaseParams, type GithubStepConfig, type GithubStepResult, type PipelineConfig, type PipelineContext, type PipelineLogger, type PipelineResult, PostGenerationPipeline, type ReplayForgetParams, type ReplayInitParams, type ReplayInitResult, type ReplayResolveParams, type ReplayRunParams, type ReplayRunResult, type ReplayStatusParams, type ReplayStepResult, consolePipelineLogger, formatBootstrapSummary, formatReplayPrBody, generateReadme, generateReadmeToStream, generateReference, generateReferenceToStream, githubPr, githubPush, githubRelease, logReplaySummary, replayForget, replayInit, replayResolve, replayRun, replayStatus };
package/dist/api.js CHANGED
@@ -844,7 +844,7 @@ var require_dist2 = __commonJS({
844
844
  function deferred2() {
845
845
  let done;
846
846
  let fail;
847
- let status = "pending";
847
+ let status2 = "pending";
848
848
  const promise = new Promise((_done, _fail) => {
849
849
  done = _done;
850
850
  fail = _fail;
@@ -852,22 +852,22 @@ var require_dist2 = __commonJS({
852
852
  return {
853
853
  promise,
854
854
  done(result) {
855
- if (status === "pending") {
856
- status = "resolved";
855
+ if (status2 === "pending") {
856
+ status2 = "resolved";
857
857
  done(result);
858
858
  }
859
859
  },
860
860
  fail(error) {
861
- if (status === "pending") {
862
- status = "rejected";
861
+ if (status2 === "pending") {
862
+ status2 = "rejected";
863
863
  fail(error);
864
864
  }
865
865
  },
866
866
  get fulfilled() {
867
- return status !== "pending";
867
+ return status2 !== "pending";
868
868
  },
869
869
  get status() {
870
- return status;
870
+ return status2;
871
871
  }
872
872
  };
873
873
  }
@@ -892,9 +892,11 @@ __export(api_exports2, {
892
892
  githubPush: () => githubPush,
893
893
  githubRelease: () => githubRelease,
894
894
  logReplaySummary: () => logReplaySummary,
895
+ replayForget: () => replayForget,
895
896
  replayInit: () => replayInit,
896
897
  replayResolve: () => replayResolve,
897
- replayRun: () => replayRun
898
+ replayRun: () => replayRun,
899
+ replayStatus: () => replayStatus
898
900
  });
899
901
  module.exports = __toCommonJS(api_exports2);
900
902
 
@@ -2827,7 +2829,7 @@ var init_parse_diff_summary = __esm({
2827
2829
  nameStatusParser = [
2828
2830
  new LineParser(
2829
2831
  /([ACDMRTUXB])([0-9]{0,3})\t(.[^\t]*)(\t(.[^\t]*))?$/,
2830
- (result, [status, similarity, from, _to, to]) => {
2832
+ (result, [status2, similarity, from, _to, to]) => {
2831
2833
  result.changed++;
2832
2834
  result.files.push({
2833
2835
  file: to ?? from,
@@ -2835,7 +2837,7 @@ var init_parse_diff_summary = __esm({
2835
2837
  insertions: 0,
2836
2838
  deletions: 0,
2837
2839
  binary: false,
2838
- status: orVoid(isDiffNameStatus(status) && status),
2840
+ status: orVoid(isDiffNameStatus(status2) && status2),
2839
2841
  from: orVoid(!!to && from !== to && from),
2840
2842
  similarity: asNumber(similarity)
2841
2843
  });
@@ -3361,10 +3363,10 @@ var init_merge = __esm({
3361
3363
  init_task();
3362
3364
  }
3363
3365
  });
3364
- function pushResultPushedItem(local, remote, status) {
3365
- const deleted = status.includes("deleted");
3366
- const tag = status.includes("tag") || /^refs\/tags/.test(local);
3367
- const alreadyUpdated = !status.includes("new");
3366
+ function pushResultPushedItem(local, remote, status2) {
3367
+ const deleted = status2.includes("deleted");
3368
+ const tag = status2.includes("tag") || /^refs\/tags/.test(local);
3369
+ const alreadyUpdated = !status2.includes("new");
3368
3370
  return {
3369
3371
  deleted,
3370
3372
  tag,
@@ -3678,7 +3680,7 @@ var init_StatusSummary = __esm({
3678
3680
  ]);
3679
3681
  parseStatusSummary = function(text) {
3680
3682
  const lines = text.split(NULL);
3681
- const status = new StatusSummary();
3683
+ const status2 = new StatusSummary();
3682
3684
  for (let i = 0, l = lines.length; i < l; ) {
3683
3685
  let line = lines[i++].trim();
3684
3686
  if (!line) {
@@ -3687,9 +3689,9 @@ var init_StatusSummary = __esm({
3687
3689
  if (line.charAt(0) === "R") {
3688
3690
  line += NULL + (lines[i++] || "");
3689
3691
  }
3690
- splitLine(status, line);
3692
+ splitLine(status2, line);
3691
3693
  }
3692
- return status;
3694
+ return status2;
3693
3695
  };
3694
3696
  }
3695
3697
  });
@@ -4116,15 +4118,15 @@ var init_BranchSummary = __esm({
4116
4118
  this.current = "";
4117
4119
  this.detached = false;
4118
4120
  }
4119
- push(status, detached, name, commit, label) {
4120
- if (status === "*") {
4121
+ push(status2, detached, name, commit, label) {
4122
+ if (status2 === "*") {
4121
4123
  this.detached = detached;
4122
4124
  this.current = name;
4123
4125
  }
4124
4126
  this.all.push(name);
4125
4127
  this.branches[name] = {
4126
- current: status === "*",
4127
- linkedWorkTree: status === "+",
4128
+ current: status2 === "*",
4129
+ linkedWorkTree: status2 === "+",
4128
4130
  name,
4129
4131
  commit,
4130
4132
  label
@@ -10488,8 +10490,14 @@ var PostGenerationPipeline = class {
10488
10490
  }
10489
10491
  };
10490
10492
 
10491
- // src/replay/replay-init.ts
10493
+ // src/replay/replay-forget.ts
10492
10494
  var import_replay2 = require("@fern-api/replay");
10495
+ function replayForget(params) {
10496
+ return (0, import_replay2.forget)(params.outputDir, params.options);
10497
+ }
10498
+
10499
+ // src/replay/replay-init.ts
10500
+ var import_replay3 = require("@fern-api/replay");
10493
10501
  var import_fs4 = require("fs");
10494
10502
  var import_path6 = require("path");
10495
10503
  var import_tmp_promise2 = __toESM(require("tmp-promise"));
@@ -10502,7 +10510,7 @@ async function replayInit(params) {
10502
10510
  installationToken: token,
10503
10511
  targetDirectory: repoPath
10504
10512
  });
10505
- const bootstrapResult = await (0, import_replay2.bootstrap)(repoPath, {
10513
+ const bootstrapResult = await (0, import_replay3.bootstrap)(repoPath, {
10506
10514
  dryRun,
10507
10515
  fernignoreAction: "skip",
10508
10516
  maxCommitsToScan: params.maxCommitsToScan,
@@ -10635,12 +10643,18 @@ function buildPrBody(result) {
10635
10643
  }
10636
10644
 
10637
10645
  // src/replay/replay-resolve.ts
10638
- var import_replay3 = require("@fern-api/replay");
10646
+ var import_replay4 = require("@fern-api/replay");
10639
10647
  async function replayResolve(params) {
10640
- return (0, import_replay3.resolve)(params.outputDir, {
10648
+ return (0, import_replay4.resolve)(params.outputDir, {
10641
10649
  checkMarkers: params.checkMarkers
10642
10650
  });
10643
10651
  }
10652
+
10653
+ // src/replay/replay-status.ts
10654
+ var import_replay5 = require("@fern-api/replay");
10655
+ function replayStatus(params) {
10656
+ return (0, import_replay5.status)(params.outputDir);
10657
+ }
10644
10658
  // Annotate the CommonJS export names for ESM import in node:
10645
10659
  0 && (module.exports = {
10646
10660
  PostGenerationPipeline,
@@ -10655,7 +10669,9 @@ async function replayResolve(params) {
10655
10669
  githubPush,
10656
10670
  githubRelease,
10657
10671
  logReplaySummary,
10672
+ replayForget,
10658
10673
  replayInit,
10659
10674
  replayResolve,
10660
- replayRun
10675
+ replayRun,
10676
+ replayStatus
10661
10677
  });
package/dist/cli.js CHANGED
@@ -19759,7 +19759,7 @@ var init_diff3 = __esm({
19759
19759
  }
19760
19760
  });
19761
19761
 
19762
- // ../../node_modules/.pnpm/@fern-api+replay@0.8.0/node_modules/@fern-api/replay/dist/index.js
19762
+ // ../../node_modules/.pnpm/@fern-api+replay@0.9.0/node_modules/@fern-api/replay/dist/index.js
19763
19763
  var dist_exports = {};
19764
19764
  __export(dist_exports, {
19765
19765
  FERN_BOT_EMAIL: () => FERN_BOT_EMAIL2,
@@ -19797,7 +19797,7 @@ function isReplayCommit(commit) {
19797
19797
  return commit.message.startsWith("[fern-replay]");
19798
19798
  }
19799
19799
  function isRevertCommit(message) {
19800
- return message.startsWith('Revert "');
19800
+ return /^Revert ".+"$/.test(message);
19801
19801
  }
19802
19802
  function parseRevertedSha(fullBody) {
19803
19803
  const match2 = fullBody.match(/This reverts commit ([0-9a-f]{40})\./);
@@ -19842,14 +19842,6 @@ function threeWayMerge(base, ours, theirs) {
19842
19842
  conflicts: conflicts2
19843
19843
  };
19844
19844
  }
19845
- function isBinaryFile(filePath) {
19846
- const ext2 = (0, import_path9.extname)(filePath).toLowerCase();
19847
- return BINARY_EXTENSIONS.has(ext2);
19848
- }
19849
- function isDiffLineForFile(diffLine, filePath) {
19850
- const match2 = diffLine.match(/^diff --git a\/.+ b\/(.+)$/);
19851
- return match2 !== null && match2[1] === filePath;
19852
- }
19853
19845
  function stripConflictMarkers(content) {
19854
19846
  const lines = content.split("\n");
19855
19847
  const result = [];
@@ -19876,6 +19868,14 @@ function stripConflictMarkers(content) {
19876
19868
  }
19877
19869
  return result.join("\n");
19878
19870
  }
19871
+ function isBinaryFile(filePath) {
19872
+ const ext2 = (0, import_path9.extname)(filePath).toLowerCase();
19873
+ return BINARY_EXTENSIONS.has(ext2);
19874
+ }
19875
+ function isDiffLineForFile(diffLine, filePath) {
19876
+ const match2 = diffLine.match(/^diff --git a\/.+ b\/(.+)$/);
19877
+ return match2 !== null && match2[1] === filePath;
19878
+ }
19879
19879
  async function bootstrap(outputDir, options) {
19880
19880
  const git = new GitClient(outputDir);
19881
19881
  const lockManager = new LockfileManager(outputDir);
@@ -20065,30 +20065,136 @@ function computeContentHash(patchContent) {
20065
20065
  const normalized = patchContent.split("\n").filter((line) => !line.startsWith("From ") && !line.startsWith("index ") && !line.startsWith("Date: ")).join("\n");
20066
20066
  return `sha256:${(0, import_crypto3.createHash)("sha256").update(normalized).digest("hex")}`;
20067
20067
  }
20068
- function forget(outputDir, filePattern, options) {
20068
+ function parseDiffStat(patchContent) {
20069
+ let additions = 0;
20070
+ let deletions = 0;
20071
+ let inDiffHunk = false;
20072
+ for (const line of patchContent.split("\n")) {
20073
+ if (line.startsWith("diff --git ")) {
20074
+ inDiffHunk = true;
20075
+ continue;
20076
+ }
20077
+ if (!inDiffHunk) continue;
20078
+ if (line.startsWith("+") && !line.startsWith("+++")) {
20079
+ additions++;
20080
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
20081
+ deletions++;
20082
+ }
20083
+ }
20084
+ return { additions, deletions };
20085
+ }
20086
+ function toMatchedPatch(patch) {
20087
+ return {
20088
+ id: patch.id,
20089
+ message: patch.original_message,
20090
+ files: patch.files,
20091
+ diffstat: parseDiffStat(patch.patch_content),
20092
+ ...patch.status ? { status: patch.status } : {}
20093
+ };
20094
+ }
20095
+ function matchesPatch(patch, pattern) {
20096
+ const fileMatch = patch.files.some(
20097
+ (file) => file === pattern || minimatch(file, pattern)
20098
+ );
20099
+ if (fileMatch) return true;
20100
+ return patch.original_message.toLowerCase().includes(pattern.toLowerCase());
20101
+ }
20102
+ function buildWarnings(patches) {
20103
+ const warnings = [];
20104
+ for (const patch of patches) {
20105
+ if (patch.status === "resolving") {
20106
+ warnings.push(
20107
+ `patch ${patch.id} has conflict markers in these files: ${patch.files.join(", ")}. Run \`git checkout -- <files>\` to restore the generated versions.`
20108
+ );
20109
+ } else if (patch.status === "unresolved") {
20110
+ warnings.push(
20111
+ `patch ${patch.id} had unresolved conflicts (files: ${patch.files.join(", ")}).`
20112
+ );
20113
+ }
20114
+ }
20115
+ return warnings;
20116
+ }
20117
+ function forget(outputDir, options) {
20069
20118
  const lockManager = new LockfileManager(outputDir);
20070
20119
  if (!lockManager.exists()) {
20071
- return { removed: [], notFound: true };
20120
+ return { ...EMPTY_RESULT };
20072
20121
  }
20073
20122
  const lock = lockManager.read();
20074
- const matchingPatches = lock.patches.filter(
20075
- (patch) => patch.files.some((file) => file === filePattern || minimatch(file, filePattern))
20076
- );
20077
- if (matchingPatches.length === 0) {
20078
- return { removed: [], notFound: true };
20123
+ const totalPatches = lock.patches.length;
20124
+ if (options?.all) {
20125
+ const removed = lock.patches.map(toMatchedPatch);
20126
+ const warnings = buildWarnings(lock.patches);
20127
+ if (!options.dryRun) {
20128
+ for (const patch of lock.patches) {
20129
+ lockManager.addForgottenHash(patch.content_hash);
20130
+ }
20131
+ lockManager.clearPatches();
20132
+ lockManager.save();
20133
+ }
20134
+ return {
20135
+ initialized: true,
20136
+ removed,
20137
+ remaining: 0,
20138
+ notFound: false,
20139
+ alreadyForgotten: [],
20140
+ totalPatches,
20141
+ warnings
20142
+ };
20079
20143
  }
20080
- const removed = matchingPatches.map((p) => ({
20081
- id: p.id,
20082
- message: p.original_message,
20083
- files: p.files
20084
- }));
20085
- if (!options?.dryRun) {
20086
- for (const patch of matchingPatches) {
20087
- lockManager.removePatch(patch.id);
20144
+ if (options?.patchIds && options.patchIds.length > 0) {
20145
+ const removed = [];
20146
+ const alreadyForgotten = [];
20147
+ const patchesToRemove = [];
20148
+ for (const id of options.patchIds) {
20149
+ const patch = lock.patches.find((p) => p.id === id);
20150
+ if (patch) {
20151
+ removed.push(toMatchedPatch(patch));
20152
+ patchesToRemove.push(patch);
20153
+ } else {
20154
+ alreadyForgotten.push(id);
20155
+ }
20088
20156
  }
20089
- lockManager.save();
20157
+ const warnings = buildWarnings(patchesToRemove);
20158
+ if (!options.dryRun) {
20159
+ for (const patch of patchesToRemove) {
20160
+ lockManager.addForgottenHash(patch.content_hash);
20161
+ lockManager.removePatch(patch.id);
20162
+ }
20163
+ lockManager.save();
20164
+ }
20165
+ return {
20166
+ initialized: true,
20167
+ removed,
20168
+ remaining: totalPatches - removed.length,
20169
+ notFound: removed.length === 0 && alreadyForgotten.length > 0,
20170
+ alreadyForgotten,
20171
+ totalPatches,
20172
+ warnings
20173
+ };
20174
+ }
20175
+ if (options?.pattern) {
20176
+ const matched = lock.patches.filter((p) => matchesPatch(p, options.pattern)).map(toMatchedPatch);
20177
+ return {
20178
+ initialized: true,
20179
+ removed: [],
20180
+ remaining: totalPatches,
20181
+ notFound: matched.length === 0,
20182
+ alreadyForgotten: [],
20183
+ totalPatches,
20184
+ warnings: [],
20185
+ matched
20186
+ };
20090
20187
  }
20091
- return { removed, notFound: false };
20188
+ return {
20189
+ initialized: true,
20190
+ removed: [],
20191
+ remaining: totalPatches,
20192
+ notFound: totalPatches === 0,
20193
+ alreadyForgotten: [],
20194
+ totalPatches,
20195
+ warnings: [],
20196
+ matched: lock.patches.map(toMatchedPatch)
20197
+ };
20092
20198
  }
20093
20199
  function reset(outputDir, options) {
20094
20200
  const lockManager = new LockfileManager(outputDir);
@@ -20202,28 +20308,53 @@ async function getChangedFiles(git, currentGen, files) {
20202
20308
  function status(outputDir) {
20203
20309
  const lockManager = new LockfileManager(outputDir);
20204
20310
  if (!lockManager.exists()) {
20205
- return { initialized: false, patches: [], lastGeneration: void 0 };
20311
+ return {
20312
+ initialized: false,
20313
+ generationCount: 0,
20314
+ lastGeneration: void 0,
20315
+ patches: [],
20316
+ unresolvedCount: 0,
20317
+ excludePatterns: []
20318
+ };
20206
20319
  }
20207
20320
  const lock = lockManager.read();
20208
20321
  const patches = lock.patches.map((patch) => ({
20209
- sha: patch.original_commit.slice(0, 7),
20210
- author: patch.original_author.split("<")[0]?.trim() ?? "unknown",
20322
+ id: patch.id,
20323
+ type: patch.patch_content.includes("new file mode") ? "added" : "modified",
20211
20324
  message: patch.original_message,
20212
- files: patch.files
20325
+ author: patch.original_author.split("<")[0]?.trim() || "unknown",
20326
+ sha: patch.original_commit.slice(0, 7),
20327
+ files: patch.files,
20328
+ fileCount: patch.files.length,
20329
+ ...patch.status ? { status: patch.status } : {}
20213
20330
  }));
20331
+ const unresolvedCount = lock.patches.filter(
20332
+ (p) => p.status === "unresolved" || p.status === "resolving"
20333
+ ).length;
20214
20334
  let lastGeneration;
20215
20335
  const lastGen = lock.generations.find((g) => g.commit_sha === lock.current_generation);
20216
20336
  if (lastGen) {
20217
20337
  lastGeneration = {
20218
- sha: lastGen.commit_sha,
20219
- timestamp: lastGen.timestamp
20338
+ sha: lastGen.commit_sha.slice(0, 7),
20339
+ timestamp: lastGen.timestamp,
20340
+ cliVersion: lastGen.cli_version,
20341
+ generatorVersions: lastGen.generator_versions
20220
20342
  };
20221
20343
  }
20222
- return { initialized: true, patches, lastGeneration };
20344
+ const config = lockManager.getCustomizationsConfig();
20345
+ const excludePatterns = config.exclude ?? [];
20346
+ return {
20347
+ initialized: true,
20348
+ generationCount: lock.generations.length,
20349
+ lastGeneration,
20350
+ patches,
20351
+ unresolvedCount,
20352
+ excludePatterns
20353
+ };
20223
20354
  }
20224
- 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;
20355
+ 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, EMPTY_RESULT;
20225
20356
  var init_dist = __esm({
20226
- "../../node_modules/.pnpm/@fern-api+replay@0.8.0/node_modules/@fern-api/replay/dist/index.js"() {
20357
+ "../../node_modules/.pnpm/@fern-api+replay@0.9.0/node_modules/@fern-api/replay/dist/index.js"() {
20227
20358
  "use strict";
20228
20359
  init_esm4();
20229
20360
  import_fs6 = require("fs");
@@ -20457,6 +20588,15 @@ var init_dist = __esm({
20457
20588
  this.ensureLoaded();
20458
20589
  this.lock.patches = [];
20459
20590
  }
20591
+ addForgottenHash(hash) {
20592
+ this.ensureLoaded();
20593
+ if (!this.lock.forgotten_hashes) {
20594
+ this.lock.forgotten_hashes = [];
20595
+ }
20596
+ if (!this.lock.forgotten_hashes.includes(hash)) {
20597
+ this.lock.forgotten_hashes.push(hash);
20598
+ }
20599
+ }
20460
20600
  getUnresolvedPatches() {
20461
20601
  this.ensureLoaded();
20462
20602
  return this.lock.patches.filter((p) => p.status === "unresolved");
@@ -20550,6 +20690,7 @@ var init_dist = __esm({
20550
20690
  }
20551
20691
  const commits = this.parseGitLog(log);
20552
20692
  const newPatches = [];
20693
+ const forgottenHashes = new Set(lock.forgotten_hashes ?? []);
20553
20694
  for (const commit of commits) {
20554
20695
  if (isGenerationCommit(commit)) {
20555
20696
  continue;
@@ -20563,7 +20704,7 @@ var init_dist = __esm({
20563
20704
  }
20564
20705
  const patchContent = await this.git.formatPatch(commit.sha);
20565
20706
  const contentHash = this.computeContentHash(patchContent);
20566
- if (lock.patches.find((p) => p.content_hash === contentHash)) {
20707
+ if (lock.patches.find((p) => p.content_hash === contentHash) || forgottenHashes.has(contentHash)) {
20567
20708
  continue;
20568
20709
  }
20569
20710
  const filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha]);
@@ -20633,6 +20774,9 @@ var init_dist = __esm({
20633
20774
  revertIndicesToRemove.add(idx);
20634
20775
  }
20635
20776
  }
20777
+ if (!matchedExisting && !matchedNew) {
20778
+ revertIndicesToRemove.add(i);
20779
+ }
20636
20780
  }
20637
20781
  const filteredPatches = newPatches.filter((_, i) => !revertIndicesToRemove.has(i));
20638
20782
  return { patches: filteredPatches, revertedPatchIds: [...revertedPatchIdSet] };
@@ -20659,7 +20803,7 @@ var init_dist = __esm({
20659
20803
  if (!diff.trim()) return { patches: [], revertedPatchIds: [] };
20660
20804
  const contentHash = this.computeContentHash(diff);
20661
20805
  const lock = this.lockManager.read();
20662
- if (lock.patches.some((p) => p.content_hash === contentHash)) {
20806
+ if (lock.patches.some((p) => p.content_hash === contentHash) || (lock.forgotten_hashes ?? []).includes(contentHash)) {
20663
20807
  return { patches: [], revertedPatchIds: [] };
20664
20808
  }
20665
20809
  const headSha = (await this.git.exec(["rev-parse", "HEAD"])).trim();
@@ -20756,7 +20900,8 @@ var init_dist = __esm({
20756
20900
  async applyPatches(patches) {
20757
20901
  this.resetAccumulator();
20758
20902
  const results = [];
20759
- for (const patch of patches) {
20903
+ for (let i = 0; i < patches.length; i++) {
20904
+ const patch = patches[i];
20760
20905
  if (this.isExcluded(patch)) {
20761
20906
  results.push({
20762
20907
  patch,
@@ -20767,6 +20912,33 @@ var init_dist = __esm({
20767
20912
  }
20768
20913
  const result = await this.applyPatchWithFallback(patch);
20769
20914
  results.push(result);
20915
+ if (result.status === "conflict" && result.fileResults) {
20916
+ const laterFiles = /* @__PURE__ */ new Set();
20917
+ for (let j = i + 1; j < patches.length; j++) {
20918
+ for (const f of patches[j].files) {
20919
+ laterFiles.add(f);
20920
+ }
20921
+ }
20922
+ const resolvedToOriginal = /* @__PURE__ */ new Map();
20923
+ if (result.resolvedFiles) {
20924
+ for (const [orig, resolved] of Object.entries(result.resolvedFiles)) {
20925
+ resolvedToOriginal.set(resolved, orig);
20926
+ }
20927
+ }
20928
+ for (const fileResult of result.fileResults) {
20929
+ if (fileResult.status !== "conflict") continue;
20930
+ const originalPath = resolvedToOriginal.get(fileResult.file) ?? fileResult.file;
20931
+ if (laterFiles.has(fileResult.file) || laterFiles.has(originalPath)) {
20932
+ const filePath = (0, import_path9.join)(this.outputDir, fileResult.file);
20933
+ try {
20934
+ const content = await (0, import_promises4.readFile)(filePath, "utf-8");
20935
+ const stripped = stripConflictMarkers(content);
20936
+ await (0, import_promises4.writeFile)(filePath, stripped);
20937
+ } catch {
20938
+ }
20939
+ }
20940
+ }
20941
+ }
20770
20942
  }
20771
20943
  return results;
20772
20944
  }
@@ -20785,7 +20957,7 @@ var init_dist = __esm({
20785
20957
  const resolvedPath = await this.resolveFilePath(filePath, baseGen.tree_hash, currentTreeHash);
20786
20958
  const base = await this.git.showFile(baseGen.tree_hash, filePath);
20787
20959
  const theirs = await this.applyPatchToContent(base, patch.patch_content, filePath, tempGit, tempDir);
20788
- if (theirs && base) {
20960
+ if (theirs) {
20789
20961
  this.fileTheirsAccumulator.set(resolvedPath, {
20790
20962
  content: theirs,
20791
20963
  baseGeneration: patch.base_generation
@@ -20917,7 +21089,7 @@ var init_dist = __esm({
20917
21089
  );
20918
21090
  let useAccumulatorAsMergeBase = false;
20919
21091
  const accumulatorEntry = this.fileTheirsAccumulator.get(resolvedPath);
20920
- if (!theirs && base && accumulatorEntry) {
21092
+ if (!theirs && accumulatorEntry) {
20921
21093
  theirs = await this.applyPatchToContent(
20922
21094
  accumulatorEntry.content,
20923
21095
  patch.patch_content,
@@ -20976,7 +21148,7 @@ var init_dist = __esm({
20976
21148
  reason: "missing-content"
20977
21149
  };
20978
21150
  }
20979
- if (!base || !ours) {
21151
+ if (!base && !useAccumulatorAsMergeBase || !ours) {
20980
21152
  return {
20981
21153
  file: resolvedPath,
20982
21154
  status: "skipped",
@@ -20984,11 +21156,18 @@ var init_dist = __esm({
20984
21156
  };
20985
21157
  }
20986
21158
  const mergeBase = useAccumulatorAsMergeBase && accumulatorEntry ? accumulatorEntry.content : base;
21159
+ if (mergeBase == null) {
21160
+ return {
21161
+ file: resolvedPath,
21162
+ status: "skipped",
21163
+ reason: "missing-content"
21164
+ };
21165
+ }
20987
21166
  const merged = threeWayMerge(mergeBase, ours, effective_theirs);
20988
21167
  const outDir = (0, import_path9.dirname)(oursPath);
20989
21168
  await (0, import_promises4.mkdir)(outDir, { recursive: true });
20990
21169
  await (0, import_promises4.writeFile)(oursPath, merged.content);
20991
- if (effective_theirs && base) {
21170
+ if (effective_theirs) {
20992
21171
  this.fileTheirsAccumulator.set(resolvedPath, {
20993
21172
  content: effective_theirs,
20994
21173
  baseGeneration: patch.base_generation
@@ -21979,6 +22158,15 @@ CLI Version: ${options.cliVersion}`;
21979
22158
  };
21980
22159
  init_GitClient();
21981
22160
  REPLAY_FERNIGNORE_ENTRIES = [".fern/replay.lock", ".fern/replay.yml"];
22161
+ EMPTY_RESULT = {
22162
+ initialized: false,
22163
+ removed: [],
22164
+ remaining: 0,
22165
+ notFound: false,
22166
+ alreadyForgotten: [],
22167
+ totalPatches: 0,
22168
+ warnings: []
22169
+ };
21982
22170
  init_GitClient();
21983
22171
  }
21984
22172
  });
@@ -32328,6 +32516,12 @@ var PostGenerationPipeline = class {
32328
32516
  }
32329
32517
  };
32330
32518
 
32519
+ // src/replay/replay-forget.ts
32520
+ init_dist();
32521
+ function replayForget(params) {
32522
+ return forget(params.outputDir, params.options);
32523
+ }
32524
+
32331
32525
  // src/replay/replay-init.ts
32332
32526
  init_dist();
32333
32527
  var import_fs13 = require("fs");
@@ -32477,6 +32671,12 @@ function buildPrBody(result) {
32477
32671
  // src/replay/replay-resolve.ts
32478
32672
  init_dist();
32479
32673
 
32674
+ // src/replay/replay-status.ts
32675
+ init_dist();
32676
+ function replayStatus(params) {
32677
+ return status(params.outputDir);
32678
+ }
32679
+
32480
32680
  // src/configuration/loadGitHubConfig.ts
32481
32681
  var import_promises6 = require("fs/promises");
32482
32682
  async function loadGitHubConfig({
@@ -32737,11 +32937,14 @@ ${result.errors.map((e) => ` - ${e}`).join("\n")}
32737
32937
  });
32738
32938
  },
32739
32939
  async (argv) => {
32740
- if (argv.github == null || argv.token == null) {
32940
+ if (argv.github == null) {
32941
+ process.stderr.write("missing required argument; please specify --github flag\n");
32942
+ process.exit(1);
32943
+ }
32944
+ if (argv.token == null) {
32741
32945
  process.stderr.write(
32742
- "missing required arguments; please specify --github and --token flags\n"
32946
+ "warning: no GitHub token provided. Clone may fail for private repos. Pass --token or set GITHUB_TOKEN.\n"
32743
32947
  );
32744
- process.exit(1);
32745
32948
  }
32746
32949
  try {
32747
32950
  const result = await replayInit({
@@ -32850,6 +33053,84 @@ Bootstrap complete! Lockfile saved to ${outputDir}/.fern/replay.lock
32850
33053
  } catch (error) {
32851
33054
  const errorMessage = extractErrorMessage(error);
32852
33055
  process.stderr.write(`Bootstrap failed: ${errorMessage}
33056
+ `);
33057
+ process.exit(1);
33058
+ }
33059
+ }
33060
+ ).command(
33061
+ "status [directory]",
33062
+ false,
33063
+ // hidden from --help
33064
+ (subYargs) => {
33065
+ return subYargs.positional("directory", {
33066
+ type: "string",
33067
+ default: ".",
33068
+ description: "SDK directory containing .fern/replay.lock"
33069
+ });
33070
+ },
33071
+ (argv) => {
33072
+ try {
33073
+ const outputDir = resolve(cwd(), argv.directory ?? ".");
33074
+ const result = replayStatus({ outputDir });
33075
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
33076
+ process.exit(0);
33077
+ } catch (error) {
33078
+ const errorMessage = extractErrorMessage(error);
33079
+ process.stderr.write(`Replay status failed: ${errorMessage}
33080
+ `);
33081
+ process.exit(1);
33082
+ }
33083
+ }
33084
+ ).command(
33085
+ "forget [args..]",
33086
+ false,
33087
+ // hidden from --help
33088
+ (subYargs) => {
33089
+ return subYargs.positional("args", {
33090
+ type: "string",
33091
+ array: true,
33092
+ description: "Patch IDs or search pattern"
33093
+ }).option("dry-run", {
33094
+ type: "boolean",
33095
+ default: false,
33096
+ description: "Show what would be removed without actually removing"
33097
+ }).option("all", {
33098
+ type: "boolean",
33099
+ default: false,
33100
+ description: "Remove all tracked patches"
33101
+ }).option("pattern", {
33102
+ type: "string",
33103
+ description: "Search pattern (file path, glob, or commit message substring)"
33104
+ });
33105
+ },
33106
+ (argv) => {
33107
+ try {
33108
+ const outputDir = resolve(cwd(), ".");
33109
+ const args = argv.args ?? [];
33110
+ const dryRun = argv["dry-run"];
33111
+ let result;
33112
+ if (argv.all) {
33113
+ result = replayForget({ outputDir, options: { all: true, dryRun } });
33114
+ } else if (argv.pattern) {
33115
+ result = replayForget({ outputDir, options: { pattern: argv.pattern, dryRun } });
33116
+ } else if (args.length > 0) {
33117
+ const allPatchIds = args.every((a) => a.startsWith("patch-"));
33118
+ if (allPatchIds) {
33119
+ result = replayForget({ outputDir, options: { patchIds: args, dryRun } });
33120
+ } else {
33121
+ result = replayForget({
33122
+ outputDir,
33123
+ options: { pattern: args[0], dryRun }
33124
+ });
33125
+ }
33126
+ } else {
33127
+ result = replayForget({ outputDir, options: { dryRun } });
33128
+ }
33129
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
33130
+ process.exit(0);
33131
+ } catch (error) {
33132
+ const errorMessage = extractErrorMessage(error);
33133
+ process.stderr.write(`Replay forget failed: ${errorMessage}
32853
33134
  `);
32854
33135
  process.exit(1);
32855
33136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fern-api/generator-cli",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fern-api/fern.git",
@@ -22,7 +22,7 @@
22
22
  "**"
23
23
  ],
24
24
  "dependencies": {
25
- "@fern-api/replay": "^0.8.0",
25
+ "@fern-api/replay": "^0.9.0",
26
26
  "@octokit/rest": "^22.0.1",
27
27
  "es-toolkit": "^1.45.1",
28
28
  "tmp-promise": "^3.0.3"