@taj-special/dravix-code 1.4.6 → 1.4.8

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/cli/index.js CHANGED
@@ -278,9 +278,9 @@ async function main() {
278
278
  }
279
279
  const rows = process.stdout.rows ?? 24;
280
280
  const cols = process.stdout.columns ?? 80;
281
- // FULL-SCREEN: clear everything including scrollback, set scroll region to entire terminal
281
+ // FULL-SCREEN: clear everything including scrollback, set scroll region for chat (bottom 3 rows fixed)
282
282
  process.stdout.write('\x1b[2J\x1b[3J'); // Clear screen + scrollback buffer
283
- process.stdout.write(`\x1b[1;${rows}r`); // Scroll region = entire terminal
283
+ process.stdout.write(`\x1b[1;${rows - 3}r`); // Scroll region = chat area (rows 1 to rows-3)
284
284
  process.stdout.write('\x1b[1;1H'); // Move to top-left
285
285
  // Draw the app (ONE logo only)
286
286
  banner();
package/dist/cli/repl.js CHANGED
@@ -749,16 +749,6 @@ async function askPermission(label, key, alwaysAllowed, noAlways, diffShown, pre
749
749
  if (!noAlways && alwaysAllowed.has(key))
750
750
  return true;
751
751
  return new Promise((resolve) => {
752
- // Auto-resolve after 5 min — prevent hanging process
753
- const timeout = setTimeout(() => {
754
- cleanup();
755
- clearMenu(menuLines);
756
- for (let i = 0; i < previewLineCount - 1; i++) {
757
- process.stdout.write('\x1b[1A\x1b[2K');
758
- }
759
- process.stdout.write(` ${colors.muted('○')} ${colors.muted('Timeout — skipped: ' + label)}\n`);
760
- resolve(false);
761
- }, 300_000);
762
752
  let sel = 0;
763
753
  let drawn = false;
764
754
  const ci = label.indexOf(': ');
@@ -812,11 +802,9 @@ async function askPermission(label, key, alwaysAllowed, noAlways, diffShown, pre
812
802
  }
813
803
  printMenu();
814
804
  function cleanup() {
815
- clearTimeout(timeout);
816
805
  process.stdin.removeListener('data', onData);
817
806
  }
818
807
  function confirm(idx) {
819
- clearTimeout(timeout);
820
808
  cleanup();
821
809
  clearMenu(menuLines);
822
810
  // Clear the diff preview lines (all except the first blank separator)
@@ -906,39 +894,17 @@ async function readLine(prompt, cwd) {
906
894
  let tabPreWord = '';
907
895
  function onResize() {
908
896
  const newCols = process.stdout.columns ?? 80;
909
- const oldSepRows = Math.ceil(sepVisualLen / newCols);
910
897
  SEP = colors.dim(' ' + '─'.repeat(Math.max(newCols - 4, 40)));
911
- sepVisualLen = 2 + Math.max(newCols - 4, 40);
912
898
  if (atMode) {
913
- for (let i = 0; i < atBoxLines + 3; i++)
914
- process.stdout.write('\x1b[1A');
915
- process.stdout.write('\r\x1b[0J');
916
- atDrawn = false;
917
- atBoxLines = 0;
918
899
  drawPicker();
919
900
  }
920
901
  else if (slashMode) {
921
- for (let i = 0; i < slashBoxLines + 3; i++)
922
- process.stdout.write('\x1b[1A');
923
- process.stdout.write('\r\x1b[0J');
924
- slashDrawn = false;
925
- slashBoxLines = 0;
926
902
  drawSlashPicker();
927
903
  }
928
904
  else if (tabMode) {
929
- for (let i = 0; i < tabBoxLines + 3; i++)
930
- process.stdout.write('\x1b[1A');
931
- process.stdout.write('\r\x1b[0J');
932
- tabDrawn = false;
933
- tabBoxLines = 0;
934
905
  drawTabPicker();
935
906
  }
936
907
  else {
937
- process.stdout.write('\r\x1b[K');
938
- process.stdout.write('\x1b[1B\x1b[2K\x1b[1A');
939
- for (let i = 0; i < oldSepRows + 1; i++)
940
- process.stdout.write('\x1b[1A\x1b[2K');
941
- process.stdout.write('\n' + SEP + '\n');
942
908
  redraw();
943
909
  }
944
910
  }
@@ -1003,232 +969,284 @@ async function readLine(prompt, cwd) {
1003
969
  const lq = q.toLowerCase();
1004
970
  return SLASH_COMMANDS.filter(c => c.name.slice(1).toLowerCase().startsWith(lq));
1005
971
  }
1006
- // Picker layout: input (A) | bot-sep (A+1) | box (A+2 .. A+1+atBoxLines)
1007
- // Clearing box: atBoxLines × \x1b[1A\x1b[2K → cursor at A+2
1008
- // then \x1b[2A\x1b[2K to skip bot-sep and clear input
1009
972
  let atDrawn = false;
1010
973
  function drawPicker() {
1011
- if (atDrawn) {
1012
- for (let i = 0; i < atBoxLines; i++)
1013
- process.stdout.write('\x1b[1A\x1b[2K');
1014
- process.stdout.write('\x1b[2A\x1b[2K');
1015
- }
1016
- else {
1017
- process.stdout.write('\r\x1b[K');
1018
- atDrawn = true;
1019
- }
1020
- atBoxLines = 0;
1021
- const inputLine = colors.primary(' › ') + colors.text(prefix) +
1022
- (pasteBlock !== null ? colors.muted(`[paste: ${pasteCount} lines]`) + colors.text(suffix) : '') +
1023
- colors.primary('@') + colors.text(atQuery);
1024
- process.stdout.write(inputLine + '\r\n' + SEP + '\r\n');
1025
- const DW = Math.min(Math.max((process.stdout.columns ?? 80) - 6, 44), 72);
1026
- const IW = DW - 6;
974
+ const cols = process.stdout.columns ?? 80;
975
+ const rows = process.stdout.rows ?? 24;
976
+ const sR = rows - 2;
977
+ const iR = rows - 1;
1027
978
  const filtered = filterFiles(atQuery);
1028
979
  if (atSel >= filtered.length && filtered.length > 0)
1029
980
  atSel = filtered.length - 1;
1030
- const shown = filtered.slice(0, 10);
981
+ if (atSel < 0)
982
+ atSel = 0;
983
+ const maxVisible = Math.min(8, Math.max(3, rows - 6));
984
+ const shown = filtered.slice(0, maxVisible);
985
+ const DW = Math.min(Math.max(cols - 6, 44), 74);
986
+ const IW = DW - 6;
987
+ const boxLines = [];
1031
988
  const qLabel = atQuery ? `@${atQuery}` : 'files';
1032
- const hDashes = Math.max(DW - 5 - qLabel.length, 1);
1033
- process.stdout.write(colors.dim(' ╭─ ') + colors.primary(qLabel) + colors.dim(' ' + '─'.repeat(hDashes) + '╮') + '\n');
1034
- atBoxLines++;
989
+ const hDashes = Math.max(DW - 4 - qLabel.length, 1);
990
+ boxLines.push(colors.dim(' ╭─ ') + colors.primary(qLabel) + colors.dim(' ' + '─'.repeat(hDashes) + '╮'));
1035
991
  if (shown.length === 0) {
1036
992
  const msg = 'no matches';
1037
993
  const pad = ' '.repeat(Math.max(IW - msg.length, 0));
1038
- process.stdout.write(colors.dim(' │') + ' ' + colors.muted(msg) + pad + colors.dim('│') + '\n');
1039
- atBoxLines++;
994
+ boxLines.push(colors.dim(' │') + ' ' + colors.muted(msg) + pad + colors.dim('│'));
1040
995
  }
1041
996
  else {
1042
997
  for (let i = 0; i < shown.length; i++) {
1043
998
  const f = shown[i];
1044
999
  const isDir = f.endsWith('/');
1045
1000
  const sel = i === atSel;
1046
- const mark = sel ? colors.primary('>') : ' ';
1001
+ const mark = sel ? colors.primary('') : ' ';
1047
1002
  const clip = f.length > IW ? f.slice(0, IW - 1) + '…' : f;
1048
1003
  const pad = ' '.repeat(Math.max(IW - clip.length, 0));
1049
1004
  const name = sel && isDir ? colors.primary.bold(clip) + pad
1050
1005
  : sel ? colors.primary.bold(clip) + pad
1051
1006
  : isDir ? chalk.hex('#74b9ff')(clip) + pad
1052
1007
  : colors.subtext(clip + pad);
1053
- process.stdout.write(colors.dim(' │') + ` ${mark} ` + name + colors.dim('│') + '\n');
1054
- atBoxLines++;
1008
+ boxLines.push(colors.dim(' │') + ` ${mark} ` + name + colors.dim('│'));
1055
1009
  }
1056
1010
  }
1057
- const hint = ' ↑↓ Enter Esc ';
1011
+ const hint = ' ↑↓ Navigate Enter Select Esc Close ';
1058
1012
  const fInner = DW - 2 - hint.length;
1059
- const fLeft = Math.floor(fInner / 2);
1060
- const fRight = fInner - fLeft;
1061
- process.stdout.write(colors.dim(' ╰' + '─'.repeat(Math.max(fLeft, 0))) + colors.muted(hint) + colors.dim('─'.repeat(Math.max(fRight, 0)) + '╯') + '\n');
1062
- atBoxLines++;
1013
+ const fLeft = Math.max(0, Math.floor(fInner / 2));
1014
+ const fRight = Math.max(0, fInner - fLeft);
1015
+ boxLines.push(colors.dim(' ╰' + '─'.repeat(fLeft)) + colors.muted(hint) + colors.dim('─'.repeat(fRight) + '╯'));
1016
+ const newLinesCount = boxLines.length;
1017
+ // Clear any leftover lines from previous taller popup
1018
+ if (atBoxLines > newLinesCount) {
1019
+ for (let r = sR - atBoxLines; r < sR - newLinesCount; r++) {
1020
+ if (r >= 1)
1021
+ process.stdout.write(`\x1b[${r};1H\x1b[2K`);
1022
+ }
1023
+ }
1024
+ // Render popup above separator
1025
+ for (let i = 0; i < newLinesCount; i++) {
1026
+ const targetRow = sR - newLinesCount + i;
1027
+ if (targetRow >= 1) {
1028
+ process.stdout.write(`\x1b[${targetRow};1H\x1b[2K${boxLines[i]}`);
1029
+ }
1030
+ }
1031
+ // Re-draw separator and input
1032
+ process.stdout.write(`\x1b[${sR};1H\x1b[2K${SEP}`);
1033
+ const inputLine = colors.primary(' › ') + colors.text(prefix) +
1034
+ (pasteBlock !== null ? colors.muted(`[paste: ${pasteCount} lines]`) + colors.text(suffix) : '') +
1035
+ colors.primary('@') + colors.text(atQuery);
1036
+ process.stdout.write(`\x1b[${iR};1H\x1b[2K${inputLine}`);
1037
+ const cursorCol = 5 + prefix.length + (pasteBlock !== null ? `[paste: ${pasteCount} lines]`.length + suffix.length : 0) + 1 + atQuery.length;
1038
+ process.stdout.write(`\x1b[${iR};${cursorCol}H`);
1039
+ atBoxLines = newLinesCount;
1040
+ atDrawn = true;
1063
1041
  }
1064
1042
  function closePicker() {
1065
- if (atDrawn) {
1066
- for (let i = 0; i < atBoxLines; i++)
1067
- process.stdout.write('\x1b[1A\x1b[2K');
1068
- process.stdout.write('\x1b[2A\x1b[2K');
1069
- atDrawn = false;
1070
- }
1071
- else {
1072
- process.stdout.write('\r\x1b[K');
1043
+ const rows = process.stdout.rows ?? 24;
1044
+ const sR = rows - 2;
1045
+ if (atDrawn && atBoxLines > 0) {
1046
+ for (let r = sR - atBoxLines; r < sR; r++) {
1047
+ if (r >= 1)
1048
+ process.stdout.write(`\x1b[${r};1H\x1b[2K`);
1049
+ }
1073
1050
  }
1074
1051
  atBoxLines = 0;
1075
1052
  atMode = false;
1076
1053
  atQuery = '';
1077
1054
  atSel = 0;
1055
+ atDrawn = false;
1078
1056
  }
1079
1057
  function drawSlashPicker() {
1080
- if (slashDrawn) {
1081
- for (let i = 0; i < slashBoxLines; i++)
1082
- process.stdout.write('\x1b[1A\x1b[2K');
1083
- process.stdout.write('\x1b[2A\x1b[2K');
1084
- }
1085
- else {
1086
- process.stdout.write('\r\x1b[K');
1087
- slashDrawn = true;
1088
- }
1089
- slashBoxLines = 0;
1090
- const inputLine = colors.primary(' › ') + colors.primary('/') + colors.text(slashQuery);
1091
- process.stdout.write(inputLine + '\r\n' + SEP + '\r\n');
1092
- const DW = Math.min(Math.max((process.stdout.columns ?? 80) - 6, 44), 72);
1093
- const nameW = 10;
1094
- const descW = DW - nameW - 8;
1058
+ const cols = process.stdout.columns ?? 80;
1059
+ const rows = process.stdout.rows ?? 24;
1060
+ const sR = rows - 2;
1061
+ const iR = rows - 1;
1095
1062
  const filtered = filterSlash(slashQuery);
1096
1063
  if (slashSel >= filtered.length && filtered.length > 0)
1097
1064
  slashSel = filtered.length - 1;
1065
+ if (slashSel < 0)
1066
+ slashSel = 0;
1067
+ const maxVisible = Math.min(8, Math.max(3, rows - 6));
1068
+ const shown = filtered.slice(0, maxVisible);
1069
+ const DW = Math.min(Math.max(cols - 6, 44), 74);
1070
+ const nameW = 11;
1071
+ const descW = Math.max(DW - nameW - 9, 10);
1072
+ const boxLines = [];
1098
1073
  const qLabel = slashQuery ? `/${slashQuery}` : 'commands';
1099
- const hDashes = Math.max(DW - 5 - qLabel.length, 1);
1100
- process.stdout.write(colors.dim(' ╭─ ') + colors.primary(qLabel) + colors.dim(' ' + '─'.repeat(hDashes) + '╮') + '\n');
1101
- slashBoxLines++;
1074
+ const hDashes = Math.max(DW - 4 - qLabel.length, 1);
1075
+ boxLines.push(colors.dim(' ╭─ ') + colors.primary(qLabel) + colors.dim(' ' + '─'.repeat(hDashes) + '╮'));
1102
1076
  if (filtered.length === 0) {
1103
1077
  const msg = 'no matching commands';
1104
- const pad = ' '.repeat(Math.max(DW - 4 - msg.length, 0));
1105
- process.stdout.write(colors.dim(' │') + ' ' + colors.muted(msg) + pad + colors.dim('│') + '\n');
1106
- slashBoxLines++;
1078
+ const pad = ' '.repeat(Math.max(DW - 4 - msg.length - 2, 0));
1079
+ boxLines.push(colors.dim(' │') + ' ' + colors.muted(msg) + pad + colors.dim('│'));
1107
1080
  }
1108
1081
  else {
1109
- for (let i = 0; i < filtered.length; i++) {
1110
- const cmd = filtered[i];
1082
+ for (let i = 0; i < shown.length; i++) {
1083
+ const cmd = shown[i];
1111
1084
  const sel = i === slashSel;
1112
- const mark = sel ? colors.primary('>') : ' ';
1085
+ const mark = sel ? colors.primary('') : ' ';
1113
1086
  const nameStr = cmd.name.padEnd(nameW);
1114
1087
  const descStr = cmd.desc.length > descW ? cmd.desc.slice(0, descW - 1) + '…' : cmd.desc;
1115
1088
  const descPad = ' '.repeat(Math.max(descW - descStr.length, 0));
1116
1089
  const namePaint = sel ? colors.primary.bold(nameStr) : colors.primary(nameStr);
1117
- const descPaint = sel ? colors.muted(descStr) : colors.subtext(descStr);
1118
- process.stdout.write(colors.dim(' │') + ` ${mark} ` + namePaint + ' ' + descPaint + descPad + colors.dim('│') + '\n');
1119
- slashBoxLines++;
1090
+ const descPaint = sel ? chalk.hex('#f1f5f9')(descStr) : colors.subtext(descStr);
1091
+ boxLines.push(colors.dim(' │') + ` ${mark} ` + namePaint + ' ' + descPaint + descPad + colors.dim('│'));
1120
1092
  }
1121
1093
  }
1122
- const hint = ' ↑↓ Enter Esc ';
1094
+ const hint = ' ↑↓ Navigate Tab Complete ↵ Run Esc Close ';
1123
1095
  const fInner = DW - 2 - hint.length;
1124
- const fLeft = Math.floor(fInner / 2);
1125
- const fRight = fInner - fLeft;
1126
- process.stdout.write(colors.dim(' ╰' + '─'.repeat(Math.max(fLeft, 0))) + colors.muted(hint) + colors.dim('─'.repeat(Math.max(fRight, 0)) + '╯') + '\n');
1127
- slashBoxLines++;
1096
+ const fLeft = Math.max(0, Math.floor(fInner / 2));
1097
+ const fRight = Math.max(0, fInner - fLeft);
1098
+ boxLines.push(colors.dim(' ╰' + '─'.repeat(fLeft)) + colors.muted(hint) + colors.dim('─'.repeat(fRight) + '╯'));
1099
+ const newLinesCount = boxLines.length;
1100
+ // Clear any leftover lines from previous taller popup
1101
+ if (slashBoxLines > newLinesCount) {
1102
+ for (let r = sR - slashBoxLines; r < sR - newLinesCount; r++) {
1103
+ if (r >= 1)
1104
+ process.stdout.write(`\x1b[${r};1H\x1b[2K`);
1105
+ }
1106
+ }
1107
+ // Render popup above separator
1108
+ for (let i = 0; i < newLinesCount; i++) {
1109
+ const targetRow = sR - newLinesCount + i;
1110
+ if (targetRow >= 1) {
1111
+ process.stdout.write(`\x1b[${targetRow};1H\x1b[2K${boxLines[i]}`);
1112
+ }
1113
+ }
1114
+ // Re-draw separator and input line
1115
+ process.stdout.write(`\x1b[${sR};1H\x1b[2K${SEP}`);
1116
+ const inputLine = colors.primary(' › ') + colors.primary('/') + colors.text(slashQuery);
1117
+ process.stdout.write(`\x1b[${iR};1H\x1b[2K${inputLine}`);
1118
+ process.stdout.write(`\x1b[${iR};${5 + 1 + slashQuery.length}H`);
1119
+ slashBoxLines = newLinesCount;
1120
+ slashDrawn = true;
1128
1121
  }
1129
1122
  function closeSlashPicker() {
1130
- if (slashDrawn) {
1131
- for (let i = 0; i < slashBoxLines; i++)
1132
- process.stdout.write('\x1b[1A\x1b[2K');
1133
- process.stdout.write('\x1b[2A\x1b[2K');
1134
- slashDrawn = false;
1135
- }
1136
- else {
1137
- process.stdout.write('\r\x1b[K');
1123
+ const rows = process.stdout.rows ?? 24;
1124
+ const sR = rows - 2;
1125
+ if (slashDrawn && slashBoxLines > 0) {
1126
+ for (let r = sR - slashBoxLines; r < sR; r++) {
1127
+ if (r >= 1)
1128
+ process.stdout.write(`\x1b[${r};1H\x1b[2K`);
1129
+ }
1138
1130
  }
1139
1131
  slashBoxLines = 0;
1140
1132
  slashMode = false;
1141
1133
  slashQuery = '';
1142
1134
  slashSel = 0;
1135
+ slashDrawn = false;
1143
1136
  }
1144
1137
  function drawTabPicker() {
1145
- if (tabDrawn) {
1146
- for (let i = 0; i < tabBoxLines; i++)
1147
- process.stdout.write('\x1b[1A\x1b[2K');
1148
- process.stdout.write('\x1b[2A\x1b[2K');
1149
- }
1150
- else {
1151
- process.stdout.write('\r\x1b[K');
1152
- tabDrawn = true;
1153
- }
1154
- tabBoxLines = 0;
1138
+ const cols = process.stdout.columns ?? 80;
1139
+ const rows = process.stdout.rows ?? 24;
1140
+ const sR = rows - 2;
1141
+ const iR = rows - 1;
1155
1142
  const TC = colors.success;
1156
- const inputLine = colors.primary(' › ') + colors.text(tabPreWord) + TC(tabQuery);
1157
- process.stdout.write(inputLine + '\r\n' + SEP + '\r\n');
1158
- const DW = Math.min(Math.max((process.stdout.columns ?? 80) - 6, 44), 72);
1143
+ const DW = Math.min(Math.max(cols - 6, 44), 74);
1159
1144
  const IW = DW - 6;
1160
1145
  const filtered = filterFiles(tabQuery);
1161
1146
  if (tabSel >= filtered.length && filtered.length > 0)
1162
1147
  tabSel = filtered.length - 1;
1163
- const shown = filtered.slice(0, 10);
1148
+ if (tabSel < 0)
1149
+ tabSel = 0;
1150
+ const maxVisible = Math.min(8, Math.max(3, rows - 6));
1151
+ const shown = filtered.slice(0, maxVisible);
1152
+ const boxLines = [];
1164
1153
  const qLabel = tabQuery || 'tab complete';
1165
- const hDashes = Math.max(DW - 5 - qLabel.length, 1);
1166
- process.stdout.write(colors.dim(' ╭─ ') + TC(qLabel) + colors.dim(' ' + '─'.repeat(hDashes) + '╮') + '\n');
1167
- tabBoxLines++;
1154
+ const hDashes = Math.max(DW - 4 - qLabel.length, 1);
1155
+ boxLines.push(colors.dim(' ╭─ ') + TC(qLabel) + colors.dim(' ' + '─'.repeat(hDashes) + '╮'));
1168
1156
  if (shown.length === 0) {
1169
1157
  const msg = 'no matches';
1170
1158
  const pad = ' '.repeat(Math.max(IW - msg.length, 0));
1171
- process.stdout.write(colors.dim(' │') + ' ' + colors.muted(msg) + pad + colors.dim('│') + '\n');
1172
- tabBoxLines++;
1159
+ boxLines.push(colors.dim(' │') + ' ' + colors.muted(msg) + pad + colors.dim('│'));
1173
1160
  }
1174
1161
  else {
1175
1162
  for (let i = 0; i < shown.length; i++) {
1176
1163
  const f = shown[i];
1177
1164
  const isDir = f.endsWith('/');
1178
1165
  const sel = i === tabSel;
1179
- const mark = sel ? TC('>') : ' ';
1166
+ const mark = sel ? TC('') : ' ';
1180
1167
  const clip = f.length > IW ? f.slice(0, IW - 1) + '…' : f;
1181
1168
  const pad = ' '.repeat(Math.max(IW - clip.length, 0));
1182
1169
  const name = sel ? TC.bold(clip) + pad
1183
1170
  : isDir ? chalk.hex('#74b9ff')(clip) + pad
1184
1171
  : colors.subtext(clip + pad);
1185
- process.stdout.write(colors.dim(' │') + ` ${mark} ` + name + colors.dim('│') + '\n');
1186
- tabBoxLines++;
1172
+ boxLines.push(colors.dim(' │') + ` ${mark} ` + name + colors.dim('│'));
1187
1173
  }
1188
1174
  }
1189
- const hint = ' Tab/↑↓ Enter Esc ';
1175
+ const hint = ' Tab/↑↓ Navigate Enter Select Esc Close ';
1190
1176
  const fInner = DW - 2 - hint.length;
1191
1177
  const fLeft = Math.floor(fInner / 2);
1192
1178
  const fRight = fInner - fLeft;
1193
- process.stdout.write(colors.dim(' ╰' + '─'.repeat(Math.max(fLeft, 0))) + colors.muted(hint) + colors.dim('─'.repeat(Math.max(fRight, 0)) + '╯') + '\n');
1194
- tabBoxLines++;
1179
+ boxLines.push(colors.dim(' ╰' + '─'.repeat(Math.max(fLeft, 0))) + colors.muted(hint) + colors.dim('─'.repeat(Math.max(fRight, 0)) + '╯'));
1180
+ const newLinesCount = boxLines.length;
1181
+ // Clear any leftover lines from previous taller popup
1182
+ if (tabBoxLines > newLinesCount) {
1183
+ for (let r = sR - tabBoxLines; r < sR - newLinesCount; r++) {
1184
+ if (r >= 1)
1185
+ process.stdout.write(`\x1b[${r};1H\x1b[2K`);
1186
+ }
1187
+ }
1188
+ // Render popup above separator
1189
+ for (let i = 0; i < newLinesCount; i++) {
1190
+ const targetRow = sR - newLinesCount + i;
1191
+ if (targetRow >= 1) {
1192
+ process.stdout.write(`\x1b[${targetRow};1H\x1b[2K${boxLines[i]}`);
1193
+ }
1194
+ }
1195
+ // Re-draw separator and input line
1196
+ process.stdout.write(`\x1b[${sR};1H\x1b[2K${SEP}`);
1197
+ const inputLine = colors.primary(' › ') + colors.text(tabPreWord) + TC(tabQuery);
1198
+ process.stdout.write(`\x1b[${iR};1H\x1b[2K${inputLine}`);
1199
+ const cursorCol = 5 + tabPreWord.length + tabQuery.length;
1200
+ process.stdout.write(`\x1b[${iR};${cursorCol}H`);
1201
+ tabBoxLines = newLinesCount;
1202
+ tabDrawn = true;
1195
1203
  }
1196
1204
  function closeTabPicker() {
1197
- if (tabDrawn) {
1198
- for (let i = 0; i < tabBoxLines; i++)
1199
- process.stdout.write('\x1b[1A\x1b[2K');
1200
- process.stdout.write('\x1b[2A\x1b[2K');
1201
- tabDrawn = false;
1202
- }
1203
- else {
1204
- process.stdout.write('\r\x1b[K');
1205
+ const rows = process.stdout.rows ?? 24;
1206
+ const sR = rows - 2;
1207
+ if (tabDrawn && tabBoxLines > 0) {
1208
+ for (let r = sR - tabBoxLines; r < sR; r++) {
1209
+ if (r >= 1)
1210
+ process.stdout.write(`\x1b[${r};1H\x1b[2K`);
1211
+ }
1205
1212
  }
1206
1213
  tabBoxLines = 0;
1207
1214
  tabMode = false;
1208
1215
  tabQuery = '';
1209
1216
  tabSel = 0;
1210
1217
  tabPreWord = '';
1218
+ tabDrawn = false;
1211
1219
  }
1212
1220
  function redraw() {
1213
1221
  const cols = process.stdout.columns ?? 80;
1214
- const maxLen = Math.max(cols - 6, 10);
1222
+ const rows = process.stdout.rows ?? 24;
1223
+ const sR = rows - 2;
1224
+ const iR = rows - 1;
1225
+ const maxLen = Math.max(cols - 8, 10);
1215
1226
  let displayPre = prefix;
1216
1227
  if (displayPre.length > maxLen)
1217
1228
  displayPre = '…' + displayPre.slice(-(maxLen - 1));
1218
1229
  let inp;
1230
+ let cursorCol = 5 + displayPre.length;
1219
1231
  if (pasteBlock !== null) {
1232
+ const pTag = `[paste: ${pasteCount} lines]`;
1220
1233
  inp = colors.primary(' › ') + colors.text(displayPre) +
1221
- colors.muted(`[paste: ${pasteCount} lines]`) + colors.text(suffix);
1234
+ colors.muted(pTag) + colors.text(suffix);
1235
+ cursorCol = 5 + displayPre.length + pTag.length + suffix.length;
1222
1236
  }
1223
1237
  else {
1224
- inp = colors.primary(' › ') + colors.text(displayPre);
1238
+ inp = colors.primary(' › ') + colors.text(displayPre) + colors.text(suffix);
1239
+ cursorCol = 5 + displayPre.length;
1225
1240
  }
1226
- process.stdout.write('\r\x1b[K' + inp);
1227
- process.stdout.write('\r\n' + SEP + '\x1b[1A\r' + inp);
1241
+ process.stdout.write(`\x1b[${sR};1H\x1b[2K${SEP}`);
1242
+ process.stdout.write(`\x1b[${iR};1H\x1b[2K${inp}`);
1243
+ process.stdout.write(`\x1b[${iR};${cursorCol}H`);
1228
1244
  }
1229
1245
  function submit() {
1230
1246
  if (atMode)
1231
1247
  closePicker();
1248
+ if (slashMode)
1249
+ closeSlashPicker();
1232
1250
  if (tabMode) {
1233
1251
  prefix = tabPreWord + tabQuery;
1234
1252
  closeTabPicker();
@@ -1243,9 +1261,12 @@ async function readLine(prompt, cwd) {
1243
1261
  const text = parts.join('\n');
1244
1262
  const lns = parts.length > 0 ? parts : (text ? [text] : []);
1245
1263
  cleanup();
1246
- process.stdout.write('\r\x1b[K');
1247
- process.stdout.write('\x1b[1B\x1b[2K');
1248
- process.stdout.write('\x1b[2A\x1b[2K');
1264
+ const rows = process.stdout.rows ?? 24;
1265
+ const sR = rows - 2;
1266
+ const iR = rows - 1;
1267
+ process.stdout.write(`\x1b[${sR};1H\x1b[2K`);
1268
+ process.stdout.write(`\x1b[${iR};1H\x1b[2K`);
1269
+ process.stdout.write(`\x1b[${sR};1H`);
1249
1270
  if (!text) {
1250
1271
  resolve(null);
1251
1272
  return;
@@ -1457,7 +1478,22 @@ async function readLine(prompt, cwd) {
1457
1478
  }
1458
1479
  return;
1459
1480
  }
1460
- if (data === '\r' || data === '\n' || data === '\t') {
1481
+ if (data === '\r' || data === '\n') {
1482
+ const filtered = filterSlash(slashQuery);
1483
+ if (filtered.length > 0) {
1484
+ const cmd = filtered[Math.min(slashSel, filtered.length - 1)];
1485
+ prefix = cmd.name;
1486
+ closeSlashPicker();
1487
+ submit();
1488
+ }
1489
+ else {
1490
+ prefix = '/' + slashQuery;
1491
+ closeSlashPicker();
1492
+ submit();
1493
+ }
1494
+ return;
1495
+ }
1496
+ if (data === '\t') {
1461
1497
  const filtered = filterSlash(slashQuery);
1462
1498
  if (filtered.length > 0) {
1463
1499
  const cmd = filtered[Math.min(slashSel, filtered.length - 1)];
@@ -1707,8 +1743,8 @@ function timeAgoShort(iso) {
1707
1743
  return `${d}d`;
1708
1744
  return `${Math.floor(d / 30)}mo`;
1709
1745
  }
1710
- async function showConversationPicker(cwdForList) {
1711
- const allConvs = listConversations(cwdForList, 100);
1746
+ async function showConversationPicker(cwd) {
1747
+ const allConvs = listConversations(cwd, 100);
1712
1748
  if (allConvs.length === 0) {
1713
1749
  printInfo('No saved conversations yet.');
1714
1750
  return null;
@@ -2145,159 +2181,123 @@ async function showDesignModeLoader(skills) {
2145
2181
  }
2146
2182
  export async function startRepl(cwd) {
2147
2183
  const alwaysAllowed = new Set();
2148
- // ── Terminal title helper ─────────────────────────────────────
2149
- function setTerminalTitle(title) {
2150
- process.stdout.write(`\x1b]0;Dravix Code — ${title}\x07`);
2151
- }
2152
- setTerminalTitle('AI-powered coding assistant');
2153
2184
  const token = getToken() ?? '';
2154
- let SYSTEM_PROMPT = `You are Dravix Code, an interactive CLI coding agent powered by DeepSeek that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
2155
-
2156
- You are a proactive, precise, and safe engineering partner. You think before you act, you verify your work, and you communicate clearly.
2157
-
2158
- IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident that the URLs are for helping the user with programming. You may use URLs provided by the user in their messages or local files.
2159
-
2160
- # Core Principles
2161
-
2162
- - Be correct, helpful, and safe above all else.
2163
- - Think step by step before writing or modifying any code. State the plan in one short sentence before acting.
2164
- - Prefer minimal, surgical changes that preserve the existing codebase structure and conventions.
2165
- - Never guess at APIs, libraries, or configurations. Read the codebase first, or admit uncertainty.
2166
- - Respect the user's time: be concise but never omit crucial details.
2167
- - If a request is genuinely ambiguous and blocks progress, ask one short clarifying question. Otherwise make the reasonable call and continue.
2168
-
2169
- # File Operations
2170
-
2171
- - read_file = read a file (returns numbered lines like " 42 │ code")
2172
- - write_file = CREATE new files only — NEVER overwrite existing files
2173
- - edit_file = MODIFY existing files (use <find>/<replace> with EXACT text from read_file)
2174
- - create_folder = create directories
2175
- - delete_file = delete files
2176
- - run_command = execute shell commands
2177
- - read_folder = list directory contents
2178
- - search_code = search file contents with regex or literal text
2179
- - glob_files = find files by glob pattern like "**/*.ts"
2180
-
2181
- # edit_file rules — CRITICAL
2182
-
2183
- 1. ALWAYS read_file first to see the current content
2184
- 2. Copy the EXACT text from the file into <find> — character by character
2185
- 3. The <find> text must match EXACTLY (including whitespace, indentation)
2186
- 4. Do NOT put line numbers in <find> (e.g., "42 │ code" is WRONG — use only "code")
2187
- 5. If edit fails, the system will show you the actual content — use it to retry
2188
- 6. Multiple edits on the same file: order from BOTTOM to TOP
2189
- 7. NEVER use write_file on an existing file — it destroys user code
2190
-
2191
- # Communication Style
2192
-
2193
- - Output is rendered in a terminal as Markdown. Be short and concise.
2194
- - Reference code with \`file_path:line_number\` so the user can jump to it.
2195
- - Do not start replies with conversational filler ("Got it", "Sure", "Great question"). State the action or result directly.
2196
- - Do not narrate internal deliberation. Give brief updates at key moments.
2197
- - End-of-turn summary: one or two sentences. What changed and what's next.
2198
- - Respond in the same language the user writes in.
2199
-
2200
- # Code Quality
2201
-
2202
- - Keep things in one function unless composable or reusable.
2203
- - Avoid try/catch where possible; avoid the any type.
2204
- - Prefer functional array methods (flatMap/filter/map) over for loops.
2205
- - Prefer const. Use ternaries or early returns instead of reassignment. Avoid else after return.
2206
- - Do not destructure unnecessarily — obj.a over const { a } = obj.
2207
- - Don't add features, refactors, or abstractions beyond what the task requires.
2208
- - Don't add error handling for impossible scenarios.
2209
- - Delete unused code completely rather than leaving _var or comments.
2210
- - Default to writing no comments. Only comment when the WHY is non-obvious.
2211
-
2212
- # Safety
2213
-
2214
- - Never generate malicious, obfuscated, or deceptive code.
2215
- - Do not output secrets, tokens, or private keys.
2216
- - Guard against SQL injection, XSS, path traversal, insecure deserialization, command injection.
2217
- - When you encounter an obstacle, do not use destructive actions as a shortcut.
2218
-
2219
- # Working discipline
2220
-
2221
- - If the user asks to create a file: use write_file, NOT code blocks.
2222
- - Read files before editing them — always!
2223
- - Plan all files first, create them in logical order, run commands last.
2224
- - Only touch files the user explicitly mentioned or clearly required by the task.
2225
- - When fixing a bug, fix the ROOT CAUSE, not just the symptom.
2226
- - If a run_command fails, read the error message and fix the issue.
2227
- - Complete the ENTIRE task in ONE response — do not ask for permission to continue.`;
2185
+ let SYSTEM_PROMPT = 'You are Dravix Code an elite AI coding assistant. The server is temporarily unreachable. Use your best judgment to help the user.';
2228
2186
  if (token) {
2229
2187
  const { prompt, webDesignerSkill } = await fetchSystemPrompt(token);
2230
2188
  if (prompt) {
2231
- SYSTEM_PROMPT = prompt + '\n\n' + SYSTEM_PROMPT;
2189
+ SYSTEM_PROMPT = prompt;
2232
2190
  _serverWebDesignerSkill = webDesignerSkill;
2233
2191
  }
2234
2192
  }
2235
2193
  // activeCwd can change when /resume loads a conversation from a different directory
2236
2194
  let activeCwd = cwd;
2237
2195
  // Rules appended to every system prompt to enforce safe file-operation behavior
2238
- const SAFE_FILE_RULES = `
2239
-
2240
- ## XML Tag Format (REQUIRED — use these EXACTLY)
2241
-
2242
- All file operations MUST use these tags — NEVER put them in markdown code blocks:
2243
-
2244
- ### <write_file path="relative/path"> ... </write_file>
2245
- Create a NEW file. The path is relative to the project root. The content goes between the tags.
2246
-
2247
- ### <edit_file path="relative/path">
2248
- ### <find>EXACT text to find</find>
2249
- ### <replace>replacement text</replace>
2250
- ### </edit_file>
2251
- Modify an EXISTING file. The <find> text must match EXACTLY copy it verbatim from read_file output, WITHOUT the line number prefixes (e.g., if read_file shows " 42 │ code", use just "code" in <find>).
2252
-
2253
- ### <read_file path="relative/path" />
2254
- Read a file. Optional: lines="100-200" to read a specific range.
2255
-
2256
- ### <read_folder path="relative/path" />
2257
- List directory contents with file sizes.
2258
-
2259
- ### <create_folder path="relative/path" />
2260
- Create a new directory (and parents if needed).
2261
-
2262
- ### <delete_file path="relative/path" />
2263
- Delete a file or directory.
2264
-
2265
- ### <run_command>command here</run_command>
2266
- Execute a shell command. Optional: cwd="subdir" to run in a subdirectory.
2267
-
2268
- ### <search_code pattern="text" />
2269
- Search file contents. Options: path="dir", include="*.ts", context="3", regex="true".
2270
-
2271
- ### <glob_files pattern="**/*.ts" />
2272
- Find files by glob pattern.
2273
-
2274
- ## Critical Reminders
2275
-
2276
- - write_file = NEW files ONLY. For existing files, ALWAYS use edit_file.
2277
- - read_file shows line numbers as " N │ code" — strip the "N " prefix when copying into <find>.
2278
- - Multiple edit_file on the same file: order from BOTTOM to TOP (line numbers shift as you edit).
2279
- - DO NOT output <write_file> / <edit_file> / <run_command> tags inside markdown code blocks — they must be raw in the response.
2280
- - Execute ALL operations in ONE response when possible — do not split work across multiple turns.
2281
- - ⚠️ CRITICAL: If you write "now I will run/start/execute/install X" — you MUST also include the <run_command> tag. Saying it is NOT enough. Always output the actual tag with the command.
2282
-
2283
- ## End-of-turn summary REQUIRED after every task
2284
-
2285
- After you finish ALL operations for the user's request, you MUST write a short, natural summary. This is the most important part of your response.
2286
-
2287
- Write 1-3 sentences in the user's language. No tables. No emoji lists. No rigid formats. Just explain what you did, like a colleague updating a teammate.
2288
-
2289
- Good examples:
2290
- - "I deleted BOT_INFO.md from the project. No other changes were made."
2291
- - "Updated main.ts changed the port from 8000 to 9000. The API now listens on port 9000."
2292
- - "Created three files: auth.ts, routes.ts, and models.ts. The basic API structure is ready."
2293
- - "Fixed the import path in app.tsx and removed the unused variable in utils.ts."
2294
-
2295
- Bad examples:
2296
- - Outputting operation tags with no human-readable text
2297
- - Saying only "Done" or "Summary:" with a rigid table of emojis
2298
- - Ending with nothing — just silence after the last XML tag
2299
-
2300
- The summary should feel like a natural conversation close — informative, brief, in the user's language.`;
2196
+ const SAFE_FILE_RULES = `
2197
+
2198
+ ## CORE RULES
2199
+
2200
+ ### READ FIRST, THEN ANSWER
2201
+ When the user asks about their code:
2202
+ 1. Find the relevant file(s)
2203
+ 2. Read them silently
2204
+ 3. Give a direct, accurate answer based on what you found
2205
+ Never guess or fabricate information.
2206
+
2207
+ ### BE ACCURATE
2208
+ - Only report what you actually see in the code
2209
+ - If you are unsure, say sodo not fabricate answers
2210
+ - Never add features or items that do not exist in the code
2211
+
2212
+ ### RESPOND IN THE SAME LANGUAGE
2213
+ - Respond in the same language the user writes in
2214
+ - If user writes in English → respond in English
2215
+ - If user writes in Russian → respond in Russian
2216
+ - And so on for any language
2217
+
2218
+ ### FORMAT ANSWERS CLEARLY
2219
+ - Use bullet points or numbered lists for multiple items
2220
+ - Put each item on its own line
2221
+ - Keep answers concise — 3-10 lines maximum
2222
+ - Do not dump entire file contents unless asked
2223
+ - Do not add unnecessary commentary
2224
+
2225
+ ### WORK SILENTLY
2226
+ - Do not say "I will read the file" — just read it and answer
2227
+ - Do not narrate your thought process
2228
+ - Do not say "I already answered your question"
2229
+ - Give your answer ONCE and STOP
2230
+
2231
+ ### WHEN CREATING/EDITING FILES
2232
+ - write_file = CREATE new files only
2233
+ - edit_file = MODIFY existing (read first, then find/replace)
2234
+ - Before editing: ALWAYS read_file first
2235
+ - Complete the ENTIRE task in ONE response
2236
+ - Use tags, NOT code blocks
2237
+
2238
+ ### WHEN CREATING/EDITING FILES
2239
+ - write_file = CREATE new files only
2240
+ - edit_file = MODIFY existing (read first, then find/replace)
2241
+ - Before editing: ALWAYS read_file first
2242
+ - Multiple edits: order from BOTTOM to TOP
2243
+ - Complete the ENTIRE task in ONE response
2244
+ - Use tags, NOT code blocks
2245
+
2246
+ ### When the user asks you to do something:
2247
+ 1. Read the relevant files first
2248
+ 2. Do the work using <write_file> / <edit_file> tags
2249
+ 3. Summarize what you did in 1-2 sentences
2250
+
2251
+ ### Act decisively
2252
+ - Use judgment and execute immediately when intent is clear.
2253
+ - Do NOT ask unnecessary clarifying questions — just do the task.
2254
+ - Make reasonable assumptions and proceed.
2255
+
2256
+ ### Creating files
2257
+ - NEVER show code in code blocks when the user asks to create files or a project.
2258
+ - ALWAYS use <write_file path="filename"> tags to create actual files.
2259
+ - Code blocks are for short inline explanations only — they do NOT create files.
2260
+ - Output ALL required files in ONE response using <write_file> tags.
2261
+
2262
+ ### File operations
2263
+ - **write_file** = CREATE only. Use it ONLY when a file does NOT exist yet.
2264
+ - **edit_file** = MODIFY existing files. For ANY change to an existing file, always use edit_file with <find>/<replace>.
2265
+ - Before editing any file: use <read_file> to see its current content first.
2266
+ - NEVER overwrite an existing file with write_file — it destroys user code.
2267
+
2268
+ ### How to use edit_file correctly
2269
+ 1. ALWAYS read_file first to see the current content
2270
+ 2. Copy the EXACT text from the file into <find> — character by character
2271
+ 3. The <find> text must match EXACTLY (including whitespace, indentation)
2272
+ 4. If edit fails, the system will show you the actual content — use it to retry
2273
+ 5. Do NOT put line numbers in <find> (e.g., "42 │ code" is WRONG)
2274
+ 6. Do NOT modify the text in <find> — copy it EXACTLY from the file
2275
+
2276
+ ### Searching and navigating
2277
+ - Use <glob_files pattern="**/*.ts" /> to find files by extension
2278
+ - Use <search_code pattern="functionName" /> to find code
2279
+ - Use <search_code pattern="regex.*pattern" regex="true" /> for regex search
2280
+ - Use <search_code pattern="error" include="*.ts" /> to search specific file types
2281
+ - Use <search_code pattern="TODO" context="2" /> to show context around matches
2282
+ - Read files before editing them — always!
2283
+
2284
+ ### Multi-file tasks
2285
+ - Plan all files FIRST (list them in your response)
2286
+ - Create/edit files in LOGICAL ORDER (dependencies first)
2287
+ - Use write_file for ALL new files
2288
+ - Use edit_file for ALL modifications
2289
+ - Run commands LAST (after all files are created)
2290
+
2291
+ ### Error recovery
2292
+ - If an edit_file fails, read the file again and retry with the actual content
2293
+ - If a run_command fails, read the error message and fix the issue
2294
+ - Never give up — keep trying until the task is complete
2295
+
2296
+ ### Scope
2297
+ - Only touch files the user explicitly mentioned or that are clearly required by the task.
2298
+ - Do NOT restructure, rename, or "improve" anything that wasn't asked.
2299
+ - When creating a new project, create ALL necessary files (not just some).
2300
+ - When fixing a bug, fix the ROOT CAUSE, not just the symptom.`;
2301
2301
  const buildSystemMsg = (dir) => ({
2302
2302
  role: 'system',
2303
2303
  content: SYSTEM_PROMPT + SAFE_FILE_RULES + '\n\nProject context:\n' + buildContext(dir),
@@ -2347,8 +2347,8 @@ The summary should feel like a natural conversation close — informative, brief
2347
2347
  process.on('exit', resetTerminal);
2348
2348
  process.on('SIGINT', () => { resetTerminal(); process.exit(0); });
2349
2349
  process.on('SIGTERM', () => { resetTerminal(); process.exit(0); });
2350
- process.on('unhandledRejection', (reason) => { logError('unhandledRejection', reason); /* don't kill — let process continue */ });
2351
- process.on('uncaughtException', (err) => { logError('uncaughtException', err); /* don't kill — let process continue */ });
2350
+ process.on('unhandledRejection', (reason) => { logError('unhandledRejection', reason); resetTerminal(); });
2351
+ process.on('uncaughtException', (err) => { logError('uncaughtException', err); resetTerminal(); });
2352
2352
  // Set raw mode ONCE — never toggle it during the session to avoid CMD getting stuck
2353
2353
  process.stdin.setRawMode(true);
2354
2354
  process.stdin.resume();
@@ -2369,8 +2369,20 @@ The summary should feel like a natural conversation close — informative, brief
2369
2369
  if (readFileContinue) {
2370
2370
  readFileContinue = false;
2371
2371
  readFileTurnCount++;
2372
- // Keep auto-continue prompt SHORTlarge prompts time out DeepSeek reasoning model
2373
- const taskInstruction = `Continue. All necessary file contents are above in context. Complete the task now — do NOT read more files. Output operations with <edit_file> or <run_command> tags.`;
2372
+ // Remind AI of the original user request show first + last line so pasted code
2373
+ // doesn't eclipse the actual instruction written at the end.
2374
+ const rawUserMsg = lastUserLine.trim();
2375
+ const msgLines = rawUserMsg.split('\n').map(l => l.trim()).filter(Boolean);
2376
+ let userReminder = '';
2377
+ if (msgLines.length > 0) {
2378
+ const first = msgLines[0].slice(0, 100);
2379
+ const last = msgLines[msgLines.length - 1].slice(0, 150);
2380
+ const display = msgLines.length <= 2 ? msgLines.join(' / ').slice(0, 200) : `${first} [...] ${last}`;
2381
+ userReminder = `The user's request: "${display}". `;
2382
+ }
2383
+ const taskInstruction = userReminder
2384
+ ? `The user's exact request was: "${rawUserMsg.slice(0, 300)}"\nNow execute THIS request and ONLY this request — nothing else. Do not invent, add, or change anything the user did not ask for. Use <edit_file> with targeted <find>/<replace> — never use <write_file> on existing files. Use the file content above to find the exact text and apply the change.`
2385
+ : `Execute the user's request using the file content above. Use <edit_file> for existing files, <write_file> only for new files.`;
2374
2386
  // Keep using FLASH_MODEL after file reads
2375
2387
  if (readFileTurnCount > 5 && !forcedEditMode) {
2376
2388
  forcedEditMode = true;
@@ -2432,7 +2444,6 @@ The summary should feel like a natural conversation close — informative, brief
2432
2444
  });
2433
2445
  sessionId = convId;
2434
2446
  conversationTitle = conv.title;
2435
- setTerminalTitle(conv.title);
2436
2447
  printConversationHistory(conv.messages);
2437
2448
  }
2438
2449
  }
@@ -2486,13 +2497,11 @@ The summary should feel like a natural conversation close — informative, brief
2486
2497
  history.splice(1, history.length - 22);
2487
2498
  }
2488
2499
  // ── Auto-inject mentioned file contents ──────────────────────
2489
- // Reduced from 3000 keeps prompt small enough for DeepSeek reasoning model
2500
+ // Files 3000 lines: full raw content injected — AI has exact text for <find>, no reads needed.
2490
2501
  const FILE_PATTERN = /[\w\-.\/\\]+\.\w{2,5}/g;
2491
2502
  const mentioned = [...new Set(line.match(FILE_PATTERN) ?? [])];
2492
2503
  let fileContext = '';
2493
- const FULL_INJECT_LINES = 400;
2494
- const MAX_INJECT_CHARS = 25000;
2495
- let injectedChars = 0;
2504
+ const FULL_INJECT_LINES = 3000;
2496
2505
  for (const fname of mentioned) {
2497
2506
  const fullPath = path.resolve(activeCwd, fname);
2498
2507
  if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
@@ -2500,15 +2509,8 @@ The summary should feel like a natural conversation close — informative, brief
2500
2509
  const content = fs.readFileSync(fullPath, 'utf-8');
2501
2510
  const fileLines = content.split('\n');
2502
2511
  const lineCount = fileLines.length;
2503
- if (lineCount <= FULL_INJECT_LINES && injectedChars < MAX_INJECT_CHARS) {
2504
- const toAdd = `\n\n[File: ${fname} — ${lineCount} lines — FULL]\n` + content;
2505
- if (injectedChars + toAdd.length > MAX_INJECT_CHARS) {
2506
- fileContext += `\n\n[File: ${fname} — ${lineCount} lines — skipped (prompt limit)]`;
2507
- }
2508
- else {
2509
- fileContext += toAdd;
2510
- injectedChars += toAdd.length;
2511
- }
2512
+ if (lineCount <= FULL_INJECT_LINES) {
2513
+ fileContext += `\n\n[File: ${fname} — ${lineCount} lines — FULL]\n` + content;
2512
2514
  }
2513
2515
  else {
2514
2516
  fileContext += `\n\n[File: ${fname} — ${lineCount} lines]\n` +
@@ -2542,7 +2544,6 @@ The summary should feel like a natural conversation close — informative, brief
2542
2544
  }
2543
2545
  // ── Ctrl+C cancel during streaming ──────────────────────────
2544
2546
  let streamCancelled = false;
2545
- let streamInputBuffer = '';
2546
2547
  const streamAbort = new AbortController();
2547
2548
  const onStreamKey = (data) => {
2548
2549
  try {
@@ -2550,10 +2551,6 @@ The summary should feel like a natural conversation close — informative, brief
2550
2551
  streamCancelled = true;
2551
2552
  streamAbort.abort();
2552
2553
  }
2553
- else {
2554
- // Capture user input during stream — will be queued for next turn
2555
- streamInputBuffer += data;
2556
- }
2557
2554
  }
2558
2555
  catch { /* ignore */ }
2559
2556
  };
@@ -2938,7 +2935,6 @@ The summary should feel like a natural conversation close — informative, brief
2938
2935
  if (_u && _a) {
2939
2936
  generateAITitle(String(_u.content), String(_a.content), token).then(t => {
2940
2937
  conversationTitle = t;
2941
- setTerminalTitle(t);
2942
2938
  saveConversation(sessionId, t, activeCwd, history.slice(1));
2943
2939
  }).catch(() => { });
2944
2940
  }
@@ -3025,8 +3021,6 @@ The summary should feel like a natural conversation close — informative, brief
3025
3021
  // Trailing newline — skip when read ops follow (they start on current line)
3026
3022
  if (hasRenderedContent && readOps.length === 0)
3027
3023
  process.stdout.write('\n');
3028
- // Synchronisation flag: ops/else must wait for readOps to finish
3029
- let readOpsDone = readOps.length === 0;
3030
3024
  // ── Handle read_file ops ──────────────────────────────
3031
3025
  // forcedEditMode: block further reads — AI must use existing context
3032
3026
  if (readOps.length > 0 && forcedEditMode) {
@@ -3037,7 +3031,6 @@ The summary should feel like a natural conversation close — informative, brief
3037
3031
  readFileContinue = true;
3038
3032
  }
3039
3033
  else if (readOps.length > 0) {
3040
- readOpsDone = false;
3041
3034
  // Absorb blank lines so reading block starts immediately after user message
3042
3035
  if (hasRenderedContent) {
3043
3036
  // Ensure cursor is on a new line, then absorb excess trailing blank lines
@@ -3215,22 +3208,18 @@ The summary should feel like a natural conversation close — informative, brief
3215
3208
  }
3216
3209
  process.stdout.write(` ` + BC('╰' + '─'.repeat(boxW - 2) + '╯') + `\n`);
3217
3210
  readFileContinue = true;
3218
- readOpsDone = true;
3211
+ resolve();
3219
3212
  }
3220
3213
  catch (e) {
3221
3214
  logError('readOps', e);
3222
- readFileContinue = true;
3223
- readOpsDone = true;
3215
+ resolve();
3224
3216
  }
3225
3217
  })();
3218
+ return;
3226
3219
  } // end else if (readOps.length > 0)
3227
- // Process write/run/mkdir/delete ops — waits for readOps to finish first
3228
3220
  if (ops.length > 0) {
3229
3221
  (async () => {
3230
3222
  try {
3231
- // Wait for any pending read operations to finish
3232
- while (!readOpsDone)
3233
- await new Promise(r => setTimeout(r, 20));
3234
3223
  const skippedPaths = new Set();
3235
3224
  const runOutputs = [];
3236
3225
  const fileOpErrors = [];
@@ -3369,12 +3358,7 @@ The summary should feel like a natural conversation close — informative, brief
3369
3358
  })();
3370
3359
  }
3371
3360
  else {
3372
- // No ops — just wait for readOps to finish, then resolve
3373
- (async () => {
3374
- while (!readOpsDone)
3375
- await new Promise(r => setTimeout(r, 20));
3376
- resolve();
3377
- })();
3361
+ resolve();
3378
3362
  }
3379
3363
  }, (err) => {
3380
3364
  clearInterval(spinnerInterval);
@@ -3405,12 +3389,6 @@ The summary should feel like a natural conversation close — informative, brief
3405
3389
  });
3406
3390
  // ── Remove streaming key listener ─────────────────────────
3407
3391
  process.stdin.removeListener('data', onStreamKey);
3408
- // ── Queue captured input for next user turn ─────────────────
3409
- const captured = streamInputBuffer.trim();
3410
- if (captured && !queuedResult) {
3411
- queuedResult = { text: captured, lines: [captured] };
3412
- }
3413
- streamInputBuffer = '';
3414
3392
  // ── Accumulate token usage (deferred — report on next user turn) ──
3415
3393
  if (cliTok && fullResponse) {
3416
3394
  pendingOutputTokens += estimateTokens(normalizeResponse(fullResponse));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taj-special/dravix-code",
3
- "version": "1.4.6",
3
+ "version": "1.4.8",
4
4
  "description": "AI-powered coding assistant CLI — Dravix Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,10 +10,10 @@
10
10
  "dist"
11
11
  ],
12
12
  "scripts": {
13
- "build": "tsc",
13
+ "build": "node node_modules/typescript/lib/tsc.js",
14
14
  "start": "node dist/cli/index.js",
15
- "dev": "tsc --watch",
16
- "prepublishOnly": "tsc"
15
+ "dev": "node node_modules/typescript/lib/tsc.js --watch",
16
+ "prepublishOnly": "node node_modules/typescript/lib/tsc.js"
17
17
  },
18
18
  "dependencies": {
19
19
  "chalk": "^5.3.0"