@fern-api/replay 0.16.1 → 0.16.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.
package/dist/index.cjs CHANGED
@@ -801,6 +801,7 @@ var LockfileManager = class {
801
801
 
802
802
  // src/ReplayDetector.ts
803
803
  var import_node_crypto = require("crypto");
804
+ var import_minimatch2 = require("minimatch");
804
805
 
805
806
  // src/shared/binary.ts
806
807
  var import_node_path2 = require("path");
@@ -860,6 +861,15 @@ function isBinaryFile(filePath) {
860
861
 
861
862
  // src/ReplayDetector.ts
862
863
  var INFRASTRUCTURE_FILES = /* @__PURE__ */ new Set([".fernignore"]);
864
+ function matchesFernignorePattern(filePath, patterns) {
865
+ if (patterns.length === 0) return false;
866
+ return patterns.some((p) => {
867
+ if (filePath === p) return true;
868
+ if ((0, import_minimatch2.minimatch)(filePath, p)) return true;
869
+ if (!p.includes("*") && !p.includes("?") && filePath.startsWith(p + "/")) return true;
870
+ return false;
871
+ });
872
+ }
863
873
  function parsePatchFileHeaders(patchContent) {
864
874
  const results = [];
865
875
  let currentPath = null;
@@ -900,21 +910,25 @@ async function capturePatchSnapshot(git, files, treeish) {
900
910
  }
901
911
  return snapshot;
902
912
  }
903
- function filterInfrastructureAndSensitiveFiles(rawFilesOutput) {
913
+ function filterInfrastructureAndSensitiveFiles(rawFilesOutput, fernignorePatterns) {
904
914
  const allFiles = rawFilesOutput.trim().split("\n").filter(Boolean).filter((f) => !INFRASTRUCTURE_FILES.has(f)).filter((f) => !f.startsWith(".fern/"));
905
915
  const sensitiveFiles = allFiles.filter((f) => isSensitiveFile(f));
906
- const files = allFiles.filter((f) => !isSensitiveFile(f));
907
- return { files, sensitiveFiles };
916
+ const nonSensitive = allFiles.filter((f) => !isSensitiveFile(f));
917
+ const fernignoredFiles = nonSensitive.filter((f) => matchesFernignorePattern(f, fernignorePatterns));
918
+ const files = nonSensitive.filter((f) => !matchesFernignorePattern(f, fernignorePatterns));
919
+ return { files, sensitiveFiles, fernignoredFiles };
908
920
  }
909
921
  var ReplayDetector = class {
910
922
  git;
911
923
  lockManager;
912
924
  sdkOutputDir;
925
+ fernignorePatterns;
913
926
  warnings = [];
914
- constructor(git, lockManager, sdkOutputDir) {
927
+ constructor(git, lockManager, sdkOutputDir, fernignorePatterns = []) {
915
928
  this.git = git;
916
929
  this.lockManager = lockManager;
917
930
  this.sdkOutputDir = sdkOutputDir;
931
+ this.fernignorePatterns = fernignorePatterns;
918
932
  }
919
933
  /**
920
934
  * Derive the previous-generation SHA from git history instead of trusting
@@ -1021,22 +1035,30 @@ var ReplayDetector = class {
1021
1035
  continue;
1022
1036
  }
1023
1037
  const filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha]);
1024
- const { files, sensitiveFiles } = filterInfrastructureAndSensitiveFiles(filesOutput);
1038
+ const { files, sensitiveFiles, fernignoredFiles } = filterInfrastructureAndSensitiveFiles(
1039
+ filesOutput,
1040
+ this.fernignorePatterns
1041
+ );
1025
1042
  if (sensitiveFiles.length > 0) {
1026
1043
  this.warnings.push(
1027
1044
  `Sensitive file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${sensitiveFiles.join(", ")}. These files will not be tracked by replay.`
1028
1045
  );
1029
- if (files.length > 0) {
1030
- const parentSha = parents[0];
1031
- if (parentSha) {
1032
- try {
1033
- patchContent = await this.git.exec(["diff", parentSha, commit.sha, "--", ...files]);
1034
- } catch {
1035
- continue;
1036
- }
1037
- if (!patchContent.trim()) continue;
1038
- contentHash = this.computeContentHash(patchContent);
1046
+ }
1047
+ if (fernignoredFiles.length > 0) {
1048
+ this.warnings.push(
1049
+ `.fernignore-protected file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${fernignoredFiles.join(", ")}. These files are managed by .fernignore, not Replay.`
1050
+ );
1051
+ }
1052
+ if ((sensitiveFiles.length > 0 || fernignoredFiles.length > 0) && files.length > 0) {
1053
+ const parentSha = parents[0];
1054
+ if (parentSha) {
1055
+ try {
1056
+ patchContent = await this.git.exec(["diff", parentSha, commit.sha, "--", ...files]);
1057
+ } catch {
1058
+ continue;
1039
1059
  }
1060
+ if (!patchContent.trim()) continue;
1061
+ contentHash = this.computeContentHash(patchContent);
1040
1062
  }
1041
1063
  }
1042
1064
  if (files.length === 0) {
@@ -1235,12 +1257,20 @@ var ReplayDetector = class {
1235
1257
  resolvedBaseGeneration = fallback;
1236
1258
  }
1237
1259
  const filesOutput = await this.git.exec(["diff", "--name-only", diffBase, "HEAD"]);
1238
- const { files, sensitiveFiles } = filterInfrastructureAndSensitiveFiles(filesOutput);
1260
+ const { files, sensitiveFiles, fernignoredFiles } = filterInfrastructureAndSensitiveFiles(
1261
+ filesOutput,
1262
+ this.fernignorePatterns
1263
+ );
1239
1264
  if (sensitiveFiles.length > 0) {
1240
1265
  this.warnings.push(
1241
1266
  `Sensitive file(s) excluded from composite patch: ${sensitiveFiles.join(", ")}. These files will not be tracked by replay.`
1242
1267
  );
1243
1268
  }
1269
+ if (fernignoredFiles.length > 0) {
1270
+ this.warnings.push(
1271
+ `.fernignore-protected file(s) excluded from composite patch: ${fernignoredFiles.join(", ")}. These files are managed by .fernignore, not Replay.`
1272
+ );
1273
+ }
1244
1274
  if (files.length === 0) return { patches: [], revertedPatchIds: [] };
1245
1275
  const diff = await this.git.exec(["diff", diffBase, "HEAD", "--", ...files]);
1246
1276
  if (!diff.trim()) return { patches: [], revertedPatchIds: [] };
@@ -1323,23 +1353,31 @@ var ReplayDetector = class {
1323
1353
  continue;
1324
1354
  }
1325
1355
  const filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha]);
1326
- const { files, sensitiveFiles } = filterInfrastructureAndSensitiveFiles(filesOutput);
1356
+ const { files, sensitiveFiles, fernignoredFiles } = filterInfrastructureAndSensitiveFiles(
1357
+ filesOutput,
1358
+ this.fernignorePatterns
1359
+ );
1327
1360
  if (sensitiveFiles.length > 0) {
1328
1361
  this.warnings.push(
1329
1362
  `Sensitive file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${sensitiveFiles.join(", ")}. These files will not be tracked by replay.`
1330
1363
  );
1331
- if (files.length > 0) {
1332
- if (parents.length === 0) {
1333
- continue;
1334
- }
1335
- try {
1336
- patchContent = await this.git.exec(["diff", parents[0], commit.sha, "--", ...files]);
1337
- } catch {
1338
- continue;
1339
- }
1340
- if (!patchContent.trim()) continue;
1341
- contentHash = this.computeContentHash(patchContent);
1364
+ }
1365
+ if (fernignoredFiles.length > 0) {
1366
+ this.warnings.push(
1367
+ `.fernignore-protected file(s) excluded from patch for commit ${commit.sha.slice(0, 7)}: ${fernignoredFiles.join(", ")}. These files are managed by .fernignore, not Replay.`
1368
+ );
1369
+ }
1370
+ if ((sensitiveFiles.length > 0 || fernignoredFiles.length > 0) && files.length > 0) {
1371
+ if (parents.length === 0) {
1372
+ continue;
1342
1373
  }
1374
+ try {
1375
+ patchContent = await this.git.exec(["diff", parents[0], commit.sha, "--", ...files]);
1376
+ } catch {
1377
+ continue;
1378
+ }
1379
+ if (!patchContent.trim()) continue;
1380
+ contentHash = this.computeContentHash(patchContent);
1343
1381
  }
1344
1382
  if (files.length === 0) {
1345
1383
  continue;
@@ -1575,7 +1613,7 @@ function applyBothPatches(baseLines, oursPatches, theirsPatches) {
1575
1613
  var import_promises3 = require("fs/promises");
1576
1614
  var import_node_os = require("os");
1577
1615
  var import_node_path5 = require("path");
1578
- var import_minimatch2 = require("minimatch");
1616
+ var import_minimatch3 = require("minimatch");
1579
1617
 
1580
1618
  // src/accumulator/MergeAccumulator.ts
1581
1619
  var MergeAccumulator = class {
@@ -2291,13 +2329,13 @@ var ReplayApplicator = class {
2291
2329
  isExcluded(patch) {
2292
2330
  const config = this.lockManager.getCustomizationsConfig();
2293
2331
  if (!config.exclude) return false;
2294
- return patch.files.some((file) => config.exclude.some((pattern) => (0, import_minimatch2.minimatch)(file, pattern)));
2332
+ return patch.files.some((file) => config.exclude.some((pattern) => (0, import_minimatch3.minimatch)(file, pattern)));
2295
2333
  }
2296
2334
  async resolveFilePath(filePath, baseTreeHash, currentTreeHash) {
2297
2335
  const config = this.lockManager.getCustomizationsConfig();
2298
2336
  if (config.moves) {
2299
2337
  for (const move of config.moves) {
2300
- if ((0, import_minimatch2.minimatch)(filePath, move.from) || filePath === move.from) {
2338
+ if ((0, import_minimatch3.minimatch)(filePath, move.from) || filePath === move.from) {
2301
2339
  if (filePath === move.from) {
2302
2340
  return move.to;
2303
2341
  }
@@ -2588,7 +2626,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
2588
2626
  var import_node_fs2 = require("fs");
2589
2627
  var import_promises6 = require("fs/promises");
2590
2628
  var import_node_path8 = require("path");
2591
- var import_minimatch4 = require("minimatch");
2629
+ var import_minimatch5 = require("minimatch");
2592
2630
  init_GitClient();
2593
2631
 
2594
2632
  // src/PatchRegionDiff.ts
@@ -2895,7 +2933,7 @@ function synthesizeDeleteFileDiff(file, content) {
2895
2933
  }
2896
2934
 
2897
2935
  // src/classifier/FileOwnership.ts
2898
- var import_minimatch3 = require("minimatch");
2936
+ var import_minimatch4 = require("minimatch");
2899
2937
  var FileOwnership = class {
2900
2938
  constructor(git) {
2901
2939
  this.git = git;
@@ -2919,7 +2957,7 @@ var FileOwnership = class {
2919
2957
  const { file, originalFileName, patch, currentGenSha, fernignorePatterns, warnedGens, warnings } = ctx;
2920
2958
  if (patch.user_owned) return true;
2921
2959
  if (file === ".fernignore") return true;
2922
- if (fernignorePatterns.some((p) => file === p || (0, import_minimatch3.minimatch)(file, p))) return true;
2960
+ if (fernignorePatterns.some((p) => file === p || (0, import_minimatch4.minimatch)(file, p))) return true;
2923
2961
  if (fileIsCreationInPatchContent(patch.patch_content, originalFileName ?? file)) {
2924
2962
  return true;
2925
2963
  }
@@ -3425,7 +3463,8 @@ var ReplayService = class {
3425
3463
  this.git = git;
3426
3464
  this.outputDir = outputDir;
3427
3465
  this.lockManager = new LockfileManager(outputDir);
3428
- this.detector = new ReplayDetector(git, this.lockManager, outputDir);
3466
+ const fernignorePatterns = this.readFernignorePatterns();
3467
+ this.detector = new ReplayDetector(git, this.lockManager, outputDir, fernignorePatterns);
3429
3468
  this.applicator = new ReplayApplicator(git, this.lockManager, outputDir);
3430
3469
  this.committer = new ReplayCommitter(git, outputDir);
3431
3470
  this.fileOwnership = new FileOwnership(git);
@@ -3445,8 +3484,60 @@ var ReplayService = class {
3445
3484
  async prepareReplay(options) {
3446
3485
  this.warnings = [];
3447
3486
  this.detector.warnings.length = 0;
3487
+ this.cleanseLegacyFernignoreEntries();
3448
3488
  return this.selectFlow(options).prepare(options);
3449
3489
  }
3490
+ /**
3491
+ * ADR 0002 migration step. Strips `.fernignore`-matched entries from
3492
+ * `patch.files`, `patch.theirs_snapshot` keys, and `diff --git` sections
3493
+ * in `patch.patch_content` for every patch in the lockfile. Patches that
3494
+ * end up with no surviving files are removed entirely.
3495
+ *
3496
+ * Idempotent: a second run is a no-op because the first run already
3497
+ * matches the contract. Per-patch try/catch ensures one bad patch does
3498
+ * not block the rest. Each affected patch emits a warning naming the
3499
+ * stripped paths. `patch_content` is left untouched if no parseable
3500
+ * `diff --git` headers are found (fail-safe for exotic legacy formats).
3501
+ */
3502
+ cleanseLegacyFernignoreEntries() {
3503
+ const patterns = this.readFernignorePatterns();
3504
+ if (patterns.length === 0) return;
3505
+ if (!this.lockManager.exists()) return;
3506
+ this.lockManager.read();
3507
+ let lockfileMutated = false;
3508
+ const patches = this.lockManager.getPatches();
3509
+ for (const patch of patches) {
3510
+ try {
3511
+ const result = computeLegacyFernignoreCleanse(
3512
+ patch,
3513
+ patterns,
3514
+ (content) => this.detector.computeContentHash(content)
3515
+ );
3516
+ if (result.unchanged) continue;
3517
+ if (result.remove) {
3518
+ this.lockManager.removePatch(patch.id);
3519
+ this.warnings.push(
3520
+ `Cleansed legacy patch ${patch.id}: all referenced files match .fernignore \u2014 patch removed. Customer's on-disk content for those files is preserved by .fernignore. (ADR 0002: .fernignore-protected files are not tracked by Replay.)`
3521
+ );
3522
+ } else {
3523
+ this.lockManager.updatePatch(patch.id, {
3524
+ files: result.files,
3525
+ patch_content: result.patch_content,
3526
+ content_hash: result.content_hash,
3527
+ ...result.theirs_snapshot !== void 0 ? { theirs_snapshot: result.theirs_snapshot } : {}
3528
+ });
3529
+ this.warnings.push(
3530
+ `Cleansed legacy patch ${patch.id}: stripped .fernignore-matched entries (${result.strippedPaths.join(", ")}). Customer's on-disk content for those files is preserved by .fernignore. (ADR 0002.)`
3531
+ );
3532
+ }
3533
+ lockfileMutated = true;
3534
+ } catch {
3535
+ }
3536
+ }
3537
+ if (lockfileMutated) {
3538
+ this.lockManager.save();
3539
+ }
3540
+ }
3450
3541
  /**
3451
3542
  * Phase 2 of the two-phase replay flow. Applies patches, rebases them against
3452
3543
  * the generation, saves the lockfile, and commits `[fern-replay]` (or stages
@@ -3578,7 +3669,7 @@ var ReplayService = class {
3578
3669
  )
3579
3670
  );
3580
3671
  const hasProtectedFiles = patch.files.some(
3581
- (file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p || (0, import_minimatch4.minimatch)(file, p))
3672
+ (file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p || (0, import_minimatch5.minimatch)(file, p))
3582
3673
  );
3583
3674
  const allUserOwned = isUserOwnedPerFile.every(Boolean);
3584
3675
  if (allUserOwned && patch.files.length > 0) {
@@ -4018,7 +4109,7 @@ var ReplayService = class {
4018
4109
  );
4019
4110
  if (snapHasStaleMarkers) continue;
4020
4111
  const isProtectedSnap = patch.files.some(
4021
- (file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p || (0, import_minimatch4.minimatch)(file, p))
4112
+ (file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p || (0, import_minimatch5.minimatch)(file, p))
4022
4113
  );
4023
4114
  if (isProtectedSnap) continue;
4024
4115
  const snapHash = this.detector.computeContentHash(snapshotDiff);
@@ -4058,7 +4149,7 @@ var ReplayService = class {
4058
4149
  continue;
4059
4150
  }
4060
4151
  const isProtected = patch.files.some(
4061
- (file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p || (0, import_minimatch4.minimatch)(file, p))
4152
+ (file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p || (0, import_minimatch5.minimatch)(file, p))
4062
4153
  );
4063
4154
  if (isProtected) {
4064
4155
  continue;
@@ -4222,7 +4313,7 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
4222
4313
  if (files.length === 0) return;
4223
4314
  const fernignorePatterns = this.readFernignorePatterns();
4224
4315
  for (const file of files) {
4225
- if (fernignorePatterns.some((pattern) => (0, import_minimatch4.minimatch)(file, pattern))) continue;
4316
+ if (fernignorePatterns.some((pattern) => (0, import_minimatch5.minimatch)(file, pattern))) continue;
4226
4317
  try {
4227
4318
  await this.git.exec(["checkout", "HEAD", "--", file]);
4228
4319
  } catch {
@@ -4307,12 +4398,99 @@ function computeCommitBuckets(results, absorbedPatchIds, patchesPassedToCommit)
4307
4398
  }
4308
4399
  return { applied, unresolved, absorbed };
4309
4400
  }
4401
+ function computeLegacyFernignoreCleanse(patch, fernignorePatterns, computeContentHash2) {
4402
+ const matchesFernignore = (filePath) => fernignorePatterns.some((p) => {
4403
+ if (filePath === p) return true;
4404
+ if ((0, import_minimatch5.minimatch)(filePath, p)) return true;
4405
+ if (!p.includes("*") && !p.includes("?") && filePath.startsWith(p + "/")) return true;
4406
+ return false;
4407
+ });
4408
+ const strippedFromFiles = patch.files.filter(matchesFernignore);
4409
+ const survivingFiles = patch.files.filter((f) => !matchesFernignore(f));
4410
+ let survivingSnapshot = void 0;
4411
+ let snapshotChanged = false;
4412
+ if (patch.theirs_snapshot) {
4413
+ survivingSnapshot = {};
4414
+ for (const [key, value] of Object.entries(patch.theirs_snapshot)) {
4415
+ if (matchesFernignore(key)) {
4416
+ snapshotChanged = true;
4417
+ } else {
4418
+ survivingSnapshot[key] = value;
4419
+ }
4420
+ }
4421
+ }
4422
+ const { stripped: patchContentStripped, content: newPatchContent, strippedSectionPaths } = stripFernignoreDiffSections(patch.patch_content, matchesFernignore);
4423
+ const unchanged = strippedFromFiles.length === 0 && !snapshotChanged && !patchContentStripped;
4424
+ if (unchanged) {
4425
+ return {
4426
+ unchanged: true,
4427
+ remove: false,
4428
+ files: patch.files,
4429
+ patch_content: patch.patch_content,
4430
+ content_hash: patch.content_hash,
4431
+ theirs_snapshot: patch.theirs_snapshot,
4432
+ strippedPaths: []
4433
+ };
4434
+ }
4435
+ const strippedPaths = Array.from(/* @__PURE__ */ new Set([...strippedFromFiles, ...strippedSectionPaths]));
4436
+ if (survivingFiles.length === 0 || newPatchContent.trim() === "") {
4437
+ return {
4438
+ unchanged: false,
4439
+ remove: true,
4440
+ files: [],
4441
+ patch_content: "",
4442
+ content_hash: "",
4443
+ strippedPaths
4444
+ };
4445
+ }
4446
+ const newContentHash = patchContentStripped ? computeContentHash2(newPatchContent) : patch.content_hash;
4447
+ return {
4448
+ unchanged: false,
4449
+ remove: false,
4450
+ files: survivingFiles,
4451
+ patch_content: newPatchContent,
4452
+ content_hash: newContentHash,
4453
+ ...survivingSnapshot !== void 0 ? { theirs_snapshot: survivingSnapshot } : {},
4454
+ strippedPaths
4455
+ };
4456
+ }
4457
+ function stripFernignoreDiffSections(patchContent, matchesFernignore) {
4458
+ const lines = patchContent.split("\n");
4459
+ const sectionStarts = [];
4460
+ for (let i = 0; i < lines.length; i++) {
4461
+ const line = lines[i];
4462
+ if (line.startsWith("diff --git ")) {
4463
+ const match = line.match(/^diff --git a\/(.+?) b\/(.+?)$/);
4464
+ const path = match ? match[2] : null;
4465
+ sectionStarts.push({ idx: i, path });
4466
+ }
4467
+ }
4468
+ if (sectionStarts.length === 0) {
4469
+ return { stripped: false, content: patchContent, strippedSectionPaths: [] };
4470
+ }
4471
+ const preamble = lines.slice(0, sectionStarts[0].idx);
4472
+ const newLines = [...preamble];
4473
+ const strippedSectionPaths = [];
4474
+ let stripped = false;
4475
+ for (let i = 0; i < sectionStarts.length; i++) {
4476
+ const section = sectionStarts[i];
4477
+ const next = sectionStarts[i + 1];
4478
+ const endIdx = next ? next.idx : lines.length;
4479
+ if (section.path !== null && matchesFernignore(section.path)) {
4480
+ stripped = true;
4481
+ strippedSectionPaths.push(section.path);
4482
+ continue;
4483
+ }
4484
+ newLines.push(...lines.slice(section.idx, endIdx));
4485
+ }
4486
+ return { stripped, content: newLines.join("\n"), strippedSectionPaths };
4487
+ }
4310
4488
 
4311
4489
  // src/FernignoreMigrator.ts
4312
4490
  var import_node_crypto2 = require("crypto");
4313
4491
  var import_node_fs3 = require("fs");
4314
4492
  var import_node_path9 = require("path");
4315
- var import_minimatch5 = require("minimatch");
4493
+ var import_minimatch6 = require("minimatch");
4316
4494
  var import_yaml2 = require("yaml");
4317
4495
  var FernignoreMigrator = class {
4318
4496
  git;
@@ -4344,7 +4522,7 @@ var FernignoreMigrator = class {
4344
4522
  const commitsOnly = [];
4345
4523
  const syntheticPatches = [];
4346
4524
  for (const pattern of patterns) {
4347
- const matchingPatch = patches.find((p) => p.files.some((f) => (0, import_minimatch5.minimatch)(f, pattern) || f === pattern));
4525
+ const matchingPatch = patches.find((p) => p.files.some((f) => (0, import_minimatch6.minimatch)(f, pattern) || f === pattern));
4348
4526
  if (matchingPatch) {
4349
4527
  trackedByBoth.push({
4350
4528
  file: pattern,
@@ -4417,7 +4595,7 @@ var FernignoreMigrator = class {
4417
4595
  fernignoreOnly.push(...remainingFernignoreOnly);
4418
4596
  }
4419
4597
  for (const patch of patches) {
4420
- const hasUnprotectedFiles = patch.files.some((f) => !patterns.some((p) => (0, import_minimatch5.minimatch)(f, p) || f === p));
4598
+ const hasUnprotectedFiles = patch.files.some((f) => !patterns.some((p) => (0, import_minimatch6.minimatch)(f, p) || f === p));
4421
4599
  if (hasUnprotectedFiles) {
4422
4600
  commitsOnly.push(patch);
4423
4601
  }
@@ -4429,7 +4607,7 @@ var FernignoreMigrator = class {
4429
4607
  const results = [];
4430
4608
  for (const pattern of patterns) {
4431
4609
  const matching = allFiles.filter(
4432
- (f) => (0, import_minimatch5.minimatch)(f, pattern) || f === pattern || f.startsWith(pattern + "/")
4610
+ (f) => (0, import_minimatch6.minimatch)(f, pattern) || f === pattern || f.startsWith(pattern + "/")
4433
4611
  );
4434
4612
  results.push({ pattern, files: matching.length > 0 ? matching : [pattern] });
4435
4613
  }
@@ -4742,7 +4920,7 @@ function computeContentHash(patchContent) {
4742
4920
  }
4743
4921
 
4744
4922
  // src/commands/forget.ts
4745
- var import_minimatch6 = require("minimatch");
4923
+ var import_minimatch7 = require("minimatch");
4746
4924
  function parseDiffStat(patchContent) {
4747
4925
  let additions = 0;
4748
4926
  let deletions = 0;
@@ -4772,7 +4950,7 @@ function toMatchedPatch(patch) {
4772
4950
  }
4773
4951
  function matchesPatch(patch, pattern) {
4774
4952
  const fileMatch = patch.files.some(
4775
- (file) => file === pattern || (0, import_minimatch6.minimatch)(file, pattern)
4953
+ (file) => file === pattern || (0, import_minimatch7.minimatch)(file, pattern)
4776
4954
  );
4777
4955
  if (fileMatch) return true;
4778
4956
  return patch.original_message.toLowerCase().includes(pattern.toLowerCase());