@fern-api/replay 0.6.0 → 0.6.2

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 (78) hide show
  1. package/dist/cli.cjs +16642 -0
  2. package/dist/cli.cjs.map +1 -0
  3. package/dist/index.cjs +2014 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +463 -0
  6. package/dist/index.d.ts +463 -12
  7. package/dist/index.js +1968 -10
  8. package/dist/index.js.map +1 -1
  9. package/package.json +16 -6
  10. package/dist/FernignoreMigrator.d.ts +0 -38
  11. package/dist/FernignoreMigrator.d.ts.map +0 -1
  12. package/dist/FernignoreMigrator.js +0 -210
  13. package/dist/FernignoreMigrator.js.map +0 -1
  14. package/dist/LockfileManager.d.ts +0 -31
  15. package/dist/LockfileManager.d.ts.map +0 -1
  16. package/dist/LockfileManager.js +0 -124
  17. package/dist/LockfileManager.js.map +0 -1
  18. package/dist/ReplayApplicator.d.ts +0 -30
  19. package/dist/ReplayApplicator.d.ts.map +0 -1
  20. package/dist/ReplayApplicator.js +0 -544
  21. package/dist/ReplayApplicator.js.map +0 -1
  22. package/dist/ReplayCommitter.d.ts +0 -19
  23. package/dist/ReplayCommitter.d.ts.map +0 -1
  24. package/dist/ReplayCommitter.js +0 -64
  25. package/dist/ReplayCommitter.js.map +0 -1
  26. package/dist/ReplayDetector.d.ts +0 -22
  27. package/dist/ReplayDetector.d.ts.map +0 -1
  28. package/dist/ReplayDetector.js +0 -147
  29. package/dist/ReplayDetector.js.map +0 -1
  30. package/dist/ReplayService.d.ts +0 -100
  31. package/dist/ReplayService.d.ts.map +0 -1
  32. package/dist/ReplayService.js +0 -596
  33. package/dist/ReplayService.js.map +0 -1
  34. package/dist/ThreeWayMerge.d.ts +0 -11
  35. package/dist/ThreeWayMerge.d.ts.map +0 -1
  36. package/dist/ThreeWayMerge.js +0 -48
  37. package/dist/ThreeWayMerge.js.map +0 -1
  38. package/dist/cli.d.ts +0 -3
  39. package/dist/cli.d.ts.map +0 -1
  40. package/dist/cli.js +0 -462
  41. package/dist/cli.js.map +0 -1
  42. package/dist/commands/bootstrap.d.ts +0 -46
  43. package/dist/commands/bootstrap.d.ts.map +0 -1
  44. package/dist/commands/bootstrap.js +0 -237
  45. package/dist/commands/bootstrap.js.map +0 -1
  46. package/dist/commands/forget.d.ts +0 -16
  47. package/dist/commands/forget.d.ts.map +0 -1
  48. package/dist/commands/forget.js +0 -27
  49. package/dist/commands/forget.js.map +0 -1
  50. package/dist/commands/index.d.ts +0 -6
  51. package/dist/commands/index.d.ts.map +0 -1
  52. package/dist/commands/index.js +0 -6
  53. package/dist/commands/index.js.map +0 -1
  54. package/dist/commands/reset.d.ts +0 -16
  55. package/dist/commands/reset.d.ts.map +0 -1
  56. package/dist/commands/reset.js +0 -25
  57. package/dist/commands/reset.js.map +0 -1
  58. package/dist/commands/resolve.d.ts +0 -16
  59. package/dist/commands/resolve.d.ts.map +0 -1
  60. package/dist/commands/resolve.js +0 -28
  61. package/dist/commands/resolve.js.map +0 -1
  62. package/dist/commands/status.d.ts +0 -26
  63. package/dist/commands/status.d.ts.map +0 -1
  64. package/dist/commands/status.js +0 -24
  65. package/dist/commands/status.js.map +0 -1
  66. package/dist/git/CommitDetection.d.ts +0 -7
  67. package/dist/git/CommitDetection.d.ts.map +0 -1
  68. package/dist/git/CommitDetection.js +0 -26
  69. package/dist/git/CommitDetection.js.map +0 -1
  70. package/dist/git/GitClient.d.ts +0 -22
  71. package/dist/git/GitClient.d.ts.map +0 -1
  72. package/dist/git/GitClient.js +0 -109
  73. package/dist/git/GitClient.js.map +0 -1
  74. package/dist/index.d.ts.map +0 -1
  75. package/dist/types.d.ts +0 -80
  76. package/dist/types.d.ts.map +0 -1
  77. package/dist/types.js +0 -3
  78. package/dist/types.js.map +0 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EACH,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,cAAc,EACd,cAAc,GACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAsB,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAA8D,MAAM,oBAAoB,CAAC;AAC/G,OAAO,EAAE,kBAAkB,EAAgD,MAAM,yBAAyB,CAAC;AAC3G,OAAO,EACH,SAAS,EACT,MAAM,EACN,KAAK,EACL,OAAO,EACP,MAAM,GACT,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"sources":["../src/git/GitClient.ts","../src/index.ts","../src/git/CommitDetection.ts","../src/LockfileManager.ts","../src/ReplayDetector.ts","../src/ThreeWayMerge.ts","../src/ReplayApplicator.ts","../src/ReplayCommitter.ts","../src/ReplayService.ts","../src/FernignoreMigrator.ts","../src/commands/bootstrap.ts","../src/commands/forget.ts","../src/commands/reset.ts","../src/commands/resolve.ts","../src/commands/status.ts"],"sourcesContent":["import { type SimpleGit, simpleGit } from \"simple-git\";\nimport type { CommitInfo } from \"../types.js\";\n\nexport class GitClient {\n private git: SimpleGit;\n private repoPath: string;\n\n constructor(repoPath: string) {\n this.repoPath = repoPath;\n this.git = simpleGit(repoPath);\n }\n\n async exec(args: string[]): Promise<string> {\n return this.git.raw(args);\n }\n\n async execWithInput(args: string[], input: string): Promise<string> {\n // simple-git's raw() doesn't support stdin directly,\n // so we use spawn for commands that need stdin (e.g. git am)\n const { spawn } = await import(\"node:child_process\");\n\n return new Promise((resolve, reject) => {\n const proc = spawn(\"git\", args, { cwd: this.repoPath });\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout.on(\"data\", (data: Buffer) => {\n stdout += data.toString();\n });\n proc.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n proc.on(\"close\", (code) => {\n if (code === 0) {\n resolve(stdout);\n } else {\n reject(new Error(`git ${args.join(\" \")} failed (code ${code}): ${stderr}`));\n }\n });\n\n proc.stdin.write(input);\n proc.stdin.end();\n });\n }\n\n async formatPatch(commitSha: string): Promise<string> {\n return this.exec([\"format-patch\", \"-1\", commitSha, \"--stdout\"]);\n }\n\n async applyPatch(patchContent: string): Promise<void> {\n await this.execWithInput([\"am\", \"--3way\"], patchContent);\n }\n\n async getTreeHash(commitSha: string): Promise<string> {\n return (await this.exec([\"rev-parse\", `${commitSha}^{tree}`])).trim();\n }\n\n async showFile(treeish: string, filePath: string): Promise<string | null> {\n try {\n return await this.exec([\"show\", `${treeish}:${filePath}`]);\n } catch {\n return null;\n }\n }\n\n async getCommitInfo(commitSha: string): Promise<CommitInfo> {\n const format = \"%H%x00%an%x00%ae%x00%s\";\n const output = await this.exec([\"log\", \"-1\", `--format=${format}`, commitSha]);\n const [sha, authorName, authorEmail, message] = output.trim().split(\"\\0\");\n return { sha, authorName, authorEmail, message };\n }\n\n async getCommitParents(commitSha: string): Promise<string[]> {\n const output = await this.exec([\"rev-parse\", `${commitSha}^@`]);\n return output.trim().split(\"\\n\").filter(Boolean);\n }\n\n async detectRenames(fromTree: string, toTree: string): Promise<Array<{ from: string; to: string }>> {\n try {\n const output = await this.exec([\"diff\", \"--find-renames\", \"--name-status\", fromTree, toTree]);\n const renames: Array<{ from: string; to: string }> = [];\n for (const line of output.trim().split(\"\\n\")) {\n if (!line) continue;\n // Rename lines look like: R095\\told/path.ts\\tnew/path.ts\n if (line.startsWith(\"R\")) {\n const parts = line.split(\"\\t\");\n if (parts.length >= 3) {\n renames.push({ from: parts[1]!, to: parts[2]! });\n }\n }\n }\n return renames;\n } catch {\n return [];\n }\n }\n\n async isAncestor(commit: string, descendant: string): Promise<boolean> {\n try {\n const mergeBase = (await this.exec([\"merge-base\", commit, descendant])).trim();\n return mergeBase === commit;\n } catch {\n // No common ancestor (e.g., orphan branches)\n return false;\n }\n }\n\n async commitExists(sha: string): Promise<boolean> {\n try {\n const type = await this.exec([\"cat-file\", \"-t\", sha]);\n return type.trim() === \"commit\";\n } catch {\n return false;\n }\n }\n\n getRepoPath(): string {\n return this.repoPath;\n }\n}\n","export type {\n GenerationLock,\n GenerationRecord,\n StoredPatch,\n CustomizationsConfig,\n MoveDeclaration,\n ReplayResult,\n FileResult,\n ConflictRegion,\n ConflictReason,\n ConflictMetadata,\n MergeResult,\n CommitInfo,\n ReplayConfig,\n} from \"./types.js\";\n\nexport { GitClient } from \"./git/GitClient.js\";\nexport {\n isGenerationCommit,\n isReplayCommit,\n FERN_BOT_NAME,\n FERN_BOT_EMAIL,\n FERN_BOT_LOGIN,\n} from \"./git/CommitDetection.js\";\nexport { LockfileManager } from \"./LockfileManager.js\";\nexport { ReplayDetector } from \"./ReplayDetector.js\";\nexport { threeWayMerge } from \"./ThreeWayMerge.js\";\nexport { ReplayApplicator } from \"./ReplayApplicator.js\";\nexport { ReplayCommitter, type CommitOptions } from \"./ReplayCommitter.js\";\nexport { ReplayService, type ReplayReport, type ReplayOptions, type ConflictDetail } from \"./ReplayService.js\";\nexport { FernignoreMigrator, type MigrationAnalysis, type MigrationResult } from \"./FernignoreMigrator.js\";\nexport {\n bootstrap, type BootstrapOptions, type BootstrapResult,\n forget, type ForgetOptions, type ForgetResult,\n reset, type ResetOptions, type ResetResult,\n resolve, type ResolveOptions, type ResolveResult,\n status, type StatusResult, type StatusPatch, type StatusGeneration,\n} from \"./commands/index.js\";\n","import type { CommitInfo } from \"../types.js\";\n\n// Constants from Fern's PR #11502\nexport const FERN_BOT_NAME = \"fern-api\";\nexport const FERN_BOT_EMAIL = \"115122769+fern-api[bot]@users.noreply.github.com\";\nexport const FERN_BOT_LOGIN = \"fern-api[bot]\";\n\n// Fern-support commits (via bot account) are excluded from generation detection.\nconst FERN_SUPPORT_NAMES = [\"fern-support\", \"Fern Support\"];\n\nexport function isGenerationCommit(commit: CommitInfo): boolean {\n const isFernSupport = FERN_SUPPORT_NAMES.includes(commit.authorName);\n\n const isBotAuthor =\n !isFernSupport &&\n (commit.authorLogin === FERN_BOT_LOGIN ||\n commit.authorEmail === FERN_BOT_EMAIL ||\n commit.authorName === FERN_BOT_NAME);\n\n const hasGenerationMarker =\n commit.message.startsWith(\"[fern-generated]\") ||\n commit.message.startsWith(\"[fern-replay]\") ||\n commit.message.includes(\"Generated by Fern\") ||\n commit.message.includes(\"\\u{1F916} Generated with Fern\") ||\n // Squash merge of a Fern-generated PR uses the PR title as commit message.\n // The default PR title is \"SDK Generation\" (from GithubStep's commitMessage default).\n // GitHub appends \"(#N)\" for the PR number, e.g. \"SDK Generation (#70)\".\n commit.message.startsWith(\"SDK Generation\");\n\n return isBotAuthor || hasGenerationMarker;\n}\n\nexport function isReplayCommit(commit: CommitInfo): boolean {\n return commit.message.startsWith(\"[fern-replay]\");\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { stringify, parse } from \"yaml\";\nimport type {\n GenerationLock,\n GenerationRecord,\n StoredPatch,\n CustomizationsConfig,\n} from \"./types.js\";\n\nconst LOCKFILE_HEADER = \"# DO NOT EDIT MANUALLY - Managed by Fern Replay\\n\";\n\nexport class LockfileManager {\n private outputDir: string;\n private lock: GenerationLock | null = null;\n\n constructor(outputDir: string) {\n this.outputDir = outputDir;\n }\n\n get lockfilePath(): string {\n return join(this.outputDir, \".fern\", \"replay.lock\");\n }\n\n get customizationsPath(): string {\n return join(this.outputDir, \".fern\", \"replay.yml\");\n }\n\n exists(): boolean {\n return existsSync(this.lockfilePath);\n }\n\n read(): GenerationLock {\n if (this.lock) {\n return this.lock;\n }\n if (!this.exists()) {\n throw new Error(`Lockfile not found: ${this.lockfilePath}`);\n }\n const content = readFileSync(this.lockfilePath, \"utf-8\");\n this.lock = parse(content) as GenerationLock;\n return this.lock;\n }\n\n initialize(firstGeneration: GenerationRecord): void {\n this.initializeInMemory(firstGeneration);\n this.save();\n }\n\n /**\n * Set up in-memory lock state without writing to disk.\n * Useful for bootstrap dry-run where we need state for detection\n * but don't want to persist anything.\n */\n initializeInMemory(firstGeneration: GenerationRecord): void {\n this.lock = {\n version: \"1.0\",\n generations: [firstGeneration],\n current_generation: firstGeneration.commit_sha,\n patches: [],\n };\n }\n\n save(): void {\n if (!this.lock) {\n throw new Error(\"No lockfile data to save. Call read() or initialize() first.\");\n }\n const dir = dirname(this.lockfilePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n const yaml = stringify(this.lock, {\n lineWidth: 0,\n blockQuote: \"literal\",\n });\n const content = LOCKFILE_HEADER + yaml;\n // Atomic write: write to temp file then rename (rename is atomic on most filesystems)\n const tmpPath = this.lockfilePath + \".tmp\";\n writeFileSync(tmpPath, content, \"utf-8\");\n renameSync(tmpPath, this.lockfilePath);\n }\n\n addGeneration(record: GenerationRecord): void {\n this.ensureLoaded();\n this.lock!.generations.push(record);\n this.lock!.current_generation = record.commit_sha;\n }\n\n addPatch(patch: StoredPatch): void {\n this.ensureLoaded();\n this.lock!.patches.push(patch);\n }\n\n updatePatch(\n patchId: string,\n updates: Partial<Pick<StoredPatch, \"base_generation\" | \"patch_content\" | \"content_hash\" | \"files\">>,\n ): void {\n this.ensureLoaded();\n const patch = this.lock!.patches.find((p) => p.id === patchId);\n if (!patch) {\n throw new Error(`Patch not found: ${patchId}`);\n }\n Object.assign(patch, updates);\n }\n\n removePatch(patchId: string): void {\n this.ensureLoaded();\n this.lock!.patches = this.lock!.patches.filter((p) => p.id !== patchId);\n }\n\n clearPatches(): void {\n this.ensureLoaded();\n this.lock!.patches = [];\n }\n\n getPatches(): StoredPatch[] {\n this.ensureLoaded();\n return this.lock!.patches;\n }\n\n setReplaySkippedAt(timestamp: string): void {\n this.ensureLoaded();\n this.lock!.replay_skipped_at = timestamp;\n }\n\n clearReplaySkippedAt(): void {\n this.ensureLoaded();\n delete this.lock!.replay_skipped_at;\n }\n\n isReplaySkipped(): boolean {\n this.ensureLoaded();\n return this.lock!.replay_skipped_at != null;\n }\n\n getGeneration(commitSha: string): GenerationRecord | undefined {\n this.ensureLoaded();\n return this.lock!.generations.find((g) => g.commit_sha === commitSha);\n }\n\n getCustomizationsConfig(): CustomizationsConfig {\n if (!existsSync(this.customizationsPath)) {\n return {};\n }\n const content = readFileSync(this.customizationsPath, \"utf-8\");\n return (parse(content) as CustomizationsConfig) ?? {};\n }\n\n private ensureLoaded(): void {\n if (!this.lock) {\n throw new Error(\"No lockfile loaded. Call read() or initialize() first.\");\n }\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { isGenerationCommit } from \"./git/CommitDetection.js\";\nimport type { GitClient } from \"./git/GitClient.js\";\nimport type { LockfileManager } from \"./LockfileManager.js\";\nimport type { CommitInfo, GenerationLock, GenerationRecord, StoredPatch } from \"./types.js\";\n\n// Infrastructure files managed by the generation pipeline, not user code.\n// Changes to these should never be captured as customization patches.\nconst INFRASTRUCTURE_FILES = new Set([\".fernignore\"]);\n\nexport class ReplayDetector {\n private git: GitClient;\n private lockManager: LockfileManager;\n private sdkOutputDir: string;\n readonly warnings: string[] = [];\n\n constructor(git: GitClient, lockManager: LockfileManager, sdkOutputDir: string) {\n this.git = git;\n this.lockManager = lockManager;\n this.sdkOutputDir = sdkOutputDir;\n }\n\n async detectNewPatches(): Promise<StoredPatch[]> {\n const lock = this.lockManager.read();\n const lastGen = this.getLastGeneration(lock);\n if (!lastGen) {\n return [];\n }\n\n const exists = await this.git.commitExists(lastGen.commit_sha);\n if (!exists) {\n this.warnings.push(\n `Generation commit ${lastGen.commit_sha.slice(0, 7)} not found in git history. ` +\n `Skipping new patch detection. Existing lockfile patches will still be applied.`\n );\n return [];\n }\n\n // Non-linear history (e.g. squash merge): fall back to tree-diff detection.\n const isAncestor = await this.git.isAncestor(lastGen.commit_sha, \"HEAD\");\n if (!isAncestor) {\n return this.detectPatchesViaTreeDiff(lastGen);\n }\n\n const log = await this.git.exec([\n \"log\",\n \"--format=%H%x00%an%x00%ae%x00%s\",\n `${lastGen.commit_sha}..HEAD`,\n \"--\",\n this.sdkOutputDir\n ]);\n\n if (!log.trim()) {\n return [];\n }\n\n const commits = this.parseGitLog(log);\n const newPatches: StoredPatch[] = [];\n\n for (const commit of commits) {\n if (isGenerationCommit(commit)) {\n continue;\n }\n\n const parents = await this.git.getCommitParents(commit.sha);\n if (parents.length > 1) {\n continue;\n }\n\n if (lock.patches.find((p) => p.original_commit === commit.sha)) {\n continue;\n }\n\n const patchContent = await this.git.formatPatch(commit.sha);\n\n const contentHash = this.computeContentHash(patchContent);\n if (lock.patches.find((p) => p.content_hash === contentHash)) {\n continue;\n }\n\n const filesOutput = await this.git.exec([\"diff-tree\", \"--no-commit-id\", \"--name-only\", \"-r\", commit.sha]);\n\n const files = filesOutput\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .filter((f) => !INFRASTRUCTURE_FILES.has(f));\n\n // Skip patches that only touch infrastructure files\n if (files.length === 0) {\n continue;\n }\n\n newPatches.push({\n id: `patch-${commit.sha.slice(0, 8)}`,\n content_hash: contentHash,\n original_commit: commit.sha,\n original_message: commit.message,\n original_author: `${commit.authorName} <${commit.authorEmail}>`,\n base_generation: lastGen.commit_sha,\n files,\n patch_content: patchContent\n });\n }\n\n // Reverse so patches apply in chronological order (oldest first)\n return newPatches.reverse();\n }\n\n /**\n * Compute content hash for deduplication.\n * Removes commit SHA line and index lines before hashing,\n * so rebased commits with same content produce the same hash.\n */\n computeContentHash(patchContent: string): string {\n const normalized = patchContent\n .split(\"\\n\")\n .filter((line) => !line.startsWith(\"From \") && !line.startsWith(\"index \") && !line.startsWith(\"Date: \"))\n .join(\"\\n\");\n\n return `sha256:${createHash(\"sha256\").update(normalized).digest(\"hex\")}`;\n }\n\n /** Detect patches via tree diff for non-linear history. Returns a composite patch. */\n private async detectPatchesViaTreeDiff(lastGen: GenerationRecord): Promise<StoredPatch[]> {\n // Get changed files first, then filter before computing the diff\n const filesOutput = await this.git.exec([\"diff\", \"--name-only\", lastGen.commit_sha, \"HEAD\"]);\n const files = filesOutput\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .filter((f) => !INFRASTRUCTURE_FILES.has(f))\n .filter((f) => !f.startsWith(\".fern/\"));\n\n if (files.length === 0) return [];\n\n // Compute diff only for the filtered files\n const diff = await this.git.exec([\"diff\", lastGen.commit_sha, \"HEAD\", \"--\", ...files]);\n if (!diff.trim()) return [];\n\n const contentHash = this.computeContentHash(diff);\n\n // Dedup against existing patches — if an existing patch already captures\n // this exact content, don't create a duplicate composite patch.\n const lock = this.lockManager.read();\n if (lock.patches.some((p) => p.content_hash === contentHash)) {\n return [];\n }\n\n const headSha = (await this.git.exec([\"rev-parse\", \"HEAD\"])).trim();\n\n return [\n {\n id: `patch-composite-${headSha.slice(0, 8)}`,\n content_hash: contentHash,\n original_commit: headSha,\n original_message: \"Customer customizations (composite)\",\n original_author: \"composite\",\n base_generation: lastGen.commit_sha,\n files,\n patch_content: diff\n }\n ];\n }\n\n private parseGitLog(log: string): CommitInfo[] {\n return log\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const [sha, authorName, authorEmail, message] = line.split(\"\\0\");\n return { sha, authorName, authorEmail, message };\n });\n }\n\n private getLastGeneration(lock: GenerationLock) {\n return lock.generations.find((g) => g.commit_sha === lock.current_generation);\n }\n}\n","import { diff3Merge } from \"node-diff3\";\nimport type { ConflictRegion, MergeResult } from \"./types.js\";\n\n/**\n * Performs a 3-way merge using the diff3 algorithm.\n *\n * @param base - The common ancestor (pristine generated state user edited against)\n * @param ours - The new generated version\n * @param theirs - The user's customized version\n * @returns MergeResult with content, conflict flag, and conflict regions\n */\nexport function threeWayMerge(base: string, ours: string, theirs: string): MergeResult {\n const baseLines = base.split(\"\\n\");\n const oursLines = ours.split(\"\\n\");\n const theirsLines = theirs.split(\"\\n\");\n\n const regions = diff3Merge(oursLines, baseLines, theirsLines);\n\n const outputLines: string[] = [];\n const conflicts: ConflictRegion[] = [];\n let currentLine = 1;\n\n for (const region of regions) {\n if (region.ok) {\n outputLines.push(...region.ok);\n currentLine += region.ok.length;\n } else if (region.conflict) {\n const startLine = currentLine;\n\n outputLines.push(\"<<<<<<< Generated\");\n outputLines.push(...region.conflict.a);\n outputLines.push(\"=======\");\n outputLines.push(...region.conflict.b);\n outputLines.push(\">>>>>>> Your customization\");\n\n // Total lines in conflict block: 3 markers + a.length + b.length\n // endLine is the line number of the \">>>>>>>\" marker\n const conflictLines = region.conflict.a.length + region.conflict.b.length + 3;\n conflicts.push({\n startLine,\n endLine: startLine + conflictLines - 1,\n ours: region.conflict.a,\n theirs: region.conflict.b\n });\n\n currentLine += conflictLines;\n }\n }\n\n return {\n content: outputLines.join(\"\\n\"),\n hasConflicts: conflicts.length > 0,\n conflicts\n };\n}\n","import { mkdir, mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, extname, join } from \"node:path\";\nimport { minimatch } from \"minimatch\";\nimport type { GitClient } from \"./git/GitClient.js\";\nimport type { LockfileManager } from \"./LockfileManager.js\";\nimport { threeWayMerge } from \"./ThreeWayMerge.js\";\nimport type {\n ConflictMetadata,\n ConflictReason,\n FileResult,\n GenerationRecord,\n ReplayResult,\n StoredPatch\n} from \"./types.js\";\n\nconst BINARY_EXTENSIONS = new Set([\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".bmp\",\n \".ico\",\n \".webp\",\n \".svg\",\n \".pdf\",\n \".doc\",\n \".docx\",\n \".xls\",\n \".xlsx\",\n \".ppt\",\n \".pptx\",\n \".zip\",\n \".gz\",\n \".tar\",\n \".bz2\",\n \".7z\",\n \".rar\",\n \".jar\",\n \".war\",\n \".ear\",\n \".class\",\n \".exe\",\n \".dll\",\n \".so\",\n \".dylib\",\n \".o\",\n \".a\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".eot\",\n \".otf\",\n \".mp3\",\n \".mp4\",\n \".avi\",\n \".mov\",\n \".wav\",\n \".flac\",\n \".sqlite\",\n \".db\",\n \".pyc\",\n \".pyo\",\n \".DS_Store\"\n]);\n\nexport class ReplayApplicator {\n private git: GitClient;\n private lockManager: LockfileManager;\n private outputDir: string;\n private renameCache = new Map<string, Array<{ from: string; to: string }>>();\n private fileTheirsAccumulator = new Map<\n string,\n {\n content: string;\n baseGeneration: string;\n }\n >();\n\n constructor(git: GitClient, lockManager: LockfileManager, outputDir: string) {\n this.git = git;\n this.lockManager = lockManager;\n this.outputDir = outputDir;\n }\n\n /** Reset inter-patch accumulator for a new cycle. */\n private resetAccumulator(): void {\n this.fileTheirsAccumulator.clear();\n }\n\n /**\n * Apply all patches, returning results for each.\n * Skips patches that match exclude patterns in replay.yml\n */\n async applyPatches(patches: StoredPatch[]): Promise<ReplayResult[]> {\n this.resetAccumulator(); // Clear accumulator for this apply cycle\n const results: ReplayResult[] = [];\n\n for (const patch of patches) {\n if (this.isExcluded(patch)) {\n results.push({\n patch,\n status: \"skipped\",\n method: \"git-am\"\n });\n continue;\n }\n\n const result = await this.applyPatchWithFallback(patch);\n results.push(result);\n }\n\n return results;\n }\n\n /** Populate accumulator after git apply succeeds. */\n private async populateAccumulatorForPatch(\n patch: StoredPatch,\n baseGen: GenerationRecord | undefined,\n currentTreeHash: string\n ): Promise<void> {\n if (!baseGen) return;\n\n // Create temp git repo to apply patches to base content\n const tempDir = await mkdtemp(join(tmpdir(), \"replay-acc-\"));\n const { GitClient } = await import(\"./git/GitClient.js\");\n const tempGit = new GitClient(tempDir);\n await tempGit.exec([\"init\"]);\n await tempGit.exec([\"config\", \"user.email\", \"replay@fern.com\"]);\n await tempGit.exec([\"config\", \"user.name\", \"Fern Replay\"]);\n\n try {\n for (const filePath of patch.files) {\n if (isBinaryFile(filePath)) continue;\n\n const resolvedPath = await this.resolveFilePath(filePath, baseGen.tree_hash, currentTreeHash);\n\n const base = await this.git.showFile(baseGen.tree_hash, filePath);\n const theirs = await this.applyPatchToContent(base, patch.patch_content, filePath, tempGit, tempDir);\n\n if (theirs && base) {\n this.fileTheirsAccumulator.set(resolvedPath, {\n content: theirs,\n baseGeneration: patch.base_generation\n });\n }\n }\n } finally {\n await rm(tempDir, { recursive: true }).catch(() => {});\n }\n }\n\n private async applyPatchWithFallback(patch: StoredPatch): Promise<ReplayResult> {\n // Resolve all file paths to check accumulator (need baseGen for resolution)\n const baseGen = this.lockManager.getGeneration(patch.base_generation);\n const lock = this.lockManager.read();\n const currentGen = lock.generations.find((g) => g.commit_sha === lock.current_generation);\n const currentTreeHash = currentGen?.tree_hash ?? baseGen?.tree_hash ?? \"\";\n\n // Check if any file in this patch needs accumulation\n // If so, skip fast path to ensure we benefit from pre-merge logic\n const needsAccumulation = await Promise.all(\n patch.files.map(async (f) => {\n if (!baseGen) return false;\n const resolved = await this.resolveFilePath(f, baseGen.tree_hash, currentTreeHash);\n return this.fileTheirsAccumulator.has(resolved);\n })\n ).then((results) => results.some(Boolean));\n\n // Strategy 1: Try git apply --3way (skip if accumulation needed)\n if (!needsAccumulation) {\n // Snapshot files before git apply (may leave conflict markers on failure)\n const snapshots = new Map<string, string | null>();\n const resolvedFiles: Record<string, string> = {};\n for (const filePath of patch.files) {\n const resolvedPath = baseGen\n ? await this.resolveFilePath(filePath, baseGen.tree_hash, currentTreeHash)\n : filePath;\n if (resolvedPath !== filePath) {\n resolvedFiles[filePath] = resolvedPath;\n }\n const fullPath = join(this.outputDir, resolvedPath);\n snapshots.set(resolvedPath, await readFile(fullPath, \"utf-8\").catch(() => null));\n }\n\n try {\n await this.git.execWithInput([\"apply\", \"--3way\"], patch.patch_content);\n\n // Success! Populate accumulator so subsequent patches on these files\n // will skip fast path and use the pre-merge logic instead\n await this.populateAccumulatorForPatch(patch, baseGen, currentTreeHash);\n\n return {\n patch,\n status: \"applied\",\n method: \"git-am\",\n ...(Object.keys(resolvedFiles).length > 0 && { resolvedFiles })\n };\n } catch {\n // git apply --3way failed and may have left conflict markers on disk\n // Restore files from snapshot to undo any corruption before fallback\n for (const [resolvedPath, content] of snapshots) {\n if (content != null) {\n await writeFile(join(this.outputDir, resolvedPath), content);\n }\n }\n }\n }\n\n // Strategy 2: Fall back to file-by-file 3-way merge (uses accumulator)\n return this.applyWithThreeWayMerge(patch);\n }\n\n private async applyWithThreeWayMerge(patch: StoredPatch): Promise<ReplayResult> {\n const fileResults: FileResult[] = [];\n const resolvedFiles: Record<string, string> = {};\n\n const tempDir = await mkdtemp(join(tmpdir(), \"replay-\"));\n const { GitClient } = await import(\"./git/GitClient.js\");\n const tempGit = new GitClient(tempDir);\n await tempGit.exec([\"init\"]);\n await tempGit.exec([\"config\", \"user.email\", \"replay@fern.com\"]);\n await tempGit.exec([\"config\", \"user.name\", \"Fern Replay\"]);\n\n try {\n for (const filePath of patch.files) {\n if (isBinaryFile(filePath)) {\n fileResults.push({\n file: filePath,\n status: \"skipped\",\n reason: \"binary-file\"\n });\n continue;\n }\n const result = await this.mergeFile(patch, filePath, tempGit, tempDir);\n if (result.file !== filePath) {\n resolvedFiles[filePath] = result.file;\n }\n fileResults.push(result);\n }\n } finally {\n await rm(tempDir, { recursive: true }).catch(() => {});\n }\n\n const conflictFiles = fileResults.filter((r) => r.status === \"conflict\");\n const hasConflicts = conflictFiles.length > 0;\n\n // Aggregate conflict reason from file-level to patch-level\n const conflictReason: ConflictReason | undefined = hasConflicts\n ? conflictFiles.some((f) => f.conflictReason === \"base-generation-mismatch\")\n ? \"base-generation-mismatch\"\n : conflictFiles[0]?.conflictReason\n : undefined;\n\n return {\n patch,\n status: hasConflicts ? \"conflict\" : \"applied\",\n method: \"3way-merge\",\n fileResults,\n conflictReason,\n ...(Object.keys(resolvedFiles).length > 0 && { resolvedFiles })\n };\n }\n\n private async mergeFile(\n patch: StoredPatch,\n filePath: string,\n tempGit: GitClient,\n tempDir: string\n ): Promise<FileResult> {\n try {\n const baseGen = this.lockManager.getGeneration(patch.base_generation);\n if (!baseGen) {\n return { file: filePath, status: \"skipped\", reason: \"base-generation-not-found\" };\n }\n\n // Resolve file path in case the generator renamed it between generations\n const lock = this.lockManager.read();\n const currentGen = lock.generations.find((g) => g.commit_sha === lock.current_generation);\n const currentTreeHash = currentGen?.tree_hash ?? baseGen.tree_hash;\n const resolvedPath = await this.resolveFilePath(filePath, baseGen.tree_hash, currentTreeHash);\n\n // Build conflict metadata for this file (used if conflict occurs)\n const metadata: ConflictMetadata = {\n patchId: patch.id,\n patchMessage: patch.original_message,\n baseGeneration: patch.base_generation,\n currentGeneration: lock.current_generation\n };\n\n // BASE: pristine state from when user made their edit (old path)\n let base = await this.git.showFile(baseGen.tree_hash, filePath);\n\n // If BASE is null, check if this file is the target of a rename in the patch.\n // Rename diffs (e.g., git mv old new) have the content at the old path.\n let renameSourcePath: string | undefined;\n if (!base) {\n const renameSource = this.extractRenameSource(patch.patch_content, filePath);\n if (renameSource) {\n base = await this.git.showFile(baseGen.tree_hash, renameSource);\n renameSourcePath = renameSource;\n }\n }\n\n // OURS: current generated state on disk (resolved/new path)\n const oursPath = join(this.outputDir, resolvedPath);\n const ours = await readFile(oursPath, \"utf-8\").catch(() => null);\n\n // THEIRS: user's version (apply patch to base using original path)\n let theirs = await this.applyPatchToContent(\n base,\n patch.patch_content,\n filePath,\n tempGit,\n tempDir,\n renameSourcePath\n );\n\n // Fall back to accumulator as merge base for incremental patches.\n let useAccumulatorAsMergeBase = false;\n const accumulatorEntry = this.fileTheirsAccumulator.get(resolvedPath);\n\n if (!theirs && base && accumulatorEntry) {\n theirs = await this.applyPatchToContent(\n accumulatorEntry.content,\n patch.patch_content,\n filePath,\n tempGit,\n tempDir\n );\n if (theirs) {\n useAccumulatorAsMergeBase = true;\n }\n }\n\n // Pre-merge with accumulated customizations (skip when accumulator is merge base).\n let effective_theirs = theirs;\n let baseMismatchSkipped = false;\n\n if (theirs && base && !useAccumulatorAsMergeBase) {\n if (accumulatorEntry && accumulatorEntry.baseGeneration === patch.base_generation) {\n // Pre-merge: combine previous customizations with current patch\n try {\n const preMerged = threeWayMerge(base, accumulatorEntry.content, theirs);\n\n if (!preMerged.hasConflicts) {\n // Clean merge - use the combined result\n effective_theirs = preMerged.content;\n } else {\n // Pre-merge has conflicts - skip accumulator for this patch\n // to avoid nested conflict markers. User patches conflict\n // with each other and require manual resolution.\n effective_theirs = theirs;\n }\n } catch {\n // If pre-merge fails unexpectedly, fall back to current_theirs\n effective_theirs = theirs;\n }\n } else if (accumulatorEntry) {\n // Base generations differ - accumulator pre-merge skipped.\n // The main merge below will still run, but if it conflicts\n // we flag it as a base-generation-mismatch.\n baseMismatchSkipped = true;\n }\n }\n\n // Handle new files (user created, didn't exist in generation)\n if (!base && !ours && effective_theirs) {\n const outDir = dirname(oursPath);\n await mkdir(outDir, { recursive: true });\n await writeFile(oursPath, effective_theirs);\n return { file: resolvedPath, status: \"merged\", reason: \"new-file\" };\n }\n\n // Handle new file created by both user and generator (conflict)\n if (!base && ours && effective_theirs) {\n const merged = threeWayMerge(\"\", ours, effective_theirs);\n const outDir = dirname(oursPath);\n await mkdir(outDir, { recursive: true });\n await writeFile(oursPath, merged.content);\n if (merged.hasConflicts) {\n return {\n file: resolvedPath,\n status: \"conflict\",\n conflicts: merged.conflicts,\n conflictReason: \"new-file-both\",\n conflictMetadata: metadata\n };\n }\n return { file: resolvedPath, status: \"merged\" };\n }\n\n if (!effective_theirs) {\n return {\n file: resolvedPath,\n status: \"skipped\",\n reason: \"missing-content\"\n };\n }\n\n if (!base || !ours) {\n return {\n file: resolvedPath,\n status: \"skipped\",\n reason: \"missing-content\"\n };\n }\n\n // Perform 3-way merge.\n // For incremental patches, use the accumulated state as the merge base\n // so the diff is computed relative to prior patches, not the raw generation.\n const mergeBase = useAccumulatorAsMergeBase && accumulatorEntry ? accumulatorEntry.content : base;\n const merged = threeWayMerge(mergeBase, ours, effective_theirs);\n\n // Write result to the resolved (current) path\n const outDir = dirname(oursPath);\n await mkdir(outDir, { recursive: true });\n await writeFile(oursPath, merged.content);\n\n // Update accumulator after successful merge\n // This allows subsequent patches on the same file to benefit from pre-merge\n if (effective_theirs && base) {\n this.fileTheirsAccumulator.set(resolvedPath, {\n content: effective_theirs,\n baseGeneration: patch.base_generation\n });\n }\n\n if (merged.hasConflicts) {\n return {\n file: resolvedPath,\n status: \"conflict\",\n conflicts: merged.conflicts,\n conflictReason: baseMismatchSkipped ? \"base-generation-mismatch\" : \"same-line-edit\",\n conflictMetadata: metadata\n };\n }\n\n return { file: resolvedPath, status: \"merged\" };\n } catch (error) {\n return {\n file: filePath,\n status: \"skipped\",\n reason: `error: ${error instanceof Error ? error.message : String(error)}`\n };\n }\n }\n\n private isExcluded(patch: StoredPatch): boolean {\n const config = this.lockManager.getCustomizationsConfig();\n if (!config.exclude) return false;\n\n return patch.files.some((file) => config.exclude!.some((pattern) => minimatch(file, pattern)));\n }\n\n private async resolveFilePath(filePath: string, baseTreeHash: string, currentTreeHash: string): Promise<string> {\n // Priority 1: Manual moves from replay.yml\n const config = this.lockManager.getCustomizationsConfig();\n if (config.moves) {\n for (const move of config.moves) {\n if (minimatch(filePath, move.from) || filePath === move.from) {\n // For exact matches, replace directly\n if (filePath === move.from) {\n return move.to;\n }\n // For glob matches, replace the matching prefix\n // e.g., from: \"src/api/**\" to: \"src/resources/**\"\n // filePath: \"src/api/client.ts\" → \"src/resources/client.ts\"\n const fromBase = move.from.replace(/\\*\\*.*$/, \"\");\n const toBase = move.to.replace(/\\*\\*.*$/, \"\");\n if (filePath.startsWith(fromBase)) {\n return toBase + filePath.slice(fromBase.length);\n }\n }\n }\n }\n\n // Priority 2: Git rename detection (cached per tree pair)\n const cacheKey = `${baseTreeHash}:${currentTreeHash}`;\n let renames = this.renameCache.get(cacheKey);\n if (!renames) {\n renames = await this.git.detectRenames(baseTreeHash, currentTreeHash);\n this.renameCache.set(cacheKey, renames);\n }\n\n const gitRename = renames.find((r) => r.from === filePath);\n if (gitRename) {\n return gitRename.to;\n }\n\n // Priority 3: No rename detected\n return filePath;\n }\n\n private async applyPatchToContent(\n base: string | null,\n patchContent: string,\n filePath: string,\n tempGit: GitClient,\n tempDir: string,\n sourceFilePath?: string\n ): Promise<string | null> {\n if (!base) {\n // New file - extract content directly from patch\n return this.extractNewFileFromPatch(patchContent, filePath);\n }\n\n // Extract only the diff for this specific file\n const fileDiff = this.extractFileDiff(patchContent, filePath);\n if (!fileDiff) return null;\n\n try {\n if (sourceFilePath) {\n // Rename case: write base at the OLD path, apply the rename diff,\n // then read from the NEW path (filePath).\n const tempSourcePath = join(tempDir, sourceFilePath);\n await mkdir(dirname(tempSourcePath), { recursive: true });\n await writeFile(tempSourcePath, base);\n\n await tempGit.exec([\"add\", sourceFilePath]);\n await tempGit.exec([\n \"commit\",\n \"-m\",\n `base for rename ${sourceFilePath} -> ${filePath}`,\n \"--allow-empty\"\n ]);\n\n await tempGit.execWithInput([\"apply\", \"--allow-empty\"], fileDiff);\n\n const tempTargetPath = join(tempDir, filePath);\n return await readFile(tempTargetPath, \"utf-8\");\n }\n\n // Normal case: write base at filePath, apply diff, read back\n const tempFilePath = join(tempDir, filePath);\n await mkdir(dirname(tempFilePath), { recursive: true });\n await writeFile(tempFilePath, base);\n\n // Stage and commit so git apply has a clean base\n await tempGit.exec([\"add\", filePath]);\n await tempGit.exec([\"commit\", \"-m\", `base for ${filePath}`, \"--allow-empty\"]);\n\n // Apply this file's diff\n await tempGit.execWithInput([\"apply\", \"--allow-empty\"], fileDiff);\n\n return await readFile(tempFilePath, \"utf-8\");\n } catch {\n return null;\n }\n }\n\n private extractFileDiff(patchContent: string, filePath: string): string | null {\n const lines = patchContent.split(\"\\n\");\n const diffLines: string[] = [];\n let inTargetFile = false;\n\n for (const line of lines) {\n if (line.startsWith(\"diff --git\")) {\n if (inTargetFile) {\n // Hit the next file's diff, stop collecting\n break;\n }\n if (isDiffLineForFile(line, filePath)) {\n inTargetFile = true;\n diffLines.push(line);\n }\n continue;\n }\n\n if (inTargetFile) {\n diffLines.push(line);\n }\n }\n\n return diffLines.length > 0 ? diffLines.join(\"\\n\") + \"\\n\" : null;\n }\n\n private extractRenameSource(patchContent: string, targetFilePath: string): string | null {\n const lines = patchContent.split(\"\\n\");\n let inTargetFile = false;\n\n for (const line of lines) {\n if (line.startsWith(\"diff --git\")) {\n if (inTargetFile) break; // past the target block\n inTargetFile = isDiffLineForFile(line, targetFilePath);\n continue;\n }\n if (!inTargetFile) continue;\n\n // Stop at first hunk — rename headers appear before @@\n if (line.startsWith(\"@@\")) break;\n\n if (line.startsWith(\"rename from \")) {\n return line.slice(\"rename from \".length);\n }\n }\n\n return null;\n }\n\n private extractNewFileFromPatch(patchContent: string, filePath: string): string | null {\n const lines = patchContent.split(\"\\n\");\n const addedLines: string[] = [];\n let inTargetFile = false;\n let inHunk = false;\n let noTrailingNewline = false;\n\n for (const line of lines) {\n if (line.startsWith(\"diff --git\")) {\n if (inTargetFile) break; // hit next file's diff\n inTargetFile = isDiffLineForFile(line, filePath);\n inHunk = false;\n continue;\n }\n if (!inTargetFile) continue;\n\n if (line.startsWith(\"@@\")) {\n inHunk = true;\n continue;\n }\n if (!inHunk) continue;\n\n if (line === \"\\\\") {\n noTrailingNewline = true;\n continue;\n }\n\n if (line.startsWith(\"+\") && !line.startsWith(\"+++\")) {\n addedLines.push(line.slice(1));\n }\n }\n\n if (addedLines.length === 0) return null;\n return addedLines.join(\"\\n\") + (noTrailingNewline ? \"\" : \"\\n\");\n }\n}\n\nfunction isBinaryFile(filePath: string): boolean {\n const ext = extname(filePath).toLowerCase();\n return BINARY_EXTENSIONS.has(ext);\n}\n\n/**\n * Check if a `diff --git` line targets the given file path.\n * Uses a regex anchored to the line format to avoid substring false positives\n * (e.g., `lib/foo.ts` matching `b/lib/foo.ts-backup`).\n */\nfunction isDiffLineForFile(diffLine: string, filePath: string): boolean {\n const match = diffLine.match(/^diff --git a\\/.+ b\\/(.+)$/);\n return match !== null && match[1] === filePath;\n}\n","import type { GitClient } from \"./git/GitClient.js\";\nimport type { GenerationRecord, StoredPatch } from \"./types.js\";\n\nexport interface CommitOptions {\n cliVersion: string;\n generatorVersions: Record<string, string>;\n baseBranchHead?: string;\n}\n\nexport class ReplayCommitter {\n private git: GitClient;\n private outputDir: string;\n\n constructor(git: GitClient, outputDir: string) {\n this.git = git;\n this.outputDir = outputDir;\n }\n\n async commitGeneration(message: string, options?: CommitOptions): Promise<string> {\n await this.stageAll();\n\n if (!(await this.hasStagedChanges())) {\n return (await this.git.exec([\"rev-parse\", \"HEAD\"])).trim();\n }\n\n let fullMessage = `[fern-generated] ${message}\\n\\nGenerated by Fern`;\n\n if (options?.cliVersion) {\n fullMessage += `\\nCLI Version: ${options.cliVersion}`;\n }\n\n if (options?.generatorVersions && Object.keys(options.generatorVersions).length > 0) {\n fullMessage += \"\\nGenerators:\";\n for (const [name, version] of Object.entries(options.generatorVersions)) {\n fullMessage += `\\n - ${name}: ${version}`;\n }\n }\n\n await this.git.exec([\"commit\", \"-m\", fullMessage]);\n return (await this.git.exec([\"rev-parse\", \"HEAD\"])).trim();\n }\n\n async commitReplay(_patchCount: number, patches?: StoredPatch[]): Promise<string> {\n await this.stageAll();\n\n if (!(await this.hasStagedChanges())) {\n return (await this.git.exec([\"rev-parse\", \"HEAD\"])).trim();\n }\n\n let fullMessage = `[fern-replay] Applied customizations`;\n\n if (patches && patches.length > 0) {\n fullMessage += \"\\n\\nPatches replayed:\";\n for (const patch of patches) {\n fullMessage += `\\n - ${patch.id}: ${patch.original_message}`;\n }\n }\n\n await this.git.exec([\"commit\", \"-m\", fullMessage]);\n return (await this.git.exec([\"rev-parse\", \"HEAD\"])).trim();\n }\n\n async createGenerationRecord(options?: CommitOptions): Promise<GenerationRecord> {\n const commitSha = (await this.git.exec([\"rev-parse\", \"HEAD\"])).trim();\n const treeHash = await this.getTreeHash(commitSha);\n\n return {\n commit_sha: commitSha,\n tree_hash: treeHash,\n timestamp: new Date().toISOString(),\n cli_version: options?.cliVersion ?? \"unknown\",\n generator_versions: options?.generatorVersions ?? {},\n base_branch_head: options?.baseBranchHead\n };\n }\n\n async stageAll(): Promise<void> {\n await this.git.exec([\"add\", \"-A\", this.outputDir]);\n }\n\n async hasStagedChanges(): Promise<boolean> {\n const output = await this.git.exec([\"diff\", \"--cached\", \"--name-only\"]);\n return output.trim().length > 0;\n }\n\n async getTreeHash(commitSha: string): Promise<string> {\n return this.git.getTreeHash(commitSha);\n }\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { minimatch } from \"minimatch\";\nimport { GitClient } from \"./git/GitClient.js\";\nimport { LockfileManager } from \"./LockfileManager.js\";\nimport { ReplayApplicator } from \"./ReplayApplicator.js\";\nimport { ReplayCommitter } from \"./ReplayCommitter.js\";\nimport { ReplayDetector } from \"./ReplayDetector.js\";\nimport type { FileResult, GenerationRecord, ReplayConfig, ReplayResult, StoredPatch } from \"./types.js\";\n\nexport interface ConflictDetail {\n patchId: string;\n patchMessage: string;\n reason?: string;\n files: FileResult[];\n /** Files that applied cleanly in a patch that also had conflicts. */\n cleanFiles?: string[];\n}\n\nexport interface ReplayReport {\n flow: \"first-generation\" | \"no-patches\" | \"normal-regeneration\" | \"skip-application\";\n patchesDetected: number;\n patchesApplied: number;\n patchesWithConflicts: number;\n patchesSkipped: number;\n patchesAbsorbed?: number;\n patchesRepointed?: number;\n patchesContentRebased?: number;\n patchesKeptAsUserOwned?: number;\n patchesPartiallyApplied?: number;\n patchesConflictResolved?: number;\n patchesRefreshed?: number;\n conflicts: FileResult[];\n conflictDetails?: ConflictDetail[];\n wouldApply?: StoredPatch[];\n warnings?: string[];\n}\n\nexport interface ReplayOptions {\n /** Log what would happen but don't modify anything */\n dryRun?: boolean;\n /** Write files and stage changes, but don't commit */\n stageOnly?: boolean;\n /** CLI version for commit metadata */\n cliVersion?: string;\n /** Generator versions for commit metadata */\n generatorVersions?: Record<string, string>;\n /** Commit generation + update lockfile, skip detection/application */\n skipApplication?: boolean;\n /** SHA of the base branch HEAD before replay runs. Stored in lockfile for squash merge recovery. */\n baseBranchHead?: string;\n}\n\nexport class ReplayService {\n private git: GitClient;\n private detector: ReplayDetector;\n private applicator: ReplayApplicator;\n private committer: ReplayCommitter;\n private lockManager: LockfileManager;\n private outputDir: string;\n\n constructor(outputDir: string, _config: ReplayConfig) {\n const git = new GitClient(outputDir);\n this.git = git;\n this.outputDir = outputDir;\n this.lockManager = new LockfileManager(outputDir);\n this.detector = new ReplayDetector(git, this.lockManager, outputDir);\n this.applicator = new ReplayApplicator(git, this.lockManager, outputDir);\n this.committer = new ReplayCommitter(git, outputDir);\n }\n\n async runReplay(options?: ReplayOptions): Promise<ReplayReport> {\n if (options?.skipApplication) {\n return this.handleSkipApplication(options);\n }\n\n const flow = this.determineFlow();\n\n switch (flow) {\n case \"first-generation\":\n return this.handleFirstGeneration(options);\n case \"no-patches\":\n return this.handleNoPatchesRegeneration(options);\n case \"normal-regeneration\":\n return this.handleNormalRegeneration(options);\n }\n }\n\n /**\n * Sync the lockfile after a divergent PR was squash-merged.\n * Call this BEFORE runReplay() when the CLI detects a merged divergent PR.\n *\n * After updating the generation record, re-detects customer patches via\n * tree diff between the generation tag (pure generation tree) and HEAD\n * (which includes customer customizations after squash merge). This ensures\n * patches survive the squash merge → regenerate cycle even when the lockfile\n * was restored from the base branch during conflict PR creation.\n */\n async syncFromDivergentMerge(\n generationCommitSha: string,\n options?: { cliVersion?: string; generatorVersions?: Record<string, string>; baseBranchHead?: string }\n ): Promise<void> {\n const treeHash = await this.git.getTreeHash(generationCommitSha);\n const timestamp = (await this.git.exec([\"log\", \"-1\", \"--format=%aI\", generationCommitSha])).trim();\n\n const record: GenerationRecord = {\n commit_sha: generationCommitSha,\n tree_hash: treeHash,\n timestamp,\n cli_version: options?.cliVersion ?? \"unknown\",\n generator_versions: options?.generatorVersions ?? {},\n base_branch_head: options?.baseBranchHead\n };\n\n if (!this.lockManager.exists()) {\n this.lockManager.initializeInMemory(record);\n } else {\n this.lockManager.read();\n this.lockManager.addGeneration(record);\n this.lockManager.clearPatches();\n }\n this.lockManager.save();\n\n // Re-detect customer patches via tree diff. The generation tag has the\n // pure generation tree, so any differences between it and HEAD represent\n // customer customizations that survived the squash merge.\n try {\n const redetectedPatches = await this.detector.detectNewPatches();\n if (redetectedPatches.length > 0) {\n for (const patch of redetectedPatches) {\n this.lockManager.addPatch(patch);\n }\n this.lockManager.save();\n }\n } catch {\n // If re-detection fails, proceed without patches.\n // The normal replay flow will still attempt detection.\n }\n }\n\n private determineFlow(): \"first-generation\" | \"no-patches\" | \"normal-regeneration\" {\n if (!this.lockManager.exists()) {\n return \"first-generation\";\n }\n\n const lock = this.lockManager.read();\n if (lock.patches.length === 0) {\n return \"no-patches\";\n }\n\n return \"normal-regeneration\";\n }\n\n private async handleFirstGeneration(options?: ReplayOptions): Promise<ReplayReport> {\n if (options?.dryRun) {\n return {\n flow: \"first-generation\",\n patchesDetected: 0,\n patchesApplied: 0,\n patchesWithConflicts: 0,\n patchesSkipped: 0,\n conflicts: []\n };\n }\n\n const commitOpts = options\n ? {\n cliVersion: options.cliVersion ?? \"unknown\",\n generatorVersions: options.generatorVersions ?? {},\n baseBranchHead: options.baseBranchHead\n }\n : undefined;\n\n await this.committer.commitGeneration(\"Initial SDK generation\", commitOpts);\n const genRecord = await this.committer.createGenerationRecord(commitOpts);\n this.lockManager.initialize(genRecord);\n\n return {\n flow: \"first-generation\",\n patchesDetected: 0,\n patchesApplied: 0,\n patchesWithConflicts: 0,\n patchesSkipped: 0,\n conflicts: []\n };\n }\n\n /**\n * Skip-application mode: commit the generation and update the lockfile\n * but don't detect or apply patches. Sets a marker so the next normal\n * run skips revert detection in preGenerationRebase().\n */\n private async handleSkipApplication(options?: ReplayOptions): Promise<ReplayReport> {\n const commitOpts = options\n ? {\n cliVersion: options.cliVersion ?? \"unknown\",\n generatorVersions: options.generatorVersions ?? {},\n baseBranchHead: options.baseBranchHead\n }\n : undefined;\n\n await this.committer.commitGeneration(\"Update SDK (replay skipped)\", commitOpts);\n const genRecord = await this.committer.createGenerationRecord(commitOpts);\n\n if (this.lockManager.exists()) {\n this.lockManager.read();\n this.lockManager.addGeneration(genRecord);\n } else {\n this.lockManager.initializeInMemory(genRecord);\n }\n\n this.lockManager.setReplaySkippedAt(new Date().toISOString());\n this.lockManager.save();\n\n // Commit the lockfile update so it's pushed with the branch.\n // Without this, the marker and generation record stay in the\n // working tree and are lost when GithubStep pushes.\n if (!options?.stageOnly) {\n await this.committer.commitReplay(0);\n } else {\n await this.committer.stageAll();\n }\n\n return {\n flow: \"skip-application\",\n patchesDetected: 0,\n patchesApplied: 0,\n patchesWithConflicts: 0,\n patchesSkipped: 0,\n conflicts: []\n };\n }\n\n private async handleNoPatchesRegeneration(options?: ReplayOptions): Promise<ReplayReport> {\n const newPatches = await this.detector.detectNewPatches();\n const warnings = [...this.detector.warnings];\n\n if (options?.dryRun) {\n return {\n flow: \"no-patches\",\n patchesDetected: newPatches.length,\n patchesApplied: 0,\n patchesWithConflicts: 0,\n patchesSkipped: 0,\n conflicts: [],\n wouldApply: newPatches,\n warnings: warnings.length > 0 ? warnings : undefined\n };\n }\n\n const commitOpts = options\n ? {\n cliVersion: options.cliVersion ?? \"unknown\",\n generatorVersions: options.generatorVersions ?? {},\n baseBranchHead: options.baseBranchHead\n }\n : undefined;\n\n await this.committer.commitGeneration(\"Update SDK\", commitOpts);\n const genRecord = await this.committer.createGenerationRecord(commitOpts);\n\n // Add generation to lockfile BEFORE applying patches so the applicator\n // can read the current generation's tree hash for rename detection.\n this.lockManager.addGeneration(genRecord);\n\n let results: ReplayResult[] = [];\n if (newPatches.length > 0) {\n results = await this.applicator.applyPatches(newPatches);\n }\n\n // Rebase cleanly applied patches to the current generation.\n // This prevents recurring conflicts on subsequent regenerations.\n const rebaseCounts = await this.rebasePatches(results, genRecord.commit_sha);\n\n // Save lockfile BEFORE commit — lockfile is source of truth.\n for (const patch of newPatches) {\n if (!rebaseCounts.absorbedPatchIds.has(patch.id)) {\n this.lockManager.addPatch(patch);\n }\n }\n this.lockManager.save();\n\n if (newPatches.length > 0) {\n if (!options?.stageOnly) {\n const appliedCount = results.filter((r) => r.status === \"applied\" || r.status === \"conflict\").length;\n if (appliedCount > 0) {\n await this.committer.commitReplay(appliedCount, newPatches);\n }\n } else {\n await this.committer.stageAll();\n }\n }\n\n return this.buildReport(\"no-patches\", newPatches, results, options, warnings, rebaseCounts);\n }\n\n private async handleNormalRegeneration(options?: ReplayOptions): Promise<ReplayReport> {\n if (options?.dryRun) {\n const existingPatches = this.lockManager.getPatches();\n const newPatches = await this.detector.detectNewPatches();\n const warnings = [...this.detector.warnings];\n const allPatches = [...existingPatches, ...newPatches];\n return {\n flow: \"normal-regeneration\",\n patchesDetected: allPatches.length,\n patchesApplied: 0,\n patchesWithConflicts: 0,\n patchesSkipped: 0,\n conflicts: [],\n wouldApply: allPatches,\n warnings: warnings.length > 0 ? warnings : undefined\n };\n }\n\n // Pre-generation rebase: update stale/modified patches while HEAD has customer code.\n let existingPatches = this.lockManager.getPatches();\n const preRebaseCounts = await this.preGenerationRebase(existingPatches);\n\n // Dedup patches by content hash after pre-generation rebase.\n existingPatches = this.lockManager.getPatches();\n const seenHashes = new Set<string>();\n for (const p of existingPatches) {\n if (seenHashes.has(p.content_hash)) {\n this.lockManager.removePatch(p.id);\n } else {\n seenHashes.add(p.content_hash);\n }\n }\n existingPatches = this.lockManager.getPatches();\n\n const newPatches = await this.detector.detectNewPatches();\n const warnings = [...this.detector.warnings];\n const allPatches = [...existingPatches, ...newPatches];\n\n const commitOpts = options\n ? {\n cliVersion: options.cliVersion ?? \"unknown\",\n generatorVersions: options.generatorVersions ?? {},\n baseBranchHead: options.baseBranchHead\n }\n : undefined;\n\n await this.committer.commitGeneration(\"Update SDK\", commitOpts);\n const genRecord = await this.committer.createGenerationRecord(commitOpts);\n\n // Add generation to lockfile BEFORE applying patches so the applicator\n // can read the current generation's tree hash for rename detection.\n this.lockManager.addGeneration(genRecord);\n\n const results = await this.applicator.applyPatches(allPatches);\n\n // Rebase cleanly applied patches to the current generation.\n const rebaseCounts = await this.rebasePatches(results, genRecord.commit_sha);\n\n // Save lockfile BEFORE commit — lockfile is source of truth.\n for (const patch of newPatches) {\n if (!rebaseCounts.absorbedPatchIds.has(patch.id)) {\n this.lockManager.addPatch(patch);\n }\n }\n this.lockManager.save();\n\n if (options?.stageOnly) {\n await this.committer.stageAll();\n } else {\n const appliedCount = results.filter((r) => r.status === \"applied\" || r.status === \"conflict\").length;\n if (appliedCount > 0) {\n await this.committer.commitReplay(appliedCount, allPatches);\n }\n }\n\n return this.buildReport(\n \"normal-regeneration\",\n allPatches,\n results,\n options,\n warnings,\n rebaseCounts,\n preRebaseCounts\n );\n }\n\n /**\n * Rebase cleanly applied patches so they are relative to the current generation.\n * This prevents recurring conflicts on subsequent regenerations.\n * Returns the number of patches rebased.\n */\n private async rebasePatches(\n results: ReplayResult[],\n currentGenSha: string\n ): Promise<{\n absorbed: number;\n repointed: number;\n contentRebased: number;\n keptAsUserOwned: number;\n absorbedPatchIds: Set<string>;\n }> {\n let absorbed = 0;\n let repointed = 0;\n let contentRebased = 0;\n let keptAsUserOwned = 0;\n // Track content hashes to deduplicate converging incremental patches.\n const seenContentHashes = new Set<string>();\n const absorbedPatchIds = new Set<string>();\n\n // Pre-pass: Update patch.files with resolved (renamed) paths from application.\n // This ensures downstream operations (user-owned check, git diff, git show)\n // use the current file paths rather than stale pre-rename paths.\n for (const result of results) {\n if (result.resolvedFiles && Object.keys(result.resolvedFiles).length > 0) {\n const patch = result.patch;\n const updatedFiles = patch.files.map((f) => result.resolvedFiles![f] ?? f);\n patch.files = updatedFiles;\n try {\n this.lockManager.updatePatch(patch.id, { files: updatedFiles });\n } catch {\n // New patches aren't in lockfile yet — in-memory update sufficient\n }\n }\n }\n\n for (const result of results) {\n if (result.status === \"conflict\" && result.fileResults) {\n // For conflict patches with mixed results, trim any clean files\n // that were absorbed by the generator. This prevents absorbed files\n // from poisoning the pre-generation rebase conflict marker check.\n await this.trimAbsorbedFiles(result, currentGenSha);\n continue;\n }\n\n // Only rebase cleanly applied patches.\n // Conflicted patches keep their old base_generation so they retry.\n if (result.status !== \"applied\") continue;\n\n const patch = result.patch;\n\n // Skip if already based on the current generation\n if (patch.base_generation === currentGenSha) continue;\n\n try {\n // Check if any files are user-owned (new, .fernignore-protected, or .fernignore itself).\n const fernignorePatterns = this.readFernignorePatterns();\n const isUserOwned = await Promise.all(\n patch.files.map(async (file) => {\n // .fernignore itself is always user-owned\n if (file === \".fernignore\") {\n return true;\n }\n // Check .fernignore patterns (cheaper than git)\n if (fernignorePatterns.some((p) => file === p || minimatch(file, p))) {\n return true;\n }\n // Check if file exists in the generation commit tree\n const content = await this.git.showFile(currentGenSha, file);\n return content === null;\n })\n );\n const hasUserOwnedFiles = isUserOwned.some(Boolean);\n\n if (hasUserOwnedFiles) {\n // Patch contains user-owned files — just update base_generation\n // to prevent re-evaluation. Keep patch_content as-is since\n // these files aren't produced by the generator.\n this.lockManager.updatePatch(patch.id, {\n base_generation: currentGenSha\n });\n keptAsUserOwned++;\n continue;\n }\n\n // All files exist in generation — safe to use git diff for\n // absorption check and patch rebasing.\n const diff = await this.git.exec([\"diff\", currentGenSha, \"--\", ...patch.files]).catch(() => null);\n\n if (!diff || !diff.trim()) {\n // Patch is now empty — customization was absorbed by the generator.\n this.lockManager.removePatch(patch.id);\n absorbedPatchIds.add(patch.id);\n absorbed++;\n continue;\n }\n\n const newContentHash = this.detector.computeContentHash(diff);\n\n // If another patch already captured this exact diff, this patch\n // is redundant (e.g., incremental patches that both resolve to\n // the same cumulative state after rebase).\n if (seenContentHashes.has(newContentHash)) {\n this.lockManager.removePatch(patch.id);\n absorbedPatchIds.add(patch.id);\n absorbed++;\n continue;\n }\n seenContentHashes.add(newContentHash);\n\n this.lockManager.updatePatch(patch.id, {\n base_generation: currentGenSha,\n patch_content: diff,\n content_hash: newContentHash\n });\n contentRebased++;\n } catch {\n // If rebasing fails for any reason, keep the original patch unchanged\n }\n }\n\n return { absorbed, repointed, contentRebased, keptAsUserOwned, absorbedPatchIds };\n }\n\n /**\n * For conflict patches with mixed results (some files merged, some conflicted),\n * check if the cleanly merged files were absorbed by the generator (empty diff).\n * If so, remove them from patch.files so they don't pollute the pre-generation\n * rebase conflict marker check (`git grep <<<<<<< -- ...patch.files`).\n *\n * Non-absorbed clean files stay in patch.files — removing them would lose\n * the customization on the next generation.\n */\n private async trimAbsorbedFiles(result: ReplayResult, currentGenSha: string): Promise<void> {\n const cleanFiles = result.fileResults!.filter((f) => f.status === \"merged\").map((f) => f.file);\n if (cleanFiles.length === 0) return;\n\n const patch = result.patch;\n const fernignorePatterns = this.readFernignorePatterns();\n\n // Filter to only generator-produced clean files (not user-owned)\n const generatorCleanFiles: string[] = [];\n for (const file of cleanFiles) {\n if (file === \".fernignore\") continue;\n if (fernignorePatterns.some((p) => file === p || minimatch(file, p))) continue;\n const content = await this.git.showFile(currentGenSha, file);\n if (content === null) continue; // user-owned new file\n generatorCleanFiles.push(file);\n }\n if (generatorCleanFiles.length === 0) return;\n\n // Check which clean files have an empty diff (absorbed by generator)\n const absorbedFiles = new Set<string>();\n for (const file of generatorCleanFiles) {\n try {\n const diff = await this.git.exec([\"diff\", currentGenSha, \"--\", file]).catch(() => null);\n if (!diff || !diff.trim()) {\n absorbedFiles.add(file);\n }\n } catch {\n // If diff fails, leave the file in the patch\n }\n }\n if (absorbedFiles.size === 0) return;\n\n // Remove absorbed files from the patch's file list\n const remainingFiles = patch.files.filter((f) => !absorbedFiles.has(f));\n if (remainingFiles.length === patch.files.length) return; // nothing changed\n\n try {\n this.lockManager.updatePatch(patch.id, { files: remainingFiles });\n } catch {\n // New patches aren't in lockfile yet — update in-memory only\n patch.files = remainingFiles;\n }\n }\n\n /**\n * Pre-generation rebase: update patches using the customer's current state.\n * Called BEFORE commitGeneration() while HEAD has customer code.\n */\n private async preGenerationRebase(\n patches: StoredPatch[]\n ): Promise<{ conflictResolved: number; conflictAbsorbed: number; contentRefreshed: number }> {\n const lock = this.lockManager.read();\n const currentGen = lock.current_generation;\n\n // If the previous run was skip-application, clear the marker and\n // skip all revert detection. Patches are preserved as-is.\n if (this.lockManager.isReplaySkipped()) {\n this.lockManager.clearReplaySkippedAt();\n return { conflictResolved: 0, conflictAbsorbed: 0, contentRefreshed: 0 };\n }\n\n let conflictResolved = 0;\n let conflictAbsorbed = 0;\n let contentRefreshed = 0;\n\n for (const patch of patches) {\n if (patch.base_generation === currentGen) {\n // Content refresh: check if user modified their customization\n // since the last replay commit.\n try {\n const diff = await this.git\n .exec([\"diff\", currentGen, \"HEAD\", \"--\", ...patch.files])\n .catch(() => null);\n\n if (diff === null) continue;\n\n if (!diff.trim()) {\n // User reverted all customizations in this patch\n this.lockManager.removePatch(patch.id);\n contentRefreshed++;\n continue;\n }\n\n const newContentHash = this.detector.computeContentHash(diff);\n if (newContentHash !== patch.content_hash) {\n // User changed their customization — update patch content\n // and recalculate the files list (some files may no longer differ)\n const filesOutput = await this.git\n .exec([\"diff\", \"--name-only\", currentGen, \"HEAD\", \"--\", ...patch.files])\n .catch(() => null);\n\n const newFiles = filesOutput\n ? filesOutput\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .filter((f) => !f.startsWith(\".fern/\"))\n : patch.files;\n\n if (newFiles.length === 0) {\n this.lockManager.removePatch(patch.id);\n } else {\n this.lockManager.updatePatch(patch.id, {\n patch_content: diff,\n content_hash: newContentHash,\n files: newFiles\n });\n }\n contentRefreshed++;\n }\n } catch {\n // If anything fails, leave the patch unchanged\n }\n continue;\n }\n\n try {\n // Check for unresolved conflict markers in HEAD\n const markerFiles = await this.git\n .exec([\"grep\", \"-l\", \"<<<<<<<\", \"HEAD\", \"--\", ...patch.files])\n .catch(() => \"\");\n\n if (markerFiles.trim()) continue;\n\n // Compute customer's resolved state relative to current generation\n const diff = await this.git.exec([\"diff\", currentGen, \"HEAD\", \"--\", ...patch.files]).catch(() => null);\n\n // Diff command failed (e.g., invalid SHA) — skip this patch\n if (diff === null) continue;\n\n if (!diff.trim()) {\n // Empty diff — customer reverted their customization\n this.lockManager.removePatch(patch.id);\n conflictAbsorbed++;\n continue;\n }\n\n // Update the patch with the resolved content\n const newContentHash = this.detector.computeContentHash(diff);\n this.lockManager.updatePatch(patch.id, {\n base_generation: currentGen,\n patch_content: diff,\n content_hash: newContentHash\n });\n conflictResolved++;\n } catch {\n // If anything fails, leave the patch unchanged\n }\n }\n\n return { conflictResolved, conflictAbsorbed, contentRefreshed };\n }\n\n private readFernignorePatterns(): string[] {\n const fernignorePath = join(this.outputDir, \".fernignore\");\n if (!existsSync(fernignorePath)) return [];\n return readFileSync(fernignorePath, \"utf-8\")\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line && !line.startsWith(\"#\"));\n }\n\n private buildReport(\n flow: \"first-generation\" | \"no-patches\" | \"normal-regeneration\" | \"skip-application\",\n patches: StoredPatch[],\n results: ReplayResult[],\n options?: ReplayOptions,\n warnings?: string[],\n rebaseCounts?: {\n absorbed: number;\n repointed: number;\n contentRebased: number;\n keptAsUserOwned: number;\n absorbedPatchIds: Set<string>;\n },\n preRebaseCounts?: { conflictResolved: number; conflictAbsorbed: number; contentRefreshed: number }\n ): ReplayReport {\n const conflictResults = results.filter((r) => r.status === \"conflict\");\n const conflictDetails = conflictResults\n .map((r) => {\n const conflictFiles = r.fileResults?.filter((f) => f.status === \"conflict\") ?? [];\n const cleanFiles = r.fileResults?.filter((f) => f.status === \"merged\").map((f) => f.file) ?? [];\n return {\n patchId: r.patch.id,\n patchMessage: r.patch.original_message,\n reason: r.conflictReason,\n files: conflictFiles,\n cleanFiles: cleanFiles.length > 0 ? cleanFiles : undefined\n };\n })\n .filter((d) => d.files.length > 0);\n const partialCount = conflictDetails.filter((d) => d.cleanFiles && d.cleanFiles.length > 0).length;\n\n return {\n flow,\n patchesDetected: patches.length,\n patchesApplied: results.filter((r) => r.status === \"applied\").length,\n patchesWithConflicts: conflictResults.length,\n patchesSkipped: results.filter((r) => r.status === \"skipped\").length,\n patchesPartiallyApplied: partialCount > 0 ? partialCount : undefined,\n patchesAbsorbed: rebaseCounts && rebaseCounts.absorbed > 0 ? rebaseCounts.absorbed : undefined,\n patchesRepointed: rebaseCounts && rebaseCounts.repointed > 0 ? rebaseCounts.repointed : undefined,\n patchesContentRebased:\n rebaseCounts && rebaseCounts.contentRebased > 0 ? rebaseCounts.contentRebased : undefined,\n patchesKeptAsUserOwned:\n rebaseCounts && rebaseCounts.keptAsUserOwned > 0 ? rebaseCounts.keptAsUserOwned : undefined,\n patchesConflictResolved:\n preRebaseCounts && preRebaseCounts.conflictResolved + preRebaseCounts.conflictAbsorbed > 0\n ? preRebaseCounts.conflictResolved + preRebaseCounts.conflictAbsorbed\n : undefined,\n patchesRefreshed:\n preRebaseCounts && preRebaseCounts.contentRefreshed > 0 ? preRebaseCounts.contentRefreshed : undefined,\n conflicts: conflictResults.flatMap((r) => r.fileResults?.filter((f) => f.status === \"conflict\") ?? []),\n conflictDetails: conflictDetails.length > 0 ? conflictDetails : undefined,\n wouldApply: options?.dryRun ? patches : undefined,\n warnings: warnings && warnings.length > 0 ? warnings : undefined\n };\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { minimatch } from \"minimatch\";\nimport { parse, stringify } from \"yaml\";\nimport type { GitClient } from \"./git/GitClient.js\";\nimport type { LockfileManager } from \"./LockfileManager.js\";\nimport { ReplayDetector } from \"./ReplayDetector.js\";\nimport type { CustomizationsConfig, StoredPatch } from \"./types.js\";\n\nexport interface MigrationAnalysis {\n /** Files in .fernignore that have git commit history (can be tracked by Replay) */\n trackedByBoth: Array<{ file: string; commit: string }>;\n /** Files in .fernignore but NO recent commit history found after last generation */\n fernignoreOnly: string[];\n /** Commits found that AREN'T in .fernignore (inline edits to generated files) */\n commitsOnly: StoredPatch[];\n /** Synthetic patches created for .fernignore files that differ from pristine generation */\n syntheticPatches: StoredPatch[];\n}\n\nexport interface MigrationResult {\n patchesCreated: number;\n filesSkipped: string[];\n warnings: string[];\n}\n\nexport class FernignoreMigrator {\n private git: GitClient;\n private lockManager: LockfileManager;\n private outputDir: string;\n\n constructor(git: GitClient, lockManager: LockfileManager, outputDir: string) {\n this.git = git;\n this.lockManager = lockManager;\n this.outputDir = outputDir;\n }\n\n fernignoreExists(): boolean {\n return existsSync(join(this.outputDir, \".fernignore\"));\n }\n\n readFernignorePatterns(): string[] {\n const fernignorePath = join(this.outputDir, \".fernignore\");\n if (!existsSync(fernignorePath)) {\n return [];\n }\n const content = readFileSync(fernignorePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line && !line.startsWith(\"#\"));\n }\n\n /** Analyze .fernignore patterns vs git history. Creates synthetic patches for files differing from pristine generation. */\n async analyzeMigration(): Promise<MigrationAnalysis> {\n const patterns = this.readFernignorePatterns();\n const detector = new ReplayDetector(this.git, this.lockManager, this.outputDir);\n const patches = await detector.detectNewPatches();\n\n const trackedByBoth: Array<{ file: string; commit: string }> = [];\n const fernignoreOnly: string[] = [];\n const commitsOnly: StoredPatch[] = [];\n const syntheticPatches: StoredPatch[] = [];\n\n // Check each fernignore pattern against recent patches\n for (const pattern of patterns) {\n const matchingPatch = patches.find((p) => p.files.some((f) => minimatch(f, pattern) || f === pattern));\n if (matchingPatch) {\n trackedByBoth.push({\n file: pattern,\n commit: matchingPatch.original_commit\n });\n } else {\n fernignoreOnly.push(pattern);\n }\n }\n\n // For fernignore-only patterns, try to create synthetic patches\n // by diffing current file state against pristine generation tree\n const lock = this.lockManager.read();\n const currentGen = lock.generations.find((g) => g.commit_sha === lock.current_generation);\n\n if (currentGen && fernignoreOnly.length > 0) {\n const resolvedFiles = await this.resolvePatterns(fernignoreOnly);\n const remainingFernignoreOnly: string[] = [];\n\n for (const { pattern, files } of resolvedFiles) {\n const patchFiles: string[] = [];\n const diffParts: string[] = [];\n\n for (const filePath of files) {\n const pristine = await this.git.showFile(currentGen.tree_hash, filePath);\n const currentContent = this.readFileContent(filePath);\n\n if (currentContent === null) {\n // File in .fernignore but not on disk — nothing to track\n continue;\n }\n\n if (pristine === null) {\n // File doesn't exist in generation tree — it's a wholly new file\n patchFiles.push(filePath);\n diffParts.push(this.createNewFileDiff(filePath, currentContent));\n } else if (pristine !== currentContent) {\n // File differs from pristine — user has customized it\n patchFiles.push(filePath);\n diffParts.push(this.createFileDiff(filePath, pristine, currentContent));\n }\n // If pristine === currentContent, file is unchanged — skip\n }\n\n if (patchFiles.length > 0) {\n const patchContent = diffParts.join(\"\\n\");\n const contentHash = `sha256:${createHash(\"sha256\").update(patchContent).digest(\"hex\")}`;\n\n syntheticPatches.push({\n id: `patch-fernignore-${createHash(\"sha256\").update(pattern).digest(\"hex\").slice(0, 8)}`,\n content_hash: contentHash,\n original_commit: currentGen.commit_sha,\n original_message: `[fernignore-migration] Customizations for ${pattern}`,\n original_author: \"Fern Replay <replay@buildwithfern.com>\",\n base_generation: currentGen.commit_sha,\n files: patchFiles,\n patch_content: patchContent\n });\n\n trackedByBoth.push({\n file: pattern,\n commit: `synthetic (differs from generated)`\n });\n } else {\n // No diff found — file matches generated output or doesn't exist\n remainingFernignoreOnly.push(pattern);\n }\n }\n\n // Replace fernignoreOnly with only truly untrackable patterns\n fernignoreOnly.length = 0;\n fernignoreOnly.push(...remainingFernignoreOnly);\n }\n\n // Find patches that touch files NOT in .fernignore\n for (const patch of patches) {\n const hasUnprotectedFiles = patch.files.some((f) => !patterns.some((p) => minimatch(f, p) || f === p));\n if (hasUnprotectedFiles) {\n commitsOnly.push(patch);\n }\n }\n\n return { trackedByBoth, fernignoreOnly, commitsOnly, syntheticPatches };\n }\n\n private async resolvePatterns(patterns: string[]): Promise<Array<{ pattern: string; files: string[] }>> {\n const allFiles = (await this.git.exec([\"ls-files\"])).trim().split(\"\\n\").filter(Boolean);\n const results: Array<{ pattern: string; files: string[] }> = [];\n\n for (const pattern of patterns) {\n const matching = allFiles.filter(\n (f) => minimatch(f, pattern) || f === pattern || f.startsWith(pattern + \"/\")\n );\n results.push({ pattern, files: matching.length > 0 ? matching : [pattern] });\n }\n\n return results;\n }\n\n private readFileContent(filePath: string): string | null {\n const fullPath = join(this.outputDir, filePath);\n if (!existsSync(fullPath)) {\n return null;\n }\n try {\n const stat = statSync(fullPath);\n if (stat.isDirectory()) {\n return null;\n }\n return readFileSync(fullPath, \"utf-8\");\n } catch {\n return null;\n }\n }\n\n private createNewFileDiff(filePath: string, content: string): string {\n const lines = content.split(\"\\n\");\n const hunks = lines.map((l) => `+${l}`).join(\"\\n\");\n return [\n `diff --git a/${filePath} b/${filePath}`,\n \"new file mode 100644\",\n `--- /dev/null`,\n `+++ b/${filePath}`,\n `@@ -0,0 +1,${lines.length} @@`,\n hunks\n ].join(\"\\n\");\n }\n\n private createFileDiff(filePath: string, pristine: string, current: string): string {\n const oldLines = pristine.split(\"\\n\");\n const newLines = current.split(\"\\n\");\n // Simple full-file replacement diff — not optimal but correct\n const removals = oldLines.map((l) => `-${l}`).join(\"\\n\");\n const additions = newLines.map((l) => `+${l}`).join(\"\\n\");\n return [\n `diff --git a/${filePath} b/${filePath}`,\n `--- a/${filePath}`,\n `+++ b/${filePath}`,\n `@@ -1,${oldLines.length} +1,${newLines.length} @@`,\n removals,\n additions\n ].join(\"\\n\");\n }\n\n async migrate(): Promise<MigrationResult> {\n const analysis = await this.analyzeMigration();\n const detector = new ReplayDetector(this.git, this.lockManager, this.outputDir);\n const patches = await detector.detectNewPatches();\n\n const warnings: string[] = [];\n let patchesCreated = 0;\n\n // Add all detected patches to lockfile\n for (const patch of patches) {\n this.lockManager.addPatch(patch);\n patchesCreated++;\n }\n\n if (patchesCreated > 0) {\n this.lockManager.save();\n }\n\n // Warn about fernignore-only files\n for (const file of analysis.fernignoreOnly) {\n warnings.push(\n `${file}: in .fernignore but no commit history found. Commit this file or keep in .fernignore as fallback.`\n );\n }\n\n return {\n patchesCreated,\n filesSkipped: analysis.fernignoreOnly,\n warnings\n };\n }\n\n movePatternsToReplayYml(patterns: string[]): void {\n const replayYmlPath = join(this.outputDir, \".fern\", \"replay.yml\");\n let config: CustomizationsConfig = {};\n\n if (existsSync(replayYmlPath)) {\n const content = readFileSync(replayYmlPath, \"utf-8\");\n config = (parse(content) as CustomizationsConfig) ?? {};\n }\n\n const existing = config.exclude ?? [];\n const merged = [...new Set([...existing, ...patterns])];\n config.exclude = merged;\n\n const dir = dirname(replayYmlPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(replayYmlPath, stringify(config, { lineWidth: 0 }), \"utf-8\");\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { FernignoreMigrator, type MigrationAnalysis } from \"../FernignoreMigrator.js\";\nimport { isGenerationCommit, isReplayCommit } from \"../git/CommitDetection.js\";\nimport { GitClient } from \"../git/GitClient.js\";\nimport { LockfileManager } from \"../LockfileManager.js\";\nimport type { CommitInfo, StoredPatch } from \"../types.js\";\n\nexport interface BootstrapOptions {\n /** Log what would happen but don't modify anything */\n dryRun?: boolean;\n /** How to handle .fernignore: \"migrate\" moves patterns to replay.yml, \"delete\" removes it, \"skip\" leaves it */\n fernignoreAction?: \"migrate\" | \"delete\" | \"skip\";\n /** Maximum number of commits to scan for generation history (default: 500) */\n maxCommitsToScan?: number;\n /** Allow overwriting an existing lockfile */\n force?: boolean;\n /** When true, scan git history for existing patches. Default: false (clean start with 0 patches). */\n importHistory?: boolean;\n}\n\nexport interface BootstrapResult {\n /** The generation commit used as baseline, or null if none found */\n generationCommit: CommitInfo | null;\n /** Number of customization patches detected */\n patchesDetected: number;\n /** Number of patches written to lockfile */\n patchesCreated: number;\n /** The detected patches (for dry-run inspection) */\n patches: StoredPatch[];\n /** .fernignore patterns found, if any */\n fernignorePatterns: string[];\n /** .fernignore migration analysis, if .fernignore exists */\n fernignoreAnalysis?: MigrationAnalysis;\n /** Whether .fernignore was updated to protect replay files */\n fernignoreUpdated: boolean;\n /** Warnings about edge cases */\n warnings: string[];\n /** Number of older generation commits that were skipped (stale commits ignored) */\n staleGenerationsSkipped: number;\n /** The generation commit SHA used as cutoff for patch detection */\n scannedSinceGeneration: string;\n}\n\n/**\n * Bootstrap Replay for an existing SDK repository.\n *\n * Scans git history to find all generation commits, identifies\n * user customization patches between them, and creates the initial replay.lock.\n *\n * This is a one-time migration step for SDKs that existed before Replay.\n */\nexport async function bootstrap(outputDir: string, options?: BootstrapOptions): Promise<BootstrapResult> {\n const git = new GitClient(outputDir);\n const lockManager = new LockfileManager(outputDir);\n const maxCommits = options?.maxCommitsToScan ?? 500;\n const warnings: string[] = [];\n\n // Check for existing lockfile\n if (lockManager.exists() && !options?.force) {\n return {\n generationCommit: null,\n patchesDetected: 0,\n patchesCreated: 0,\n patches: [],\n fernignorePatterns: [],\n fernignoreUpdated: false,\n warnings: [\"Replay lockfile already exists. Use --force to overwrite.\"],\n staleGenerationsSkipped: 0,\n scannedSinceGeneration: \"\"\n };\n }\n\n // 1. Find ALL generation commits (newest first, excluding replay commits)\n const genCommits = await findAllGenerationCommits(git, maxCommits);\n\n if (genCommits.length === 0) {\n return {\n generationCommit: null,\n patchesDetected: 0,\n patchesCreated: 0,\n patches: [],\n fernignorePatterns: [],\n fernignoreUpdated: false,\n warnings: [\n \"No generation commits found in the last \" +\n maxCommits +\n \" commits. \" +\n \"Run 'fern generate' first to establish a baseline.\"\n ],\n staleGenerationsSkipped: 0,\n scannedSinceGeneration: \"\"\n };\n }\n\n const latestGen = genCommits[0]!;\n\n // 2. Set up in-memory lockfile (don't write to disk yet — we may be in dry-run mode)\n // For clean start (no importHistory), use HEAD as current_generation so that\n // the next fern generate detects 0 old commits. When importing history, use\n // the actual generation commit as the base for patch detection.\n const anchorSha = options?.importHistory ? latestGen.sha : (await git.exec([\"rev-parse\", \"HEAD\"])).trim();\n const treeHash = await git.getTreeHash(anchorSha);\n const genRecord = {\n commit_sha: anchorSha,\n tree_hash: treeHash,\n timestamp: new Date().toISOString(),\n cli_version: \"unknown\",\n generator_versions: {} as Record<string, string>\n };\n\n lockManager.initializeInMemory(genRecord);\n\n // 3. Scan for user patches (only when importing history)\n const patches: StoredPatch[] = options?.importHistory ? await findAllUserPatches(git, genCommits) : [];\n\n // 4. Handle .fernignore if present (only when importing history)\n const migrator = new FernignoreMigrator(git, lockManager, outputDir);\n const fernignorePatterns = migrator.readFernignorePatterns();\n let fernignoreAnalysis: MigrationAnalysis | undefined;\n\n if (options?.importHistory && migrator.fernignoreExists() && fernignorePatterns.length > 0) {\n fernignoreAnalysis = await migrator.analyzeMigration();\n\n // Add synthetic patches from .fernignore analysis\n if (fernignoreAnalysis.syntheticPatches.length > 0) {\n patches.push(...fernignoreAnalysis.syntheticPatches);\n }\n\n if (fernignoreAnalysis.fernignoreOnly.length > 0) {\n for (const file of fernignoreAnalysis.fernignoreOnly) {\n warnings.push(\n `${file}: in .fernignore but no recent commits found since last generation. ` +\n \"File may be stale, matches generated output, or does not exist. No customization to track.\"\n );\n }\n }\n }\n\n // In dry-run mode, don't persist anything\n if (options?.dryRun) {\n return {\n generationCommit: latestGen,\n patchesDetected: patches.length,\n patchesCreated: 0,\n patches,\n fernignorePatterns,\n fernignoreAnalysis,\n fernignoreUpdated: false,\n warnings,\n staleGenerationsSkipped: genCommits.length - 1,\n scannedSinceGeneration: latestGen.sha\n };\n }\n\n // 5. Persist patches to lockfile\n for (const patch of patches) {\n lockManager.addPatch(patch);\n }\n lockManager.save();\n\n // 6. Ensure .fernignore protects replay files from generation wipe\n const fernignoreUpdated = ensureFernignoreEntries(outputDir);\n\n // 7. Handle .fernignore migration\n if (migrator.fernignoreExists() && fernignorePatterns.length > 0) {\n const action = options?.fernignoreAction ?? \"skip\";\n if (action === \"migrate\") {\n // Move truly untrackable patterns (no diff from generated) to replay.yml exclude list\n const patternsToExclude = fernignoreAnalysis?.fernignoreOnly ?? [];\n if (patternsToExclude.length > 0) {\n migrator.movePatternsToReplayYml(patternsToExclude);\n }\n }\n }\n\n return {\n generationCommit: latestGen,\n patchesDetected: patches.length,\n patchesCreated: patches.length,\n patches,\n fernignorePatterns,\n fernignoreAnalysis,\n fernignoreUpdated,\n warnings,\n staleGenerationsSkipped: genCommits.length - 1,\n scannedSinceGeneration: latestGen.sha\n };\n}\n\nasync function findAllGenerationCommits(git: GitClient, maxCommits: number): Promise<CommitInfo[]> {\n const log = await git.exec([\"log\", \"--format=%H%x00%an%x00%ae%x00%s\", `-${maxCommits}`]);\n\n if (!log.trim()) {\n return [];\n }\n\n const genCommits: CommitInfo[] = [];\n for (const line of log.trim().split(\"\\n\")) {\n if (!line) continue;\n const [sha, authorName, authorEmail, message] = line.split(\"\\0\");\n const commit: CommitInfo = { sha, authorName, authorEmail, message };\n if (isGenerationCommit(commit) && !isReplayCommit(commit)) {\n genCommits.push(commit);\n }\n }\n\n return genCommits;\n}\n\nasync function findAllUserPatches(\n git: GitClient,\n genCommits: CommitInfo[] // newest first\n): Promise<StoredPatch[]> {\n const patches: StoredPatch[] = [];\n const seenHashes = new Set<string>();\n\n // Only scan since the LAST (most recent) generation commit to HEAD\n // This ignores \"stale\" commits that were regenerated over before the customer activated replay\n const latestGen = genCommits[0]!;\n const recentPatches = await extractUserPatches(git, latestGen.sha, \"HEAD\", latestGen.sha, seenHashes);\n patches.push(...recentPatches);\n\n return patches;\n}\n\nasync function extractUserPatches(\n git: GitClient,\n fromSha: string,\n toRef: string,\n baseGeneration: string,\n seenHashes: Set<string>\n): Promise<StoredPatch[]> {\n const log = await git.exec([\"log\", \"--format=%H%x00%an%x00%ae%x00%s\", `${fromSha}..${toRef}`]);\n\n if (!log.trim()) {\n return [];\n }\n\n const commits = parseGitLog(log);\n const patches: StoredPatch[] = [];\n\n // Process in reverse (oldest first) for chronological order\n for (const commit of commits.reverse()) {\n if (isGenerationCommit(commit)) continue;\n\n const parents = await git.getCommitParents(commit.sha);\n if (parents.length > 1) continue;\n\n const patchContent = await git.formatPatch(commit.sha);\n const contentHash = computeContentHash(patchContent);\n\n if (seenHashes.has(contentHash)) continue;\n seenHashes.add(contentHash);\n\n const filesOutput = await git.exec([\"diff-tree\", \"--no-commit-id\", \"--name-only\", \"-r\", commit.sha]);\n\n patches.push({\n id: `patch-${commit.sha.slice(0, 8)}`,\n content_hash: contentHash,\n original_commit: commit.sha,\n original_message: commit.message,\n original_author: `${commit.authorName} <${commit.authorEmail}>`,\n base_generation: baseGeneration,\n files: filesOutput.trim().split(\"\\n\").filter(Boolean),\n patch_content: patchContent\n });\n }\n\n return patches;\n}\n\nfunction parseGitLog(log: string): CommitInfo[] {\n return log\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .map((line) => {\n const [sha, authorName, authorEmail, message] = line.split(\"\\0\");\n return { sha, authorName, authorEmail, message };\n });\n}\n\nconst REPLAY_FERNIGNORE_ENTRIES = [\".fern/replay.lock\", \".fern/replay.yml\"];\n\nfunction ensureFernignoreEntries(outputDir: string): boolean {\n const fernignorePath = join(outputDir, \".fernignore\");\n let content = \"\";\n\n if (existsSync(fernignorePath)) {\n content = readFileSync(fernignorePath, \"utf-8\");\n }\n\n const lines = content.split(\"\\n\");\n const toAdd: string[] = [];\n\n for (const entry of REPLAY_FERNIGNORE_ENTRIES) {\n if (!lines.some((line) => line.trim() === entry)) {\n toAdd.push(entry);\n }\n }\n\n if (toAdd.length === 0) {\n return false;\n }\n\n if (content && !content.endsWith(\"\\n\")) {\n content += \"\\n\";\n }\n content += toAdd.join(\"\\n\") + \"\\n\";\n writeFileSync(fernignorePath, content, \"utf-8\");\n return true;\n}\n\nfunction computeContentHash(patchContent: string): string {\n const normalized = patchContent\n .split(\"\\n\")\n .filter((line) => !line.startsWith(\"From \") && !line.startsWith(\"index \") && !line.startsWith(\"Date: \"))\n .join(\"\\n\");\n\n return `sha256:${createHash(\"sha256\").update(normalized).digest(\"hex\")}`;\n}\n","import { minimatch } from \"minimatch\";\nimport { LockfileManager } from \"../LockfileManager.js\";\n\nexport interface ForgetOptions {\n /** Don't actually remove, just show what would be removed */\n dryRun?: boolean;\n}\n\nexport interface ForgetResult {\n /** Patches that were (or would be) removed */\n removed: Array<{ id: string; message: string; files: string[] }>;\n /** True if no patches matched the pattern */\n notFound: boolean;\n}\n\nexport function forget(outputDir: string, filePattern: string, options?: ForgetOptions): ForgetResult {\n const lockManager = new LockfileManager(outputDir);\n\n if (!lockManager.exists()) {\n return { removed: [], notFound: true };\n }\n\n const lock = lockManager.read();\n\n // Find patches that match the pattern\n const matchingPatches = lock.patches.filter((patch) =>\n patch.files.some((file) => file === filePattern || minimatch(file, filePattern))\n );\n\n if (matchingPatches.length === 0) {\n return { removed: [], notFound: true };\n }\n\n const removed = matchingPatches.map((p) => ({\n id: p.id,\n message: p.original_message,\n files: p.files\n }));\n\n if (!options?.dryRun) {\n for (const patch of matchingPatches) {\n lockManager.removePatch(patch.id);\n }\n lockManager.save();\n }\n\n return { removed, notFound: false };\n}\n","import { unlinkSync } from \"node:fs\";\nimport { LockfileManager } from \"../LockfileManager.js\";\n\nexport interface ResetOptions {\n /** Don't actually delete, just show what would happen */\n dryRun?: boolean;\n}\n\nexport interface ResetResult {\n /** Whether reset was successful (or would be) */\n success: boolean;\n /** Number of patches that were (or would be) removed */\n patchesRemoved: number;\n /** Whether the lockfile was (or would be) deleted */\n lockfileDeleted: boolean;\n /** True if there was nothing to reset */\n nothingToReset: boolean;\n}\n\nexport function reset(outputDir: string, options?: ResetOptions): ResetResult {\n const lockManager = new LockfileManager(outputDir);\n\n if (!lockManager.exists()) {\n return {\n success: true,\n patchesRemoved: 0,\n lockfileDeleted: false,\n nothingToReset: true\n };\n }\n\n const lock = lockManager.read();\n const patchCount = lock.patches.length;\n\n if (!options?.dryRun) {\n unlinkSync(lockManager.lockfilePath);\n }\n\n return {\n success: true,\n patchesRemoved: patchCount,\n lockfileDeleted: true,\n nothingToReset: false\n };\n}\n","import { GitClient } from \"../git/GitClient.js\";\nimport { LockfileManager } from \"../LockfileManager.js\";\nimport { ReplayCommitter } from \"../ReplayCommitter.js\";\n\nexport interface ResolveOptions {\n /** Check for remaining conflict markers before committing. Default: true */\n checkMarkers?: boolean;\n}\n\nexport interface ResolveResult {\n /** Whether the resolve succeeded */\n success: boolean;\n /** Commit SHA of the [fern-replay] commit, if created */\n commitSha?: string;\n /** Reason for failure */\n reason?: string;\n /** Files that still have conflict markers */\n unresolvedFiles?: string[];\n}\n\nexport async function resolve(outputDir: string, options?: ResolveOptions): Promise<ResolveResult> {\n const lockManager = new LockfileManager(outputDir);\n\n if (!lockManager.exists()) {\n return { success: false, reason: \"no-lockfile\" };\n }\n\n const lock = lockManager.read();\n if (lock.patches.length === 0) {\n return { success: false, reason: \"no-patches\" };\n }\n\n const git = new GitClient(outputDir);\n\n // Check for remaining conflict markers\n if (options?.checkMarkers !== false) {\n const markerFiles = await git.exec([\"grep\", \"-l\", \"<<<<<<<\", \"--\", \".\"]).catch(() => \"\");\n if (markerFiles.trim()) {\n const files = markerFiles.trim().split(\"\\n\").filter(Boolean);\n return { success: false, reason: \"unresolved-conflicts\", unresolvedFiles: files };\n }\n }\n\n // Stage all changes and create [fern-replay] commit\n const committer = new ReplayCommitter(git, outputDir);\n await committer.stageAll();\n const commitSha = await committer.commitReplay(lock.patches.length, lock.patches);\n\n return { success: true, commitSha };\n}\n","import { LockfileManager } from \"../LockfileManager.js\";\n\nexport interface StatusResult {\n /** Whether replay is initialized (lockfile exists) */\n initialized: boolean;\n /** Tracked customization patches */\n patches: StatusPatch[];\n /** Last generation info, if available */\n lastGeneration: StatusGeneration | undefined;\n}\n\nexport interface StatusPatch {\n /** Short SHA of the original commit */\n sha: string;\n /** Author name (without email) */\n author: string;\n /** Original commit message */\n message: string;\n /** Files touched by this patch */\n files: string[];\n}\n\nexport interface StatusGeneration {\n /** Full commit SHA */\n sha: string;\n /** Generation timestamp */\n timestamp: string;\n}\n\nexport function status(outputDir: string): StatusResult {\n const lockManager = new LockfileManager(outputDir);\n\n if (!lockManager.exists()) {\n return { initialized: false, patches: [], lastGeneration: undefined };\n }\n\n const lock = lockManager.read();\n\n const patches: StatusPatch[] = lock.patches.map((patch) => ({\n sha: patch.original_commit.slice(0, 7),\n author: patch.original_author.split(\"<\")[0]?.trim() ?? \"unknown\",\n message: patch.original_message,\n files: patch.files\n }));\n\n let lastGeneration: StatusGeneration | undefined;\n const lastGen = lock.generations.find((g) => g.commit_sha === lock.current_generation);\n if (lastGen) {\n lastGeneration = {\n sha: lastGen.commit_sha,\n timestamp: lastGen.timestamp\n };\n }\n\n return { initialized: true, patches, lastGeneration };\n}\n"],"mappings":"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","names":["resolve","dirname","join","join","GitClient","outDir","dirname","merged","existsSync","readFileSync","join","minimatch","existingPatches","newPatches","warnings","allPatches","minimatch","join","existsSync","readFileSync","createHash","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","join","minimatch","parse","stringify","existsSync","join","readFileSync","minimatch","createHash","parse","dirname","mkdirSync","writeFileSync","stringify","createHash","existsSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","writeFileSync","createHash","minimatch","minimatch"]}
package/package.json CHANGED
@@ -1,22 +1,31 @@
1
1
  {
2
2
  "name": "@fern-api/replay",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Automatically preserves SDK customizations across Fern regenerations",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
8
- "import": "./dist/index.js",
9
- "types": "./dist/index.d.ts"
8
+ "import": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "require": {
13
+ "types": "./dist/index.d.cts",
14
+ "default": "./dist/index.cjs"
15
+ }
10
16
  }
11
17
  },
18
+ "main": "./dist/index.cjs",
19
+ "module": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
12
21
  "bin": {
13
- "fern-replay": "./dist/cli.js"
22
+ "fern-replay": "./dist/cli.cjs"
14
23
  },
15
24
  "files": [
16
25
  "dist"
17
26
  ],
18
27
  "scripts": {
19
- "build": "tsc",
28
+ "build": "node build.mjs",
20
29
  "dev": "npx tsx src/cli.ts",
21
30
  "test": "vitest run",
22
31
  "test:watch": "vitest",
@@ -30,6 +39,7 @@
30
39
  },
31
40
  "devDependencies": {
32
41
  "@types/node": "^20.0.0",
42
+ "tsup": "^8.5.1",
33
43
  "tsx": "^4.21.0",
34
44
  "typescript": "^5.5.0",
35
45
  "vitest": "^2.0.0"
@@ -40,6 +50,6 @@
40
50
  "license": "MIT",
41
51
  "repository": {
42
52
  "type": "git",
43
- "url": "https://github.com/fern-api/fern-replay.git"
53
+ "url": "git+https://github.com/fern-api/fern-replay.git"
44
54
  }
45
55
  }
@@ -1,38 +0,0 @@
1
- import type { GitClient } from "./git/GitClient.js";
2
- import type { LockfileManager } from "./LockfileManager.js";
3
- import type { StoredPatch } from "./types.js";
4
- export interface MigrationAnalysis {
5
- /** Files in .fernignore that have git commit history (can be tracked by Replay) */
6
- trackedByBoth: Array<{
7
- file: string;
8
- commit: string;
9
- }>;
10
- /** Files in .fernignore but NO recent commit history found after last generation */
11
- fernignoreOnly: string[];
12
- /** Commits found that AREN'T in .fernignore (inline edits to generated files) */
13
- commitsOnly: StoredPatch[];
14
- /** Synthetic patches created for .fernignore files that differ from pristine generation */
15
- syntheticPatches: StoredPatch[];
16
- }
17
- export interface MigrationResult {
18
- patchesCreated: number;
19
- filesSkipped: string[];
20
- warnings: string[];
21
- }
22
- export declare class FernignoreMigrator {
23
- private git;
24
- private lockManager;
25
- private outputDir;
26
- constructor(git: GitClient, lockManager: LockfileManager, outputDir: string);
27
- fernignoreExists(): boolean;
28
- readFernignorePatterns(): string[];
29
- /** Analyze .fernignore patterns vs git history. Creates synthetic patches for files differing from pristine generation. */
30
- analyzeMigration(): Promise<MigrationAnalysis>;
31
- private resolvePatterns;
32
- private readFileContent;
33
- private createNewFileDiff;
34
- private createFileDiff;
35
- migrate(): Promise<MigrationResult>;
36
- movePatternsToReplayYml(patterns: string[]): void;
37
- }
38
- //# sourceMappingURL=FernignoreMigrator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FernignoreMigrator.d.ts","sourceRoot":"","sources":["../src/FernignoreMigrator.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,KAAK,EAAwB,WAAW,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,WAAW,iBAAiB;IAC9B,mFAAmF;IACnF,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,oFAAoF;IACpF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,iFAAiF;IACjF,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,2FAA2F;IAC3F,gBAAgB,EAAE,WAAW,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,SAAS,CAAS;gBAEd,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM;IAM3E,gBAAgB,IAAI,OAAO;IAI3B,sBAAsB,IAAI,MAAM,EAAE;IAYlC,2HAA2H;IACrH,gBAAgB,IAAI,OAAO,CAAC,iBAAiB,CAAC;YAkGtC,eAAe;IAc7B,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,cAAc;IAgBhB,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;IAgCzC,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;CAmBpD"}