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 CHANGED
@@ -93,6 +93,7 @@ export async function runAgent(input, opts, events) {
93
93
  continue;
94
94
  }
95
95
  const result = await opts.registry.execute(call.name, call.args, ctx);
96
+ events?.onToolResult?.(call.name);
96
97
  messages.push({
97
98
  role: "tool",
98
99
  content: result.content,
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, startThinkingSpinner } from "./loading.js";
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
- // Show a "thinking" spinner until the first token arrives, then stream.
81
- const stopThinking = startThinkingSpinner("Thinking");
82
- let thinking = true;
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
- if (thinking) {
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
- const answer = await approvalRl.question(`\nApprove ${call.name}? [y/N]: `);
101
- return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
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
- if (thinking) {
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
- if (thinking)
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) => process.stdout.write(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) => approveToolCall(rl, call, controller?.signal),
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}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeshark-cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "An open-source terminal coding agent. Eight model choices, project tools, and approval before every tool action.",
5
5
  "type": "module",
6
6
  "bin": {