draftify-cli 1.0.62 → 1.0.66
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/repl.js +115 -6
- package/dist/utils/api.js +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
|
-
//
|
|
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
|
-
//
|
|
683
|
+
// === STRATEGY 3: Whitespace-flexible regex ===
|
|
683
684
|
const escapedSearch = trimmedSearch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+');
|
|
684
|
-
const
|
|
685
|
-
if (
|
|
686
|
-
fileContent = normalizedFile.replace(
|
|
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
|
-
|
|
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/dist/utils/api.js
CHANGED
|
@@ -250,9 +250,12 @@ new lines
|
|
|
250
250
|
>>>>>>>
|
|
251
251
|
</FILE_MODIFY>
|
|
252
252
|
CRITICAL RULES FOR <FILE_MODIFY>:
|
|
253
|
-
-
|
|
253
|
+
- **MANDATORY: ALWAYS use <READ_FILE> to read the file FIRST before writing any <FILE_MODIFY> block.** Never rely on memory of what a file contains — your memory of file content degrades over conversation turns. ALWAYS re-read.
|
|
254
|
+
- The SEARCH block MUST PERFECTLY MATCH the existing file content character-for-character. Do not use ellipses (...) or comments like "// ... existing code ..." to skip lines.
|
|
254
255
|
- The indentation in the SEARCH block must exactly match the file.
|
|
255
|
-
-
|
|
256
|
+
- Keep SEARCH blocks SHORT: include only 1-5 lines — the absolute minimum needed to uniquely identify the replacement location. Shorter blocks are far less likely to mismatch.
|
|
257
|
+
- You can use multiple SEARCH/REPLACE blocks within a single <FILE_MODIFY> tag.
|
|
258
|
+
- **LARGE CHANGES RULE: If you need to change more than ~30% of a file, do NOT use <FILE_MODIFY> with many SEARCH blocks. Instead, use <FILE_CREATE> with the complete new file content to overwrite it entirely.** This is much more reliable than multiple diffs.
|
|
256
259
|
|
|
257
260
|
3. To delete a file:
|
|
258
261
|
<FILE_DELETE path="relative/path/to/file.ext" />
|
|
@@ -278,6 +281,7 @@ CRITICAL RULES FOR <FILE_MODIFY>:
|
|
|
278
281
|
|
|
279
282
|
AUTONOMOUS WORKSPACE EXPLORATION:
|
|
280
283
|
If the user asks you to modify the project (e.g. "Add a game over menu", "Fix the bug in the header") but you don't know the exact file structure or contents, DO NOT ask the user to show you the files. Instead, use <LIST_DIR path="." /> and <READ_FILE path="..." /> to autonomously explore the workspace. You can read multiple files or list directories in a single response. Wait for the CLI to return the results of your exploration before you generate the code modifications. You act like an autonomous agent.
|
|
284
|
+
IMPORTANT: You MUST ALWAYS <READ_FILE> any file before attempting <FILE_MODIFY> on it. This is not optional — it prevents diff mismatch failures.
|
|
281
285
|
|
|
282
286
|
Always use these tags when you write code, explore files, or ask questions so the CLI can apply it automatically! Keep explanations short and outside the tags.
|
|
283
287
|
|