swarm-code 0.1.20 → 0.1.22

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.
@@ -44,47 +44,93 @@ export function readTextInput(_prompt) {
44
44
  process.stdin.setRawMode(true);
45
45
  process.stdin.resume();
46
46
  process.stdin.setEncoding("utf-8");
47
- // Track how many terminal rows we've drawn so we can clear them
48
- let drawnRows = 0;
49
- function drawBox() {
50
- const out = process.stderr;
51
- // Clear previously drawn rows
52
- if (drawnRows > 0) {
53
- for (let i = 0; i < drawnRows; i++) {
54
- out.write("\x1b[1A\x1b[2K");
55
- }
56
- }
57
- // Top border — thin dim line
58
- const topLine = `${BORDER_COLOR}${"─".repeat(w)}${RESET}`;
59
- out.write(`${topLine}\n`);
60
- // Content rows — dark background, full width
47
+ // Tracks cursor position relative to top border after each drawBox()
48
+ let cursorRowFromTop = 0;
49
+ let prevTotalRows = 0;
50
+ function buildRows() {
51
+ const promptVisibleLen = 2;
52
+ const rows = [];
53
+ // Top border
54
+ rows.push(`${BORDER_COLOR}${"".repeat(w)}${RESET}`);
55
+ // Content rows
61
56
  const promptChar = `${ACCENT_COLOR}❯${RESET} `;
62
- const promptVisibleLen = 2; // "❯ "
63
57
  for (let i = 0; i < linesBuf.length; i++) {
64
58
  const lineText = linesBuf[i];
65
59
  const prefix = i === 0 ? promptChar : `${dim("·")} `;
66
- const prefixVisibleLen = promptVisibleLen;
67
- // How much space for text content
68
- const contentWidth = w - prefixVisibleLen;
69
- // Truncate display if line is too long
60
+ const contentWidth = w - promptVisibleLen;
70
61
  const displayText = lineText.length > contentWidth ? lineText.slice(0, contentWidth - 1) + "…" : lineText;
71
62
  const padding = Math.max(0, contentWidth - displayText.length);
72
- out.write(`${BG_DARK}${prefix}${displayText}${" ".repeat(padding)}${RESET}\n`);
63
+ rows.push(`${BG_DARK}${prefix}${displayText}${" ".repeat(padding)}${RESET}`);
64
+ }
65
+ // Bottom border
66
+ rows.push(`${ACCENT_COLOR}${"─".repeat(w)}${RESET}`);
67
+ // Hints — pad to full width so it fully overwrites old content
68
+ const hintsText = " enter submit esc exit";
69
+ const hintsPad = Math.max(0, w - hintsText.length);
70
+ rows.push(`${dim(hintsText)}${" ".repeat(hintsPad)}`);
71
+ return rows;
72
+ }
73
+ function drawBox() {
74
+ const out = process.stderr;
75
+ const promptVisibleLen = 2;
76
+ const rows = buildRows();
77
+ const totalRows = rows.length;
78
+ if (prevTotalRows > 0) {
79
+ // ── Redraw: overwrite rows in place (no \n, no scrolling) ──
80
+ // Move cursor to top border row
81
+ if (cursorRowFromTop > 0) {
82
+ out.write(`\x1b[${cursorRowFromTop}A`);
83
+ }
84
+ out.write("\r");
85
+ // Overwrite each row in place
86
+ const commonRows = Math.min(totalRows, prevTotalRows);
87
+ for (let i = 0; i < commonRows; i++) {
88
+ out.write(`\x1b[2K${rows[i]}`);
89
+ if (i < commonRows - 1) {
90
+ out.write("\x1b[1B\r"); // cursor down 1 (no scroll), start of line
91
+ }
92
+ }
93
+ if (totalRows > prevTotalRows) {
94
+ // More rows than before (multi-line paste) — append with \n
95
+ for (let i = commonRows; i < totalRows; i++) {
96
+ out.write(`\n\x1b[2K${rows[i]}`);
97
+ }
98
+ }
99
+ else if (prevTotalRows > totalRows) {
100
+ // Fewer rows than before — erase leftover old rows
101
+ for (let i = totalRows; i < prevTotalRows; i++) {
102
+ out.write("\x1b[1B\r\x1b[2K");
103
+ }
104
+ // Move back to last new row
105
+ const extra = prevTotalRows - totalRows;
106
+ if (extra > 0)
107
+ out.write(`\x1b[${extra}A`);
108
+ }
109
+ // Cursor is now on the last row (hints).
110
+ }
111
+ else {
112
+ // ── Initial draw: use \n between rows, no trailing \n ──
113
+ for (let i = 0; i < totalRows; i++) {
114
+ if (i > 0)
115
+ out.write("\n");
116
+ out.write(rows[i]);
117
+ }
118
+ // Cursor is on the hints line (last row).
73
119
  }
74
- // Bottom border — accent colored
75
- const bottomLine = `${ACCENT_COLOR}${"─".repeat(w)}${RESET}`;
76
- out.write(`${bottomLine}\n`);
77
- // Hints
78
- out.write(`${dim(" enter submit esc exit")}\n`);
79
- drawnRows = linesBuf.length + 3; // top border + content lines + bottom border + hints
80
- // Position cursor inside the text area
81
- // We're at the bottom (after hints). Move up to the correct content row.
82
- const currentLineIdx = linesBuf.length - 1; // cursor is always on last line
83
- const rowsFromBottom = 2 + (linesBuf.length - 1 - currentLineIdx); // hints + bottom border + lines below cursor
84
- out.write(`\x1b[${rowsFromBottom}A`);
85
- // Move to correct column: prefix width + cursor position
120
+ prevTotalRows = totalRows;
121
+ // Position cursor at the active content row
122
+ // Cursor is currently on the hints line (last row = index totalRows-1).
123
+ // Content cursor is on row (1 + currentLineIdx).
124
+ const currentLineIdx = linesBuf.length - 1;
125
+ const targetRow = 1 + currentLineIdx;
126
+ const hintsRow = totalRows - 1;
127
+ const rowsUp = hintsRow - targetRow;
128
+ if (rowsUp > 0)
129
+ out.write(`\x1b[${rowsUp}A`);
130
+ // Set column
86
131
  const col = promptVisibleLen + cursorPos + 1;
87
132
  out.write(`\x1b[${col}G`);
133
+ cursorRowFromTop = targetRow;
88
134
  }
89
135
  // Initial draw
90
136
  process.stderr.write(HIDE_CURSOR);
@@ -106,8 +152,6 @@ export function readTextInput(_prompt) {
106
152
  linesBuf.push(line);
107
153
  cursorPos = line.length;
108
154
  }
109
- // Move cursor back to top of box before redraw
110
- moveCursorToBoxTop();
111
155
  drawBox();
112
156
  return;
113
157
  }
@@ -120,28 +164,24 @@ export function readTextInput(_prompt) {
120
164
  if (code === "C" && cursorPos < linesBuf[linesBuf.length - 1].length) {
121
165
  cursorPos++;
122
166
  i += 2;
123
- moveCursorToBoxTop();
124
167
  drawBox();
125
168
  continue;
126
169
  }
127
170
  if (code === "D" && cursorPos > 0) {
128
171
  cursorPos--;
129
172
  i += 2;
130
- moveCursorToBoxTop();
131
173
  drawBox();
132
174
  continue;
133
175
  }
134
176
  if (code === "H") {
135
177
  cursorPos = 0;
136
178
  i += 2;
137
- moveCursorToBoxTop();
138
179
  drawBox();
139
180
  continue;
140
181
  }
141
182
  if (code === "F") {
142
183
  cursorPos = linesBuf[linesBuf.length - 1].length;
143
184
  i += 2;
144
- moveCursorToBoxTop();
145
185
  drawBox();
146
186
  continue;
147
187
  }
@@ -179,7 +219,6 @@ export function readTextInput(_prompt) {
179
219
  const line = linesBuf[linesBuf.length - 1];
180
220
  linesBuf[linesBuf.length - 1] = line.slice(0, cursorPos - 1) + line.slice(cursorPos);
181
221
  cursorPos--;
182
- moveCursorToBoxTop();
183
222
  drawBox();
184
223
  }
185
224
  continue;
@@ -189,7 +228,6 @@ export function readTextInput(_prompt) {
189
228
  const line = linesBuf[linesBuf.length - 1];
190
229
  linesBuf[linesBuf.length - 1] = line.slice(0, cursorPos) + " " + line.slice(cursorPos);
191
230
  cursorPos += 2;
192
- moveCursorToBoxTop();
193
231
  drawBox();
194
232
  continue;
195
233
  }
@@ -198,32 +236,20 @@ export function readTextInput(_prompt) {
198
236
  const line = linesBuf[linesBuf.length - 1];
199
237
  linesBuf[linesBuf.length - 1] = line.slice(0, cursorPos) + ch + line.slice(cursorPos);
200
238
  cursorPos++;
201
- moveCursorToBoxTop();
202
239
  drawBox();
203
240
  }
204
241
  }
205
242
  };
