@ikyyofc/gemini-cli 1.0.6 → 1.0.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.
Files changed (3) hide show
  1. package/index.js +8 -13
  2. package/package.json +1 -1
  3. package/src/agent.js +46 -45
package/index.js CHANGED
@@ -81,20 +81,13 @@ function cleanup() {
81
81
  disableBracketedPaste();
82
82
  try { if (process.stdin.isTTY) process.stdin.setRawMode(false); } catch {}
83
83
  }
84
- process.on("exit", cleanup);
84
+ process.on("exit", cleanup);
85
85
  process.on("SIGTERM", () => { cleanup(); process.exit(0); });
86
86
 
87
- // When terminal is resized (or focus returns on some terminals),
88
- // only redraw prompt if we are NOT currently processing
89
- process.on("SIGWINCH", () => { if (!processing) showPrompt(); });
90
-
91
87
  // ─────────────────────────────────────────────────────────────────
92
88
  // Prompt
93
89
  // ─────────────────────────────────────────────────────────────────
94
90
  function showPrompt() {
95
- // Never redraw prompt while a request is in flight
96
- if (processing) return;
97
-
98
91
  const mode = agentMode ? chalk.hex("#4EC9B0")("agent") : chalk.dim("chat");
99
92
  const yolo = autoApprove ? chalk.red(" yolo") : "";
100
93
  const file = pendingFile ? chalk.yellow(` +${path.basename(pendingPath)}`) : "";
@@ -141,20 +134,22 @@ async function send(rawLine) {
141
134
  history.push({ role: "assistant", content: res.finalResponse });
142
135
  }
143
136
  } else {
144
- process.stdout.write(chalk.hex("#555566")(" ⋯ ") + chalk.dim("thinking…") + "\n");
137
+ const { default: ora } = await import("ora");
138
+ const sp = ora({
139
+ text: "thinking…", spinner: "dots", color: "cyan",
140
+ prefixText: " ", discardStdin: false
141
+ }).start();
145
142
  const msgs = [];
146
143
  if (sysInstruction()) msgs.push({ role: "system", content: sysInstruction() });
147
144
  msgs.push(...history, { role: "user", content: userText });
148
145
  try {
149
146
  const t0 = Date.now();
150
147
  const reply = await chat(msgs, pendingFile || null);
151
- // clear "thinking…" line, print done
152
- if (process.stdout.isTTY) process.stdout.write("\x1b[1A\x1b[2K");
153
- process.stdout.write(chalk.hex("#555566")(" ⋯ ") + chalk.dim(`done (${((Date.now()-t0)/1000).toFixed(1)}s)`) + "\n");
148
+ sp.succeed(chalk.dim(`done (${((Date.now()-t0)/1000).toFixed(1)}s)`));
154
149
  history.push({ role: "user", content: userText }, { role: "assistant", content: reply });
155
150
  printAssistant(reply);
156
151
  } catch (e) {
157
- if (process.stdout.isTTY) process.stdout.write("\x1b[1A\x1b[2K");
152
+ sp.fail("failed");
158
153
  printError(e.message);
159
154
  }
160
155
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikyyofc/gemini-cli",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "AI CLI Agent powered by Gemini — your own terminal assistant",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/src/agent.js CHANGED
@@ -1,115 +1,116 @@
1
- // src/agent.js — ReAct agent loop, native Gemini function calling
1
+ // src/agent.js — ReAct agent loop using NATIVE Gemini function calling
2
2
  import chalk from "chalk";
3
+ import ora from "ora";
3
4
  import { callGemini } from "./gemini.js";
4
5
  import { GEMINI_TOOLS, executeTool } from "./tools.js";
5
6
  import { printAssistant, printError, printWarning, printToolCall, printToolResult } from "./renderer.js";
6
7
 
7
- // ─────────────────────────────────────────────────────────────────
8
- // Static progress line write once, no continuous redraw
9
- // Safe when terminal loses focus (no ANSI overwrite loops)
10
- // ─────────────────────────────────────────────────────────────────
11
- function printStep(n, label) {
12
- process.stdout.write(
13
- chalk.hex("#555566")(" ⋯ ") +
14
- chalk.dim(label) +
15
- (n > 1 ? chalk.hex("#555566")(` (step ${n})`) : "") +
16
- "\n"
17
- );
18
- }
19
-
20
- function clearLastLine() {
21
- // Move cursor up one line and clear it — only called right after printStep
22
- // so we can replace "thinking…" with the actual tool section header
23
- if (process.stdout.isTTY) {
24
- process.stdout.write("\x1b[1A\x1b[2K");
25
- }
26
- }
27
-
8
+ /**
9
+ * Run the agent loop until the model gives a final text answer (no more tool calls).
10
+ *
11
+ * Native function calling flow:
12
+ * 1. Send contents + tools (functionDeclarations) to Gemini
13
+ * 2. Response parts may contain: { functionCall: { name, args } }
14
+ * 3. Execute the tool → get result
15
+ * 4. Append model turn (with functionCall) + user turn (with functionResponse)
16
+ * 5. Repeat until response has only text parts → done
17
+ */
28
18
  export async function runAgentLoop(userMessage, history, {
29
19
  systemInstruction = null,
30
20
  autoApprove = false,
31
21
  maxIterations = 40,
32
22
  } = {}) {
33
23
 
24
+ // Build initial message list
34
25
  const messages = [
35
26
  ...history,
36
27
  { role: "user", content: userMessage }
37
28
  ];
38
29
 
39
- let iteration = 0;
40
- let sectionOpen = false;
30
+ let iteration = 0;
41
31
 
42
32
  while (iteration < maxIterations) {
43
33
  iteration++;
44
34
 
45
- // Print a static "thinking…" line written once, not looping
46
- printStep(iteration, iteration === 1 ? "thinking…" : "thinking…");
35
+ // ── Call Gemini API with tools ──────────────────────────
36
+ const spinner = ora({
37
+ text: chalk.dim(iteration === 1 ? "thinking…" : `step ${iteration}…`),
38
+ spinner: "dots",
39
+ color: "cyan",
40
+ prefixText: " ",
41
+ discardStdin: false, // prevent ora from pausing stdin after stop
42
+ }).start();
47
43
 
48
44
  let parts;
49
45
  try {
50
46
  const res = await callGemini({ messages, tools: GEMINI_TOOLS, systemInstruction });
51
47
  parts = res.parts;
52
- // Clear the "thinking…" line now that we have a response
53
- clearLastLine();
48
+ spinner.stop();
49
+ clearLine();
54
50
  } catch (err) {
55
- clearLastLine();
51
+ spinner.fail(chalk.hex("#F44747")("API error"));
56
52
  printError(err.message);
57
53
  return null;
58
54
  }
59
55
 
60
- const textParts = parts.filter(p => p.text != null);
56
+ // ── Separate text parts from functionCall parts ─────────
57
+ const textParts = parts.filter(p => p.text != null);
61
58
  const callParts = parts.filter(p => p.functionCall != null);
62
59
 
63
- // Model thinking aloud alongside tool calls
60
+ // Print any accompanying text (model thinking aloud)
64
61
  const textContent = textParts.map(p => p.text).join("").trim();
65
62
  if (textContent && callParts.length > 0) {
66
63
  process.stdout.write(
67
- chalk.hex("#555566")(" ┄ ") +
68
- chalk.dim(textContent.split("\n")[0].slice(0, 80)) +
69
- "\n"
64
+ chalk.dim("" + textContent.replace(/\n/g, "\n ")) + "\n"
70
65
  );
71
66
  }
72
67
 
73
- // ── Final answer (no more tool calls) ─────────────────────
68
+ // ── No tool calls final answer ────────────────────────
74
69
  if (callParts.length === 0) {
75
70
  const final = textParts.map(p => p.text).join("").trim();
76
- if (sectionOpen) {
71
+ // Close the tool section if it was opened
72
+ if (iteration > 1) {
77
73
  process.stdout.write(
78
74
  chalk.hex("#4A9EFF")(" ╰") +
79
75
  chalk.hex("#555566")("─".repeat(47)) + "\n"
80
76
  );
81
- sectionOpen = false;
82
77
  }
83
78
  if (final) printAssistant(final);
84
79
  return { finalResponse: final, iterations: iteration };
85
80
  }
86
81
 
87
- // ── Tool calls ─────────────────────────────────────────────
82
+ // ── Execute each tool call ───────────────────────────────
88
83
  messages.push({ role: "model", parts });
89
84
 
90
- if (!sectionOpen) {
85
+ // Print tool section header on first iteration
86
+ if (iteration === 1) {
91
87
  process.stdout.write(
92
88
  "\n" + chalk.hex("#4A9EFF")(" ╭─ working") +
93
89
  chalk.hex("#555566")(" " + "─".repeat(36)) + "\n"
94
90
  );
95
- sectionOpen = true;
96
91
  }
97
92
 
98
93
  const responseParts = [];
94
+
99
95
  for (const part of callParts) {
100
96
  const { name, args } = part.functionCall;
101
97
  printToolCall(name, args);
98
+
102
99
  const result = await executeTool(name, args ?? {}, { autoApprove });
103
100
  printToolResult(result);
104
- responseParts.push({ functionResponse: { name, response: result } });
101
+
102
+ responseParts.push({
103
+ functionResponse: { name, response: result }
104
+ });
105
105
  }
106
106
 
107
107
  messages.push({ role: "user", parts: responseParts });
108
108
  }
109
109
 
110
- if (sectionOpen) {
111
- process.stdout.write(chalk.hex("#4A9EFF")(" ╰") + chalk.hex("#555566")("─".repeat(47)) + "\n");
112
- }
113
- printWarning(`max iterations (${maxIterations}) reached`);
110
+ printWarning(`Max iterations (${maxIterations}) reached.`);
114
111
  return { finalResponse: null, iterations: iteration };
115
112
  }
113
+
114
+ function clearLine() {
115
+ if (process.stdout.clearLine) { process.stdout.clearLine(0); process.stdout.cursorTo(0); }
116
+ }