codeshark-cli 0.1.6 → 0.1.7
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/agent.js +1 -0
- package/dist/index.js +30 -18
- package/dist/loading.js +69 -0
- package/dist/repl.js +28 -2
- package/package.json +1 -1
package/dist/agent.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createInterface } from "node:readline/promises";
|
|
|
4
4
|
import { stdin as input, stdout as output } from "node:process";
|
|
5
5
|
import { bold, dim, hex } from "./ansi.js";
|
|
6
6
|
import { printBanner } from "./banner.js";
|
|
7
|
-
import { showLoading,
|
|
7
|
+
import { showLoading, startActivityIndicator, toolPhase } from "./loading.js";
|
|
8
8
|
import { loadConfig, saveConfig } from "./config.js";
|
|
9
9
|
import { readTerms } from "./terms.js";
|
|
10
10
|
import { resolveClients } from "./provider/index.js";
|
|
@@ -77,35 +77,48 @@ async function runOneShot(prompt) {
|
|
|
77
77
|
const cwd = process.cwd();
|
|
78
78
|
const registry = createRegistry();
|
|
79
79
|
const clients = resolveClients(cfg, (m) => console.error(dim(m)));
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
// A live indicator shows what the agent is doing (Thinking, Reading
|
|
81
|
+
// files, Running commands, …) until the first token arrives, then the
|
|
82
|
+
// answer streams over it.
|
|
83
|
+
let activityActive = true;
|
|
84
|
+
const stopActivity = () => {
|
|
85
|
+
if (activityActive) {
|
|
86
|
+
activityActive = false;
|
|
87
|
+
activity.stop();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const activity = startActivityIndicator("Thinking");
|
|
83
91
|
const events = {
|
|
84
92
|
onText: (d) => {
|
|
85
|
-
|
|
86
|
-
thinking = false;
|
|
87
|
-
stopThinking();
|
|
88
|
-
}
|
|
93
|
+
stopActivity();
|
|
89
94
|
process.stdout.write(d);
|
|
90
95
|
},
|
|
96
|
+
onToolCall: (call) => {
|
|
97
|
+
activity.setPhase(toolPhase(call.name));
|
|
98
|
+
},
|
|
99
|
+
onToolResult: () => {
|
|
100
|
+
activity.setPhase("Thinking");
|
|
101
|
+
},
|
|
91
102
|
};
|
|
92
103
|
const approvalRl = process.stdin.isTTY ? createInterface({ input, output }) : undefined;
|
|
93
104
|
try {
|
|
94
105
|
const result = await runAgent(prompt, {
|
|
95
106
|
clients,
|
|
96
107
|
registry,
|
|
97
|
-
cwd,
|
|
98
|
-
approveToolCall: approvalRl
|
|
108
|
+
cwd, approveToolCall: approvalRl
|
|
99
109
|
? async (call) => {
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
activity.pause();
|
|
111
|
+
try {
|
|
112
|
+
const answer = await approvalRl.question(`\nApprove ${call.name}? [y/N]: `);
|
|
113
|
+
return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
activity.resume(toolPhase(call.name));
|
|
117
|
+
}
|
|
102
118
|
}
|
|
103
119
|
: async () => false,
|
|
104
120
|
}, events);
|
|
105
|
-
|
|
106
|
-
thinking = false;
|
|
107
|
-
stopThinking();
|
|
108
|
-
}
|
|
121
|
+
stopActivity();
|
|
109
122
|
if (result.streamedText) {
|
|
110
123
|
if (!result.streamedText.endsWith("\n"))
|
|
111
124
|
process.stdout.write("\n");
|
|
@@ -115,8 +128,7 @@ async function runOneShot(prompt) {
|
|
|
115
128
|
}
|
|
116
129
|
}
|
|
117
130
|
catch (e) {
|
|
118
|
-
|
|
119
|
-
stopThinking();
|
|
131
|
+
stopActivity();
|
|
120
132
|
console.error(hex("#f87171", `✗ ${errorMessage(e)}`));
|
|
121
133
|
process.exitCode = 1;
|
|
122
134
|
}
|
package/dist/loading.js
CHANGED
|
@@ -61,3 +61,72 @@ export function startThinkingSpinner(label) {
|
|
|
61
61
|
process.stdout.write("\r\u001b[2K");
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
|
+
/** Human phase label for a tool call — shown by activity indicators. */
|
|
65
|
+
export function toolPhase(name) {
|
|
66
|
+
switch (name) {
|
|
67
|
+
case "run_command":
|
|
68
|
+
return "Running command";
|
|
69
|
+
case "read_file":
|
|
70
|
+
return "Reading files";
|
|
71
|
+
case "write_file":
|
|
72
|
+
return "Writing files";
|
|
73
|
+
case "edit_file":
|
|
74
|
+
return "Editing files";
|
|
75
|
+
case "glob":
|
|
76
|
+
return "Finding files";
|
|
77
|
+
case "list_directory":
|
|
78
|
+
return "Listing directory";
|
|
79
|
+
case "code_search":
|
|
80
|
+
return "Searching code";
|
|
81
|
+
case "finish":
|
|
82
|
+
return "Wrapping up";
|
|
83
|
+
default:
|
|
84
|
+
return `Running ${name}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export function startActivityIndicator(initialPhase = "Thinking") {
|
|
88
|
+
if (!canAnimate()) {
|
|
89
|
+
return { setPhase: () => { }, pause: () => { }, resume: () => { }, stop: () => { } };
|
|
90
|
+
}
|
|
91
|
+
let phase = initialPhase;
|
|
92
|
+
let frame = 0;
|
|
93
|
+
let paused = false;
|
|
94
|
+
let pausedLine = "";
|
|
95
|
+
const startedAt = Date.now();
|
|
96
|
+
const render = (pausedText) => {
|
|
97
|
+
const seconds = Math.floor((Date.now() - startedAt) / 1000);
|
|
98
|
+
const suffix = pausedText ?? `${phase} · ${seconds}s`;
|
|
99
|
+
process.stdout.write(`\r\u001b[2K ${hex(ACCENT, FRAMES[frame % FRAMES.length])} ${suffix}`);
|
|
100
|
+
};
|
|
101
|
+
render();
|
|
102
|
+
const timer = setInterval(() => {
|
|
103
|
+
frame++;
|
|
104
|
+
if (!paused)
|
|
105
|
+
render();
|
|
106
|
+
}, 90);
|
|
107
|
+
const clearLine = () => process.stdout.write("\r\u001b[2K");
|
|
108
|
+
return {
|
|
109
|
+
setPhase(next) {
|
|
110
|
+
phase = next;
|
|
111
|
+
if (!paused)
|
|
112
|
+
render();
|
|
113
|
+
},
|
|
114
|
+
pause() {
|
|
115
|
+
if (paused)
|
|
116
|
+
return;
|
|
117
|
+
paused = true;
|
|
118
|
+
const seconds = Math.floor((Date.now() - startedAt) / 1000);
|
|
119
|
+
pausedLine = `${phase} · ${seconds}s — waiting for your approval`;
|
|
120
|
+
render(hex("#fbbf24", `⏸ ${pausedLine}`));
|
|
121
|
+
},
|
|
122
|
+
resume(next) {
|
|
123
|
+
phase = next;
|
|
124
|
+
paused = false;
|
|
125
|
+
render();
|
|
126
|
+
},
|
|
127
|
+
stop() {
|
|
128
|
+
clearInterval(timer);
|
|
129
|
+
clearLine();
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
package/dist/repl.js
CHANGED
|
@@ -8,6 +8,7 @@ import { loadConfig, modelLabel, saveConfig } from "./config.js";
|
|
|
8
8
|
import { DEFAULT_MODEL_ID, MODELS, findModel } from "./models.js";
|
|
9
9
|
import { launchKeysPage } from "./keysPage.js";
|
|
10
10
|
import { defaultSystemPrompt } from "./system.js";
|
|
11
|
+
import { startActivityIndicator, toolPhase } from "./loading.js";
|
|
11
12
|
import { modelAvailability, modelAvailabilityReason } from "./modelAvailability.js";
|
|
12
13
|
const PROMPT = "> ";
|
|
13
14
|
function replClosed(rl) {
|
|
@@ -212,10 +213,25 @@ export async function startRepl(opts) {
|
|
|
212
213
|
// Keep input active so Ctrl+C can cancel while the model is working.
|
|
213
214
|
rl.resume();
|
|
214
215
|
console.log("");
|
|
216
|
+
const activity = startActivityIndicator("Thinking");
|
|
217
|
+
let activityActive = true;
|
|
218
|
+
const stopActivity = () => {
|
|
219
|
+
if (activityActive) {
|
|
220
|
+
activityActive = false;
|
|
221
|
+
activity.stop();
|
|
222
|
+
}
|
|
223
|
+
};
|
|
215
224
|
const events = {
|
|
216
|
-
onText: (delta) =>
|
|
225
|
+
onText: (delta) => {
|
|
226
|
+
stopActivity();
|
|
227
|
+
process.stdout.write(delta);
|
|
228
|
+
},
|
|
217
229
|
onToolCall: (call) => {
|
|
218
230
|
console.log(dim(` ⚙ ${call.name}(${briefArgs(call.args)})`));
|
|
231
|
+
activity.setPhase(toolPhase(call.name));
|
|
232
|
+
},
|
|
233
|
+
onToolResult: (toolName) => {
|
|
234
|
+
activity.setPhase(toolName === "finish" ? "Wrapping up" : "Thinking");
|
|
219
235
|
},
|
|
220
236
|
onDebug: (msg) => console.log(dim(msg)),
|
|
221
237
|
};
|
|
@@ -228,11 +244,20 @@ export async function startRepl(opts) {
|
|
|
228
244
|
systemPrompt: cfg.systemPrompt ?? defaultSystemPrompt(opts.cwd, mode),
|
|
229
245
|
readOnly: mode === "plan",
|
|
230
246
|
maxIterations: cfg.maxIterations,
|
|
231
|
-
approveToolCall: (call) =>
|
|
247
|
+
approveToolCall: async (call) => {
|
|
248
|
+
activity.pause();
|
|
249
|
+
try {
|
|
250
|
+
return await approveToolCall(rl, call, controller?.signal);
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
activity.resume(toolPhase(call.name));
|
|
254
|
+
}
|
|
255
|
+
},
|
|
232
256
|
signal: controller.signal,
|
|
233
257
|
debug: opts.debug,
|
|
234
258
|
initialMessages: history,
|
|
235
259
|
}, events);
|
|
260
|
+
stopActivity();
|
|
236
261
|
history = result.history;
|
|
237
262
|
if (result.streamedText) {
|
|
238
263
|
if (!result.streamedText.endsWith("\n"))
|
|
@@ -243,6 +268,7 @@ export async function startRepl(opts) {
|
|
|
243
268
|
}
|
|
244
269
|
}
|
|
245
270
|
catch (e) {
|
|
271
|
+
stopActivity();
|
|
246
272
|
console.log("");
|
|
247
273
|
if (e instanceof ProviderError) {
|
|
248
274
|
console.log(hex("#f87171", ` ✗ ${e.message}`));
|