@ikyyofc/gemini-cli 4.0.1 → 4.0.3
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/package.json +1 -1
- package/src/agent.js +25 -9
- package/src/renderer.js +22 -16
- package/src/tools.js +32 -9
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -7,7 +7,7 @@ import { loadSkills, buildSkillsPrompt } from "./skills.js";
|
|
|
7
7
|
import {
|
|
8
8
|
printAssistant, printError, printWarning,
|
|
9
9
|
printToolCall, printToolResult,
|
|
10
|
-
printStepHeader, printStepFooter,
|
|
10
|
+
printStepHeader, printStepFooter, bw,
|
|
11
11
|
} from "./renderer.js";
|
|
12
12
|
|
|
13
13
|
const TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
|
@@ -109,9 +109,24 @@ export async function runAgentLoop(userMessage, history, {
|
|
|
109
109
|
return null;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
const
|
|
112
|
+
// Separate: thought parts (p.thought===true), text parts, tool call parts
|
|
113
|
+
const thoughtParts = parts.filter(p => p.thought === true && p.text);
|
|
114
|
+
const textParts = parts.filter(p => p.text != null && !p.thought);
|
|
115
|
+
const callParts = parts.filter(p => p.functionCall != null);
|
|
116
|
+
const textContent = textParts.map(p => p.text).join("").trim();
|
|
117
|
+
const thinkContent = thoughtParts.map(p => p.text).join("").trim();
|
|
118
|
+
|
|
119
|
+
// ── Show thinking (thought parts) ─────────────────────────
|
|
120
|
+
if (thinkContent) {
|
|
121
|
+
process.stdout.write(
|
|
122
|
+
"\n" + chalk.hex("#3A3A4E")(" ╭─ thinking ") +
|
|
123
|
+
chalk.hex("#3A3A4E")("─".repeat(Math.max(2, bw() - 13))) + "\n"
|
|
124
|
+
);
|
|
125
|
+
thinkContent.split("\n").forEach(line =>
|
|
126
|
+
process.stdout.write(chalk.hex("#3A3A4E")(" │ ") + chalk.hex("#5A5A7A").italic(line) + "\n")
|
|
127
|
+
);
|
|
128
|
+
process.stdout.write(chalk.hex("#3A3A4E")(" ╰" + "─".repeat(bw() - 1)) + "\n\n");
|
|
129
|
+
}
|
|
115
130
|
|
|
116
131
|
// ── Final answer — no more tool calls ─────────────────────
|
|
117
132
|
if (callParts.length === 0) {
|
|
@@ -119,12 +134,13 @@ export async function runAgentLoop(userMessage, history, {
|
|
|
119
134
|
return { finalResponse: textContent, iterations: iteration };
|
|
120
135
|
}
|
|
121
136
|
|
|
122
|
-
// ── Model reasoning text
|
|
137
|
+
// ── Model reasoning text alongside tool calls ──────────────
|
|
123
138
|
if (textContent) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
139
|
+
textContent.split("\n").forEach((line, i) =>
|
|
140
|
+
process.stdout.write(
|
|
141
|
+
chalk.hex("#4A4A5E")(i === 0 ? " ┄ " : " ") +
|
|
142
|
+
chalk.hex("#7A7A9A").italic(line) + "\n"
|
|
143
|
+
)
|
|
128
144
|
);
|
|
129
145
|
}
|
|
130
146
|
|
package/src/renderer.js
CHANGED
|
@@ -9,8 +9,8 @@ const C = {
|
|
|
9
9
|
comment: "#6A9955", num: "#B5CEA8", fn: "#FFD080",
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
const tw = () => Math.min(process.stdout.columns || 72, 84);
|
|
13
|
-
const bw = () => Math.min(tw() - 4, 68);
|
|
12
|
+
export const tw = () => Math.min(process.stdout.columns || 72, 84);
|
|
13
|
+
export const bw = () => Math.min(tw() - 4, 68);
|
|
14
14
|
const vlen = s => s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
15
15
|
|
|
16
16
|
// Word-wrap a PLAIN (no ANSI) string to maxW, returns array of lines
|
|
@@ -285,32 +285,38 @@ export function printStepFooter() {
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
export function printToolCall(name, args = {}) {
|
|
288
|
-
|
|
289
|
-
const raw = String(v).replace(/\n/g, "↵");
|
|
290
|
-
const val = raw.length > 42 ? raw.slice(0, 42) + "…" : raw;
|
|
291
|
-
return chalk.hex(C.muted)(k + ":") + chalk.hex(C.orange)(val);
|
|
292
|
-
}).join(" ");
|
|
288
|
+
// Tool name
|
|
293
289
|
process.stdout.write(
|
|
294
|
-
chalk.hex(C.yellow)("├─ ") + chalk.hex(C.blue).bold(name) +
|
|
295
|
-
(argStr ? " " + argStr : "") + "\n"
|
|
290
|
+
chalk.hex(C.yellow)("├─ ") + chalk.hex(C.blue).bold(name) + "\n"
|
|
296
291
|
);
|
|
292
|
+
// Each arg on its own line, full value, no truncation
|
|
293
|
+
Object.entries(args).forEach(([k, v]) => {
|
|
294
|
+
const raw = String(v);
|
|
295
|
+
const lines = raw.split("\n");
|
|
296
|
+
process.stdout.write(
|
|
297
|
+
chalk.hex(C.dimmer)("│ ") +
|
|
298
|
+
chalk.hex(C.muted)(k + ": ") +
|
|
299
|
+
chalk.hex(C.orange)(lines[0]) + "\n"
|
|
300
|
+
);
|
|
301
|
+
// Multi-line values (e.g. file content passed as arg)
|
|
302
|
+
lines.slice(1).forEach(l =>
|
|
303
|
+
process.stdout.write(chalk.hex(C.dimmer)("│ ") + chalk.hex(C.orange)(l) + "\n")
|
|
304
|
+
);
|
|
305
|
+
});
|
|
297
306
|
}
|
|
298
307
|
|
|
299
308
|
export function printToolResult(result) {
|
|
300
|
-
const W = bw() - 5;
|
|
301
309
|
const isErr = typeof result === "object" && result.error;
|
|
302
310
|
const text = typeof result === "object"
|
|
303
311
|
? (result.result ?? result.error ?? JSON.stringify(result, null, 2))
|
|
304
312
|
: String(result);
|
|
305
313
|
const color = isErr ? chalk.hex(C.red) : chalk.hex(C.muted);
|
|
306
314
|
const border = chalk.hex(C.dimmer)("│ ");
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
process.stdout.write(border + color(l.length > W ? l.slice(0, W) + "…" : l) + "\n")
|
|
315
|
+
|
|
316
|
+
// Print ALL lines — no limit, no truncation
|
|
317
|
+
text.split("\n").forEach(l =>
|
|
318
|
+
process.stdout.write(border + color(l) + "\n")
|
|
312
319
|
);
|
|
313
|
-
if (extra > 0) process.stdout.write(border + chalk.hex(C.dim)(`… +${extra} more lines`) + "\n");
|
|
314
320
|
}
|
|
315
321
|
|
|
316
322
|
// ─────────────────────────────────────────────────────────────────
|
package/src/tools.js
CHANGED
|
@@ -668,25 +668,48 @@ export async function executeTool(name, args = {}, { autoApprove = false } = {})
|
|
|
668
668
|
// ── Real-time ─────────────────────────────────────────
|
|
669
669
|
case "web_search": {
|
|
670
670
|
const q = encodeURIComponent(args.query);
|
|
671
|
+
|
|
672
|
+
// 1. DuckDuckGo Instant Answer API (best for facts/definitions)
|
|
671
673
|
const { stdout: ddg } = await execAsync(
|
|
672
|
-
`curl -sL --max-time 10 "https://api.duckduckgo.com/?q=${q}&format=json&no_redirect=1&no_html=1&skip_disambig=1"`
|
|
674
|
+
`curl -sL --max-time 10 -H "User-Agent: Mozilla/5.0" "https://api.duckduckgo.com/?q=${q}&format=json&no_redirect=1&no_html=1&skip_disambig=1"`
|
|
673
675
|
).catch(() => ({ stdout: "" }));
|
|
674
|
-
|
|
675
|
-
|
|
676
|
+
|
|
677
|
+
// 2. DuckDuckGo Lite HTML (more reliable for general search)
|
|
678
|
+
const { stdout: lite } = await execAsync(
|
|
679
|
+
`curl -sL --max-time 12 -H "User-Agent: Mozilla/5.0 (Linux; Android 10)" -H "Accept-Language: id,en" "https://lite.duckduckgo.com/lite/?q=${q}&kl=id-id" | grep -A1 'class="result-snippet"' | grep -v "result-snippet" | sed 's/<[^>]*>//g' | sed 's/^[[:space:]]*//' | grep -v '^$' | head -12`
|
|
680
|
+
).catch(() => ({ stdout: "" }));
|
|
681
|
+
|
|
682
|
+
// 3. Also grab result titles from lite
|
|
683
|
+
const { stdout: titles } = await execAsync(
|
|
684
|
+
`curl -sL --max-time 12 -H "User-Agent: Mozilla/5.0 (Linux; Android 10)" "https://lite.duckduckgo.com/lite/?q=${q}&kl=id-id" | grep -oP '(?<=<a class="result-link"[^>]*>)[^<]+' | head -8`
|
|
676
685
|
).catch(() => ({ stdout: "" }));
|
|
677
686
|
|
|
678
687
|
let out = "";
|
|
688
|
+
|
|
689
|
+
// Parse instant answer
|
|
679
690
|
try {
|
|
680
691
|
const d = JSON.parse(ddg);
|
|
681
|
-
if (d.AbstractText) out +=
|
|
682
|
-
if (d.Answer) out +=
|
|
692
|
+
if (d.AbstractText) out += `📖 ${d.AbstractText}\n → ${d.AbstractURL}\n\n`;
|
|
693
|
+
if (d.Answer) out += `💡 ${d.Answer}\n\n`;
|
|
694
|
+
if (d.Definition) out += `📝 ${d.Definition}\n\n`;
|
|
683
695
|
if (d.RelatedTopics?.length) {
|
|
684
|
-
|
|
685
|
-
|
|
696
|
+
const related = d.RelatedTopics.slice(0, 4).map(t => t.Text).filter(Boolean);
|
|
697
|
+
if (related.length) out += "Related:\n" + related.map(t => ` · ${t}`).join("\n") + "\n\n";
|
|
686
698
|
}
|
|
687
699
|
} catch {}
|
|
688
|
-
|
|
689
|
-
|
|
700
|
+
|
|
701
|
+
// Add titles + snippets from lite
|
|
702
|
+
const titleList = titles.trim().split("\n").filter(Boolean);
|
|
703
|
+
const snippetList = lite.trim().split("\n").filter(Boolean);
|
|
704
|
+
if (titleList.length) {
|
|
705
|
+
out += "Search Results:\n";
|
|
706
|
+
titleList.forEach((title, i) => {
|
|
707
|
+
out += ` ${i+1}. ${title.trim()}\n`;
|
|
708
|
+
if (snippetList[i]) out += ` ${snippetList[i].trim()}\n`;
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return { result: out.trim() || "No results found. Try rephrasing the query." };
|
|
690
713
|
}
|
|
691
714
|
|
|
692
715
|
case "get_weather": {
|