tsunami-code 3.9.0 → 3.10.0
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/index.js +51 -6
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from './lib/memory.js';
|
|
27
27
|
import { listMemories, readMemory, saveMemory, deleteMemory, getMemdirPath } from './lib/memdir.js';
|
|
28
28
|
|
|
29
|
-
const VERSION = '3.
|
|
29
|
+
const VERSION = '3.10.0';
|
|
30
30
|
const CONFIG_DIR = join(os.homedir(), '.tsunami-code');
|
|
31
31
|
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
32
32
|
const DEFAULT_SERVER = 'https://radiometric-reita-amuck.ngrok-free.dev';
|
|
@@ -68,13 +68,38 @@ function printBanner(serverUrl) {
|
|
|
68
68
|
console.log(dim(' International AI Wars\n'));
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
function printToolCall(name, args) {
|
|
71
|
+
function printToolCall(name, args, elapsedMs) {
|
|
72
72
|
let parsed = {};
|
|
73
73
|
try { parsed = JSON.parse(args); } catch {}
|
|
74
74
|
const preview = Object.entries(parsed)
|
|
75
75
|
.map(([k, v]) => `${k}=${JSON.stringify(String(v).slice(0, 60))}`)
|
|
76
76
|
.join(', ');
|
|
77
|
-
|
|
77
|
+
const timing = elapsedMs != null ? chalk.dim(` (${(elapsedMs / 1000).toFixed(1)}s)`) : '';
|
|
78
|
+
process.stdout.write('\n' + dim(` ⚙ ${name}`) + timing + dim(`(${preview})\n`));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Spinner — shows while waiting for first token ────────────────────────────
|
|
82
|
+
function createSpinner() {
|
|
83
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
84
|
+
const labels = ['thinking', 'pondering', 'reasoning', 'analyzing'];
|
|
85
|
+
let fi = 0, li = 0, ticks = 0, interval = null;
|
|
86
|
+
|
|
87
|
+
const start = () => {
|
|
88
|
+
interval = setInterval(() => {
|
|
89
|
+
ticks++;
|
|
90
|
+
if (ticks % 25 === 0) li = (li + 1) % labels.length; // rotate label every ~2s
|
|
91
|
+
process.stdout.write(`\r ${dim(frames[fi++ % frames.length] + ' ' + labels[li] + '...')} `);
|
|
92
|
+
}, 80);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const stop = () => {
|
|
96
|
+
if (!interval) return;
|
|
97
|
+
clearInterval(interval);
|
|
98
|
+
interval = null;
|
|
99
|
+
process.stdout.write('\r' + ' '.repeat(35) + '\r');
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return { start, stop };
|
|
78
103
|
}
|
|
79
104
|
|
|
80
105
|
function formatBytes(bytes) {
|
|
@@ -1188,26 +1213,45 @@ Output ONLY the TSUNAMI.md content, starting with "# Project: <name>"`;
|
|
|
1188
1213
|
// Open a new undo turn — all Write/Edit calls during this turn grouped together
|
|
1189
1214
|
beginUndoTurn();
|
|
1190
1215
|
|
|
1216
|
+
const spinner = createSpinner();
|
|
1217
|
+
const turnStart = Date.now();
|
|
1191
1218
|
let firstToken = true;
|
|
1219
|
+
let toolTimers = {}; // track per-tool duration
|
|
1220
|
+
|
|
1221
|
+
spinner.start();
|
|
1192
1222
|
const highlight = createHighlighter((s) => process.stdout.write(s));
|
|
1223
|
+
|
|
1193
1224
|
try {
|
|
1194
1225
|
await agentLoop(
|
|
1195
1226
|
currentServerUrl,
|
|
1196
1227
|
fullMessages,
|
|
1197
1228
|
(token) => {
|
|
1198
|
-
if (firstToken) {
|
|
1229
|
+
if (firstToken) {
|
|
1230
|
+
spinner.stop();
|
|
1231
|
+
process.stdout.write(' ');
|
|
1232
|
+
firstToken = false;
|
|
1233
|
+
}
|
|
1199
1234
|
highlight(token);
|
|
1200
1235
|
},
|
|
1201
1236
|
(toolName, toolArgs) => {
|
|
1202
1237
|
flushHighlighter(highlight);
|
|
1203
|
-
|
|
1238
|
+
spinner.stop();
|
|
1239
|
+
const elapsed = toolTimers[toolName] != null ? Date.now() - toolTimers[toolName] : null;
|
|
1240
|
+
toolTimers[toolName] = Date.now();
|
|
1241
|
+
printToolCall(toolName, toolArgs, elapsed);
|
|
1242
|
+
spinner.start();
|
|
1204
1243
|
firstToken = true;
|
|
1205
1244
|
},
|
|
1206
1245
|
{ sessionDir, cwd, planMode },
|
|
1207
1246
|
makeConfirmCallback(rl)
|
|
1208
1247
|
);
|
|
1248
|
+
spinner.stop();
|
|
1209
1249
|
flushHighlighter(highlight);
|
|
1210
1250
|
|
|
1251
|
+
const elapsed = ((Date.now() - turnStart) / 1000).toFixed(1);
|
|
1252
|
+
const tok = tokenStats.output > 0 ? ` · ${tokenStats.output - (_outputTokens || 0)} tok` : '';
|
|
1253
|
+
process.stdout.write(dim(`\n ↳ ${elapsed}s${tok}\n`));
|
|
1254
|
+
|
|
1211
1255
|
// Token estimation
|
|
1212
1256
|
const inputChars = fullMessages.reduce((s, m) => s + (typeof m.content === 'string' ? m.content.length : 0), 0);
|
|
1213
1257
|
_inputTokens += Math.round(inputChars / 4);
|
|
@@ -1230,8 +1274,9 @@ Output ONLY the TSUNAMI.md content, starting with "# Project: <name>"`;
|
|
|
1230
1274
|
const newSkills = loadSkills(cwd);
|
|
1231
1275
|
if (newSkills.length !== skills.length) skills = newSkills;
|
|
1232
1276
|
|
|
1233
|
-
process.stdout.write('\n
|
|
1277
|
+
process.stdout.write('\n');
|
|
1234
1278
|
} catch (e) {
|
|
1279
|
+
spinner.stop();
|
|
1235
1280
|
process.stdout.write('\n');
|
|
1236
1281
|
console.error(red(` Error: ${e.message}\n`));
|
|
1237
1282
|
}
|