draftify-cli 1.0.92 → 1.0.95
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 +73 -16
- package/package.json +1 -1
package/dist/repl.js
CHANGED
|
@@ -578,6 +578,7 @@ async function startRepl(initialUsername) {
|
|
|
578
578
|
// 3. AI Chat Prompt (default)
|
|
579
579
|
async function applyFileOperations(result) {
|
|
580
580
|
let displayResult = result;
|
|
581
|
+
const operationErrors = [];
|
|
581
582
|
// Pre-process native tool calls (<tool_call> and <longcat_tool_call>) to translate them to standard XML tags
|
|
582
583
|
const toolCallRegex = /<(?:tool_call|longcat_tool_call)>([\s\S]*?)<\/(?:tool_call|tool|longcat_tool_call)>/gi;
|
|
583
584
|
let tcMatch;
|
|
@@ -644,7 +645,9 @@ async function startRepl(initialUsername) {
|
|
|
644
645
|
const filePath = match[1];
|
|
645
646
|
const fullPath = path_1.default.resolve(process.cwd(), filePath);
|
|
646
647
|
if (!isSafePath(fullPath)) {
|
|
647
|
-
|
|
648
|
+
const err = `Security error: Cannot delete file outside workspace (${filePath})`;
|
|
649
|
+
ui_1.ui.error(err);
|
|
650
|
+
operationErrors.push(err);
|
|
648
651
|
displayResult = displayResult.replace(match[0], `[Blocked: ${filePath} is outside workspace]`);
|
|
649
652
|
continue;
|
|
650
653
|
}
|
|
@@ -655,7 +658,9 @@ async function startRepl(initialUsername) {
|
|
|
655
658
|
displayResult = displayResult.replace(match[0], '');
|
|
656
659
|
}
|
|
657
660
|
catch (e) {
|
|
658
|
-
|
|
661
|
+
const err = `Failed to delete ${filePath}: ${e.message}`;
|
|
662
|
+
ui_1.ui.error(err);
|
|
663
|
+
operationErrors.push(err);
|
|
659
664
|
}
|
|
660
665
|
}
|
|
661
666
|
}
|
|
@@ -664,7 +669,9 @@ async function startRepl(initialUsername) {
|
|
|
664
669
|
const filePath = match[1];
|
|
665
670
|
const fullPath = path_1.default.resolve(process.cwd(), filePath);
|
|
666
671
|
if (!isSafePath(fullPath)) {
|
|
667
|
-
|
|
672
|
+
const err = `Security error: Cannot create file outside workspace (${filePath})`;
|
|
673
|
+
ui_1.ui.error(err);
|
|
674
|
+
operationErrors.push(err);
|
|
668
675
|
displayResult = displayResult.replace(match[0], `\n[Blocked: ${filePath} is outside workspace]\n`);
|
|
669
676
|
continue;
|
|
670
677
|
}
|
|
@@ -681,7 +688,10 @@ async function startRepl(initialUsername) {
|
|
|
681
688
|
displayResult = displayResult.replace(match[0], '');
|
|
682
689
|
}
|
|
683
690
|
catch (e) {
|
|
684
|
-
|
|
691
|
+
const err = `Failed to create ${filePath}: ${e.message}`;
|
|
692
|
+
ui_1.ui.error(err);
|
|
693
|
+
operationErrors.push(err);
|
|
694
|
+
displayResult = displayResult.replace(match[0], `[Failed to create: ${filePath}]`);
|
|
685
695
|
}
|
|
686
696
|
}
|
|
687
697
|
const modifyRegex = /<FILE_MODIFY\s+path=['"]([^'"]+)['"]>([\s\S]*?)<\/(?:FILE|SCRIPT|STYLE|HTML)_MODIFY>/gi;
|
|
@@ -690,7 +700,9 @@ async function startRepl(initialUsername) {
|
|
|
690
700
|
const block = match[2];
|
|
691
701
|
const fullPath = path_1.default.resolve(process.cwd(), filePath);
|
|
692
702
|
if (!isSafePath(fullPath)) {
|
|
693
|
-
|
|
703
|
+
const err = `Security error: Cannot modify file outside workspace (${filePath})`;
|
|
704
|
+
ui_1.ui.error(err);
|
|
705
|
+
operationErrors.push(err);
|
|
694
706
|
displayResult = displayResult.replace(match[0], `[Blocked: ${filePath} is outside workspace]`);
|
|
695
707
|
continue;
|
|
696
708
|
}
|
|
@@ -826,17 +838,23 @@ async function startRepl(initialUsername) {
|
|
|
826
838
|
ui_1.ui.info(`Partial-matched diff for ${filePath} (unique line anchors)`);
|
|
827
839
|
}
|
|
828
840
|
else {
|
|
829
|
-
|
|
841
|
+
const err = `Could not apply diff to ${filePath}: SEARCH block not matched (all 6 strategies failed).`;
|
|
842
|
+
ui_1.ui.error(err);
|
|
843
|
+
operationErrors.push(err);
|
|
830
844
|
}
|
|
831
845
|
}
|
|
832
846
|
else {
|
|
833
|
-
|
|
847
|
+
const err = `Could not apply diff to ${filePath}: SEARCH block not matched (all strategies failed).`;
|
|
848
|
+
ui_1.ui.error(err);
|
|
849
|
+
operationErrors.push(err);
|
|
834
850
|
}
|
|
835
851
|
}
|
|
836
852
|
}
|
|
837
853
|
}
|
|
838
854
|
else {
|
|
839
|
-
|
|
855
|
+
const err = `Could not apply diff to ${filePath}: SEARCH block is empty.`;
|
|
856
|
+
ui_1.ui.error(err);
|
|
857
|
+
operationErrors.push(err);
|
|
840
858
|
}
|
|
841
859
|
}
|
|
842
860
|
}
|
|
@@ -848,14 +866,20 @@ async function startRepl(initialUsername) {
|
|
|
848
866
|
displayResult = displayResult.replace(match[0], '');
|
|
849
867
|
}
|
|
850
868
|
else if (!hasDiffBlocks) {
|
|
851
|
-
|
|
869
|
+
const err = `Invalid FILE_MODIFY block for ${filePath}`;
|
|
870
|
+
ui_1.ui.error(err);
|
|
871
|
+
operationErrors.push(err);
|
|
872
|
+
displayResult = displayResult.replace(match[0], `[Failed to modify: ${filePath} - Invalid block]`);
|
|
852
873
|
}
|
|
853
874
|
else {
|
|
854
875
|
displayResult = displayResult.replace(match[0], `[Failed to modify: ${filePath} - SEARCH block mismatch]`);
|
|
855
876
|
}
|
|
856
877
|
}
|
|
857
878
|
else {
|
|
858
|
-
|
|
879
|
+
const err = `Cannot modify ${filePath}: File not found.`;
|
|
880
|
+
ui_1.ui.error(err);
|
|
881
|
+
operationErrors.push(err);
|
|
882
|
+
displayResult = displayResult.replace(match[0], `[Failed to modify: ${filePath} - File not found]`);
|
|
859
883
|
}
|
|
860
884
|
}
|
|
861
885
|
const commandsToRun = [];
|
|
@@ -881,7 +905,8 @@ async function startRepl(initialUsername) {
|
|
|
881
905
|
cleanResult: displayResult.replace(/\n\s*\n\s*\n/g, '\n\n').trim(),
|
|
882
906
|
commandsToRun,
|
|
883
907
|
filesToReadAuto,
|
|
884
|
-
dirsToListAuto
|
|
908
|
+
dirsToListAuto,
|
|
909
|
+
errors: operationErrors
|
|
885
910
|
};
|
|
886
911
|
}
|
|
887
912
|
exports.globalAbortController = new AbortController();
|
|
@@ -905,18 +930,32 @@ async function startRepl(initialUsername) {
|
|
|
905
930
|
let retryCount = 0;
|
|
906
931
|
let success = false;
|
|
907
932
|
let partialText = "";
|
|
908
|
-
|
|
933
|
+
// Detect if a successful response was actually truncated mid-content
|
|
934
|
+
function detectTruncation(text) {
|
|
935
|
+
// Check for unclosed FILE_CREATE
|
|
936
|
+
const createOpenMatch = text.match(/<FILE_CREATE\s+path="([^"]+)"[^>]*>/);
|
|
937
|
+
if (createOpenMatch && !/<\/FILE_CREATE>/i.test(text)) {
|
|
938
|
+
return { truncated: true, filePath: createOpenMatch[1], tagType: 'FILE_CREATE' };
|
|
939
|
+
}
|
|
940
|
+
// Check for unclosed FILE_MODIFY
|
|
941
|
+
const modifyOpenMatch = text.match(/<FILE_MODIFY\s+path="([^"]+)"[^>]*>/);
|
|
942
|
+
if (modifyOpenMatch && !/<\/FILE_MODIFY>/i.test(text)) {
|
|
943
|
+
return { truncated: true, filePath: modifyOpenMatch[1], tagType: 'FILE_MODIFY' };
|
|
944
|
+
}
|
|
945
|
+
return { truncated: false };
|
|
946
|
+
}
|
|
947
|
+
while (retryCount < 4 && !success) {
|
|
909
948
|
try {
|
|
910
949
|
let retryHistory = [...conversationHistory];
|
|
911
950
|
let retryPrompt = finalPrompt;
|
|
912
951
|
if (partialText) {
|
|
913
952
|
retryHistory.push({ role: "user", content: finalPrompt });
|
|
914
953
|
retryHistory.push({ role: "assistant", content: partialText });
|
|
915
|
-
retryPrompt = "Continue exactly from where you left off. Do not repeat
|
|
954
|
+
retryPrompt = "Continue exactly from where you left off. Do not repeat anything you already wrote. Continue the file content from the exact character where it was cut off.";
|
|
916
955
|
}
|
|
917
956
|
spinner = (0, ui_1.createSpinner)("Working...").start();
|
|
918
957
|
if (retryCount > 0) {
|
|
919
|
-
spinner.setPrefix(`
|
|
958
|
+
spinner.setPrefix(`Continuing... (${retryCount}/3)`);
|
|
920
959
|
}
|
|
921
960
|
let thisAttemptText = "";
|
|
922
961
|
const attemptResult = await (0, api_1.refactorCodeApi)(requestPayloadFileName, requestPayloadCode, retryPrompt, retryHistory, currentModel, activeSkillsData, (0, config_1.getThinkingLevel)(), (chunk) => {
|
|
@@ -948,7 +987,22 @@ async function startRepl(initialUsername) {
|
|
|
948
987
|
spinner.setPrefix("Analyzing...");
|
|
949
988
|
}
|
|
950
989
|
}, exports.globalAbortController.signal);
|
|
951
|
-
|
|
990
|
+
const combined = partialText + attemptResult;
|
|
991
|
+
// --- TRUNCATION DETECTION ---
|
|
992
|
+
// If the stream ended cleanly but the content is still incomplete (token-limited),
|
|
993
|
+
// treat it like a retry scenario and continue generation.
|
|
994
|
+
const truncationInfo = detectTruncation(combined);
|
|
995
|
+
if (truncationInfo.truncated && retryCount < 3) {
|
|
996
|
+
if (spinner)
|
|
997
|
+
spinner.stop();
|
|
998
|
+
ui_1.ui.info(`Response truncated mid-file (${truncationInfo.filePath}). Continuing...`);
|
|
999
|
+
partialText = combined;
|
|
1000
|
+
retryCount++;
|
|
1001
|
+
currentStreamedText = combined;
|
|
1002
|
+
await new Promise(r => setTimeout(r, 500));
|
|
1003
|
+
continue; // restart the while loop with updated partialText
|
|
1004
|
+
}
|
|
1005
|
+
rawResult = combined;
|
|
952
1006
|
success = true;
|
|
953
1007
|
}
|
|
954
1008
|
catch (e) {
|
|
@@ -981,7 +1035,7 @@ async function startRepl(initialUsername) {
|
|
|
981
1035
|
}
|
|
982
1036
|
}
|
|
983
1037
|
spinner.stop();
|
|
984
|
-
const { cleanResult, commandsToRun, filesToReadAuto, dirsToListAuto } = await applyFileOperations(rawResult);
|
|
1038
|
+
const { cleanResult, commandsToRun, filesToReadAuto, dirsToListAuto, errors: fileOpErrors } = await applyFileOperations(rawResult);
|
|
985
1039
|
// --- PARSE CLARIFICATION QUESTIONS ---
|
|
986
1040
|
let finalDisplayResult = cleanResult;
|
|
987
1041
|
let questions = [];
|
|
@@ -1076,6 +1130,9 @@ async function startRepl(initialUsername) {
|
|
|
1076
1130
|
autoExplorationOutputs.push(`Failed to read ${filePath}: ${e.message}`);
|
|
1077
1131
|
}
|
|
1078
1132
|
}
|
|
1133
|
+
if (fileOpErrors && fileOpErrors.length > 0) {
|
|
1134
|
+
autoExplorationOutputs.push(`Some file operations failed:\n${fileOpErrors.map(e => `- ${e}`).join('\n')}`);
|
|
1135
|
+
}
|
|
1079
1136
|
if (autoExplorationOutputs.length > 0) {
|
|
1080
1137
|
let promptAdditions = autoPrompt ? [autoPrompt] : [];
|
|
1081
1138
|
promptAdditions.push(`I executed your file system requests. Here are the results:\n${autoExplorationOutputs.join('\n\n')}`);
|