@fern-api/replay 0.1.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.
Files changed (86) hide show
  1. package/README.md +33 -0
  2. package/dist/ConflictPrompt.d.ts +13 -0
  3. package/dist/ConflictPrompt.d.ts.map +1 -0
  4. package/dist/ConflictPrompt.js +38 -0
  5. package/dist/ConflictPrompt.js.map +1 -0
  6. package/dist/ConflictResolver.d.ts +11 -0
  7. package/dist/ConflictResolver.d.ts.map +1 -0
  8. package/dist/ConflictResolver.js +53 -0
  9. package/dist/ConflictResolver.js.map +1 -0
  10. package/dist/FernignoreMigrator.d.ts +71 -0
  11. package/dist/FernignoreMigrator.d.ts.map +1 -0
  12. package/dist/FernignoreMigrator.js +243 -0
  13. package/dist/FernignoreMigrator.js.map +1 -0
  14. package/dist/LockfileManager.d.ts +27 -0
  15. package/dist/LockfileManager.d.ts.map +1 -0
  16. package/dist/LockfileManager.js +108 -0
  17. package/dist/LockfileManager.js.map +1 -0
  18. package/dist/ReplayApplicator.d.ts +68 -0
  19. package/dist/ReplayApplicator.d.ts.map +1 -0
  20. package/dist/ReplayApplicator.js +492 -0
  21. package/dist/ReplayApplicator.js.map +1 -0
  22. package/dist/ReplayCommitter.d.ts +39 -0
  23. package/dist/ReplayCommitter.d.ts.map +1 -0
  24. package/dist/ReplayCommitter.js +84 -0
  25. package/dist/ReplayCommitter.js.map +1 -0
  26. package/dist/ReplayDetector.d.ts +25 -0
  27. package/dist/ReplayDetector.d.ts.map +1 -0
  28. package/dist/ReplayDetector.js +110 -0
  29. package/dist/ReplayDetector.js.map +1 -0
  30. package/dist/ReplayService.d.ts +103 -0
  31. package/dist/ReplayService.d.ts.map +1 -0
  32. package/dist/ReplayService.js +457 -0
  33. package/dist/ReplayService.js.map +1 -0
  34. package/dist/ThreeWayMerge.d.ts +11 -0
  35. package/dist/ThreeWayMerge.d.ts.map +1 -0
  36. package/dist/ThreeWayMerge.js +48 -0
  37. package/dist/ThreeWayMerge.js.map +1 -0
  38. package/dist/cli.d.ts +3 -0
  39. package/dist/cli.d.ts.map +1 -0
  40. package/dist/cli.js +464 -0
  41. package/dist/cli.js.map +1 -0
  42. package/dist/commands/bootstrap.d.ts +44 -0
  43. package/dist/commands/bootstrap.d.ts.map +1 -0
  44. package/dist/commands/bootstrap.js +268 -0
  45. package/dist/commands/bootstrap.js.map +1 -0
  46. package/dist/commands/forget.d.ts +26 -0
  47. package/dist/commands/forget.d.ts.map +1 -0
  48. package/dist/commands/forget.js +37 -0
  49. package/dist/commands/forget.js.map +1 -0
  50. package/dist/commands/index.d.ts +6 -0
  51. package/dist/commands/index.d.ts.map +1 -0
  52. package/dist/commands/index.js +6 -0
  53. package/dist/commands/index.js.map +1 -0
  54. package/dist/commands/reset.d.ts +28 -0
  55. package/dist/commands/reset.d.ts.map +1 -0
  56. package/dist/commands/reset.js +37 -0
  57. package/dist/commands/reset.js.map +1 -0
  58. package/dist/commands/resolve.d.ts +16 -0
  59. package/dist/commands/resolve.d.ts.map +1 -0
  60. package/dist/commands/resolve.js +28 -0
  61. package/dist/commands/resolve.js.map +1 -0
  62. package/dist/commands/status.d.ts +34 -0
  63. package/dist/commands/status.d.ts.map +1 -0
  64. package/dist/commands/status.js +32 -0
  65. package/dist/commands/status.js.map +1 -0
  66. package/dist/environment.d.ts +5 -0
  67. package/dist/environment.d.ts.map +1 -0
  68. package/dist/environment.js +14 -0
  69. package/dist/environment.js.map +1 -0
  70. package/dist/git/CommitDetection.d.ts +17 -0
  71. package/dist/git/CommitDetection.d.ts.map +1 -0
  72. package/dist/git/CommitDetection.js +33 -0
  73. package/dist/git/CommitDetection.js.map +1 -0
  74. package/dist/git/GitClient.d.ts +28 -0
  75. package/dist/git/GitClient.d.ts.map +1 -0
  76. package/dist/git/GitClient.js +104 -0
  77. package/dist/git/GitClient.js.map +1 -0
  78. package/dist/index.d.ts +15 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +14 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/types.d.ts +80 -0
  83. package/dist/types.d.ts.map +1 -0
  84. package/dist/types.js +9 -0
  85. package/dist/types.js.map +1 -0
  86. package/package.json +45 -0
