@ikyyofc/gemini-cli 1.0.6 → 1.0.8
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 +9 -14
- package/package.json +1 -1
- 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",
|
|
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)}`) : "";
|
|
@@ -134,27 +127,29 @@ async function send(rawLine) {
|
|
|
134
127
|
const res = await runAgentLoop(userText, history, {
|
|
135
128
|
systemInstruction: sysInstruction(),
|
|
136
129
|
autoApprove,
|
|
137
|
-
maxIterations:
|
|
130
|
+
maxIterations: 5000,
|
|
138
131
|
});
|
|
139
132
|
if (res?.finalResponse) {
|
|
140
133
|
history.push({ role: "user", content: userText });
|
|
141
134
|
history.push({ role: "assistant", content: res.finalResponse });
|
|
142
135
|
}
|
|
143
136
|
} else {
|
|
144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
152
|
+
sp.fail("failed");
|
|
158
153
|
printError(e.message);
|
|
159
154
|
}
|
|
160
155
|
}
|
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -1,115 +1,116 @@
|
|
|
1
|
-
// src/agent.js — ReAct agent loop
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
40
|
-
let sectionOpen = false;
|
|
30
|
+
let iteration = 0;
|
|
41
31
|
|
|
42
32
|
while (iteration < maxIterations) {
|
|
43
33
|
iteration++;
|
|
44
34
|
|
|
45
|
-
//
|
|
46
|
-
|
|
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
|
-
|
|
53
|
-
|
|
48
|
+
spinner.stop();
|
|
49
|
+
clearLine();
|
|
54
50
|
} catch (err) {
|
|
55
|
-
|
|
51
|
+
spinner.fail(chalk.hex("#F44747")("API error"));
|
|
56
52
|
printError(err.message);
|
|
57
53
|
return null;
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
|
|
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
|
-
//
|
|
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.
|
|
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
|
-
// ──
|
|
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
|
|
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
|
-
// ──
|
|
82
|
+
// ── Execute each tool call ───────────────────────────────
|
|
88
83
|
messages.push({ role: "model", parts });
|
|
89
84
|
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|