mini-agents-cli 0.0.1
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/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/index.mjs +428 -0
- package/dist/onboarding-BPrwWMQk.mjs +81 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 liruifengv
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
## mini-agents-cli
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { stdin, stdout } from "node:process";
|
|
6
|
+
import { Agent, LLMClient } from "mini-agents";
|
|
7
|
+
import { SkillLoader, createBashTool, createEditTool, createGetSkillTool, createReadTool, createWriteTool } from "mini-agents/tools";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
//#region src/config.ts
|
|
13
|
+
/**
|
|
14
|
+
* CLI 配置管理
|
|
15
|
+
*
|
|
16
|
+
* 使用 Zod 定义配置 schema,从 ~/.mini-agents-cli/settings.json 加载配置。
|
|
17
|
+
*/
|
|
18
|
+
/** 重试配置 schema */
|
|
19
|
+
const RetryConfigSchema = z.object({
|
|
20
|
+
enabled: z.boolean().default(true),
|
|
21
|
+
maxRetries: z.number().default(3),
|
|
22
|
+
initialDelay: z.number().default(1),
|
|
23
|
+
maxDelay: z.number().default(60),
|
|
24
|
+
exponentialBase: z.number().default(2)
|
|
25
|
+
});
|
|
26
|
+
/** LLM 配置 schema */
|
|
27
|
+
const LLMConfigSchema = z.object({
|
|
28
|
+
apiKey: z.string(),
|
|
29
|
+
apiBase: z.string(),
|
|
30
|
+
model: z.string(),
|
|
31
|
+
provider: z.enum(["anthropic", "openai"]),
|
|
32
|
+
retry: z.unknown().default({}).pipe(RetryConfigSchema)
|
|
33
|
+
});
|
|
34
|
+
/** Agent 配置 schema */
|
|
35
|
+
const AgentConfigSchema = z.object({
|
|
36
|
+
maxSteps: z.number().default(50),
|
|
37
|
+
workspaceDir: z.string().default("./workspace"),
|
|
38
|
+
systemPromptPath: z.string().default("system_prompt.md")
|
|
39
|
+
});
|
|
40
|
+
/** MCP 超时配置 schema */
|
|
41
|
+
const MCPConfigSchema = z.object({
|
|
42
|
+
connectTimeout: z.number().default(10),
|
|
43
|
+
executeTimeout: z.number().default(60),
|
|
44
|
+
sseReadTimeout: z.number().default(120)
|
|
45
|
+
});
|
|
46
|
+
/** 工具配置 schema */
|
|
47
|
+
const ToolsConfigSchema = z.object({
|
|
48
|
+
enableFileTools: z.boolean().default(true),
|
|
49
|
+
enableBash: z.boolean().default(true),
|
|
50
|
+
enableNote: z.boolean().default(true),
|
|
51
|
+
enableSkills: z.boolean().default(true),
|
|
52
|
+
skillsDir: z.string().default("./skills"),
|
|
53
|
+
enableMcp: z.boolean().default(true),
|
|
54
|
+
mcpConfigPath: z.string().default("mcp.json"),
|
|
55
|
+
mcp: z.unknown().default({}).pipe(MCPConfigSchema)
|
|
56
|
+
});
|
|
57
|
+
/** 顶层配置 schema */
|
|
58
|
+
const SettingsSchema = z.object({
|
|
59
|
+
llm: LLMConfigSchema,
|
|
60
|
+
agent: z.unknown().default({}).pipe(AgentConfigSchema),
|
|
61
|
+
tools: z.unknown().default({}).pipe(ToolsConfigSchema)
|
|
62
|
+
});
|
|
63
|
+
/** 配置文件目录:~/.mini-agents-cli */
|
|
64
|
+
const CONFIG_DIR = join(homedir(), ".mini-agents-cli");
|
|
65
|
+
/** 用户配置文件路径:~/.mini-agents-cli/settings.json */
|
|
66
|
+
const USER_SETTINGS_FILE = join(CONFIG_DIR, "settings.json");
|
|
67
|
+
/** 本地配置文件名(开发模式) */
|
|
68
|
+
const LOCAL_SETTINGS_FILE = "settings.json";
|
|
69
|
+
/** 默认配置模板 */
|
|
70
|
+
const DEFAULT_SETTINGS = { llm: {
|
|
71
|
+
apiKey: "YOUR_API_KEY_HERE",
|
|
72
|
+
apiBase: "https://api.anthropic.com",
|
|
73
|
+
model: "claude-sonnet-4-20250514",
|
|
74
|
+
provider: "anthropic"
|
|
75
|
+
} };
|
|
76
|
+
/**
|
|
77
|
+
* 按优先级搜索配置文件
|
|
78
|
+
*
|
|
79
|
+
* 搜索顺序:
|
|
80
|
+
* 1. ~/.mini-agents-cli/settings.json(用户自定义)
|
|
81
|
+
* 2. {packageDir}/config/settings.json(包内置,开发模式)
|
|
82
|
+
*
|
|
83
|
+
* @returns 找到的配置文件路径,未找到返回 null
|
|
84
|
+
*/
|
|
85
|
+
function findSettingsFile() {
|
|
86
|
+
return findConfigFile(LOCAL_SETTINGS_FILE);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 按优先级搜索配置文件(通用版)
|
|
90
|
+
*
|
|
91
|
+
* 搜索顺序:
|
|
92
|
+
* 1. ~/.mini-agents-cli/{filename}(用户自定义)
|
|
93
|
+
* 2. {packageDir}/config/{filename}(包内置默认)
|
|
94
|
+
*
|
|
95
|
+
* @param filename - 要搜索的文件名(如 system_prompt.md)
|
|
96
|
+
* @returns 找到的文件路径,未找到返回 null
|
|
97
|
+
*/
|
|
98
|
+
function findConfigFile(filename) {
|
|
99
|
+
const userPath = join(CONFIG_DIR, filename);
|
|
100
|
+
if (existsSync(userPath)) return userPath;
|
|
101
|
+
const builtinPath = join(dirname(dirname(fileURLToPath(import.meta.url))), "config", filename);
|
|
102
|
+
if (existsSync(builtinPath)) return builtinPath;
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 创建默认配置文件
|
|
107
|
+
*
|
|
108
|
+
* 在 ~/.mini-agents-cli/settings.json 创建模板配置。
|
|
109
|
+
*
|
|
110
|
+
* @returns 创建的配置文件路径
|
|
111
|
+
*/
|
|
112
|
+
function createDefaultSettings() {
|
|
113
|
+
ensureConfigDir();
|
|
114
|
+
writeFileSync(USER_SETTINGS_FILE, JSON.stringify(DEFAULT_SETTINGS, null, 2), "utf-8");
|
|
115
|
+
return USER_SETTINGS_FILE;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 确保配置目录存在
|
|
119
|
+
*/
|
|
120
|
+
function ensureConfigDir() {
|
|
121
|
+
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 从指定路径加载并验证配置
|
|
125
|
+
*
|
|
126
|
+
* @param filePath - JSON 配置文件路径
|
|
127
|
+
* @returns 验证后的 Settings 对象
|
|
128
|
+
* @throws 文件不存在、JSON 格式错误、schema 验证失败时抛出错误
|
|
129
|
+
*/
|
|
130
|
+
function loadSettingsFromFile(filePath) {
|
|
131
|
+
if (!existsSync(filePath)) throw new Error(`Settings file not found: ${filePath}`);
|
|
132
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
133
|
+
let data;
|
|
134
|
+
try {
|
|
135
|
+
data = JSON.parse(raw);
|
|
136
|
+
} catch {
|
|
137
|
+
throw new Error(`Invalid JSON in settings file: ${filePath}`);
|
|
138
|
+
}
|
|
139
|
+
const result = SettingsSchema.safeParse(data);
|
|
140
|
+
if (!result.success) {
|
|
141
|
+
const issues = result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
|
|
142
|
+
throw new Error(`Invalid settings file:\n${issues}`);
|
|
143
|
+
}
|
|
144
|
+
return result.data;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/cli.ts
|
|
149
|
+
/**
|
|
150
|
+
* 极简 CLI 实现
|
|
151
|
+
*
|
|
152
|
+
* 使用纯 Node.js API,无第三方渲染库
|
|
153
|
+
*/
|
|
154
|
+
const colors = {
|
|
155
|
+
reset: "\x1B[0m",
|
|
156
|
+
bright: "\x1B[1m",
|
|
157
|
+
dim: "\x1B[2m",
|
|
158
|
+
green: "\x1B[32m",
|
|
159
|
+
cyan: "\x1B[36m",
|
|
160
|
+
yellow: "\x1B[33m",
|
|
161
|
+
red: "\x1B[31m",
|
|
162
|
+
gray: "\x1B[90m",
|
|
163
|
+
blue: "\x1B[34m"
|
|
164
|
+
};
|
|
165
|
+
var SimpleCLI = class {
|
|
166
|
+
settings;
|
|
167
|
+
workspaceDir;
|
|
168
|
+
agent;
|
|
169
|
+
inputBuffer = "";
|
|
170
|
+
isProcessing = false;
|
|
171
|
+
abortController = null;
|
|
172
|
+
constructor(settings, workspaceDir) {
|
|
173
|
+
this.settings = settings;
|
|
174
|
+
this.workspaceDir = workspaceDir;
|
|
175
|
+
this.agent = this.createAgent();
|
|
176
|
+
}
|
|
177
|
+
createAgent() {
|
|
178
|
+
const llmClient = new LLMClient({
|
|
179
|
+
apiKey: this.settings.llm.apiKey,
|
|
180
|
+
provider: this.settings.llm.provider,
|
|
181
|
+
apiBase: this.settings.llm.apiBase,
|
|
182
|
+
model: this.settings.llm.model
|
|
183
|
+
});
|
|
184
|
+
const tools = [];
|
|
185
|
+
if (this.settings.tools.enableFileTools) {
|
|
186
|
+
tools.push(createReadTool(this.workspaceDir));
|
|
187
|
+
tools.push(createWriteTool(this.workspaceDir));
|
|
188
|
+
tools.push(createEditTool(this.workspaceDir));
|
|
189
|
+
}
|
|
190
|
+
if (this.settings.tools.enableBash) tools.push(createBashTool());
|
|
191
|
+
let skillsMetadata = "";
|
|
192
|
+
if (this.settings.tools.enableSkills) {
|
|
193
|
+
const skillsDir = this.settings.tools.skillsDir;
|
|
194
|
+
const loader = new SkillLoader(skillsDir);
|
|
195
|
+
if (loader.discoverSkills().length > 0) {
|
|
196
|
+
tools.push(createGetSkillTool(loader));
|
|
197
|
+
skillsMetadata = loader.getSkillsMetadataPrompt();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
let systemPrompt;
|
|
201
|
+
const promptPath = findConfigFile(this.settings.agent.systemPromptPath);
|
|
202
|
+
if (promptPath) systemPrompt = readFileSync(promptPath, "utf-8");
|
|
203
|
+
else systemPrompt = "You are Mini-Agent, an intelligent AI assistant that helps users complete tasks using available tools. Be concise and helpful.";
|
|
204
|
+
if (skillsMetadata) systemPrompt = systemPrompt.replace("{{SKILLS_METADATA}}", skillsMetadata);
|
|
205
|
+
else systemPrompt = systemPrompt.replace("{{SKILLS_METADATA}}", "");
|
|
206
|
+
if (!systemPrompt.includes("Current Workspace")) systemPrompt += `\n\n## Current Workspace\nYou are currently working in: \`${this.workspaceDir}\`\nAll relative paths will be resolved relative to this directory.`;
|
|
207
|
+
return new Agent(llmClient, systemPrompt, tools);
|
|
208
|
+
}
|
|
209
|
+
printBanner() {
|
|
210
|
+
console.log();
|
|
211
|
+
console.log(`${colors.cyan}${colors.bright}🤖 Mini Agent${colors.reset}`);
|
|
212
|
+
console.log(`${colors.gray}${this.settings.llm.model} · ${this.getToolCount()} tools · ${this.workspaceDir}${colors.reset}`);
|
|
213
|
+
console.log(`${colors.gray}${"─".repeat(60)}${colors.reset}`);
|
|
214
|
+
console.log();
|
|
215
|
+
}
|
|
216
|
+
getToolCount() {
|
|
217
|
+
return [
|
|
218
|
+
this.settings.tools.enableFileTools,
|
|
219
|
+
this.settings.tools.enableBash,
|
|
220
|
+
this.settings.tools.enableNote,
|
|
221
|
+
this.settings.tools.enableSkills,
|
|
222
|
+
this.settings.tools.enableMcp
|
|
223
|
+
].filter(Boolean).length;
|
|
224
|
+
}
|
|
225
|
+
printPrompt() {
|
|
226
|
+
stdout.write(`${colors.green}You › ${colors.reset}`);
|
|
227
|
+
}
|
|
228
|
+
printThinking(text) {
|
|
229
|
+
const display = text.length > 300 ? `${text.slice(0, 300)}...` : text;
|
|
230
|
+
console.log(`${colors.gray} 💭 ${display}${colors.reset}`);
|
|
231
|
+
}
|
|
232
|
+
printToolCall(name, args) {
|
|
233
|
+
const argsStr = Object.entries(args).map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
234
|
+
const display = argsStr.length > 200 ? `${argsStr.slice(0, 200)}...` : argsStr;
|
|
235
|
+
console.log(`${colors.yellow} ⚡ ${name}(${display})${colors.reset}`);
|
|
236
|
+
}
|
|
237
|
+
printToolResult(isError) {
|
|
238
|
+
if (isError) console.log(`${colors.red} ✗ Failed${colors.reset}`);
|
|
239
|
+
else console.log(`${colors.gray} ✓ Done${colors.reset}`);
|
|
240
|
+
}
|
|
241
|
+
printAssistantMessage(content) {
|
|
242
|
+
console.log(`${colors.cyan}Assistant › ${content}${colors.reset}`);
|
|
243
|
+
}
|
|
244
|
+
printError(error) {
|
|
245
|
+
console.log(`${colors.red}Error: ${error}${colors.reset}`);
|
|
246
|
+
}
|
|
247
|
+
printHelp() {
|
|
248
|
+
console.log();
|
|
249
|
+
console.log(`${colors.bright}Available commands:${colors.reset}`);
|
|
250
|
+
console.log(` /help - Show this help message`);
|
|
251
|
+
console.log(` /clear - Clear the screen`);
|
|
252
|
+
console.log(` /exit - Exit the CLI`);
|
|
253
|
+
console.log();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* 处理 AgentMessageEvent,实时打印
|
|
257
|
+
*/
|
|
258
|
+
handleAgentMessageEvent(event) {
|
|
259
|
+
switch (event.type) {
|
|
260
|
+
case "thinking":
|
|
261
|
+
if (event.thinking) this.printThinking(event.thinking);
|
|
262
|
+
break;
|
|
263
|
+
case "toolCall":
|
|
264
|
+
this.printToolCall(event.toolCall.function.name, event.toolCall.function.arguments);
|
|
265
|
+
break;
|
|
266
|
+
case "toolResult":
|
|
267
|
+
this.printToolResult(!event.result.success);
|
|
268
|
+
break;
|
|
269
|
+
case "assistantMessage":
|
|
270
|
+
if (event.content?.trim()) this.printAssistantMessage(event.content);
|
|
271
|
+
break;
|
|
272
|
+
case "summarized":
|
|
273
|
+
console.log(`${colors.yellow} 📝 Messages summarized (${event.beforeTokens} → ${event.afterTokens} tokens)${colors.reset}`);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async processMessage(text) {
|
|
278
|
+
if (this.isProcessing) return;
|
|
279
|
+
this.isProcessing = true;
|
|
280
|
+
this.abortController = new AbortController();
|
|
281
|
+
this.agent.addUserMessage(text);
|
|
282
|
+
let loadingFrame = 0;
|
|
283
|
+
const loadingFrames = [
|
|
284
|
+
"⠋",
|
|
285
|
+
"⠙",
|
|
286
|
+
"⠹",
|
|
287
|
+
"⠸",
|
|
288
|
+
"⠼",
|
|
289
|
+
"⠴",
|
|
290
|
+
"⠦",
|
|
291
|
+
"⠧",
|
|
292
|
+
"⠇",
|
|
293
|
+
"⠏"
|
|
294
|
+
];
|
|
295
|
+
const loadingInterval = setInterval(() => {
|
|
296
|
+
stdout.write(`\r${colors.blue} ${loadingFrames[loadingFrame]} Thinking...${colors.reset}`);
|
|
297
|
+
loadingFrame = (loadingFrame + 1) % loadingFrames.length;
|
|
298
|
+
}, 100);
|
|
299
|
+
try {
|
|
300
|
+
let hasReceivedEvent = false;
|
|
301
|
+
for await (const event of this.agent.run({ signal: this.abortController.signal })) {
|
|
302
|
+
if (!hasReceivedEvent) {
|
|
303
|
+
clearInterval(loadingInterval);
|
|
304
|
+
stdout.write(`\r${" ".repeat(30)}\r`);
|
|
305
|
+
hasReceivedEvent = true;
|
|
306
|
+
}
|
|
307
|
+
if (event.type === "cancelled") {
|
|
308
|
+
console.log(`${colors.yellow}⏹ Task cancelled by user.${colors.reset}`);
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
this.handleAgentMessageEvent(event);
|
|
312
|
+
}
|
|
313
|
+
if (!hasReceivedEvent) {
|
|
314
|
+
clearInterval(loadingInterval);
|
|
315
|
+
stdout.write(`\r${" ".repeat(30)}\r`);
|
|
316
|
+
}
|
|
317
|
+
console.log();
|
|
318
|
+
} catch (err) {
|
|
319
|
+
clearInterval(loadingInterval);
|
|
320
|
+
stdout.write(`\r${" ".repeat(30)}\r`);
|
|
321
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
322
|
+
this.printError(errorMsg);
|
|
323
|
+
console.log();
|
|
324
|
+
} finally {
|
|
325
|
+
this.abortController = null;
|
|
326
|
+
this.isProcessing = false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async start() {
|
|
330
|
+
this.printBanner();
|
|
331
|
+
stdin.setRawMode(true);
|
|
332
|
+
stdin.setEncoding("utf8");
|
|
333
|
+
stdin.resume();
|
|
334
|
+
this.printPrompt();
|
|
335
|
+
stdin.on("data", async (data) => {
|
|
336
|
+
for (let i = 0; i < data.length; i++) {
|
|
337
|
+
const char = data[i];
|
|
338
|
+
const charCode = char.charCodeAt(0);
|
|
339
|
+
if (charCode === 3) {
|
|
340
|
+
console.log();
|
|
341
|
+
console.log(`${colors.cyan}Goodbye!${colors.reset}`);
|
|
342
|
+
process.exit(0);
|
|
343
|
+
}
|
|
344
|
+
if (charCode === 27 && this.isProcessing && this.abortController) {
|
|
345
|
+
this.abortController.abort();
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (charCode === 13) {
|
|
349
|
+
console.log();
|
|
350
|
+
const text = this.inputBuffer;
|
|
351
|
+
this.inputBuffer = "";
|
|
352
|
+
if (!text.trim()) {
|
|
353
|
+
this.printPrompt();
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (text.startsWith("/")) {
|
|
357
|
+
const cmd = text.slice(1).toLowerCase();
|
|
358
|
+
switch (cmd) {
|
|
359
|
+
case "exit":
|
|
360
|
+
case "quit":
|
|
361
|
+
console.log(`${colors.cyan}Goodbye!${colors.reset}`);
|
|
362
|
+
process.exit(0);
|
|
363
|
+
case "clear":
|
|
364
|
+
console.clear();
|
|
365
|
+
this.printBanner();
|
|
366
|
+
this.printPrompt();
|
|
367
|
+
continue;
|
|
368
|
+
case "help":
|
|
369
|
+
this.printHelp();
|
|
370
|
+
this.printPrompt();
|
|
371
|
+
continue;
|
|
372
|
+
default:
|
|
373
|
+
console.log(`${colors.red}Unknown command: ${cmd}${colors.reset}`);
|
|
374
|
+
this.printPrompt();
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
await this.processMessage(text);
|
|
379
|
+
this.printPrompt();
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (charCode === 127 || charCode === 8) {
|
|
383
|
+
if (this.inputBuffer.length > 0) {
|
|
384
|
+
const lastChar = this.inputBuffer[this.inputBuffer.length - 1];
|
|
385
|
+
const charWidth = lastChar && lastChar.charCodeAt(0) > 127 ? 2 : 1;
|
|
386
|
+
this.inputBuffer = this.inputBuffer.slice(0, -1);
|
|
387
|
+
if (charWidth === 2) stdout.write("\b\b \b\b");
|
|
388
|
+
else stdout.write("\b \b");
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (charCode < 32) continue;
|
|
393
|
+
this.inputBuffer += char;
|
|
394
|
+
stdout.write(char);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region src/index.ts
|
|
402
|
+
/**
|
|
403
|
+
* Mini Agent CLI - 极简版本
|
|
404
|
+
*
|
|
405
|
+
* 纯 Node.js 实现,无第三方渲染库
|
|
406
|
+
*/
|
|
407
|
+
const program = new Command();
|
|
408
|
+
program.name("mini-agents-cli").description("Mini Agent - AI assistant with tool support").version("0.3.0").option("-w, --workspace <dir>", "Workspace directory", process.cwd()).action(async (options) => {
|
|
409
|
+
let settingsPath = findSettingsFile();
|
|
410
|
+
if (!settingsPath) {
|
|
411
|
+
const { runOnboarding } = await import("./onboarding-BPrwWMQk.mjs");
|
|
412
|
+
settingsPath = await runOnboarding();
|
|
413
|
+
}
|
|
414
|
+
let settings;
|
|
415
|
+
try {
|
|
416
|
+
settings = loadSettingsFromFile(settingsPath);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
419
|
+
console.error(`Error: ${msg}`);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
const workspaceDir = resolve(options.workspace);
|
|
423
|
+
await new SimpleCLI(settings, workspaceDir).start();
|
|
424
|
+
});
|
|
425
|
+
program.parse();
|
|
426
|
+
|
|
427
|
+
//#endregion
|
|
428
|
+
export { createDefaultSettings as t };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { t as createDefaultSettings } from "./index.mjs";
|
|
2
|
+
import { stdin, stdout } from "node:process";
|
|
3
|
+
import * as readline from "node:readline/promises";
|
|
4
|
+
|
|
5
|
+
//#region src/onboarding.ts
|
|
6
|
+
/**
|
|
7
|
+
* 首次使用引导流程
|
|
8
|
+
*
|
|
9
|
+
* 交互式询问用户配置 LLM provider、API Key、API URL、Model,
|
|
10
|
+
* 保存到 ~/.mini-agents-cli/settings.json。
|
|
11
|
+
*/
|
|
12
|
+
const CYAN = "\x1B[36m";
|
|
13
|
+
const GREEN = "\x1B[32m";
|
|
14
|
+
const YELLOW = "\x1B[33m";
|
|
15
|
+
const DIM = "\x1B[2m";
|
|
16
|
+
const BOLD = "\x1B[1m";
|
|
17
|
+
const RESET = "\x1B[0m";
|
|
18
|
+
/** Provider 预设 */
|
|
19
|
+
const PROVIDER_PRESETS = {
|
|
20
|
+
anthropic: {
|
|
21
|
+
apiBase: "https://api.anthropic.com",
|
|
22
|
+
model: "claude-sonnet-4-20250514"
|
|
23
|
+
},
|
|
24
|
+
openai: {
|
|
25
|
+
apiBase: "https://api.openai.com/v1",
|
|
26
|
+
model: "gpt-4o"
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* 运行 onboarding 流程
|
|
31
|
+
*
|
|
32
|
+
* @returns 配置文件路径
|
|
33
|
+
*/
|
|
34
|
+
async function runOnboarding() {
|
|
35
|
+
const rl = readline.createInterface({
|
|
36
|
+
input: stdin,
|
|
37
|
+
output: stdout
|
|
38
|
+
});
|
|
39
|
+
console.log();
|
|
40
|
+
console.log(`${BOLD}${CYAN}🤖 Welcome to Mini Agent CLI!${RESET}`);
|
|
41
|
+
console.log(`${DIM}Let's set up your configuration.${RESET}`);
|
|
42
|
+
console.log();
|
|
43
|
+
try {
|
|
44
|
+
console.log(`${BOLD}${YELLOW}[1/4] LLM Provider${RESET}`);
|
|
45
|
+
console.log(` ${GREEN}1${RESET}) Anthropic (Claude)`);
|
|
46
|
+
console.log(` ${GREEN}2${RESET}) OpenAI`);
|
|
47
|
+
const provider = (await rl.question(`${DIM}Choose [1/2] (default: 1): ${RESET}`)).trim() === "2" ? "openai" : "anthropic";
|
|
48
|
+
const preset = PROVIDER_PRESETS[provider];
|
|
49
|
+
console.log(` → ${CYAN}${provider}${RESET}`);
|
|
50
|
+
console.log();
|
|
51
|
+
console.log(`${BOLD}${YELLOW}[2/4] API Key${RESET}`);
|
|
52
|
+
const apiKey = await rl.question(`${DIM}Enter your API key: ${RESET}`);
|
|
53
|
+
if (!apiKey.trim()) console.log(`${YELLOW}⚠ No API key provided. You can edit the config later.${RESET}`);
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(`${BOLD}${YELLOW}[3/4] API Base URL${RESET}`);
|
|
56
|
+
const apiBase = (await rl.question(`${DIM}URL (default: ${preset.apiBase}): ${RESET}`)).trim() || preset.apiBase;
|
|
57
|
+
console.log(` → ${CYAN}${apiBase}${RESET}`);
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(`${BOLD}${YELLOW}[4/4] Model${RESET}`);
|
|
60
|
+
const model = (await rl.question(`${DIM}Model name (default: ${preset.model}): ${RESET}`)).trim() || preset.model;
|
|
61
|
+
console.log(` → ${CYAN}${model}${RESET}`);
|
|
62
|
+
console.log();
|
|
63
|
+
const settingsPath = createDefaultSettings();
|
|
64
|
+
const { writeFileSync } = await import("node:fs");
|
|
65
|
+
const settings = { llm: {
|
|
66
|
+
apiKey: apiKey.trim() || "YOUR_API_KEY_HERE",
|
|
67
|
+
apiBase,
|
|
68
|
+
model,
|
|
69
|
+
provider
|
|
70
|
+
} };
|
|
71
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
72
|
+
console.log(`${GREEN}✅ Config saved to: ${settingsPath}${RESET}`);
|
|
73
|
+
console.log();
|
|
74
|
+
return settingsPath;
|
|
75
|
+
} finally {
|
|
76
|
+
rl.close();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { runOnboarding };
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mini-agents-cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A mini AI agent in CLI",
|
|
5
|
+
"main": "dist/index.mjs",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mini-agents-cli": "dist/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ai",
|
|
15
|
+
"agent",
|
|
16
|
+
"cli"
|
|
17
|
+
],
|
|
18
|
+
"author": "",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.0.0",
|
|
22
|
+
"tsdown": "^0.16.6",
|
|
23
|
+
"tsx": "^4.20.6",
|
|
24
|
+
"typescript": "^5.0.0"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"commander": "^12.0.0",
|
|
28
|
+
"zod": "^4.1.12",
|
|
29
|
+
"mini-agents": "0.0.1"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsdown",
|
|
36
|
+
"dev": "node dist/index.mjs"
|
|
37
|
+
}
|
|
38
|
+
}
|