draftify-cli 1.0.62 → 1.0.64

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 (2) hide show
  1. package/dist/repl.js +115 -6
  2. package/package.json +1 -1
package/dist/repl.js CHANGED
@@ -668,26 +668,135 @@ async function startRepl(initialUsername) {
668
668
  search = search.replace(/\r\n/g, '\n');
669
669
  replace = replace.replace(/\r\n/g, '\n');
670
670
  if (normalizedFile.includes(search)) {
671
+ // === STRATEGY 1: Exact match ===
671
672
  fileContent = normalizedFile.replace(search, () => replace);
672
673
  diffApplied = true;
673
674
  }
674
675
  else {
675
- // Fallback 1: try stripping leading/trailing empty lines/whitespace from the search block
676
+ // === STRATEGY 2: Trimmed match ===
676
677
  const trimmedSearch = search.trim();
677
678
  if (trimmedSearch && normalizedFile.includes(trimmedSearch)) {
678
679
  fileContent = normalizedFile.replace(trimmedSearch, () => replace.trim());
679
680
  diffApplied = true;
680
681
  }
681
682
  else {
682
- // Fallback 2: Regex fuzzy matching that ignores exact whitespace differences (e.g. indentation or newlines)
683
+ // === STRATEGY 3: Whitespace-flexible regex ===
683
684
  const escapedSearch = trimmedSearch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+');
684
- const regex = new RegExp(escapedSearch);
685
- if (regex.test(normalizedFile)) {
686
- fileContent = normalizedFile.replace(regex, () => replace.trim());
685
+ const wsRegex = new RegExp(escapedSearch);
686
+ if (wsRegex.test(normalizedFile)) {
687
+ fileContent = normalizedFile.replace(wsRegex, () => replace.trim());
687
688
  diffApplied = true;
688
689
  }
689
690
  else {
690
- ui_1.ui.error(`Could not apply diff to ${filePath}: SEARCH block not exactly matched.`);
691
+ // === STRATEGY 4: Line-by-line fuzzy matching ===
692
+ // Find the region in the file that best matches the search block
693
+ const searchLines = trimmedSearch.split('\n').map(l => l.trim()).filter(l => l.length > 0);
694
+ const fileLines = normalizedFile.split('\n');
695
+ if (searchLines.length > 0) {
696
+ let bestStartIdx = -1;
697
+ let bestEndIdx = -1;
698
+ let bestScore = 0;
699
+ // Slide a window over the file lines to find the best matching region
700
+ for (let i = 0; i <= fileLines.length - searchLines.length; i++) {
701
+ let matchedCount = 0;
702
+ let lastMatchIdx = i - 1;
703
+ for (const searchLine of searchLines) {
704
+ // Look for this search line within a reasonable range
705
+ for (let j = lastMatchIdx + 1; j < Math.min(i + searchLines.length + 5, fileLines.length); j++) {
706
+ if (fileLines[j].trim() === searchLine) {
707
+ matchedCount++;
708
+ lastMatchIdx = j;
709
+ break;
710
+ }
711
+ }
712
+ }
713
+ const score = matchedCount / searchLines.length;
714
+ if (score > bestScore && score >= 0.6) {
715
+ bestScore = score;
716
+ bestStartIdx = i;
717
+ bestEndIdx = Math.min(lastMatchIdx, i + searchLines.length + 4);
718
+ }
719
+ }
720
+ if (bestStartIdx >= 0 && bestScore >= 0.6) {
721
+ const before = fileLines.slice(0, bestStartIdx).join('\n');
722
+ const after = fileLines.slice(bestEndIdx + 1).join('\n');
723
+ fileContent = before + (before ? '\n' : '') + replace.trim() + (after ? '\n' : '') + after;
724
+ diffApplied = true;
725
+ ui_1.ui.info(`Fuzzy matched diff for ${filePath} (${Math.round(bestScore * 100)}% confidence)`);
726
+ }
727
+ else {
728
+ // === STRATEGY 5: Anchor line matching (first + last non-empty line) ===
729
+ const firstSearchLine = searchLines[0];
730
+ const lastSearchLine = searchLines[searchLines.length - 1];
731
+ let anchorStart = -1;
732
+ let anchorEnd = -1;
733
+ for (let i = 0; i < fileLines.length; i++) {
734
+ if (fileLines[i].trim() === firstSearchLine) {
735
+ anchorStart = i;
736
+ break;
737
+ }
738
+ }
739
+ if (anchorStart >= 0) {
740
+ for (let i = anchorStart + 1; i < fileLines.length; i++) {
741
+ if (fileLines[i].trim() === lastSearchLine) {
742
+ anchorEnd = i;
743
+ break;
744
+ }
745
+ }
746
+ }
747
+ if (anchorStart >= 0 && anchorEnd >= 0 && (anchorEnd - anchorStart) <= searchLines.length * 2) {
748
+ const before = fileLines.slice(0, anchorStart).join('\n');
749
+ const after = fileLines.slice(anchorEnd + 1).join('\n');
750
+ fileContent = before + (before ? '\n' : '') + replace.trim() + (after ? '\n' : '') + after;
751
+ diffApplied = true;
752
+ ui_1.ui.info(`Anchor-matched diff for ${filePath} (first/last line anchors)`);
753
+ }
754
+ else {
755
+ // === STRATEGY 6: Partial content - find longest unique line sequence ===
756
+ // Use the 3 longest unique lines from the search as anchors
757
+ const uniqueSearchLines = searchLines
758
+ .filter(l => l.length > 10)
759
+ .sort((a, b) => b.length - a.length)
760
+ .slice(0, 3);
761
+ if (uniqueSearchLines.length >= 2) {
762
+ const firstUnique = uniqueSearchLines[0];
763
+ const secondUnique = uniqueSearchLines[1];
764
+ let uStart = -1, uEnd = -1;
765
+ for (let i = 0; i < fileLines.length; i++) {
766
+ if (fileLines[i].trim().includes(firstUnique) || firstUnique.includes(fileLines[i].trim())) {
767
+ if (uStart < 0)
768
+ uStart = i;
769
+ uEnd = i;
770
+ }
771
+ if (fileLines[i].trim().includes(secondUnique) || secondUnique.includes(fileLines[i].trim())) {
772
+ if (uStart < 0)
773
+ uStart = i;
774
+ uEnd = i;
775
+ }
776
+ }
777
+ if (uStart >= 0 && uEnd >= uStart) {
778
+ // Expand slightly to capture the full block
779
+ const expandedStart = Math.max(0, uStart - 1);
780
+ const expandedEnd = Math.min(fileLines.length - 1, uEnd + 1);
781
+ const before = fileLines.slice(0, expandedStart).join('\n');
782
+ const after = fileLines.slice(expandedEnd + 1).join('\n');
783
+ fileContent = before + (before ? '\n' : '') + replace.trim() + (after ? '\n' : '') + after;
784
+ diffApplied = true;
785
+ ui_1.ui.info(`Partial-matched diff for ${filePath} (unique line anchors)`);
786
+ }
787
+ else {
788
+ ui_1.ui.error(`Could not apply diff to ${filePath}: SEARCH block not matched (all 6 strategies failed).`);
789
+ }
790
+ }
791
+ else {
792
+ ui_1.ui.error(`Could not apply diff to ${filePath}: SEARCH block not matched (all strategies failed).`);
793
+ }
794
+ }
795
+ }
796
+ }
797
+ else {
798
+ ui_1.ui.error(`Could not apply diff to ${filePath}: SEARCH block is empty.`);
799
+ }
691
800
  }
692
801
  }
693
802
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "draftify-cli",
3
- "version": "1.0.62",
3
+ "version": "1.0.64",
4
4
  "description": "Draftify AI CLI tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {