swarm-code 0.1.19 → 0.1.21
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/interactive-swarm.js +34 -0
- package/dist/ui/text-input.js +18 -33
- package/package.json +1 -1
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import "./env.js";
|
|
20
20
|
import * as fs from "node:fs";
|
|
21
|
+
import * as os from "node:os";
|
|
21
22
|
import * as path from "node:path";
|
|
22
23
|
import { readTextInput } from "./ui/text-input.js";
|
|
23
24
|
// Dynamic imports — ensures env.js has set process.env BEFORE pi-ai loads
|
|
@@ -691,6 +692,25 @@ async function cmdConfigure(config, resolved) {
|
|
|
691
692
|
default:
|
|
692
693
|
logWarn("Invalid option");
|
|
693
694
|
}
|
|
695
|
+
// Persist config changes to ~/.swarm/config.yaml
|
|
696
|
+
if (choice >= "1" && choice <= "7") {
|
|
697
|
+
try {
|
|
698
|
+
const configDir = path.join(os.homedir(), ".swarm");
|
|
699
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
700
|
+
const configLines = [
|
|
701
|
+
"# Swarm user preferences (updated by /configure)",
|
|
702
|
+
`# Updated: ${new Date().toISOString()}`,
|
|
703
|
+
"",
|
|
704
|
+
`default_agent: ${config.default_agent}`,
|
|
705
|
+
`default_model: ${config.default_model}`,
|
|
706
|
+
"",
|
|
707
|
+
];
|
|
708
|
+
fs.writeFileSync(path.join(configDir, "config.yaml"), configLines.join("\n"), "utf-8");
|
|
709
|
+
}
|
|
710
|
+
catch {
|
|
711
|
+
// Non-fatal
|
|
712
|
+
}
|
|
713
|
+
}
|
|
694
714
|
out.write("\n");
|
|
695
715
|
}
|
|
696
716
|
// ── Interactive banner ──────────────────────────────────────────────────────
|
|
@@ -842,6 +862,7 @@ export async function runInteractiveSwarm(rawArgs) {
|
|
|
842
862
|
// Start Python REPL
|
|
843
863
|
await repl.start(sessionAc.signal);
|
|
844
864
|
let currentTaskAc = null;
|
|
865
|
+
let currentRunLog = null;
|
|
845
866
|
let cleanupCalled = false;
|
|
846
867
|
async function cleanup() {
|
|
847
868
|
if (cleanupCalled)
|
|
@@ -885,6 +906,17 @@ export async function runInteractiveSwarm(rawArgs) {
|
|
|
885
906
|
},
|
|
886
907
|
files,
|
|
887
908
|
});
|
|
909
|
+
// Log thread to current run
|
|
910
|
+
currentRunLog?.addThread({
|
|
911
|
+
id: threadId,
|
|
912
|
+
task,
|
|
913
|
+
agent: resolvedAgent,
|
|
914
|
+
model: resolvedModel,
|
|
915
|
+
success: result.success,
|
|
916
|
+
durationMs: result.durationMs,
|
|
917
|
+
filesChanged: result.filesChanged,
|
|
918
|
+
error: result.success ? undefined : result.summary,
|
|
919
|
+
});
|
|
888
920
|
// Record episode or failure
|
|
889
921
|
if (result.success) {
|
|
890
922
|
if (episodicMemory && routeSlot) {
|
|
@@ -946,6 +978,7 @@ export async function runInteractiveSwarm(rawArgs) {
|
|
|
946
978
|
spinner.start();
|
|
947
979
|
const startTime = Date.now();
|
|
948
980
|
const runLog = new RunLogger(query, resolved.model.id, config.default_agent, args.dir, config.max_iterations || 20);
|
|
981
|
+
currentRunLog = runLog;
|
|
949
982
|
try {
|
|
950
983
|
// Update episodic memory hints per-task
|
|
951
984
|
let taskSystemPrompt = systemPrompt;
|
|
@@ -1017,6 +1050,7 @@ export async function runInteractiveSwarm(rawArgs) {
|
|
|
1017
1050
|
finally {
|
|
1018
1051
|
sessionAc.signal.removeEventListener("abort", onSessionAbort);
|
|
1019
1052
|
currentTaskAc = null;
|
|
1053
|
+
currentRunLog = null;
|
|
1020
1054
|
}
|
|
1021
1055
|
};
|
|
1022
1056
|
// Process a line of input
|
package/dist/ui/text-input.js
CHANGED
|
@@ -44,28 +44,31 @@ export function readTextInput(_prompt) {
|
|
|
44
44
|
process.stdin.setRawMode(true);
|
|
45
45
|
process.stdin.resume();
|
|
46
46
|
process.stdin.setEncoding("utf-8");
|
|
47
|
-
//
|
|
48
|
-
let
|
|
47
|
+
// How many rows below the top border the cursor sits after drawBox()
|
|
48
|
+
let cursorRowFromTop = 0;
|
|
49
|
+
let hasDrawn = false;
|
|
49
50
|
function drawBox() {
|
|
50
51
|
const out = process.stderr;
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
const promptVisibleLen = 2; // "❯ "
|
|
53
|
+
if (hasDrawn) {
|
|
54
|
+
// Move cursor from its current position back to the top border row
|
|
55
|
+
if (cursorRowFromTop > 0) {
|
|
56
|
+
out.write(`\x1b[${cursorRowFromTop}A`);
|
|
55
57
|
}
|
|
58
|
+
out.write("\x1b[0G"); // go to column 0
|
|
59
|
+
out.write("\x1b[J"); // erase from cursor to end of screen
|
|
56
60
|
}
|
|
61
|
+
hasDrawn = true;
|
|
57
62
|
// Top border — thin dim line
|
|
58
63
|
const topLine = `${BORDER_COLOR}${"─".repeat(w)}${RESET}`;
|
|
59
64
|
out.write(`${topLine}\n`);
|
|
60
65
|
// Content rows — dark background, full width
|
|
61
66
|
const promptChar = `${ACCENT_COLOR}❯${RESET} `;
|
|
62
|
-
const promptVisibleLen = 2; // "❯ "
|
|
63
67
|
for (let i = 0; i < linesBuf.length; i++) {
|
|
64
68
|
const lineText = linesBuf[i];
|
|
65
69
|
const prefix = i === 0 ? promptChar : `${dim("·")} `;
|
|
66
|
-
const prefixVisibleLen = promptVisibleLen;
|
|
67
70
|
// How much space for text content
|
|
68
|
-
const contentWidth = w -
|
|
71
|
+
const contentWidth = w - promptVisibleLen;
|
|
69
72
|
// Truncate display if line is too long
|
|
70
73
|
const displayText = lineText.length > contentWidth ? lineText.slice(0, contentWidth - 1) + "…" : lineText;
|
|
71
74
|
const padding = Math.max(0, contentWidth - displayText.length);
|
|
@@ -76,7 +79,6 @@ export function readTextInput(_prompt) {
|
|
|
76
79
|
out.write(`${bottomLine}\n`);
|
|
77
80
|
// Hints
|
|
78
81
|
out.write(`${dim(" enter submit esc exit")}\n`);
|
|
79
|
-
drawnRows = linesBuf.length + 3; // top border + content lines + bottom border + hints
|
|
80
82
|
// Position cursor inside the text area
|
|
81
83
|
// We're at the bottom (after hints). Move up to the correct content row.
|
|
82
84
|
const currentLineIdx = linesBuf.length - 1; // cursor is always on last line
|
|
@@ -85,6 +87,8 @@ export function readTextInput(_prompt) {
|
|
|
85
87
|
// Move to correct column: prefix width + cursor position
|
|
86
88
|
const col = promptVisibleLen + cursorPos + 1;
|
|
87
89
|
out.write(`\x1b[${col}G`);
|
|
90
|
+
// Track where cursor ended up (row 0 = top border)
|
|
91
|
+
cursorRowFromTop = 1 + currentLineIdx;
|
|
88
92
|
}
|
|
89
93
|
// Initial draw
|
|
90
94
|
process.stderr.write(HIDE_CURSOR);
|
|
@@ -106,8 +110,6 @@ export function readTextInput(_prompt) {
|
|
|
106
110
|
linesBuf.push(line);
|
|
107
111
|
cursorPos = line.length;
|
|
108
112
|
}
|
|
109
|
-
// Move cursor back to top of box before redraw
|
|
110
|
-
moveCursorToBoxTop();
|
|
111
113
|
drawBox();
|
|
112
114
|
return;
|
|
113
115
|
}
|
|
@@ -120,28 +122,24 @@ export function readTextInput(_prompt) {
|
|
|
120
122
|
if (code === "C" && cursorPos < linesBuf[linesBuf.length - 1].length) {
|
|
121
123
|
cursorPos++;
|
|
122
124
|
i += 2;
|
|
123
|
-
moveCursorToBoxTop();
|
|
124
125
|
drawBox();
|
|
125
126
|
continue;
|
|
126
127
|
}
|
|
127
128
|
if (code === "D" && cursorPos > 0) {
|
|
128
129
|
cursorPos--;
|
|
129
130
|
i += 2;
|
|
130
|
-
moveCursorToBoxTop();
|
|
131
131
|
drawBox();
|
|
132
132
|
continue;
|
|
133
133
|
}
|
|
134
134
|
if (code === "H") {
|
|
135
135
|
cursorPos = 0;
|
|
136
136
|
i += 2;
|
|
137
|
-
moveCursorToBoxTop();
|
|
138
137
|
drawBox();
|
|
139
138
|
continue;
|
|
140
139
|
}
|
|
141
140
|
if (code === "F") {
|
|
142
141
|
cursorPos = linesBuf[linesBuf.length - 1].length;
|
|
143
142
|
i += 2;
|
|
144
|
-
moveCursorToBoxTop();
|
|
145
143
|
drawBox();
|
|
146
144
|
continue;
|
|
147
145
|
}
|
|
@@ -179,7 +177,6 @@ export function readTextInput(_prompt) {
|
|
|
179
177
|
const line = linesBuf[linesBuf.length - 1];
|
|
180
178
|
linesBuf[linesBuf.length - 1] = line.slice(0, cursorPos - 1) + line.slice(cursorPos);
|
|
181
179
|
cursorPos--;
|
|
182
|
-
moveCursorToBoxTop();
|
|
183
180
|
drawBox();
|
|
184
181
|
}
|
|
185
182
|
continue;
|
|
@@ -189,7 +186,6 @@ export function readTextInput(_prompt) {
|
|
|
189
186
|
const line = linesBuf[linesBuf.length - 1];
|
|
190
187
|
linesBuf[linesBuf.length - 1] = line.slice(0, cursorPos) + " " + line.slice(cursorPos);
|
|
191
188
|
cursorPos += 2;
|
|
192
|
-
moveCursorToBoxTop();
|
|
193
189
|
drawBox();
|
|
194
190
|
continue;
|
|
195
191
|
}
|
|
@@ -198,31 +194,20 @@ export function readTextInput(_prompt) {
|
|
|
198
194
|
const line = linesBuf[linesBuf.length - 1];
|
|
199
195
|
linesBuf[linesBuf.length - 1] = line.slice(0, cursorPos) + ch + line.slice(cursorPos);
|
|
200
196
|
cursorPos++;
|
|
201
|
-
moveCursorToBoxTop();
|
|
202
197
|
drawBox();
|
|
203
198
|
}
|
|
204
199
|
}
|
|
205
200
|
};
|
|
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
201
|
function finishAndClear() {
|
|
220
202
|
process.stdin.removeListener("data", onData);
|
|
221
203
|
if (origRawMode !== undefined) {
|
|
222
204
|
process.stdin.setRawMode(origRawMode);
|
|
223
205
|
}
|
|
224
206
|
// Move cursor to top of box and clear everything
|
|
225
|
-
|
|
207
|
+
if (cursorRowFromTop > 0) {
|
|
208
|
+
process.stderr.write(`\x1b[${cursorRowFromTop}A`);
|
|
209
|
+
}
|
|
210
|
+
process.stderr.write("\x1b[0G");
|
|
226
211
|
process.stderr.write("\x1b[J"); // erase to end of screen
|
|
227
212
|
// Write the submitted text as a clean line (so it's visible in scrollback)
|
|
228
213
|
const fullText = linesBuf.join("\n").trim();
|
package/package.json
CHANGED