arki 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 +125 -0
- package/bin/arki.js +4 -0
- package/dist/config/config.json +14 -0
- package/dist/index.d.ts +355 -0
- package/dist/index.js +1003 -0
- package/package.json +56 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1003 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import * as readline from "readline";
|
|
5
|
+
import * as fs5 from "fs";
|
|
6
|
+
import * as path5 from "path";
|
|
7
|
+
import * as os2 from "os";
|
|
8
|
+
|
|
9
|
+
// src/config/config.ts
|
|
10
|
+
import * as fs from "fs/promises";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
import * as os from "os";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
function getConfigPath() {
|
|
15
|
+
return path.join(os.homedir(), ".config", "arki", "config.json");
|
|
16
|
+
}
|
|
17
|
+
function getDefaultConfigPath() {
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
return path.join(__dirname, "config.json");
|
|
20
|
+
}
|
|
21
|
+
var ConfigManager = class {
|
|
22
|
+
config;
|
|
23
|
+
loaded = false;
|
|
24
|
+
/**
|
|
25
|
+
* Load configuration (called at program startup)
|
|
26
|
+
*/
|
|
27
|
+
async load() {
|
|
28
|
+
if (this.loaded) {
|
|
29
|
+
return this.config;
|
|
30
|
+
}
|
|
31
|
+
const configPath = getConfigPath();
|
|
32
|
+
try {
|
|
33
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
34
|
+
this.config = JSON.parse(content);
|
|
35
|
+
} catch {
|
|
36
|
+
const defaultContent = await fs.readFile(getDefaultConfigPath(), "utf-8");
|
|
37
|
+
this.config = JSON.parse(defaultContent);
|
|
38
|
+
const configDir = path.dirname(configPath);
|
|
39
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
40
|
+
await fs.writeFile(configPath, defaultContent, "utf-8");
|
|
41
|
+
}
|
|
42
|
+
this.loadEnvApiKeys();
|
|
43
|
+
this.loaded = true;
|
|
44
|
+
return this.config;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Save configuration
|
|
48
|
+
*/
|
|
49
|
+
async save() {
|
|
50
|
+
const configPath = getConfigPath();
|
|
51
|
+
const configToSave = { ...this.config };
|
|
52
|
+
if (this.config.apiKeys) {
|
|
53
|
+
configToSave.apiKeys = { ...this.config.apiKeys };
|
|
54
|
+
if (process.env.OPENAI_API_KEY && configToSave.apiKeys.openai === process.env.OPENAI_API_KEY) {
|
|
55
|
+
delete configToSave.apiKeys.openai;
|
|
56
|
+
}
|
|
57
|
+
if (process.env.ANTHROPIC_API_KEY && configToSave.apiKeys.anthropic === process.env.ANTHROPIC_API_KEY) {
|
|
58
|
+
delete configToSave.apiKeys.anthropic;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
await fs.writeFile(configPath, JSON.stringify(configToSave, null, 2), "utf-8");
|
|
62
|
+
}
|
|
63
|
+
get() {
|
|
64
|
+
return this.config;
|
|
65
|
+
}
|
|
66
|
+
getApiKey(provider) {
|
|
67
|
+
return this.config.apiKeys?.[provider];
|
|
68
|
+
}
|
|
69
|
+
getAgentConfig(agentType) {
|
|
70
|
+
const agentConfig = this.config.agents[agentType];
|
|
71
|
+
if (!agentConfig) {
|
|
72
|
+
throw new Error(`Agent config not found for type: ${agentType}`);
|
|
73
|
+
}
|
|
74
|
+
return agentConfig;
|
|
75
|
+
}
|
|
76
|
+
loadEnvApiKeys() {
|
|
77
|
+
if (process.env.OPENAI_API_KEY) {
|
|
78
|
+
if (!this.config.apiKeys) this.config.apiKeys = {};
|
|
79
|
+
if (!this.config.apiKeys.openai) {
|
|
80
|
+
this.config.apiKeys.openai = process.env.OPENAI_API_KEY;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
84
|
+
if (!this.config.apiKeys) this.config.apiKeys = {};
|
|
85
|
+
if (!this.config.apiKeys.anthropic) {
|
|
86
|
+
this.config.apiKeys.anthropic = process.env.ANTHROPIC_API_KEY;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (process.env.GOOGLE_API_KEY) {
|
|
90
|
+
if (!this.config.apiKeys) this.config.apiKeys = {};
|
|
91
|
+
if (!this.config.apiKeys.google) {
|
|
92
|
+
this.config.apiKeys.google = process.env.GOOGLE_API_KEY;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
var config = new ConfigManager();
|
|
98
|
+
|
|
99
|
+
// src/adapter/openai.ts
|
|
100
|
+
import OpenAI from "openai";
|
|
101
|
+
|
|
102
|
+
// src/adapter/Adapter.ts
|
|
103
|
+
var TEMPERATURE = 0.2;
|
|
104
|
+
var MAX_COMPLETION_TOKENS = 4096;
|
|
105
|
+
var Adapter = class {
|
|
106
|
+
apiKey;
|
|
107
|
+
model;
|
|
108
|
+
/** Use Flex API (OpenAI) - low priority, low cost */
|
|
109
|
+
flex;
|
|
110
|
+
/** Reasoning effort (thinking mode) */
|
|
111
|
+
reasoningEffort;
|
|
112
|
+
/** Available tools list */
|
|
113
|
+
tools;
|
|
114
|
+
constructor(config2) {
|
|
115
|
+
this.apiKey = config2.apiKey;
|
|
116
|
+
this.model = config2.model;
|
|
117
|
+
this.flex = config2.flex;
|
|
118
|
+
this.reasoningEffort = config2.reasoningEffort;
|
|
119
|
+
this.tools = config2.tools;
|
|
120
|
+
}
|
|
121
|
+
getModel() {
|
|
122
|
+
return this.model;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/agent/Msg.ts
|
|
127
|
+
var MsgType = /* @__PURE__ */ ((MsgType2) => {
|
|
128
|
+
MsgType2["System"] = "system";
|
|
129
|
+
MsgType2["User"] = "user";
|
|
130
|
+
MsgType2["AI"] = "ai";
|
|
131
|
+
MsgType2["ToolCall"] = "tool_call";
|
|
132
|
+
MsgType2["ToolResult"] = "tool_result";
|
|
133
|
+
return MsgType2;
|
|
134
|
+
})(MsgType || {});
|
|
135
|
+
var Msg = class {
|
|
136
|
+
timestamp;
|
|
137
|
+
content;
|
|
138
|
+
constructor(content) {
|
|
139
|
+
this.timestamp = Date.now();
|
|
140
|
+
this.content = content;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var SystemMsg = class extends Msg {
|
|
144
|
+
type = "system" /* System */;
|
|
145
|
+
constructor(content) {
|
|
146
|
+
super(content);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var UserMsg = class extends Msg {
|
|
150
|
+
type = "user" /* User */;
|
|
151
|
+
constructor(content) {
|
|
152
|
+
super(content);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var AIMsg = class extends Msg {
|
|
156
|
+
type = "ai" /* AI */;
|
|
157
|
+
constructor(content) {
|
|
158
|
+
super(content);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
var ToolCallMsg = class extends Msg {
|
|
162
|
+
type = "tool_call" /* ToolCall */;
|
|
163
|
+
toolCalls;
|
|
164
|
+
constructor(content, toolCalls) {
|
|
165
|
+
super(content);
|
|
166
|
+
this.toolCalls = toolCalls;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
var ToolResultMsg = class extends Msg {
|
|
170
|
+
type = "tool_result" /* ToolResult */;
|
|
171
|
+
toolName;
|
|
172
|
+
result;
|
|
173
|
+
isError;
|
|
174
|
+
constructor(toolName, result, isError) {
|
|
175
|
+
super("");
|
|
176
|
+
this.toolName = toolName;
|
|
177
|
+
this.result = result;
|
|
178
|
+
this.isError = isError;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/log/debug.ts
|
|
183
|
+
var _debugMode = false;
|
|
184
|
+
function isDebugMode() {
|
|
185
|
+
return _debugMode;
|
|
186
|
+
}
|
|
187
|
+
function setDebugMode(enabled) {
|
|
188
|
+
_debugMode = enabled;
|
|
189
|
+
}
|
|
190
|
+
function debug(category, message, data) {
|
|
191
|
+
if (!_debugMode) return;
|
|
192
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
193
|
+
const prefix = `${colors.gray}[${timestamp}]${colors.reset} ${colors.magenta}[DEBUG:${category}]${colors.reset}`;
|
|
194
|
+
console.log(`${prefix} ${colors.cyan}${message}${colors.reset}`);
|
|
195
|
+
if (data !== void 0) {
|
|
196
|
+
const dataStr = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
197
|
+
const lines = dataStr.split("\n");
|
|
198
|
+
const maxLines = 20;
|
|
199
|
+
const truncated = lines.length > maxLines;
|
|
200
|
+
const displayLines = truncated ? lines.slice(-maxLines) : lines;
|
|
201
|
+
if (truncated) {
|
|
202
|
+
console.log(colors.dim + ` ... (${lines.length - maxLines} earlier lines)` + colors.reset);
|
|
203
|
+
}
|
|
204
|
+
console.log(colors.dim + displayLines.map((l) => ` ${l}`).join("\n") + colors.reset);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/log/log.ts
|
|
209
|
+
function log(color, ...args) {
|
|
210
|
+
console.log(colors[color], ...args, colors.reset);
|
|
211
|
+
}
|
|
212
|
+
function info(message) {
|
|
213
|
+
console.log(`${colors.blue}\u2139${colors.reset} ${message}`);
|
|
214
|
+
}
|
|
215
|
+
function success(message) {
|
|
216
|
+
console.log(`${colors.green}\u2714${colors.reset} ${message}`);
|
|
217
|
+
}
|
|
218
|
+
function warn(message) {
|
|
219
|
+
console.log(`${colors.yellow}\u26A0${colors.reset} ${message}`);
|
|
220
|
+
}
|
|
221
|
+
function error(message) {
|
|
222
|
+
console.log(`${colors.red}\u2716${colors.reset} ${message}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/log/index.ts
|
|
226
|
+
var colors = {
|
|
227
|
+
reset: "\x1B[0m",
|
|
228
|
+
bold: "\x1B[1m",
|
|
229
|
+
dim: "\x1B[2m",
|
|
230
|
+
italic: "\x1B[3m",
|
|
231
|
+
underline: "\x1B[4m",
|
|
232
|
+
inverse: "\x1B[7m",
|
|
233
|
+
strikethrough: "\x1B[9m",
|
|
234
|
+
red: "\x1B[31m",
|
|
235
|
+
green: "\x1B[32m",
|
|
236
|
+
yellow: "\x1B[33m",
|
|
237
|
+
blue: "\x1B[34m",
|
|
238
|
+
magenta: "\x1B[35m",
|
|
239
|
+
cyan: "\x1B[36m",
|
|
240
|
+
gray: "\x1B[90m"
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/adapter/openai.ts
|
|
244
|
+
var OpenAIAdapter = class extends Adapter {
|
|
245
|
+
client;
|
|
246
|
+
constructor(config2) {
|
|
247
|
+
super(config2);
|
|
248
|
+
if (!this.apiKey) {
|
|
249
|
+
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY environment variable.");
|
|
250
|
+
}
|
|
251
|
+
this.client = new OpenAI({ apiKey: this.apiKey });
|
|
252
|
+
}
|
|
253
|
+
toOpenAIMessages(messages) {
|
|
254
|
+
const result = [];
|
|
255
|
+
let pendingIds = [];
|
|
256
|
+
for (const msg of messages) {
|
|
257
|
+
if (msg.type === "system" /* System */ || msg.type === "user" /* User */) {
|
|
258
|
+
result.push({ role: msg.type, content: msg.content });
|
|
259
|
+
} else if (msg.type === "ai" /* AI */) {
|
|
260
|
+
result.push({ role: "assistant", content: msg.content });
|
|
261
|
+
} else if (msg.type === "tool_call" /* ToolCall */) {
|
|
262
|
+
const toolCallMsg = msg;
|
|
263
|
+
pendingIds = toolCallMsg.toolCalls.map((_, i) => `call_${msg.timestamp}_${i}`);
|
|
264
|
+
result.push({
|
|
265
|
+
role: "assistant",
|
|
266
|
+
content: msg.content || null,
|
|
267
|
+
tool_calls: toolCallMsg.toolCalls.map((tc, i) => ({
|
|
268
|
+
id: pendingIds[i],
|
|
269
|
+
type: "function",
|
|
270
|
+
function: { name: tc.name, arguments: JSON.stringify(tc.arguments) }
|
|
271
|
+
}))
|
|
272
|
+
});
|
|
273
|
+
} else if (msg.type === "tool_result" /* ToolResult */) {
|
|
274
|
+
const toolResultMsg = msg;
|
|
275
|
+
result.push({
|
|
276
|
+
role: "tool",
|
|
277
|
+
tool_call_id: pendingIds.shift() || `call_${msg.timestamp}`,
|
|
278
|
+
content: toolResultMsg.isError ? `Error: ${toolResultMsg.result}` : toolResultMsg.result
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
getTools() {
|
|
285
|
+
return this.tools?.map((t) => ({
|
|
286
|
+
type: "function",
|
|
287
|
+
function: {
|
|
288
|
+
name: t.name,
|
|
289
|
+
description: t.description,
|
|
290
|
+
parameters: {
|
|
291
|
+
type: "object",
|
|
292
|
+
properties: t.parameters,
|
|
293
|
+
required: t.required.length > 0 ? t.required : void 0
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
async chat(messages, onChunk) {
|
|
299
|
+
debug("API", `Requesting OpenAI (model: ${this.model}, messages: ${messages.length})`);
|
|
300
|
+
const openaiMessages = this.toOpenAIMessages(messages);
|
|
301
|
+
debug("API", "Sent prompt:", openaiMessages);
|
|
302
|
+
const startTime = Date.now();
|
|
303
|
+
const requestParams = {
|
|
304
|
+
model: this.model,
|
|
305
|
+
messages: openaiMessages,
|
|
306
|
+
tools: this.getTools(),
|
|
307
|
+
temperature: TEMPERATURE,
|
|
308
|
+
max_completion_tokens: MAX_COMPLETION_TOKENS,
|
|
309
|
+
stream: true,
|
|
310
|
+
stream_options: { include_usage: true },
|
|
311
|
+
service_tier: this.flex ? "flex" : void 0,
|
|
312
|
+
reasoning_effort: this.reasoningEffort
|
|
313
|
+
};
|
|
314
|
+
const stream = await this.client.chat.completions.create(requestParams);
|
|
315
|
+
let text = "";
|
|
316
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
317
|
+
let usage;
|
|
318
|
+
for await (const chunk of stream) {
|
|
319
|
+
const delta = chunk.choices[0]?.delta;
|
|
320
|
+
if (delta?.content) {
|
|
321
|
+
text += delta.content;
|
|
322
|
+
onChunk?.(delta.content);
|
|
323
|
+
}
|
|
324
|
+
if (delta?.tool_calls) {
|
|
325
|
+
for (const tc of delta.tool_calls) {
|
|
326
|
+
const cur = toolCalls.get(tc.index) || { name: "", args: "" };
|
|
327
|
+
cur.name += tc.function?.name || "";
|
|
328
|
+
cur.args += tc.function?.arguments || "";
|
|
329
|
+
toolCalls.set(tc.index, cur);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (chunk.usage) usage = chunk.usage;
|
|
333
|
+
}
|
|
334
|
+
const elapsed = Date.now() - startTime;
|
|
335
|
+
const cachedTokens = usage?.prompt_tokens_details?.cached_tokens;
|
|
336
|
+
const usageData = usage && {
|
|
337
|
+
promptTokens: usage.prompt_tokens,
|
|
338
|
+
completionTokens: usage.completion_tokens,
|
|
339
|
+
totalTokens: usage.total_tokens,
|
|
340
|
+
cachedTokens
|
|
341
|
+
};
|
|
342
|
+
if (toolCalls.size > 0) {
|
|
343
|
+
const calls = [...toolCalls.values()].map((tc) => ({
|
|
344
|
+
name: tc.name,
|
|
345
|
+
arguments: JSON.parse(tc.args || "{}")
|
|
346
|
+
}));
|
|
347
|
+
debug("API", `Completed (${elapsed}ms, tools: ${calls.map((t) => t.name).join(", ")})`);
|
|
348
|
+
return { message: new ToolCallMsg(text, calls), hasToolCalls: true, usage: usageData };
|
|
349
|
+
}
|
|
350
|
+
debug("API", `Completed (${elapsed}ms, tokens: ${usage?.total_tokens || "N/A"})`);
|
|
351
|
+
return { message: new AIMsg(text), hasToolCalls: false, usage: usageData };
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// src/global.ts
|
|
356
|
+
var workingDir = process.cwd();
|
|
357
|
+
function setWorkingDir(dir) {
|
|
358
|
+
workingDir = dir;
|
|
359
|
+
}
|
|
360
|
+
var TOOLS = {};
|
|
361
|
+
var adapter = null;
|
|
362
|
+
function initAdapter() {
|
|
363
|
+
if (adapter) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
const mainAgentConfig = config.getAgentConfig("main");
|
|
367
|
+
adapter = new OpenAIAdapter({
|
|
368
|
+
apiKey: config.getApiKey("openai") || "",
|
|
369
|
+
model: mainAgentConfig.model,
|
|
370
|
+
flex: mainAgentConfig.flex,
|
|
371
|
+
tools: Object.values(TOOLS)
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
async function init(cwd) {
|
|
375
|
+
workingDir = cwd || process.cwd();
|
|
376
|
+
await config.load();
|
|
377
|
+
initAdapter();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/tool/Tool.ts
|
|
381
|
+
var Tool = class _Tool {
|
|
382
|
+
name;
|
|
383
|
+
description;
|
|
384
|
+
parameters;
|
|
385
|
+
required;
|
|
386
|
+
manual;
|
|
387
|
+
_execute;
|
|
388
|
+
constructor(config2) {
|
|
389
|
+
this.name = config2.name;
|
|
390
|
+
this.parameters = config2.parameters;
|
|
391
|
+
this.required = config2.required;
|
|
392
|
+
this._execute = config2.execute;
|
|
393
|
+
const { description, manual } = _Tool.parseManual(config2.manualContent);
|
|
394
|
+
this.description = description;
|
|
395
|
+
this.manual = manual;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Parse manual.md content
|
|
399
|
+
* First line format: "tool_name: description", extract description
|
|
400
|
+
* Remaining content is the manual
|
|
401
|
+
*/
|
|
402
|
+
static parseManual(content) {
|
|
403
|
+
const lines = content.split("\n");
|
|
404
|
+
const firstLine = lines[0] || "";
|
|
405
|
+
let description = "";
|
|
406
|
+
const colonIndex = firstLine.indexOf(":");
|
|
407
|
+
if (colonIndex > 0) {
|
|
408
|
+
description = firstLine.slice(colonIndex + 1).trim();
|
|
409
|
+
}
|
|
410
|
+
const manual = lines.slice(1).join("\n").trim();
|
|
411
|
+
return { description, manual };
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Execute tool (with error handling and logging)
|
|
415
|
+
*/
|
|
416
|
+
async run(args) {
|
|
417
|
+
debug("Tool", `Starting tool execution: ${this.name}`, args);
|
|
418
|
+
const startTime = Date.now();
|
|
419
|
+
try {
|
|
420
|
+
const result = await this._execute(args);
|
|
421
|
+
const elapsed = Date.now() - startTime;
|
|
422
|
+
if (typeof result === "string") {
|
|
423
|
+
debug("Tool", `Tool execution successful: ${this.name} (elapsed: ${elapsed}ms, result length: ${result.length})`);
|
|
424
|
+
return new ToolResultMsg(this.name, result);
|
|
425
|
+
}
|
|
426
|
+
debug("Tool", `Tool execution completed: ${this.name} (elapsed: ${elapsed}ms, isError: ${result.isError || false})`);
|
|
427
|
+
return new ToolResultMsg(this.name, result.content, result.isError);
|
|
428
|
+
} catch (error2) {
|
|
429
|
+
const elapsed = Date.now() - startTime;
|
|
430
|
+
const errorMsg = error2 instanceof Error ? error2.message : String(error2);
|
|
431
|
+
debug("Tool", `Tool execution failed: ${this.name} (elapsed: ${elapsed}ms)`, errorMsg);
|
|
432
|
+
return new ToolResultMsg(this.name, `Error: ${errorMsg}`, true);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
// src/tool/read_file/index.ts
|
|
438
|
+
import * as fs2 from "fs/promises";
|
|
439
|
+
import * as path2 from "path";
|
|
440
|
+
|
|
441
|
+
// src/tool/read_file/manual.md
|
|
442
|
+
var manual_default = "read_file: Read the content of a specified file\n\n## Parameters\n\n- `path` (string, required): File path\n";
|
|
443
|
+
|
|
444
|
+
// src/tool/read_file/index.ts
|
|
445
|
+
TOOLS["read_file"] = new Tool({
|
|
446
|
+
name: "read_file",
|
|
447
|
+
parameters: {
|
|
448
|
+
path: { type: "string", description: "File path" }
|
|
449
|
+
},
|
|
450
|
+
required: ["path"],
|
|
451
|
+
manualContent: manual_default,
|
|
452
|
+
execute: async (args) => {
|
|
453
|
+
const filePath = args.path;
|
|
454
|
+
try {
|
|
455
|
+
const fullPath = path2.resolve(workingDir, filePath);
|
|
456
|
+
return await fs2.readFile(fullPath, "utf-8");
|
|
457
|
+
} catch (error2) {
|
|
458
|
+
return {
|
|
459
|
+
content: `Failed to read file: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
460
|
+
isError: true
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// src/tool/write_file/index.ts
|
|
467
|
+
import * as fs3 from "fs/promises";
|
|
468
|
+
import * as path3 from "path";
|
|
469
|
+
|
|
470
|
+
// src/tool/write_file/manual.md
|
|
471
|
+
var manual_default2 = "write_file: Write content to a specified file, create the file if it doesn't exist\n\n## Parameters\n\n- `path` (string, required): File path\n- `content` (string, required): Content to write\n";
|
|
472
|
+
|
|
473
|
+
// src/tool/write_file/index.ts
|
|
474
|
+
TOOLS["write_file"] = new Tool({
|
|
475
|
+
name: "write_file",
|
|
476
|
+
parameters: {
|
|
477
|
+
path: { type: "string", description: "File path" },
|
|
478
|
+
content: { type: "string", description: "Content to write" }
|
|
479
|
+
},
|
|
480
|
+
required: ["path", "content"],
|
|
481
|
+
manualContent: manual_default2,
|
|
482
|
+
execute: async (args) => {
|
|
483
|
+
const filePath = args.path;
|
|
484
|
+
const content = args.content;
|
|
485
|
+
try {
|
|
486
|
+
const fullPath = path3.resolve(workingDir, filePath);
|
|
487
|
+
await fs3.mkdir(path3.dirname(fullPath), { recursive: true });
|
|
488
|
+
await fs3.writeFile(fullPath, content, "utf-8");
|
|
489
|
+
return `File written successfully: ${filePath}`;
|
|
490
|
+
} catch (error2) {
|
|
491
|
+
return {
|
|
492
|
+
content: `Failed to write file: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
493
|
+
isError: true
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// src/tool/list_directory/index.ts
|
|
500
|
+
import * as fs4 from "fs/promises";
|
|
501
|
+
import * as path4 from "path";
|
|
502
|
+
|
|
503
|
+
// src/tool/list_directory/manual.md
|
|
504
|
+
var manual_default3 = "list_directory: List files and subdirectories in a specified directory\n\n## Parameters\n\n- `path` (string, optional): Directory path, defaults to current directory\n";
|
|
505
|
+
|
|
506
|
+
// src/tool/list_directory/index.ts
|
|
507
|
+
TOOLS["list_directory"] = new Tool({
|
|
508
|
+
name: "list_directory",
|
|
509
|
+
parameters: {
|
|
510
|
+
path: { type: "string", description: "Directory path" }
|
|
511
|
+
},
|
|
512
|
+
required: [],
|
|
513
|
+
manualContent: manual_default3,
|
|
514
|
+
execute: async (args) => {
|
|
515
|
+
const dirPath = args.path || ".";
|
|
516
|
+
try {
|
|
517
|
+
const fullPath = path4.resolve(workingDir, dirPath);
|
|
518
|
+
const entries = await fs4.readdir(fullPath, { withFileTypes: true });
|
|
519
|
+
const result = entries.map((entry) => {
|
|
520
|
+
const type = entry.isDirectory() ? "[DIR]" : "[FILE]";
|
|
521
|
+
return `${type} ${entry.name}`;
|
|
522
|
+
});
|
|
523
|
+
return result.join("\n") || "Directory is empty";
|
|
524
|
+
} catch (error2) {
|
|
525
|
+
return {
|
|
526
|
+
content: `Failed to list directory: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
527
|
+
isError: true
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// src/tool/run_command/manual.md
|
|
534
|
+
var manual_default4 = "run_command: Execute shell command in the working directory\n\n## Parameters\n\n- `command` (string, required): Command to execute\n\n## Notes\n\n- Timeout is 30 seconds\n- Returns both stdout and stderr content\n";
|
|
535
|
+
|
|
536
|
+
// src/tool/run_command/index.ts
|
|
537
|
+
TOOLS["run_command"] = new Tool({
|
|
538
|
+
name: "run_command",
|
|
539
|
+
parameters: {
|
|
540
|
+
command: { type: "string", description: "Command to execute" }
|
|
541
|
+
},
|
|
542
|
+
required: ["command"],
|
|
543
|
+
manualContent: manual_default4,
|
|
544
|
+
execute: async (args) => {
|
|
545
|
+
const command = args.command;
|
|
546
|
+
try {
|
|
547
|
+
const { exec } = await import("child_process");
|
|
548
|
+
const { promisify } = await import("util");
|
|
549
|
+
const execAsync = promisify(exec);
|
|
550
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
551
|
+
cwd: workingDir,
|
|
552
|
+
timeout: 3e4
|
|
553
|
+
});
|
|
554
|
+
let result = "";
|
|
555
|
+
if (stdout) result += stdout;
|
|
556
|
+
if (stderr) result += `
|
|
557
|
+
[stderr]
|
|
558
|
+
${stderr}`;
|
|
559
|
+
return result.trim() || "Command executed successfully (no output)";
|
|
560
|
+
} catch (error2) {
|
|
561
|
+
return {
|
|
562
|
+
content: `Command execution failed: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
563
|
+
isError: true
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// src/tool/get_tool_info/manual.md
|
|
570
|
+
var manual_default5 = "get_tool_info: View detailed usage instructions for a specified tool\n\n## Parameters\n\n- `tool_name` (string, required): Tool name to view\n\n";
|
|
571
|
+
|
|
572
|
+
// src/tool/get_tool_info/index.ts
|
|
573
|
+
TOOLS["get_tool_info"] = new Tool({
|
|
574
|
+
name: "get_tool_info",
|
|
575
|
+
parameters: {
|
|
576
|
+
tool_name: { type: "string", description: "Tool name to view" }
|
|
577
|
+
},
|
|
578
|
+
required: ["tool_name"],
|
|
579
|
+
manualContent: manual_default5,
|
|
580
|
+
execute: async (args) => {
|
|
581
|
+
const toolName = args.tool_name;
|
|
582
|
+
const foundTool = TOOLS[toolName];
|
|
583
|
+
if (!foundTool) {
|
|
584
|
+
const availableTools = Object.keys(TOOLS).join(", ");
|
|
585
|
+
return {
|
|
586
|
+
content: `Tool not found: ${toolName}
|
|
587
|
+
Available tools: ${availableTools}`,
|
|
588
|
+
isError: true
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
return `# ${foundTool.name}
|
|
592
|
+
|
|
593
|
+
${foundTool.description}
|
|
594
|
+
|
|
595
|
+
${foundTool.manual}`;
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
// src/model/models.ts
|
|
600
|
+
var MODELS = {
|
|
601
|
+
"gpt-5.2": {
|
|
602
|
+
name: "GPT-5.2",
|
|
603
|
+
provider: "openai",
|
|
604
|
+
capabilities: {
|
|
605
|
+
streaming: true,
|
|
606
|
+
vision: true,
|
|
607
|
+
contextWindow: 2e5
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
"gpt-5.1": {
|
|
611
|
+
name: "GPT-5.1",
|
|
612
|
+
provider: "openai",
|
|
613
|
+
capabilities: {
|
|
614
|
+
streaming: true,
|
|
615
|
+
vision: true,
|
|
616
|
+
contextWindow: 2e5
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
"gpt-5": {
|
|
620
|
+
name: "GPT-5",
|
|
621
|
+
provider: "openai",
|
|
622
|
+
capabilities: {
|
|
623
|
+
streaming: true,
|
|
624
|
+
vision: true,
|
|
625
|
+
contextWindow: 2e5
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
"gpt-5-nano": {
|
|
629
|
+
name: "GPT-5 Nano",
|
|
630
|
+
provider: "openai",
|
|
631
|
+
capabilities: {
|
|
632
|
+
streaming: true,
|
|
633
|
+
vision: true,
|
|
634
|
+
contextWindow: 128e3
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
// src/agent/Agent.ts
|
|
640
|
+
var Agent = class {
|
|
641
|
+
config;
|
|
642
|
+
messages = [];
|
|
643
|
+
constructor(config2) {
|
|
644
|
+
this.config = config2;
|
|
645
|
+
this.messages = [...config2.messages];
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Render template string, replacing {{variable}} style variables
|
|
649
|
+
*/
|
|
650
|
+
static renderTemplate(template, variables) {
|
|
651
|
+
return template.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
|
|
652
|
+
const value = variables[varName];
|
|
653
|
+
if (value !== void 0) {
|
|
654
|
+
return String(value);
|
|
655
|
+
}
|
|
656
|
+
console.warn(`Warning: Variable "{{${varName}}}" not found in template`);
|
|
657
|
+
return match;
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
async run(userInput) {
|
|
661
|
+
debug("Agent", `Received user input (length: ${userInput.length})`);
|
|
662
|
+
this.messages.push(new UserMsg(userInput));
|
|
663
|
+
const toolCallHistory = [];
|
|
664
|
+
let totalUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0, cachedTokens: 0 };
|
|
665
|
+
while (true) {
|
|
666
|
+
debug("Agent", `Starting conversation (message count: ${this.messages.length})`);
|
|
667
|
+
const response = await this.config.adapter.chat(this.messages, this.config.onStream);
|
|
668
|
+
if (response.usage) {
|
|
669
|
+
totalUsage.promptTokens += response.usage.promptTokens;
|
|
670
|
+
totalUsage.completionTokens += response.usage.completionTokens;
|
|
671
|
+
totalUsage.totalTokens += response.usage.totalTokens;
|
|
672
|
+
totalUsage.cachedTokens += response.usage.cachedTokens || 0;
|
|
673
|
+
}
|
|
674
|
+
this.messages.push(response.message);
|
|
675
|
+
if (!response.hasToolCalls) {
|
|
676
|
+
debug("Agent", `Conversation completed (total tokens: ${totalUsage.totalTokens})`);
|
|
677
|
+
const content = response.message.type === "ai" /* AI */ ? response.message.content : "";
|
|
678
|
+
return {
|
|
679
|
+
response: content,
|
|
680
|
+
toolCalls: toolCallHistory,
|
|
681
|
+
usage: totalUsage.totalTokens > 0 ? totalUsage : void 0
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
if (response.message.type !== "tool_call" /* ToolCall */) continue;
|
|
685
|
+
const toolCallMsg = response.message;
|
|
686
|
+
const toolCalls = toolCallMsg.toolCalls;
|
|
687
|
+
debug("Agent", `Received tool call request`, toolCalls.map((tc) => tc.name));
|
|
688
|
+
this.config.onToolCallMsg?.(toolCallMsg);
|
|
689
|
+
for (const tc of toolCalls) {
|
|
690
|
+
debug("Agent", `Executing tool: ${tc.name}`, tc.arguments);
|
|
691
|
+
const tool = TOOLS[tc.name];
|
|
692
|
+
const result = tool ? await tool.run(tc.arguments) : new ToolResultMsg(tc.name, `Unknown tool: ${tc.name}`, true);
|
|
693
|
+
debug("Agent", `Tool execution completed: ${tc.name}`, {
|
|
694
|
+
isError: result.isError,
|
|
695
|
+
contentLength: result.result.length
|
|
696
|
+
});
|
|
697
|
+
toolCallHistory.push({
|
|
698
|
+
name: tc.name,
|
|
699
|
+
arguments: tc.arguments,
|
|
700
|
+
result: result.result
|
|
701
|
+
});
|
|
702
|
+
this.config.onToolResult?.(tc.name, result.result);
|
|
703
|
+
this.messages.push(result);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
reset() {
|
|
708
|
+
this.messages = [...this.config.messages];
|
|
709
|
+
return this;
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
// src/agent/main/colors.ts
|
|
714
|
+
var tagNames = Object.keys(colors).filter((k) => k !== "reset").join("|");
|
|
715
|
+
var tagRegex = new RegExp(`<(\\/?)(${tagNames})>`, "gi");
|
|
716
|
+
function convertColorTags(str) {
|
|
717
|
+
return str.replace(tagRegex, (_, closing, tag) => {
|
|
718
|
+
return closing ? colors.reset : colors[tag.toLowerCase()] || "";
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
function createColorConverter() {
|
|
722
|
+
let buffer = "";
|
|
723
|
+
return (chunk) => {
|
|
724
|
+
buffer += chunk;
|
|
725
|
+
const lastOpen = buffer.lastIndexOf("<");
|
|
726
|
+
if (lastOpen === -1) {
|
|
727
|
+
const out2 = convertColorTags(buffer);
|
|
728
|
+
buffer = "";
|
|
729
|
+
return out2;
|
|
730
|
+
}
|
|
731
|
+
if (buffer.indexOf(">", lastOpen) !== -1) {
|
|
732
|
+
const out2 = convertColorTags(buffer);
|
|
733
|
+
buffer = "";
|
|
734
|
+
return out2;
|
|
735
|
+
}
|
|
736
|
+
const out = convertColorTags(buffer.slice(0, lastOpen));
|
|
737
|
+
buffer = buffer.slice(lastOpen);
|
|
738
|
+
return out;
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// src/agent/main/system.md
|
|
743
|
+
var system_default = "You are Arki, a professional AI programming assistant. You work in the codebase directory `{{working_dir}}`.\n\n## Available Tools\n\n{{tools}}\n\nTools can be called multiple times at once. Make good use of this to improve efficiency.\nIf you need to understand the detailed usage of a tool, use the `get_tool_info` tool to view it.\n\n## Working Principles\n\n1. **Accuracy**: Before answering questions, use tools to view relevant code first. Don't base statements on assumptions. If you don't know something, just admit it - it's no big deal.\n2. **Safety**: Consider potential risks before executing commands.\n3. **Conciseness**: Keep answers brief and concise, avoid repetition and redundancy. Keep each response within 200 words unless the user requests detailed explanation.\n4. **Proactivity**: Actively suggest improvements when you find issues.\n\n## Response Style\n\n- Answer questions directly, avoid excessive pleasantries\n- Don't use emojis\n- Don't repeatedly ask about user needs, once is enough. Don't ask and answer yourself.\n\nThe user is talking to you via **CLI terminal**. **Do not** output Markdown. Use numbered lists for ordered lists, and \u2022 symbol for unordered lists.\nUse the following tags to format output:\n\n| Scenario | Format |\n|----------|--------|\n| Error/Danger | `<red>...</red>` |\n| Warning/Notice | `<yellow>...</yellow>` |\n| Success/Complete | `<green>...</green>` |\n| File path | `<cyan>...</cyan>` |\n| Code/Command | `<dim>...</dim>` |\n| Emphasis | `<bold>...</bold>` |\n\nTags can be combined, e.g., `<bold><red>Critical Error</red></bold>`\n\nPlease answer questions in the language the user is using, and flexibly use available tools to complete tasks.\n";
|
|
744
|
+
|
|
745
|
+
// src/agent/main/main.ts
|
|
746
|
+
function createMainAgent() {
|
|
747
|
+
if (!adapter) {
|
|
748
|
+
throw new Error("Adapter not initialized, please call init() first");
|
|
749
|
+
}
|
|
750
|
+
const toolDescriptions = Object.values(TOOLS).map((t) => `${t.name}: ${t.description}`).join("\n");
|
|
751
|
+
const systemInstruction = Agent.renderTemplate(system_default, {
|
|
752
|
+
working_dir: workingDir,
|
|
753
|
+
current_time: (/* @__PURE__ */ new Date()).toLocaleString(),
|
|
754
|
+
tools: toolDescriptions
|
|
755
|
+
});
|
|
756
|
+
const convertColor = createColorConverter();
|
|
757
|
+
const agent = new Agent({
|
|
758
|
+
adapter,
|
|
759
|
+
messages: [new SystemMsg(systemInstruction)],
|
|
760
|
+
onStream: (chunk) => {
|
|
761
|
+
process.stdout.write(convertColor(chunk));
|
|
762
|
+
},
|
|
763
|
+
onToolCallMsg: (msg) => {
|
|
764
|
+
for (const tc of msg.toolCalls) {
|
|
765
|
+
console.log();
|
|
766
|
+
log("yellow", `\u{1F527} Calling tool: ${tc.name}`);
|
|
767
|
+
log("dim", ` Arguments: ${JSON.stringify(tc.arguments)}`);
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
onToolResult: (_name, result) => {
|
|
771
|
+
const preview = result.length > 200 ? result.substring(0, 200) + "..." : result;
|
|
772
|
+
log("green", ` Result: ${preview.split("\n")[0]}`);
|
|
773
|
+
console.log();
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
return agent;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// package.json
|
|
780
|
+
var package_default = {
|
|
781
|
+
name: "arki",
|
|
782
|
+
version: "0.0.1",
|
|
783
|
+
description: "AI Agent Programming Assistant",
|
|
784
|
+
type: "module",
|
|
785
|
+
main: "dist/index.js",
|
|
786
|
+
types: "dist/index.d.ts",
|
|
787
|
+
bin: {
|
|
788
|
+
arki: "./bin/arki.js"
|
|
789
|
+
},
|
|
790
|
+
scripts: {
|
|
791
|
+
dev: "NODE_OPTIONS='--import ./register-md.mjs' tsx src/index.ts",
|
|
792
|
+
"dev:watch": "NODE_OPTIONS='--import ./register-md.mjs' tsx watch src/index.ts",
|
|
793
|
+
build: "tsup",
|
|
794
|
+
start: "node dist/index.js",
|
|
795
|
+
typecheck: "tsc --noEmit",
|
|
796
|
+
test: "vitest run",
|
|
797
|
+
"test:watch": "vitest",
|
|
798
|
+
"test:coverage": "vitest run --coverage",
|
|
799
|
+
"test:e2e": "vitest run --testNamePattern='E2E'"
|
|
800
|
+
},
|
|
801
|
+
keywords: [
|
|
802
|
+
"ai",
|
|
803
|
+
"agent",
|
|
804
|
+
"cli",
|
|
805
|
+
"coding",
|
|
806
|
+
"assistant"
|
|
807
|
+
],
|
|
808
|
+
author: "arki.moe",
|
|
809
|
+
license: "MIT",
|
|
810
|
+
repository: {
|
|
811
|
+
type: "git",
|
|
812
|
+
url: "https://github.com/your-username/arki.git"
|
|
813
|
+
},
|
|
814
|
+
packageManager: "pnpm@10.15.1",
|
|
815
|
+
engines: {
|
|
816
|
+
node: ">=18"
|
|
817
|
+
},
|
|
818
|
+
files: [
|
|
819
|
+
"dist",
|
|
820
|
+
"bin",
|
|
821
|
+
"README.md",
|
|
822
|
+
"LICENSE"
|
|
823
|
+
],
|
|
824
|
+
dependencies: {
|
|
825
|
+
openai: "^6.15.0"
|
|
826
|
+
},
|
|
827
|
+
devDependencies: {
|
|
828
|
+
"@types/node": "^25.0.3",
|
|
829
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
830
|
+
tsup: "^8.5.1",
|
|
831
|
+
tsx: "^4.21.0",
|
|
832
|
+
typescript: "^5.9.3",
|
|
833
|
+
vitest: "^4.0.16"
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
// src/index.ts
|
|
838
|
+
function getConfigPath2() {
|
|
839
|
+
return path5.join(os2.homedir(), ".config", "arki", "config.json");
|
|
840
|
+
}
|
|
841
|
+
function resetConfig() {
|
|
842
|
+
const configPath = getConfigPath2();
|
|
843
|
+
if (fs5.existsSync(configPath)) {
|
|
844
|
+
fs5.unlinkSync(configPath);
|
|
845
|
+
console.log(`Configuration file deleted: ${configPath}`);
|
|
846
|
+
console.log("Default configuration will be used on next startup.");
|
|
847
|
+
} else {
|
|
848
|
+
console.log("Configuration file does not exist, no reset needed.");
|
|
849
|
+
}
|
|
850
|
+
process.exit(0);
|
|
851
|
+
}
|
|
852
|
+
function parseArgs() {
|
|
853
|
+
const args = process.argv.slice(2);
|
|
854
|
+
let targetDir = process.cwd();
|
|
855
|
+
let enableDebug = false;
|
|
856
|
+
for (let i = 0; i < args.length; i++) {
|
|
857
|
+
if (args[i] === "-p" && args[i + 1]) {
|
|
858
|
+
targetDir = args[i + 1];
|
|
859
|
+
i++;
|
|
860
|
+
} else if (args[i] === "--debug" || args[i] === "-d") {
|
|
861
|
+
enableDebug = true;
|
|
862
|
+
} else if (args[i] === "--reset") {
|
|
863
|
+
resetConfig();
|
|
864
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
865
|
+
console.log(`
|
|
866
|
+
Usage: arki [options]
|
|
867
|
+
|
|
868
|
+
Options:
|
|
869
|
+
-p <path> Specify working directory
|
|
870
|
+
--debug, -d Enable debug mode, show detailed logs
|
|
871
|
+
--reset Reset configuration to factory defaults
|
|
872
|
+
--help, -h Show help information
|
|
873
|
+
`);
|
|
874
|
+
process.exit(0);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return { targetDir, enableDebug };
|
|
878
|
+
}
|
|
879
|
+
async function main() {
|
|
880
|
+
const { targetDir, enableDebug } = parseArgs();
|
|
881
|
+
if (enableDebug) {
|
|
882
|
+
setDebugMode(true);
|
|
883
|
+
debug("Init", "Debug mode enabled");
|
|
884
|
+
}
|
|
885
|
+
await init(targetDir);
|
|
886
|
+
const mainAgentConfig = config.getAgentConfig("main");
|
|
887
|
+
const model = MODELS[mainAgentConfig.model];
|
|
888
|
+
console.log();
|
|
889
|
+
log("cyan", `Arki AI Agent v${package_default.version}`);
|
|
890
|
+
console.log();
|
|
891
|
+
log("dim", `Model: ${mainAgentConfig.model}${model ? ` (${model.name})` : ""}`);
|
|
892
|
+
log("dim", `Working directory: ${workingDir}`);
|
|
893
|
+
if (isDebugMode()) {
|
|
894
|
+
log("yellow", `\u{1F41B} Debug mode enabled`);
|
|
895
|
+
}
|
|
896
|
+
console.log();
|
|
897
|
+
log("dim", `Loaded ${Object.keys(TOOLS).length} tools`);
|
|
898
|
+
if (isDebugMode()) {
|
|
899
|
+
debug("Init", "Loaded tools", Object.keys(TOOLS));
|
|
900
|
+
debug("Init", "Agent config", mainAgentConfig);
|
|
901
|
+
}
|
|
902
|
+
console.log();
|
|
903
|
+
const agent = createMainAgent();
|
|
904
|
+
const rl = readline.createInterface({
|
|
905
|
+
input: process.stdin,
|
|
906
|
+
output: process.stdout
|
|
907
|
+
});
|
|
908
|
+
log("blue", "Enter your question and press Enter to send. Type /exit or /quit to exit.");
|
|
909
|
+
log("blue", "Type /clear to clear conversation history.");
|
|
910
|
+
console.log();
|
|
911
|
+
const prompt = () => {
|
|
912
|
+
rl.question(`${colors.green}> ${colors.reset}`, async (input) => {
|
|
913
|
+
const trimmed = input.trim();
|
|
914
|
+
if (trimmed === "/exit" || trimmed === "/quit") {
|
|
915
|
+
log("cyan", "Goodbye!");
|
|
916
|
+
rl.close();
|
|
917
|
+
process.exit(0);
|
|
918
|
+
}
|
|
919
|
+
if (trimmed === "/clear") {
|
|
920
|
+
agent.reset();
|
|
921
|
+
log("yellow", "Conversation history cleared");
|
|
922
|
+
console.log();
|
|
923
|
+
prompt();
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (trimmed === "/help") {
|
|
927
|
+
console.log();
|
|
928
|
+
log("cyan", "Available commands:");
|
|
929
|
+
log("dim", " /exit, /quit - Exit program");
|
|
930
|
+
log("dim", " /clear - Clear conversation history");
|
|
931
|
+
log("dim", " /debug - Toggle debug mode");
|
|
932
|
+
log("dim", " /help - Show help");
|
|
933
|
+
console.log();
|
|
934
|
+
prompt();
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
if (trimmed === "/debug") {
|
|
938
|
+
setDebugMode(!isDebugMode());
|
|
939
|
+
log("yellow", `Debug mode ${isDebugMode() ? "enabled \u{1F41B}" : "disabled"}`);
|
|
940
|
+
console.log();
|
|
941
|
+
prompt();
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
if (!trimmed) {
|
|
945
|
+
prompt();
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
console.log();
|
|
949
|
+
try {
|
|
950
|
+
const result = await agent.run(trimmed);
|
|
951
|
+
console.log();
|
|
952
|
+
if (result.usage) {
|
|
953
|
+
const contextLimit = model?.capabilities.contextWindow || "N/A";
|
|
954
|
+
log(
|
|
955
|
+
"dim",
|
|
956
|
+
`[Tokens: ${result.usage.totalTokens} (prompt: ${result.usage.promptTokens}, cached: ${result.usage.cachedTokens || 0}, limit: ${contextLimit})]`
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
console.log();
|
|
960
|
+
} catch (error2) {
|
|
961
|
+
log("red", `Error: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
962
|
+
console.log();
|
|
963
|
+
}
|
|
964
|
+
prompt();
|
|
965
|
+
});
|
|
966
|
+
};
|
|
967
|
+
prompt();
|
|
968
|
+
}
|
|
969
|
+
main().catch((error2) => {
|
|
970
|
+
console.error("Fatal error:", error2);
|
|
971
|
+
process.exit(1);
|
|
972
|
+
});
|
|
973
|
+
export {
|
|
974
|
+
AIMsg,
|
|
975
|
+
Adapter,
|
|
976
|
+
Agent,
|
|
977
|
+
MAX_COMPLETION_TOKENS,
|
|
978
|
+
MODELS,
|
|
979
|
+
Msg,
|
|
980
|
+
MsgType,
|
|
981
|
+
OpenAIAdapter,
|
|
982
|
+
SystemMsg,
|
|
983
|
+
TEMPERATURE,
|
|
984
|
+
TOOLS,
|
|
985
|
+
Tool,
|
|
986
|
+
ToolCallMsg,
|
|
987
|
+
ToolResultMsg,
|
|
988
|
+
UserMsg,
|
|
989
|
+
adapter,
|
|
990
|
+
colors,
|
|
991
|
+
config,
|
|
992
|
+
debug,
|
|
993
|
+
error,
|
|
994
|
+
info,
|
|
995
|
+
init,
|
|
996
|
+
isDebugMode,
|
|
997
|
+
log,
|
|
998
|
+
setDebugMode,
|
|
999
|
+
setWorkingDir,
|
|
1000
|
+
success,
|
|
1001
|
+
warn,
|
|
1002
|
+
workingDir
|
|
1003
|
+
};
|