@ikyyofc/gemini-cli 1.0.5 → 1.0.6

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