@@ -0,0 +1,268 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, writeFileSync, existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { GitClient } from "../git/GitClient.js";
5
+ import { isGenerationCommit, isReplayCommit } from "../git/CommitDetection.js";
6
+ import { LockfileManager } from "../LockfileManager.js";
7
+ import { FernignoreMigrator } from "../FernignoreMigrator.js";
8
+ /**
9
+ * Bootstrap Replay for an existing SDK repository.
10
+ *
11
+ * Scans git history to find all generation commits, identifies
12
+ * user customization patches between them, and creates the initial replay.lock.
13
+ *
14
+ * This is a one-time migration step for SDKs that existed before Replay.
15
+ */
16
+ export async function bootstrap(outputDir, options) {
17
+ const git = new GitClient(outputDir);
18
+ const lockManager = new LockfileManager(outputDir);
19
+ const maxCommits = options?.maxCommitsToScan ?? 500;
20
+ const warnings = [];
21
+ // Check for existing lockfile
22
+ if (lockManager.exists() && !options?.force) {
23
+ return {
24
+ generationCommit: null,
25
+ patchesDetected: 0,
26
+ patchesCreated: 0,
27
+ patches: [],
28
+ fernignorePatterns: [],
29
+ fernignoreUpdated: false,
30
+ warnings: [
31
+ "Replay lockfile already exists. Use --force to overwrite.",
32
+ ],
33
+ staleGenerationsSkipped: 0,
34
+ scannedSinceGeneration: "",
35
+ };
36
+ }
37
+ // 1. Find ALL generation commits (newest first, excluding replay commits)
38
+ const genCommits = await findAllGenerationCommits(git, maxCommits);
39
+ if (genCommits.length === 0) {
40
+ return {
41
+ generationCommit: null,
42
+ patchesDetected: 0,
43
+ patchesCreated: 0,
44
+ patches: [],
45
+ fernignorePatterns: [],
46
+ fernignoreUpdated: false,
47
+ warnings: [
48
+ "No generation commits found in the last " + maxCommits + " commits. " +
49
+ "Run 'fern generate' first to establish a baseline.",
50
+ ],
51
+ staleGenerationsSkipped: 0,
52
+ scannedSinceGeneration: "",
53
+ };
54
+ }
55
+ const latestGen = genCommits[0];
56
+ // 2. Set up in-memory lockfile (don't write to disk yet — we may be in dry-run mode)
57
+ const treeHash = await git.getTreeHash(latestGen.sha);
58
+ const genRecord = {
59
+ commit_sha: latestGen.sha,
60
+ tree_hash: treeHash,
61
+ timestamp: new Date().toISOString(),
62
+ cli_version: "unknown",
63
+ generator_versions: {},
64
+ };
65
+ lockManager.initializeInMemory(genRecord);
66
+ // 3. Scan between ALL generation commit pairs for user patches
67
+ const patches = await findAllUserPatches(git, genCommits);
68
+ // 4. Handle .fernignore if present
69
+ const migrator = new FernignoreMigrator(git, lockManager, outputDir);
70
+ const fernignorePatterns = migrator.readFernignorePatterns();
71
+ let fernignoreAnalysis;
72
+ if (migrator.fernignoreExists() && fernignorePatterns.length > 0) {
73
+ fernignoreAnalysis = await migrator.analyzeMigration();
74
+ // Add synthetic patches from .fernignore analysis
75
+ if (fernignoreAnalysis.syntheticPatches.length > 0) {
76
+ patches.push(...fernignoreAnalysis.syntheticPatches);
77
+ }
78
+ if (fernignoreAnalysis.fernignoreOnly.length > 0) {
79
+ for (const file of fernignoreAnalysis.fernignoreOnly) {
80
+ warnings.push(`${file}: in .fernignore but no recent commits found since last generation. ` +
81
+ "File may be stale, matches generated output, or does not exist. No customization to track.");
82
+ }
83
+ }
84
+ }
85
+ // In dry-run mode, don't persist anything
86
+ if (options?.dryRun) {
87
+ return {
88
+ generationCommit: latestGen,
89
+ patchesDetected: patches.length,
90
+ patchesCreated: 0,
91
+ patches,
92
+ fernignorePatterns,
93
+ fernignoreAnalysis,
94
+ fernignoreUpdated: false,
95
+ warnings,
96
+ staleGenerationsSkipped: genCommits.length - 1,
97
+ scannedSinceGeneration: latestGen.sha,
98
+ };
99
+ }
100
+ // 5. Persist patches to lockfile
101
+ for (const patch of patches) {
102
+ lockManager.addPatch(patch);
103
+ }
104
+ lockManager.save();
105
+ // 6. Ensure .fernignore protects replay files from generation wipe
106
+ const fernignoreUpdated = ensureFernignoreEntries(outputDir);
107
+ // 7. Handle .fernignore migration
108
+ if (migrator.fernignoreExists() && fernignorePatterns.length > 0) {
109
+ const action = options?.fernignoreAction ?? "skip";
110
+ if (action === "migrate") {
111
+ // Move truly untrackable patterns (no diff from generated) to replay.yml exclude list
112
+ const patternsToExclude = fernignoreAnalysis?.fernignoreOnly ?? [];
113
+ if (patternsToExclude.length > 0) {
114
+ migrator.movePatternsToReplayYml(patternsToExclude);
115
+ }
116
+ }
117
+ }
118
+ return {
119
+ generationCommit: latestGen,
120
+ patchesDetected: patches.length,
121
+ patchesCreated: patches.length,
122
+ patches,
123
+ fernignorePatterns,
124
+ fernignoreAnalysis,
125
+ fernignoreUpdated,
126
+ warnings,
127
+ staleGenerationsSkipped: genCommits.length - 1,
128
+ scannedSinceGeneration: latestGen.sha,
129
+ };
130
+ }
131
+ /**
132
+ * Find all generation commits in history, newest first.
133
+ * Excludes replay commits (which also match isGenerationCommit).
134
+ */
135
+ async function findAllGenerationCommits(git, maxCommits) {
136
+ const log = await git.exec([
137
+ "log",
138
+ "--format=%H%x00%an%x00%ae%x00%s",
139
+ `-${maxCommits}`,
140
+ ]);
141
+ if (!log.trim()) {
142
+ return [];
143
+ }
144
+ const genCommits = [];
145
+ for (const line of log.trim().split("\n")) {
146
+ if (!line)
147
+ continue;
148
+ const [sha, authorName, authorEmail, message] = line.split("\0");
149
+ const commit = { sha, authorName, authorEmail, message };
150
+ if (isGenerationCommit(commit) && !isReplayCommit(commit)) {
151
+ genCommits.push(commit);
152
+ }
153
+ }
154
+ return genCommits;
155
+ }
156
+ /**
157
+ * Find user customization patches since the LAST (most recent) generation.
158
+ *
159
+ * Only scans commits between the latest generation and HEAD, ignoring stale
160
+ * commits from older generations. This infers customer intent: if they didn't
161
+ * protect old edits and let them be regenerated over multiple times, they
162
+ * likely don't need them tracked.
163
+ *
164
+ * Deduplicates by content hash (handles rebased/cherry-picked commits).
165
+ */
166
+ async function findAllUserPatches(git, genCommits) {
167
+ const patches = [];
168
+ const seenHashes = new Set();
169
+ // Only scan since the LAST (most recent) generation commit to HEAD
170
+ // This ignores "stale" commits that were regenerated over before the customer activated replay
171
+ const latestGen = genCommits[0];
172
+ const recentPatches = await extractUserPatches(git, latestGen.sha, "HEAD", latestGen.sha, seenHashes);
173
+ patches.push(...recentPatches);
174
+ return patches;
175
+ }
176
+ /**
177
+ * Find user commits in a range (fromSha..toRef) and extract patches.
178
+ * Returns patches in chronological order (oldest first).
179
+ */
180
+ async function extractUserPatches(git, fromSha, toRef, baseGeneration, seenHashes) {
181
+ const log = await git.exec([
182
+ "log",
183
+ "--format=%H%x00%an%x00%ae%x00%s",
184
+ `${fromSha}..${toRef}`,
185
+ ]);
186
+ if (!log.trim()) {
187
+ return [];
188
+ }
189
+ const commits = parseGitLog(log);
190
+ const patches = [];
191
+ // Process in reverse (oldest first) for chronological order
192
+ for (const commit of commits.reverse()) {
193
+ if (isGenerationCommit(commit))
194
+ continue;
195
+ const parents = await git.getCommitParents(commit.sha);
196
+ if (parents.length > 1)
197
+ continue;
198
+ const patchContent = await git.formatPatch(commit.sha);
199
+ const contentHash = computeContentHash(patchContent);
200
+ if (seenHashes.has(contentHash))
201
+ continue;
202
+ seenHashes.add(contentHash);
203
+ const filesOutput = await git.exec([
204
+ "diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha,
205
+ ]);
206
+ patches.push({
207
+ id: `patch-${commit.sha.slice(0, 8)}`,
208
+ content_hash: contentHash,
209
+ original_commit: commit.sha,
210
+ original_message: commit.message,
211
+ original_author: `${commit.authorName} <${commit.authorEmail}>`,
212
+ base_generation: baseGeneration,
213
+ files: filesOutput.trim().split("\n").filter(Boolean),
214
+ patch_content: patchContent,
215
+ });
216
+ }
217
+ return patches;
218
+ }
219
+ function parseGitLog(log) {
220
+ return log
221
+ .trim()
222
+ .split("\n")
223
+ .filter(Boolean)
224
+ .map((line) => {
225
+ const [sha, authorName, authorEmail, message] = line.split("\0");
226
+ return { sha, authorName, authorEmail, message };
227
+ });
228
+ }
229
+ const REPLAY_FERNIGNORE_ENTRIES = [".fern/replay.lock", ".fern/replay.yml"];
230
+ /**
231
+ * Ensure .fernignore contains entries for replay files so they
232
+ * survive the `git rm -rf .` wipe during generation.
233
+ * Creates .fernignore if it doesn't exist.
234
+ * Returns true if .fernignore was modified.
235
+ */
236
+ function ensureFernignoreEntries(outputDir) {
237
+ const fernignorePath = join(outputDir, ".fernignore");
238
+ let content = "";
239
+ if (existsSync(fernignorePath)) {
240
+ content = readFileSync(fernignorePath, "utf-8");
241
+ }
242
+ const lines = content.split("\n");
243
+ const toAdd = [];
244
+ for (const entry of REPLAY_FERNIGNORE_ENTRIES) {
245
+ if (!lines.some(line => line.trim() === entry)) {
246
+ toAdd.push(entry);
247
+ }
248
+ }
249
+ if (toAdd.length === 0) {
250
+ return false;
251
+ }
252
+ if (content && !content.endsWith("\n")) {
253
+ content += "\n";
254
+ }
255
+ content += toAdd.join("\n") + "\n";
256
+ writeFileSync(fernignorePath, content, "utf-8");
257
+ return true;
258
+ }
259
+ function computeContentHash(patchContent) {
260
+ const normalized = patchContent
261
+ .split("\n")
262
+ .filter((line) => !line.startsWith("From ") &&
263
+ !line.startsWith("index ") &&
264
+ !line.startsWith("Date: "))
265
+ .join("\n");
266
+ return `sha256:${createHash("sha256").update(normalized).digest("hex")}`;
267
+ }
268
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/commands/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAA0B,MAAM,0BAA0B,CAAC;AAqCtF;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAiB,EAAE,OAA0B;IACzE,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC,SAAS,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,OAAO,EAAE,gBAAgB,IAAI,GAAG,CAAC;IACpD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,8BAA8B;IAC9B,IAAI,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;QAC1C,OAAO;YACH,gBAAgB,EAAE,IAAI;YACtB,eAAe,EAAE,CAAC;YAClB,cAAc,EAAE,CAAC;YACjB,OAAO,EAAE,EAAE;YACX,kBAAkB,EAAE,EAAE;YACtB,iBAAiB,EAAE,KAAK;YACxB,QAAQ,EAAE;gBACN,2DAA2D;aAC9D;YACD,uBAAuB,EAAE,CAAC;YAC1B,sBAAsB,EAAE,EAAE;SAC7B,CAAC;IACN,CAAC;IAED,0EAA0E;IAC1E,MAAM,UAAU,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAEnE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACH,gBAAgB,EAAE,IAAI;YACtB,eAAe,EAAE,CAAC;YAClB,cAAc,EAAE,CAAC;YACjB,OAAO,EAAE,EAAE;YACX,kBAAkB,EAAE,EAAE;YACtB,iBAAiB,EAAE,KAAK;YACxB,QAAQ,EAAE;gBACN,0CAA0C,GAAG,UAAU,GAAG,YAAY;oBACtE,oDAAoD;aACvD;YACD,uBAAuB,EAAE,CAAC;YAC1B,sBAAsB,EAAE,EAAE;SAC7B,CAAC;IACN,CAAC;IAED,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAE,CAAC;IAEjC,qFAAqF;IACrF,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG;QACd,UAAU,EAAE,SAAS,CAAC,GAAG;QACzB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,WAAW,EAAE,SAAS;QACtB,kBAAkB,EAAE,EAA4B;KACnD,CAAC;IAEF,WAAW,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAE1C,+DAA+D;IAC/D,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAE1D,mCAAmC;IACnC,MAAM,QAAQ,GAAG,IAAI,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;IACrE,MAAM,kBAAkB,GAAG,QAAQ,CAAC,sBAAsB,EAAE,CAAC;IAC7D,IAAI,kBAAiD,CAAC;IAEtD,IAAI,QAAQ,CAAC,gBAAgB,EAAE,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/D,kBAAkB,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QAEvD,kDAAkD;QAClD,IAAI,kBAAkB,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjD,OAAO,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,kBAAkB,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/C,KAAK,MAAM,IAAI,IAAI,kBAAkB,CAAC,cAAc,EAAE,CAAC;gBACnD,QAAQ,CAAC,IAAI,CACT,GAAG,IAAI,sEAAsE;oBAC7E,4FAA4F,CAC/F,CAAC;YACN,CAAC;QACL,CAAC;IACL,CAAC;IAED,0CAA0C;IAC1C,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,OAAO;YACH,gBAAgB,EAAE,SAAS;YAC3B,eAAe,EAAE,OAAO,CAAC,MAAM;YAC/B,cAAc,EAAE,CAAC;YACjB,OAAO;YACP,kBAAkB;YAClB,kBAAkB;YAClB,iBAAiB,EAAE,KAAK;YACxB,QAAQ;YACR,uBAAuB,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC;YAC9C,sBAAsB,EAAE,SAAS,CAAC,GAAG;SACxC,CAAC;IACN,CAAC;IAED,iCAAiC;IACjC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IACD,WAAW,CAAC,IAAI,EAAE,CAAC;IAEnB,mEAAmE;IACnE,MAAM,iBAAiB,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;IAE7D,kCAAkC;IAClC,IAAI,QAAQ,CAAC,gBAAgB,EAAE,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/D,MAAM,MAAM,GAAG,OAAO,EAAE,gBAAgB,IAAI,MAAM,CAAC;QACnD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACvB,sFAAsF;YACtF,MAAM,iBAAiB,GAAG,kBAAkB,EAAE,cAAc,IAAI,EAAE,CAAC;YACnE,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/B,QAAQ,CAAC,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;YACxD,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO;QACH,gBAAgB,EAAE,SAAS;QAC3B,eAAe,EAAE,OAAO,CAAC,MAAM;QAC/B,cAAc,EAAE,OAAO,CAAC,MAAM;QAC9B,OAAO;QACP,kBAAkB;QAClB,kBAAkB;QAClB,iBAAiB;QACjB,QAAQ;QACR,uBAAuB,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC;QAC9C,sBAAsB,EAAE,SAAS,CAAC,GAAG;KACxC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,wBAAwB,CACnC,GAAc,EACd,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC;QACvB,KAAK;QACL,iCAAiC;QACjC,IAAI,UAAU,EAAE;KACnB,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QACd,OAAO,EAAE,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAiB,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,MAAM,GAAe,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;QACrE,IAAI,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;YACxD,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK,UAAU,kBAAkB,CAC7B,GAAc,EACd,UAAwB;IAExB,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAErC,mEAAmE;IACnE,+FAA+F;IAC/F,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAE,CAAC;IACjC,MAAM,aAAa,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACtG,OAAO,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;IAE/B,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,kBAAkB,CAC7B,GAAc,EACd,OAAe,EACf,KAAa,EACb,cAAsB,EACtB,UAAuB;IAEvB,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC;QACvB,KAAK;QACL,iCAAiC;QACjC,GAAG,OAAO,KAAK,KAAK,EAAE;KACzB,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QACd,OAAO,EAAE,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,4DAA4D;IAC5D,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACrC,IAAI,kBAAkB,CAAC,MAAM,CAAC;YAAE,SAAS;QAEzC,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAEjC,MAAM,YAAY,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvD,MAAM,WAAW,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;QAErD,IAAI,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC;YAAE,SAAS;QAC1C,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAE5B,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC;YAC/B,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG;SACjE,CAAC,CAAC;QAEH,OAAO,CAAC,IAAI,CAAC;YACT,EAAE,EAAE,SAAS,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;YACrC,YAAY,EAAE,WAAW;YACzB,eAAe,EAAE,MAAM,CAAC,GAAG;YAC3B,gBAAgB,EAAE,MAAM,CAAC,OAAO;YAChC,eAAe,EAAE,GAAG,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,WAAW,GAAG;YAC/D,eAAe,EAAE,cAAc;YAC/B,KAAK,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;YACrD,aAAa,EAAE,YAAY;SAC9B,CAAC,CAAC;IACP,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC5B,OAAO,GAAG;SACL,IAAI,EAAE;SACN,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACV,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;AACX,CAAC;AAED,MAAM,yBAAyB,GAAG,CAAC,mBAAmB,EAAE,kBAAkB,CAAC,CAAC;AAE5E;;;;;GAKG;AACH,SAAS,uBAAuB,CAAC,SAAiB;IAC9C,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IACtD,IAAI,OAAO,GAAG,EAAE,CAAC;IAEjB,IAAI,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,KAAK,IAAI,yBAAyB,EAAE,CAAC;QAC5C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;YAC7C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACL,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,OAAO,IAAI,IAAI,CAAC;IACpB,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACnC,aAAa,CAAC,cAAc,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,kBAAkB,CAAC,YAAoB;IAC5C,MAAM,UAAU,GAAG,YAAY;SAC1B,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACb,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QACzB,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAC1B,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAC7B;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEhB,OAAO,UAAU,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,26 @@
1
+ export interface ForgetOptions {
2
+ /** Don't actually remove, just show what would be removed */
3
+ dryRun?: boolean;
4
+ }
5
+ export interface ForgetResult {
6
+ /** Patches that were (or would be) removed */
7
+ removed: Array<{
8
+ id: string;
9
+ message: string;
10
+ files: string[];
11
+ }>;
12
+ /** True if no patches matched the pattern */
13
+ notFound: boolean;
14
+ }
15
+ /**
16
+ * Remove patches matching a file path or glob pattern.
17
+ *
18
+ * The next regeneration will NOT replay these patches -
19
+ * generated code will replace the user's customizations for those files.
20
+ *
21
+ * @param outputDir - SDK output directory
22
+ * @param filePattern - File path or glob pattern (e.g., "src/utils/retry.ts" or "src/legacy/**")
23
+ * @param options - ForgetOptions
24
+ */
25
+ export declare function forget(outputDir: string, filePattern: string, options?: ForgetOptions): ForgetResult;
26
+ //# sourceMappingURL=forget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forget.d.ts","sourceRoot":"","sources":["../../src/commands/forget.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC1B,6DAA6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IACzB,8CAA8C;IAC9C,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACjE,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAClB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,aAAa,GACxB,YAAY,CAkCd"}
@@ -0,0 +1,37 @@
1
+ import { LockfileManager } from "../LockfileManager.js";
2
+ import { minimatch } from "minimatch";
3
+ /**
4
+ * Remove patches matching a file path or glob pattern.
5
+ *
6
+ * The next regeneration will NOT replay these patches -
7
+ * generated code will replace the user's customizations for those files.
8
+ *
9
+ * @param outputDir - SDK output directory
10
+ * @param filePattern - File path or glob pattern (e.g., "src/utils/retry.ts" or "src/legacy/**")
11
+ * @param options - ForgetOptions
12
+ */
13
+ export function forget(outputDir, filePattern, options) {
14
+ const lockManager = new LockfileManager(outputDir);
15
+ if (!lockManager.exists()) {
16
+ return { removed: [], notFound: true };
17
+ }
18
+ const lock = lockManager.read();
19
+ // Find patches that match the pattern
20
+ const matchingPatches = lock.patches.filter((patch) => patch.files.some((file) => file === filePattern || minimatch(file, filePattern)));
21
+ if (matchingPatches.length === 0) {
22
+ return { removed: [], notFound: true };
23
+ }
24
+ const removed = matchingPatches.map((p) => ({
25
+ id: p.id,
26
+ message: p.original_message,
27
+ files: p.files,
28
+ }));
29
+ if (!options?.dryRun) {
30
+ for (const patch of matchingPatches) {
31
+ lockManager.removePatch(patch.id);
32
+ }
33
+ lockManager.save();
34
+ }
35
+ return { removed, notFound: false };
36
+ }
37
+ //# sourceMappingURL=forget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forget.js","sourceRoot":"","sources":["../../src/commands/forget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AActC;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CAClB,SAAiB,EACjB,WAAmB,EACnB,OAAuB;IAEvB,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC,SAAS,CAAC,CAAC;IAEnD,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IAEhC,sCAAsC;IACtC,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAClD,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CACtB,IAAI,KAAK,WAAW,IAAI,SAAS,CAAC,IAAI,EAAE,WAAW,CAAC,CACvD,CACJ,CAAC;IAEF,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxC,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,OAAO,EAAE,CAAC,CAAC,gBAAgB;QAC3B,KAAK,EAAE,CAAC,CAAC,KAAK;KACjB,CAAC,CAAC,CAAC;IAEJ,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YAClC,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,WAAW,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { bootstrap, type BootstrapOptions, type BootstrapResult } from "./bootstrap.js";
2
+ export { forget, type ForgetOptions, type ForgetResult } from "./forget.js";
3
+ export { reset, type ResetOptions, type ResetResult } from "./reset.js";
4
+ export { resolve, type ResolveOptions, type ResolveResult } from "./resolve.js";
5
+ export { status, type StatusResult, type StatusPatch, type StatusGeneration } from "./status.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { bootstrap } from "./bootstrap.js";
2
+ export { forget } from "./forget.js";
3
+ export { reset } from "./reset.js";
4
+ export { resolve } from "./resolve.js";
5
+ export { status } from "./status.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA+C,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,MAAM,EAAyC,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAuC,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,OAAO,EAA2C,MAAM,cAAc,CAAC;AAChF,OAAO,EAAE,MAAM,EAA8D,MAAM,aAAa,CAAC"}
@@ -0,0 +1,28 @@
1
+ export interface ResetOptions {
2
+ /** Don't actually delete, just show what would happen */
3
+ dryRun?: boolean;
4
+ }
5
+ export interface ResetResult {
6
+ /** Whether reset was successful (or would be) */
7
+ success: boolean;
8
+ /** Number of patches that were (or would be) removed */
9
+ patchesRemoved: number;
10
+ /** Whether the lockfile was (or would be) deleted */
11
+ lockfileDeleted: boolean;
12
+ /** True if there was nothing to reset */
13
+ nothingToReset: boolean;
14
+ }
15
+ /**
16
+ * Reset Replay - removes replay.lock entirely.
17
+ *
18
+ * User's code changes remain in the repository, but they will NOT be
19
+ * automatically preserved on the next regeneration. The next `fern generate`
20
+ * will overwrite everything.
21
+ *
22
+ * Recovery: Run `fern-replay bootstrap` to re-initialize from git history.
23
+ *
24
+ * @param outputDir - SDK output directory
25
+ * @param options - ResetOptions
26
+ */
27
+ export declare function reset(outputDir: string, options?: ResetOptions): ResetResult;
28
+ //# sourceMappingURL=reset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.d.ts","sourceRoot":"","sources":["../../src/commands/reset.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IACzB,yDAAyD;IACzD,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IACxB,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,wDAAwD;IACxD,cAAc,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,eAAe,EAAE,OAAO,CAAC;IACzB,yCAAyC;IACzC,cAAc,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW,CAyB5E"}
@@ -0,0 +1,37 @@
1
+ import { LockfileManager } from "../LockfileManager.js";
2
+ import { unlinkSync } from "node:fs";
3
+ /**
4
+ * Reset Replay - removes replay.lock entirely.
5
+ *
6
+ * User's code changes remain in the repository, but they will NOT be
7
+ * automatically preserved on the next regeneration. The next `fern generate`
8
+ * will overwrite everything.
9
+ *
10
+ * Recovery: Run `fern-replay bootstrap` to re-initialize from git history.
11
+ *
12
+ * @param outputDir - SDK output directory
13
+ * @param options - ResetOptions
14
+ */
15
+ export function reset(outputDir, options) {
16
+ const lockManager = new LockfileManager(outputDir);
17
+ if (!lockManager.exists()) {
18
+ return {
19
+ success: true,
20
+ patchesRemoved: 0,
21
+ lockfileDeleted: false,
22
+ nothingToReset: true,
23
+ };
24
+ }
25
+ const lock = lockManager.read();
26
+ const patchCount = lock.patches.length;
27
+ if (!options?.dryRun) {
28
+ unlinkSync(lockManager.lockfilePath);
29
+ }
30
+ return {
31
+ success: true,
32
+ patchesRemoved: patchCount,
33
+ lockfileDeleted: true,
34
+ nothingToReset: false,
35
+ };
36
+ }
37
+ //# sourceMappingURL=reset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.js","sourceRoot":"","sources":["../../src/commands/reset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAkBrC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,KAAK,CAAC,SAAiB,EAAE,OAAsB;IAC3D,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC,SAAS,CAAC,CAAC;IAEnD,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QACxB,OAAO;YACH,OAAO,EAAE,IAAI;YACb,cAAc,EAAE,CAAC;YACjB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;SACvB,CAAC;IACN,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IAChC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAEvC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACnB,UAAU,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC;IAED,OAAO;QACH,OAAO,EAAE,IAAI;QACb,cAAc,EAAE,UAAU;QAC1B,eAAe,EAAE,IAAI;QACrB,cAAc,EAAE,KAAK;KACxB,CAAC;AACN,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface ResolveOptions {
2
+ /** Check for remaining conflict markers before committing. Default: true */
3
+ checkMarkers?: boolean;
4
+ }
5
+ export interface ResolveResult {
6
+ /** Whether the resolve succeeded */
7
+ success: boolean;
8
+ /** Commit SHA of the [fern-replay] commit, if created */
9
+ commitSha?: string;
10
+ /** Reason for failure */
11
+ reason?: string;
12
+ /** Files that still have conflict markers */
13
+ unresolvedFiles?: string[];
14
+ }
15
+ export declare function resolve(outputDir: string, options?: ResolveOptions): Promise<ResolveResult>;
16
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/commands/resolve.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC3B,4EAA4E;IAC5E,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC1B,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,wBAAsB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CA6BjG"}
@@ -0,0 +1,28 @@
1
+ import { GitClient } from "../git/GitClient.js";
2
+ import { LockfileManager } from "../LockfileManager.js";
3
+ import { ReplayCommitter } from "../ReplayCommitter.js";
4
+ export async function resolve(outputDir, options) {
5
+ const lockManager = new LockfileManager(outputDir);
6
+ if (!lockManager.exists()) {
7
+ return { success: false, reason: "no-lockfile" };
8
+ }
9
+ const lock = lockManager.read();
10
+ if (lock.patches.length === 0) {
11
+ return { success: false, reason: "no-patches" };
12
+ }
13
+ const git = new GitClient(outputDir);
14
+ // Check for remaining conflict markers
15
+ if (options?.checkMarkers !== false) {
16
+ const markerFiles = await git.exec(["grep", "-l", "<<<<<<<", "--", "."]).catch(() => "");
17
+ if (markerFiles.trim()) {
18
+ const files = markerFiles.trim().split("\n").filter(Boolean);
19
+ return { success: false, reason: "unresolved-conflicts", unresolvedFiles: files };
20
+ }
21
+ }
22
+ // Stage all changes and create [fern-replay] commit
23
+ const committer = new ReplayCommitter(git, outputDir);
24
+ await committer.stageAll();
25
+ const commitSha = await committer.commitReplay(lock.patches.length, lock.patches);
26
+ return { success: true, commitSha };
27
+ }
28
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/commands/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAkBxD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,SAAiB,EAAE,OAAwB;IACrE,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC,SAAS,CAAC,CAAC;IAEnD,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IACrD,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,SAAS,CAAC,CAAC;IAErC,uCAAuC;IACvC,IAAI,OAAO,EAAE,YAAY,KAAK,KAAK,EAAE,CAAC;QAClC,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACzF,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC7D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;QACtF,CAAC;IACL,CAAC;IAED,oDAAoD;IACpD,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACtD,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAElF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,34 @@
1
+ export interface StatusResult {
2
+ /** Whether replay is initialized (lockfile exists) */
3
+ initialized: boolean;
4
+ /** Tracked customization patches */
5
+ patches: StatusPatch[];
6
+ /** Last generation info, if available */
7
+ lastGeneration: StatusGeneration | undefined;
8
+ }
9
+ export interface StatusPatch {
10
+ /** Short SHA of the original commit */
11
+ sha: string;
12
+ /** Author name (without email) */
13
+ author: string;
14
+ /** Original commit message */
15
+ message: string;
16
+ /** Files touched by this patch */
17
+ files: string[];
18
+ }
19
+ export interface StatusGeneration {
20
+ /** Full commit SHA */
21
+ sha: string;
22
+ /** Generation timestamp */
23
+ timestamp: string;
24
+ }
25
+ /**
26
+ * Get the current Replay status for an SDK repository.
27
+ *
28
+ * Returns structured info about tracked patches and the last generation.
29
+ * The CLI can format this however it wants.
30
+ *
31
+ * @param outputDir - SDK output directory
32
+ */
33
+ export declare function status(outputDir: string): StatusResult;
34
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IACzB,sDAAsD;IACtD,WAAW,EAAE,OAAO,CAAC;IACrB,oCAAoC;IACpC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,yCAAyC;IACzC,cAAc,EAAE,gBAAgB,GAAG,SAAS,CAAC;CAChD;AAED,MAAM,WAAW,WAAW;IACxB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC7B,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CA0BtD"}
@@ -0,0 +1,32 @@
1
+ import { LockfileManager } from "../LockfileManager.js";
2
+ /**
3
+ * Get the current Replay status for an SDK repository.
4
+ *
5
+ * Returns structured info about tracked patches and the last generation.
6
+ * The CLI can format this however it wants.
7
+ *
8
+ * @param outputDir - SDK output directory
9
+ */
10
+ export function status(outputDir) {
11
+ const lockManager = new LockfileManager(outputDir);
12
+ if (!lockManager.exists()) {
13
+ return { initialized: false, patches: [], lastGeneration: undefined };
14
+ }
15
+ const lock = lockManager.read();
16
+ const patches = lock.patches.map((patch) => ({
17
+ sha: patch.original_commit.slice(0, 7),
18
+ author: patch.original_author.split("<")[0]?.trim() ?? "unknown",
19
+ message: patch.original_message,
20
+ files: patch.files,
21
+ }));
22
+ let lastGeneration;
23
+ const lastGen = lock.generations.find((g) => g.commit_sha === lock.current_generation);
24
+ if (lastGen) {
25
+ lastGeneration = {
26
+ sha: lastGen.commit_sha,
27
+ timestamp: lastGen.timestamp,
28
+ };
29
+ }
30
+ return { initialized: true, patches, lastGeneration };
31
+ }
32
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA6BxD;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CAAC,SAAiB;IACpC,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC,SAAS,CAAC,CAAC;IAEnD,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QACxB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC;IAC1E,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IAEhC,MAAM,OAAO,GAAkB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACxD,GAAG,EAAE,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS;QAChE,OAAO,EAAE,KAAK,CAAC,gBAAgB;QAC/B,KAAK,EAAE,KAAK,CAAC,KAAK;KACrB,CAAC,CAAC,CAAC;IAEJ,IAAI,cAA4C,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACvF,IAAI,OAAO,EAAE,CAAC;QACV,cAAc,GAAG;YACb,GAAG,EAAE,OAAO,CAAC,UAAU;YACvB,SAAS,EAAE,OAAO,CAAC,SAAS;SAC/B,CAAC;IACN,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Detect whether the current process is running in a CI environment.
3
+ */
4
+ export declare function isCI(): boolean;
5
+ //# sourceMappingURL=environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../src/environment.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,IAAI,IAAI,OAAO,CAW9B"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Detect whether the current process is running in a CI environment.
3
+ */
4
+ export function isCI() {
5
+ return !!(process.env.CI ||
6
+ process.env.CONTINUOUS_INTEGRATION ||
7
+ process.env.GITHUB_ACTIONS ||
8
+ process.env.GITLAB_CI ||
9
+ process.env.CIRCLECI ||
10
+ process.env.BUILDKITE ||
11
+ process.env.JENKINS_URL ||
12
+ process.env.TRAVIS);
13
+ }
14
+ //# sourceMappingURL=environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.js","sourceRoot":"","sources":["../src/environment.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,IAAI;IAChB,OAAO,CAAC,CAAC,CACL,OAAO,CAAC,GAAG,CAAC,EAAE;QACd,OAAO,CAAC,GAAG,CAAC,sBAAsB;QAClC,OAAO,CAAC,GAAG,CAAC,cAAc;QAC1B,OAAO,CAAC,GAAG,CAAC,SAAS;QACrB,OAAO,CAAC,GAAG,CAAC,QAAQ;QACpB,OAAO,CAAC,GAAG,CAAC,SAAS;QACrB,OAAO,CAAC,GAAG,CAAC,WAAW;QACvB,OAAO,CAAC,GAAG,CAAC,MAAM,CACrB,CAAC;AACN,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { CommitInfo } from "../types.js";
2
+ export declare const FERN_BOT_NAME = "fern-api";
3
+ export declare const FERN_BOT_EMAIL = "115122769+fern-api[bot]@users.noreply.github.com";
4
+ export declare const FERN_BOT_LOGIN = "fern-api[bot]";
5
+ /**
6
+ * Determines if a commit is a Fern generation commit using dual signals:
7
+ * 1. Author is fern-api bot (but NOT fern-support using the bot account)
8
+ * 2. Commit message contains generation markers
9
+ *
10
+ * Either signal indicates a generation commit.
11
+ */
12
+ export declare function isGenerationCommit(commit: CommitInfo): boolean;
13
+ /**
14
+ * Determines if a commit is a replay commit (applied customizations).
15
+ */
16
+ export declare function isReplayCommit(commit: CommitInfo): boolean;
17
+ //# sourceMappingURL=CommitDetection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommitDetection.d.ts","sourceRoot":"","sources":["../../src/git/CommitDetection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,eAAO,MAAM,aAAa,aAAa,CAAC;AACxC,eAAO,MAAM,cAAc,qDAAqD,CAAC;AACjF,eAAO,MAAM,cAAc,kBAAkB,CAAC;AAO9C;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAiB9D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAE1D"}