206
- function moveCursorToBoxTop() {
207
- // From current cursor position (inside the text area), move to the line
208
- // before the top border so drawBox() can clear and redraw from there.
209
- // Current cursor is at content row (linesBuf.length - 1 from top border)
210
- // We need to go up past: content rows above cursor + top border
211
- // But drawBox handles clearing with drawnRows, so just go up to start
212
- const currentLineIdx = linesBuf.length - 1;
213
- const rowsUp = currentLineIdx + 1; // content lines above + top border
214
- if (rowsUp > 0) {
215
- process.stderr.write(`\x1b[${rowsUp}A`);
216
- }
217
- process.stderr.write("\x1b[0G");
218
- }
219
243
  function finishAndClear() {
220
244
  process.stdin.removeListener("data", onData);
221
245
  if (origRawMode !== undefined) {
222
246
  process.stdin.setRawMode(origRawMode);
223
247
  }
224
- // Move cursor to top of box and clear everything
225
- moveCursorToBoxTop();
226
- process.stderr.write("\x1b[J"); // erase to end of screen
248
+ // Move cursor to top of box
249
+ if (cursorRowFromTop > 0) {
250
+ process.stderr.write(`\x1b[${cursorRowFromTop}A`);
251
+ }
252
+ process.stderr.write("\r\x1b[J"); // erase from here to end of screen
227
253
  // Write the submitted text as a clean line (so it's visible in scrollback)
228
254
  const fullText = linesBuf.join("\n").trim();
229
255
  if (fullText) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swarm-code",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "Open-source swarm-native coding agent orchestrator — spawns parallel coding agents in isolated git worktrees, built on RLM (arXiv:2512.24601)",
5
5
  "type": "module",
6
6
  "bin": {