@ikyyofc/gemini-cli 4.0.7 → 4.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 -51
- package/package.json +1 -1
- package/src/agent.js +44 -74
package/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import fs from "fs";
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
|
|
7
|
-
import { chat
|
|
7
|
+
import { chat } from "./src/gemini.js";
|
|
8
8
|
import { runAgentLoop } from "./src/agent.js";
|
|
9
9
|
import {
|
|
10
10
|
loadMemory, buildContextString, memoryShow, memoryAdd, ensureGlobalDir, GLOBAL_DIR
|
|
@@ -163,62 +163,20 @@ async function send(rawLine) {
|
|
|
163
163
|
history.push({ role: "assistant", content: res.finalResponse });
|
|
164
164
|
}
|
|
165
165
|
} else {
|
|
166
|
-
// Chat mode — streaming
|
|
166
|
+
// Chat mode — non-streaming
|
|
167
|
+
const sp = new Spinner();
|
|
167
168
|
const msgs = [];
|
|
168
169
|
if (sysInstruction()) msgs.push({ role: "system", content: sysInstruction() });
|
|
169
170
|
msgs.push(...history, { role: "user", content: userText });
|
|
170
|
-
|
|
171
|
-
const W = bw();
|
|
172
|
-
const prefix = chalk.hex("#00D4AA")(" │ ");
|
|
173
|
-
let started = false;
|
|
174
|
-
let fullText = "";
|
|
175
|
-
let thinkText = "";
|
|
176
|
-
|
|
177
|
-
const sp = new Spinner();
|
|
178
171
|
sp.start("thinking…", "#4A9EFF");
|
|
179
|
-
|
|
180
172
|
try {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
},
|
|
187
|
-
onText: (chunk) => {
|
|
188
|
-
if (!started) {
|
|
189
|
-
sp.stop();
|
|
190
|
-
// Show thinking first if any
|
|
191
|
-
if (thinkText.trim()) {
|
|
192
|
-
process.stdout.write(
|
|
193
|
-
"\n" + chalk.hex("#2A2A40")(" ╭─ thinking " + "─".repeat(Math.max(2, W - 13))) + "\n"
|
|
194
|
-
);
|
|
195
|
-
thinkText.trim().split("\n").forEach(line =>
|
|
196
|
-
process.stdout.write(chalk.hex("#2A2A40")(" │ ") + chalk.hex("#4A4A6A").italic(line) + "\n")
|
|
197
|
-
);
|
|
198
|
-
process.stdout.write(chalk.hex("#2A2A40")(" ╰" + "─".repeat(W + 1)) + "\n\n");
|
|
199
|
-
}
|
|
200
|
-
process.stdout.write(
|
|
201
|
-
"\n" + chalk.hex("#00D4AA")(" ╭─") +
|
|
202
|
-
chalk.hex("#00D4AA").bold(" gemini ") +
|
|
203
|
-
chalk.hex("#4A4A5E")("─".repeat(Math.max(0, W - 10))) + "\n"
|
|
204
|
-
);
|
|
205
|
-
started = true;
|
|
206
|
-
}
|
|
207
|
-
fullText += chunk;
|
|
208
|
-
process.stdout.write(prefix + chunk.replace(/\n/g, "\n" + prefix));
|
|
209
|
-
},
|
|
210
|
-
onDone: () => {
|
|
211
|
-
if (!started) sp.stop();
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
if (started) {
|
|
216
|
-
process.stdout.write("\n" + chalk.hex("#00D4AA")(" ╰" + "─".repeat(W + 1)) + "\n\n");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
history.push({ role: "user", content: userText }, { role: "assistant", content: fullText });
|
|
173
|
+
const t0 = Date.now();
|
|
174
|
+
const reply = await chat(msgs, pendingFile || null);
|
|
175
|
+
sp.succeed(`done ${((Date.now()-t0)/1000).toFixed(1)}s`);
|
|
176
|
+
history.push({ role: "user", content: userText }, { role: "assistant", content: reply });
|
|
177
|
+
printAssistant(reply);
|
|
220
178
|
} catch (e) {
|
|
221
|
-
sp.
|
|
179
|
+
sp.fail(e.message);
|
|
222
180
|
printError(e.message);
|
|
223
181
|
}
|
|
224
182
|
}
|
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// src/agent.js — ReAct agent loop
|
|
1
|
+
// src/agent.js — ReAct agent loop
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { callGemini
|
|
3
|
+
import { callGemini } from "./gemini.js";
|
|
4
4
|
import { GEMINI_TOOLS, FUNCTION_DECLARATIONS, executeTool } from "./tools.js";
|
|
5
5
|
import { Spinner } from "./utils/spinner.js";
|
|
6
6
|
import { loadSkills, SKILLS_DIR } from "./skills.js";
|
|
@@ -16,52 +16,66 @@ const TIMEOUT_MS = 10 * 60 * 1000;
|
|
|
16
16
|
// System prompt
|
|
17
17
|
// ─────────────────────────────────────────────────────────────────
|
|
18
18
|
function buildSystemPrompt(extra = "") {
|
|
19
|
-
const toolList
|
|
20
|
-
const skills
|
|
21
|
-
const skillIndex = skills.length
|
|
22
|
-
? skills.map(s => {
|
|
23
|
-
const firstLine = s.content.split("\n").find(l => l.trim() && !l.startsWith("#")) ?? "";
|
|
24
|
-
return ` - ${s.slug}: ${firstLine.slice(0, 80)}`;
|
|
25
|
-
}).join("\n")
|
|
26
|
-
: null;
|
|
19
|
+
const toolList = FUNCTION_DECLARATIONS.map(t => `- ${t.name}: ${t.description}`).join("\n");
|
|
20
|
+
const skills = loadSkills();
|
|
27
21
|
|
|
28
22
|
const now = new Date();
|
|
29
23
|
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
30
24
|
const datetime = now.toLocaleString("id-ID", { timeZone: tz, dateStyle: "full", timeStyle: "long" });
|
|
31
25
|
|
|
26
|
+
// Build skill section
|
|
27
|
+
let skillSection = "";
|
|
28
|
+
if (skills.length) {
|
|
29
|
+
const index = skills.map(s => {
|
|
30
|
+
const desc = s.content.split("\n").find(l => l.trim() && !l.startsWith("#")) ?? "";
|
|
31
|
+
return ` - ${s.slug}: ${desc.slice(0, 80)}`;
|
|
32
|
+
}).join("\n");
|
|
33
|
+
|
|
34
|
+
skillSection = `
|
|
35
|
+
## SKILLS — MANDATORY
|
|
36
|
+
You have skills installed. Skills contain expert instructions for specific tasks.
|
|
37
|
+
|
|
38
|
+
**RULE: You MUST read the relevant skill file BEFORE starting any task.**
|
|
39
|
+
Do NOT proceed with a task until you have read the applicable skill(s).
|
|
40
|
+
|
|
41
|
+
Steps:
|
|
42
|
+
1. Look at the installed skills below
|
|
43
|
+
2. Identify which skill(s) apply to the current task
|
|
44
|
+
3. Use read_file to load the skill: read_file("${SKILLS_DIR}/<slug>/SKILL.md")
|
|
45
|
+
4. Follow the skill's instructions exactly
|
|
46
|
+
5. Only then proceed with the task
|
|
47
|
+
|
|
48
|
+
Installed skills:
|
|
49
|
+
${index}
|
|
50
|
+
|
|
51
|
+
Skills directory: ${SKILLS_DIR}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
return `You are an autonomous AI coding agent running in the user's terminal.
|
|
33
55
|
|
|
34
56
|
## CURRENT TIME
|
|
35
57
|
${datetime} (${tz})
|
|
58
|
+
${skillSection}
|
|
36
59
|
|
|
37
60
|
## CORE RULE — NEVER ASK, ALWAYS ACT
|
|
38
61
|
Use tools to complete every task. Never instruct the user to do anything themselves.
|
|
39
62
|
|
|
40
63
|
## WORKFLOW
|
|
41
|
-
|
|
42
|
-
2.
|
|
43
|
-
3.
|
|
44
|
-
4.
|
|
45
|
-
5. REPORT — brief summary
|
|
64
|
+
${skills.length ? "0. READ SKILL — load the relevant SKILL.md first if applicable\n" : ""}1. EXPLORE — understand the project structure
|
|
65
|
+
2. ACT — complete the task fully using tools
|
|
66
|
+
3. VERIFY — test after changes
|
|
67
|
+
4. REPORT — brief summary
|
|
46
68
|
|
|
47
69
|
## TOOLS
|
|
48
70
|
${toolList}
|
|
49
71
|
|
|
50
|
-
${skillIndex ? `## AVAILABLE SKILLS
|
|
51
|
-
You have skills installed. ALWAYS check if a skill is relevant before starting a task.
|
|
52
|
-
If relevant, read it first: read_file("${SKILLS_DIR}/<slug>/SKILL.md")
|
|
53
|
-
|
|
54
|
-
Installed:
|
|
55
|
-
${skillIndex}` : ""}
|
|
56
|
-
|
|
57
72
|
## RULES
|
|
58
|
-
- Always read files before editing
|
|
59
|
-
- Use patch_file for targeted edits
|
|
73
|
+
- Always read files before editing them
|
|
74
|
+
- Use patch_file for targeted edits (safer than full rewrite)
|
|
60
75
|
- Verify code works after changes
|
|
61
76
|
- If a command fails, diagnose and retry
|
|
62
77
|
- Platform: ${process.platform} | CWD: ${process.cwd()}
|
|
63
|
-
|
|
64
|
-
${extra ? `## EXTRA\n${extra}` : ""}`.trim();
|
|
78
|
+
${extra ? `\n## EXTRA\n${extra}` : ""}`.trim();
|
|
65
79
|
}
|
|
66
80
|
|
|
67
81
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -125,16 +139,13 @@ export async function runAgentLoop(userMessage, history, {
|
|
|
125
139
|
// Show thinking
|
|
126
140
|
printThinkBlock(thinkContent);
|
|
127
141
|
|
|
128
|
-
// ── Final answer
|
|
142
|
+
// ── Final answer ───────────────────────────────────────────
|
|
129
143
|
if (callParts.length === 0) {
|
|
130
|
-
if (textContent)
|
|
131
|
-
// Stream the final answer for better UX
|
|
132
|
-
await streamFinalAnswer(messages, fullSystem, thinkContent);
|
|
133
|
-
}
|
|
144
|
+
if (textContent) printAssistant(textContent);
|
|
134
145
|
return { finalResponse: textContent, iterations: iteration };
|
|
135
146
|
}
|
|
136
147
|
|
|
137
|
-
//
|
|
148
|
+
// Reasoning text before tool block
|
|
138
149
|
if (textContent) {
|
|
139
150
|
textContent.split("\n").forEach((line, i) =>
|
|
140
151
|
process.stdout.write(
|
|
@@ -145,7 +156,7 @@ export async function runAgentLoop(userMessage, history, {
|
|
|
145
156
|
}
|
|
146
157
|
|
|
147
158
|
// ── Tool calls ─────────────────────────────────────────────
|
|
148
|
-
//
|
|
159
|
+
// Push ALL parts (including thinking) into history for context continuity
|
|
149
160
|
messages.push({ role: "model", parts });
|
|
150
161
|
|
|
151
162
|
printStepHeader(iteration);
|
|
@@ -164,44 +175,3 @@ export async function runAgentLoop(userMessage, history, {
|
|
|
164
175
|
messages.push({ role: "user", parts: responseParts });
|
|
165
176
|
}
|
|
166
177
|
}
|
|
167
|
-
|
|
168
|
-
// ─────────────────────────────────────────────────────────────────
|
|
169
|
-
// Stream the final answer with live typing effect
|
|
170
|
-
// ─────────────────────────────────────────────────────────────────
|
|
171
|
-
async function streamFinalAnswer(messages, systemInstruction, prevThink) {
|
|
172
|
-
const W = bw();
|
|
173
|
-
const prefix = chalk.hex("#00D4AA")(" │ ");
|
|
174
|
-
let started = false;
|
|
175
|
-
let thinkAcc = "";
|
|
176
|
-
|
|
177
|
-
const printHeader = () => {
|
|
178
|
-
process.stdout.write(
|
|
179
|
-
"\n" + chalk.hex("#00D4AA")(" ╭─") +
|
|
180
|
-
chalk.hex("#00D4AA").bold(" gemini ") +
|
|
181
|
-
chalk.hex("#4A4A5E")("─".repeat(Math.max(0, W - 10))) + "\n"
|
|
182
|
-
);
|
|
183
|
-
started = true;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
await callGeminiStream({
|
|
187
|
-
messages,
|
|
188
|
-
systemInstruction,
|
|
189
|
-
onThought: (chunk) => {
|
|
190
|
-
thinkAcc += chunk;
|
|
191
|
-
},
|
|
192
|
-
onText: (chunk) => {
|
|
193
|
-
if (!started) printHeader();
|
|
194
|
-
process.stdout.write(prefix + chunk.replace(/\n/g, "\n" + prefix));
|
|
195
|
-
},
|
|
196
|
-
onDone: (parts) => {
|
|
197
|
-
// Show thinking if new thinking arrived in final answer
|
|
198
|
-
if (thinkAcc.trim() && thinkAcc !== prevThink) {
|
|
199
|
-
printThinkBlock(thinkAcc);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
if (started) {
|
|
205
|
-
process.stdout.write("\n" + chalk.hex("#00D4AA")(" ╰" + "─".repeat(W + 1)) + "\n\n");
|
|
206
|
-
}
|
|
207
|
-
}
|