@ikyyofc/gemini-cli 4.0.2 → 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 +23 -9
- package/src/renderer.js +2 -2
- 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,15 +134,14 @@ export async function runAgentLoop(userMessage, history, {
|
|
|
119
134
|
return { finalResponse: textContent, iterations: iteration };
|
|
120
135
|
}
|
|
121
136
|
|
|
122
|
-
// ── Model reasoning
|
|
137
|
+
// ── Model reasoning text alongside tool calls ──────────────
|
|
123
138
|
if (textContent) {
|
|
124
|
-
|
|
125
|
-
lines.forEach((line, i) => {
|
|
139
|
+
textContent.split("\n").forEach((line, i) =>
|
|
126
140
|
process.stdout.write(
|
|
127
141
|
chalk.hex("#4A4A5E")(i === 0 ? " ┄ " : " ") +
|
|
128
142
|
chalk.hex("#7A7A9A").italic(line) + "\n"
|
|
129
|
-
)
|
|
130
|
-
|
|
143
|
+
)
|
|
144
|
+
);
|
|
131
145
|
}
|
|
132
146
|
|
|
133
147
|
// ── Step block — fresh per iteration ──────────────────────
|
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
|
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": {
|