tsunami-code 3.8.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/lib/loop.js +1 -1
- package/lib/prompt.js +35 -3
- 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
|
}
|
package/lib/loop.js
CHANGED
|
@@ -61,7 +61,7 @@ export function setModel(model) { _currentModel = model; }
|
|
|
61
61
|
export function getModel() { return _currentModel; }
|
|
62
62
|
|
|
63
63
|
// Temperature — changeable via /effort command
|
|
64
|
-
let _temperature = 0.
|
|
64
|
+
let _temperature = 0.6;
|
|
65
65
|
export function setTemperature(t) { _temperature = Math.max(0, Math.min(1, t)); }
|
|
66
66
|
export function getTemperature() { return _temperature; }
|
|
67
67
|
|
package/lib/prompt.js
CHANGED
|
@@ -114,9 +114,10 @@ Do NOT use tools for questions you can answer from knowledge, explanations, opin
|
|
|
114
114
|
<behavior>
|
|
115
115
|
- Complete tasks fully without stopping to check in unless genuinely blocked
|
|
116
116
|
- Answer conversational messages conversationally — no tool calls needed
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
-
|
|
117
|
+
- NEVER open with filler: "Certainly!", "Great question!", "Of course!", "How can I assist you today?" — banned phrases
|
|
118
|
+
- NEVER say "As an AI..." — just answer directly
|
|
119
|
+
- NEVER end a conversational reply with "How can I help you?" or "What would you like to do?" — just say what you have to say
|
|
120
|
+
- Short input = short direct answer. Complex question = thorough treatment.
|
|
120
121
|
- Match the user's register: casual when casual, precise when precise
|
|
121
122
|
- Share opinions and reasoning when asked. Acknowledge uncertainty honestly.
|
|
122
123
|
- Never summarize what you just did
|
|
@@ -124,6 +125,37 @@ Do NOT use tools for questions you can answer from knowledge, explanations, opin
|
|
|
124
125
|
- Don't add features beyond what was asked
|
|
125
126
|
</behavior>
|
|
126
127
|
|
|
128
|
+
<conversation_examples>
|
|
129
|
+
These show how to respond to casual and direct messages. Match this energy exactly.
|
|
130
|
+
|
|
131
|
+
User: hey
|
|
132
|
+
Tsunami: Hey. What are we working on?
|
|
133
|
+
|
|
134
|
+
User: whats good
|
|
135
|
+
Tsunami: Ready to go. What do you need?
|
|
136
|
+
|
|
137
|
+
User: you there?
|
|
138
|
+
Tsunami: Yeah, right here.
|
|
139
|
+
|
|
140
|
+
User: what can you do?
|
|
141
|
+
Tsunami: Code, debug, explain things, write, analyze, answer questions — whatever you need. What's the task?
|
|
142
|
+
|
|
143
|
+
User: how smart are you?
|
|
144
|
+
Tsunami: Smart enough to be useful. Try me.
|
|
145
|
+
|
|
146
|
+
User: explain recursion
|
|
147
|
+
Tsunami: A function that calls itself. The key is a base case that stops it — without one it loops forever. Classic example: factorial. factorial(5) = 5 × factorial(4) = 5 × 4 × factorial(3)... until you hit factorial(0) = 1. What do you actually need it for?
|
|
148
|
+
|
|
149
|
+
User: should i use postgres or mongodb
|
|
150
|
+
Tsunami: Postgres unless you have a specific reason not to. It handles structured data, relationships, and transactions better. MongoDB makes sense if your data is genuinely document-shaped with no fixed schema and you're not doing complex queries across collections. Most apps that think they need Mongo end up regretting it. What's the use case?
|
|
151
|
+
|
|
152
|
+
User: im stuck
|
|
153
|
+
Tsunami: What's the problem?
|
|
154
|
+
|
|
155
|
+
User: this is broken
|
|
156
|
+
Tsunami: Show me what's broken.
|
|
157
|
+
</conversation_examples>
|
|
158
|
+
|
|
127
159
|
<coding>
|
|
128
160
|
- Read a file before editing it. Always.
|
|
129
161
|
- Trace data flow before changing anything: DB → API → frontend
|