arki 0.0.4 → 0.0.6

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/dist/index.js CHANGED
@@ -1,385 +1,733 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
2
11
 
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";
12
+ // src/fs/paths.ts
13
+ import os from "os";
14
+ import path from "path";
13
15
  import { fileURLToPath } from "url";
14
- function getConfigPath() {
15
- return path.join(os.homedir(), ".config", "arki", "config.json");
16
+ function getOSName() {
17
+ const platform = os.platform();
18
+ switch (platform) {
19
+ case "win32":
20
+ return "windows";
21
+ case "darwin":
22
+ return "mac";
23
+ case "linux":
24
+ return "linux";
25
+ default:
26
+ return "other";
27
+ }
16
28
  }
17
- function getDefaultConfigPath() {
29
+ function setWorkingDir(dir) {
30
+ workingDir = dir;
31
+ }
32
+ function getGlobalConfigDir() {
33
+ if (OS.name === "windows") {
34
+ return path.join(
35
+ process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming"),
36
+ "arki"
37
+ );
38
+ }
39
+ return path.join(os.homedir(), ".config", "arki");
40
+ }
41
+ function getPackageDir(relativePath) {
18
42
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
- return path.join(__dirname, "config.json");
43
+ return path.join(__dirname, "..", relativePath);
20
44
  }
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
+ var OS, workingDir, PATHS;
46
+ var init_paths = __esm({
47
+ "src/fs/paths.ts"() {
48
+ "use strict";
49
+ OS = {
50
+ name: getOSName(),
51
+ version: os.release()
52
+ };
53
+ workingDir = process.cwd();
54
+ PATHS = {
55
+ /** Global config directory (~/.config/arki or %APPDATA%\arki) */
56
+ globalConfig: getGlobalConfigDir(),
57
+ /** Project config directory (.arki/) - returns path based on current workingDir */
58
+ get projectConfig() {
59
+ return path.join(workingDir, ".arki");
60
+ },
61
+ /** Package's global config template directory */
62
+ globalTemplate: getPackageDir("config/arki"),
63
+ /** Package's project config template directory */
64
+ projectTemplate: getPackageDir("config/.arki")
65
+ };
45
66
  }
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");
67
+ });
68
+
69
+ // src/fs/file.ts
70
+ import * as fs from "fs/promises";
71
+ async function fileExists(filePath) {
72
+ try {
73
+ const stat3 = await fs.stat(filePath);
74
+ return stat3.isFile();
75
+ } catch {
76
+ return false;
62
77
  }
63
- get() {
64
- return this.config;
78
+ }
79
+ async function readJsonFile(filePath) {
80
+ try {
81
+ const content = await fs.readFile(filePath, "utf-8");
82
+ return JSON.parse(content);
83
+ } catch {
84
+ return null;
65
85
  }
66
- getApiKey(provider) {
67
- return this.config.apiKeys?.[provider];
86
+ }
87
+ async function writeJsonFile(filePath, data) {
88
+ await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
89
+ }
90
+ async function readFile2(filePath) {
91
+ try {
92
+ return await fs.readFile(filePath, "utf-8");
93
+ } catch {
94
+ return null;
68
95
  }
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;
96
+ }
97
+ async function writeFile2(filePath, content) {
98
+ await fs.writeFile(filePath, content, "utf-8");
99
+ }
100
+ var init_file = __esm({
101
+ "src/fs/file.ts"() {
102
+ "use strict";
75
103
  }
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
- }
104
+ });
105
+
106
+ // src/fs/dir.ts
107
+ import * as fs2 from "fs/promises";
108
+ import * as path2 from "path";
109
+ async function copyDir(src, dest) {
110
+ await fs2.mkdir(dest, { recursive: true });
111
+ const entries = await fs2.readdir(src, { withFileTypes: true });
112
+ for (const entry of entries) {
113
+ const srcPath = path2.join(src, entry.name);
114
+ const destPath = path2.join(dest, entry.name);
115
+ if (entry.isDirectory()) {
116
+ await copyDir(srcPath, destPath);
117
+ } else {
118
+ await fs2.copyFile(srcPath, destPath);
94
119
  }
95
120
  }
96
- };
97
- var config = new ConfigManager();
121
+ }
122
+ async function dirExists(dirPath) {
123
+ try {
124
+ const stat3 = await fs2.stat(dirPath);
125
+ return stat3.isDirectory();
126
+ } catch {
127
+ return false;
128
+ }
129
+ }
130
+ async function mkdir2(dirPath) {
131
+ await fs2.mkdir(dirPath, { recursive: true });
132
+ }
133
+ var init_dir = __esm({
134
+ "src/fs/dir.ts"() {
135
+ "use strict";
136
+ }
137
+ });
98
138
 
99
- // src/adapter/openai.ts
100
- import OpenAI from "openai";
139
+ // src/fs/index.ts
140
+ var fs_exports = {};
141
+ __export(fs_exports, {
142
+ OS: () => OS,
143
+ PATHS: () => PATHS,
144
+ copyDir: () => copyDir,
145
+ dirExists: () => dirExists,
146
+ fileExists: () => fileExists,
147
+ mkdir: () => mkdir2,
148
+ readFile: () => readFile2,
149
+ readJsonFile: () => readJsonFile,
150
+ setWorkingDir: () => setWorkingDir,
151
+ workingDir: () => workingDir,
152
+ writeFile: () => writeFile2,
153
+ writeJsonFile: () => writeJsonFile
154
+ });
155
+ var init_fs = __esm({
156
+ "src/fs/index.ts"() {
157
+ "use strict";
158
+ init_paths();
159
+ init_file();
160
+ init_dir();
161
+ }
162
+ });
101
163
 
102
164
  // 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;
165
+ var TEMPERATURE, MAX_COMPLETION_TOKENS, Adapter;
166
+ var init_Adapter = __esm({
167
+ "src/adapter/Adapter.ts"() {
168
+ "use strict";
169
+ TEMPERATURE = 0.2;
170
+ MAX_COMPLETION_TOKENS = 4096;
171
+ Adapter = class {
172
+ apiKey;
173
+ model;
174
+ /** Use Flex API (OpenAI) - low priority, low cost */
175
+ flex;
176
+ /** Reasoning effort (thinking mode) */
177
+ reasoningEffort;
178
+ /** Available tools list */
179
+ tools;
180
+ constructor(config) {
181
+ this.apiKey = config.apiKey;
182
+ this.model = config.model;
183
+ this.flex = config.flex;
184
+ this.reasoningEffort = config.reasoningEffort;
185
+ this.tools = config.tools;
186
+ }
187
+ getModel() {
188
+ return this.model;
189
+ }
190
+ };
123
191
  }
124
- };
192
+ });
125
193
 
126
194
  // 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 _ToolResultMsg extends Msg {
170
- type = "tool_result" /* ToolResult */;
171
- toolResults;
172
- constructor(toolResults) {
173
- super("");
174
- this.toolResults = toolResults;
195
+ var MsgType, Msg, SystemMsg, UserMsg, AIMsg, ToolCallMsg, ToolResultMsg;
196
+ var init_Msg = __esm({
197
+ "src/agent/Msg.ts"() {
198
+ "use strict";
199
+ MsgType = /* @__PURE__ */ ((MsgType2) => {
200
+ MsgType2["System"] = "system";
201
+ MsgType2["User"] = "user";
202
+ MsgType2["AI"] = "ai";
203
+ MsgType2["ToolCall"] = "tool_call";
204
+ MsgType2["ToolResult"] = "tool_result";
205
+ return MsgType2;
206
+ })(MsgType || {});
207
+ Msg = class {
208
+ timestamp;
209
+ content;
210
+ constructor(content) {
211
+ this.timestamp = Date.now();
212
+ this.content = content;
213
+ }
214
+ };
215
+ SystemMsg = class extends Msg {
216
+ type = "system" /* System */;
217
+ constructor(content) {
218
+ super(content);
219
+ }
220
+ };
221
+ UserMsg = class extends Msg {
222
+ type = "user" /* User */;
223
+ constructor(content) {
224
+ super(content);
225
+ }
226
+ };
227
+ AIMsg = class extends Msg {
228
+ type = "ai" /* AI */;
229
+ constructor(content) {
230
+ super(content);
231
+ }
232
+ };
233
+ ToolCallMsg = class extends Msg {
234
+ type = "tool_call" /* ToolCall */;
235
+ toolCalls;
236
+ constructor(content, toolCalls) {
237
+ super(content);
238
+ this.toolCalls = toolCalls;
239
+ }
240
+ };
241
+ ToolResultMsg = class _ToolResultMsg extends Msg {
242
+ type = "tool_result" /* ToolResult */;
243
+ toolResults;
244
+ constructor(toolResults) {
245
+ super("");
246
+ this.toolResults = toolResults;
247
+ }
248
+ /** Helper: create from single result */
249
+ static single(toolName, result, isError) {
250
+ return new _ToolResultMsg([{ toolName, result, isError }]);
251
+ }
252
+ };
175
253
  }
176
- /** Helper: create from single result */
177
- static single(toolName, result, isError) {
178
- return new _ToolResultMsg([{ toolName, result, isError }]);
254
+ });
255
+
256
+ // src/log/log.ts
257
+ function getTimestamp() {
258
+ return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
259
+ }
260
+ function print(message) {
261
+ console.log(convertColorTags(message));
262
+ }
263
+ function log(message) {
264
+ print(`<gray>[${getTimestamp()}]</gray> ${message}`);
265
+ }
266
+ function info(message) {
267
+ log(`<blue>[INFO]</blue> ${message}`);
268
+ }
269
+ function success(message) {
270
+ log(`<green>[OK]</green> ${message}`);
271
+ }
272
+ function warn(message) {
273
+ log(`<yellow>[WARN]</yellow> ${message}`);
274
+ }
275
+ function error(message) {
276
+ log(`<red>[ERROR]</red> ${message}`);
277
+ }
278
+ var init_log = __esm({
279
+ "src/log/log.ts"() {
280
+ "use strict";
281
+ init_log2();
179
282
  }
180
- };
283
+ });
181
284
 
182
285
  // src/log/debug.ts
183
- var _debugMode = false;
184
286
  function isDebugMode() {
185
287
  return _debugMode;
186
288
  }
187
289
  function setDebugMode(enabled) {
188
290
  _debugMode = enabled;
189
291
  }
292
+ function formatData(data, maxLen = 100) {
293
+ if (data === void 0) return "";
294
+ const str = typeof data === "string" ? data : JSON.stringify(data);
295
+ const singleLine = str.replace(/\s+/g, " ").trim();
296
+ return singleLine.length > maxLen ? singleLine.slice(0, maxLen) + "..." : singleLine;
297
+ }
190
298
  function debug(category, message, data) {
191
299
  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
- }
300
+ const dataStr = data !== void 0 ? ` <dim>${formatData(data)}</dim>` : "";
301
+ log(`<magenta>[${category}]</magenta> <cyan>${message}</cyan>${dataStr}`);
206
302
  }
303
+ var _debugMode;
304
+ var init_debug = __esm({
305
+ "src/log/debug.ts"() {
306
+ "use strict";
307
+ init_log();
308
+ _debugMode = false;
309
+ }
310
+ });
207
311
 
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}`);
312
+ // src/log/index.ts
313
+ function convertColorTags(str) {
314
+ return str.replace(tagRegex, (_, closing, tag) => {
315
+ return closing ? colors.reset : colors[tag.toLowerCase()] || "";
316
+ });
220
317
  }
221
- function error(message) {
222
- console.log(`${colors.red}\u2716${colors.reset} ${message}`);
318
+ function createColorConverter() {
319
+ let buffer = "";
320
+ return (chunk) => {
321
+ buffer += chunk;
322
+ const lastOpen = buffer.lastIndexOf("<");
323
+ if (lastOpen === -1) {
324
+ const out2 = convertColorTags(buffer);
325
+ buffer = "";
326
+ return out2;
327
+ }
328
+ if (buffer.indexOf(">", lastOpen) !== -1) {
329
+ const out2 = convertColorTags(buffer);
330
+ buffer = "";
331
+ return out2;
332
+ }
333
+ const out = convertColorTags(buffer.slice(0, lastOpen));
334
+ buffer = buffer.slice(lastOpen);
335
+ return out;
336
+ };
223
337
  }
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
- };
338
+ var colors, tagNames, tagRegex;
339
+ var init_log2 = __esm({
340
+ "src/log/index.ts"() {
341
+ "use strict";
342
+ init_debug();
343
+ init_log();
344
+ colors = {
345
+ reset: "\x1B[0m",
346
+ bold: "\x1B[1m",
347
+ dim: "\x1B[2m",
348
+ italic: "\x1B[3m",
349
+ underline: "\x1B[4m",
350
+ inverse: "\x1B[7m",
351
+ strikethrough: "\x1B[9m",
352
+ red: "\x1B[31m",
353
+ green: "\x1B[32m",
354
+ yellow: "\x1B[33m",
355
+ blue: "\x1B[34m",
356
+ magenta: "\x1B[35m",
357
+ cyan: "\x1B[36m",
358
+ gray: "\x1B[90m"
359
+ };
360
+ tagNames = Object.keys(colors).filter((k) => k !== "reset").join("|");
361
+ tagRegex = new RegExp(`<(\\/?)(${tagNames})>`, "gi");
362
+ }
363
+ });
242
364
 
243
365
  // 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
- for (const tr of toolResultMsg.toolResults) {
276
- result.push({
277
- role: "tool",
278
- tool_call_id: pendingIds.shift() || `call_${msg.timestamp}`,
279
- content: tr.isError ? `Error: ${tr.result}` : tr.result
280
- });
366
+ import OpenAI from "openai";
367
+ var OpenAIAdapter;
368
+ var init_openai = __esm({
369
+ "src/adapter/openai.ts"() {
370
+ "use strict";
371
+ init_Adapter();
372
+ init_Msg();
373
+ init_log2();
374
+ OpenAIAdapter = class extends Adapter {
375
+ client;
376
+ constructor(config) {
377
+ super(config);
378
+ if (!this.apiKey) {
379
+ throw new Error("OpenAI API key is required. Set OPENAI_API_KEY environment variable.");
281
380
  }
381
+ this.client = new OpenAI({ apiKey: this.apiKey });
282
382
  }
283
- }
284
- return result;
285
- }
286
- getTools() {
287
- return this.tools?.map((t) => ({
288
- type: "function",
289
- function: {
290
- name: t.name,
291
- description: t.description,
292
- parameters: {
293
- type: "object",
294
- properties: t.parameters,
295
- required: t.required.length > 0 ? t.required : void 0
383
+ toOpenAIMessages(messages) {
384
+ const result = [];
385
+ let pendingIds = [];
386
+ for (const msg of messages) {
387
+ if (msg.type === "system" /* System */ || msg.type === "user" /* User */) {
388
+ result.push({ role: msg.type, content: msg.content });
389
+ } else if (msg.type === "ai" /* AI */) {
390
+ result.push({ role: "assistant", content: msg.content });
391
+ } else if (msg.type === "tool_call" /* ToolCall */) {
392
+ const toolCallMsg = msg;
393
+ pendingIds = toolCallMsg.toolCalls.map((_, i) => `call_${msg.timestamp}_${i}`);
394
+ result.push({
395
+ role: "assistant",
396
+ content: msg.content || null,
397
+ tool_calls: toolCallMsg.toolCalls.map((tc, i) => ({
398
+ id: pendingIds[i],
399
+ type: "function",
400
+ function: { name: tc.name, arguments: JSON.stringify(tc.arguments) }
401
+ }))
402
+ });
403
+ } else if (msg.type === "tool_result" /* ToolResult */) {
404
+ const toolResultMsg = msg;
405
+ for (const tr of toolResultMsg.toolResults) {
406
+ result.push({
407
+ role: "tool",
408
+ tool_call_id: pendingIds.shift() || `call_${msg.timestamp}`,
409
+ content: tr.isError ? `Error: ${tr.result}` : tr.result
410
+ });
411
+ }
412
+ }
296
413
  }
414
+ return result;
297
415
  }
298
- }));
299
- }
300
- async chat(messages, onChunk) {
301
- debug("API", `Requesting OpenAI (model: ${this.model}, messages: ${messages.length})`);
302
- const openaiMessages = this.toOpenAIMessages(messages);
303
- debug("API", "Sent prompt:", openaiMessages);
304
- const startTime = Date.now();
305
- const requestParams = {
306
- model: this.model,
307
- messages: openaiMessages,
308
- tools: this.getTools(),
309
- temperature: TEMPERATURE,
310
- max_completion_tokens: MAX_COMPLETION_TOKENS,
311
- stream: true,
312
- stream_options: { include_usage: true },
313
- service_tier: this.flex ? "flex" : void 0,
314
- reasoning_effort: this.reasoningEffort
315
- };
316
- const stream = await this.client.chat.completions.create(requestParams);
317
- let text = "";
318
- const toolCalls = /* @__PURE__ */ new Map();
319
- let usage;
320
- for await (const chunk of stream) {
321
- const delta = chunk.choices[0]?.delta;
322
- if (delta?.content) {
323
- text += delta.content;
324
- onChunk?.(delta.content);
416
+ getTools() {
417
+ return this.tools?.map((t) => ({
418
+ type: "function",
419
+ function: {
420
+ name: t.name,
421
+ description: t.description,
422
+ parameters: {
423
+ type: "object",
424
+ properties: t.parameters,
425
+ required: t.required.length > 0 ? t.required : void 0
426
+ }
427
+ }
428
+ }));
325
429
  }
326
- if (delta?.tool_calls) {
327
- for (const tc of delta.tool_calls) {
328
- const cur = toolCalls.get(tc.index) || { name: "", args: "" };
329
- cur.name += tc.function?.name || "";
330
- cur.args += tc.function?.arguments || "";
331
- toolCalls.set(tc.index, cur);
430
+ async chat(messages, onChunk) {
431
+ debug("API", `Requesting OpenAI (model: ${this.model}, messages: ${messages.length})`);
432
+ const openaiMessages = this.toOpenAIMessages(messages);
433
+ const startTime = Date.now();
434
+ const requestParams = {
435
+ model: this.model,
436
+ messages: openaiMessages,
437
+ tools: this.getTools(),
438
+ temperature: TEMPERATURE,
439
+ max_completion_tokens: MAX_COMPLETION_TOKENS,
440
+ stream: true,
441
+ stream_options: { include_usage: true },
442
+ service_tier: this.flex ? "flex" : void 0,
443
+ reasoning_effort: this.reasoningEffort
444
+ };
445
+ const stream = await this.client.chat.completions.create(requestParams);
446
+ let text = "";
447
+ const toolCalls = /* @__PURE__ */ new Map();
448
+ let usage;
449
+ for await (const chunk of stream) {
450
+ const delta = chunk.choices[0]?.delta;
451
+ if (delta?.content) {
452
+ text += delta.content;
453
+ onChunk?.(delta.content);
454
+ }
455
+ if (delta?.tool_calls) {
456
+ for (const tc of delta.tool_calls) {
457
+ const cur = toolCalls.get(tc.index) || { name: "", args: "" };
458
+ cur.name += tc.function?.name || "";
459
+ cur.args += tc.function?.arguments || "";
460
+ toolCalls.set(tc.index, cur);
461
+ }
462
+ }
463
+ if (chunk.usage) usage = chunk.usage;
464
+ }
465
+ const elapsed = Date.now() - startTime;
466
+ const cachedTokens = usage?.prompt_tokens_details?.cached_tokens;
467
+ const usageData = usage && {
468
+ promptTokens: usage.prompt_tokens,
469
+ completionTokens: usage.completion_tokens,
470
+ totalTokens: usage.total_tokens,
471
+ cachedTokens
472
+ };
473
+ if (toolCalls.size > 0) {
474
+ const calls = [...toolCalls.values()].map((tc) => ({
475
+ name: tc.name,
476
+ arguments: JSON.parse(tc.args || "{}")
477
+ }));
478
+ debug("API", `Completed (${elapsed}ms, tools: ${calls.map((t) => t.name).join(", ")})`);
479
+ return { message: new ToolCallMsg(text, calls), hasToolCalls: true, usage: usageData };
332
480
  }
481
+ debug("API", `Completed (${elapsed}ms, tokens: ${usage?.total_tokens || "N/A"})`);
482
+ return { message: new AIMsg(text), hasToolCalls: false, usage: usageData };
333
483
  }
334
- if (chunk.usage) usage = chunk.usage;
335
- }
336
- const elapsed = Date.now() - startTime;
337
- const cachedTokens = usage?.prompt_tokens_details?.cached_tokens;
338
- const usageData = usage && {
339
- promptTokens: usage.prompt_tokens,
340
- completionTokens: usage.completion_tokens,
341
- totalTokens: usage.total_tokens,
342
- cachedTokens
343
484
  };
344
- if (toolCalls.size > 0) {
345
- const calls = [...toolCalls.values()].map((tc) => ({
346
- name: tc.name,
347
- arguments: JSON.parse(tc.args || "{}")
348
- }));
349
- debug("API", `Completed (${elapsed}ms, tools: ${calls.map((t) => t.name).join(", ")})`);
350
- return { message: new ToolCallMsg(text, calls), hasToolCalls: true, usage: usageData };
485
+ }
486
+ });
487
+
488
+ // src/init/loader.ts
489
+ var loader_exports = {};
490
+ __export(loader_exports, {
491
+ getAgentConfig: () => getAgentConfig,
492
+ getApiKey: () => getApiKey,
493
+ getConfig: () => getConfig,
494
+ loadConfigs: () => loadConfigs,
495
+ saveConfig: () => saveConfig
496
+ });
497
+ import * as path3 from "path";
498
+ function deepMerge(base, override) {
499
+ const result = {
500
+ ...base,
501
+ agents: { ...base.agents }
502
+ };
503
+ if (override.apiKeys) {
504
+ result.apiKeys = { ...base.apiKeys, ...override.apiKeys };
505
+ }
506
+ if (override.agents) {
507
+ for (const key of Object.keys(override.agents)) {
508
+ const overrideAgent = override.agents[key];
509
+ if (overrideAgent) {
510
+ result.agents[key] = { ...result.agents[key], ...overrideAgent };
511
+ }
351
512
  }
352
- debug("API", `Completed (${elapsed}ms, tokens: ${usage?.total_tokens || "N/A"})`);
353
- return { message: new AIMsg(text), hasToolCalls: false, usage: usageData };
354
513
  }
355
- };
514
+ return result;
515
+ }
516
+ function loadEnvApiKeys(config) {
517
+ if (process.env.OPENAI_API_KEY) {
518
+ if (!config.apiKeys) config.apiKeys = {};
519
+ if (!config.apiKeys.openai) {
520
+ config.apiKeys.openai = process.env.OPENAI_API_KEY;
521
+ }
522
+ }
523
+ if (process.env.ANTHROPIC_API_KEY) {
524
+ if (!config.apiKeys) config.apiKeys = {};
525
+ if (!config.apiKeys.anthropic) {
526
+ config.apiKeys.anthropic = process.env.ANTHROPIC_API_KEY;
527
+ }
528
+ }
529
+ if (process.env.GOOGLE_API_KEY) {
530
+ if (!config.apiKeys) config.apiKeys = {};
531
+ if (!config.apiKeys.google) {
532
+ config.apiKeys.google = process.env.GOOGLE_API_KEY;
533
+ }
534
+ }
535
+ }
536
+ async function loadConfigs() {
537
+ if (mergedConfig) {
538
+ return mergedConfig;
539
+ }
540
+ const globalConfigPath = path3.join(PATHS.globalConfig, "config.json");
541
+ const globalConfig = await readJsonFile(globalConfigPath);
542
+ if (!globalConfig) {
543
+ throw new Error(`Failed to load global config: ${globalConfigPath}`);
544
+ }
545
+ const projectConfigPath = path3.join(PATHS.projectConfig, "config.json");
546
+ const projectConfig = await readJsonFile(projectConfigPath);
547
+ if (projectConfig) {
548
+ mergedConfig = deepMerge(globalConfig, projectConfig);
549
+ } else {
550
+ mergedConfig = globalConfig;
551
+ }
552
+ loadEnvApiKeys(mergedConfig);
553
+ return mergedConfig;
554
+ }
555
+ function getConfig() {
556
+ if (!mergedConfig) {
557
+ throw new Error("Config not loaded yet. Please call loadConfigs() first.");
558
+ }
559
+ return mergedConfig;
560
+ }
561
+ function getApiKey(provider) {
562
+ return getConfig().apiKeys?.[provider];
563
+ }
564
+ function getAgentConfig(agentType) {
565
+ const agentConfig = getConfig().agents[agentType];
566
+ if (!agentConfig) {
567
+ throw new Error(`Agent config not found: ${agentType}`);
568
+ }
569
+ return agentConfig;
570
+ }
571
+ async function saveConfig() {
572
+ const config = getConfig();
573
+ const configPath = path3.join(PATHS.globalConfig, "config.json");
574
+ const configToSave = { ...config };
575
+ if (config.apiKeys) {
576
+ configToSave.apiKeys = { ...config.apiKeys };
577
+ if (process.env.OPENAI_API_KEY && configToSave.apiKeys.openai === process.env.OPENAI_API_KEY) {
578
+ delete configToSave.apiKeys.openai;
579
+ }
580
+ if (process.env.ANTHROPIC_API_KEY && configToSave.apiKeys.anthropic === process.env.ANTHROPIC_API_KEY) {
581
+ delete configToSave.apiKeys.anthropic;
582
+ }
583
+ if (process.env.GOOGLE_API_KEY && configToSave.apiKeys.google === process.env.GOOGLE_API_KEY) {
584
+ delete configToSave.apiKeys.google;
585
+ }
586
+ }
587
+ await writeJsonFile(configPath, configToSave);
588
+ }
589
+ var mergedConfig;
590
+ var init_loader = __esm({
591
+ "src/init/loader.ts"() {
592
+ "use strict";
593
+ init_fs();
594
+ mergedConfig = null;
595
+ }
596
+ });
356
597
 
357
- // src/global.ts
358
- var workingDir = process.cwd();
359
- function setWorkingDir(dir) {
360
- workingDir = dir;
598
+ // src/init/index.ts
599
+ var init_exports = {};
600
+ __export(init_exports, {
601
+ PROCEDURES: () => PROCEDURES,
602
+ TOOLS: () => TOOLS,
603
+ adapter: () => adapter,
604
+ getAgentConfig: () => getAgentConfig,
605
+ getApiKey: () => getApiKey,
606
+ getConfig: () => getConfig,
607
+ init: () => init,
608
+ initAdapter: () => initAdapter,
609
+ loadConfigs: () => loadConfigs,
610
+ saveConfig: () => saveConfig
611
+ });
612
+ var init_init = __esm({
613
+ "src/init/index.ts"() {
614
+ "use strict";
615
+ init_global();
616
+ init_loader();
617
+ }
618
+ });
619
+
620
+ // src/init/project.ts
621
+ var project_exports = {};
622
+ __export(project_exports, {
623
+ initProject: () => initProject
624
+ });
625
+ import * as path4 from "path";
626
+ import * as readline from "readline";
627
+ async function askQuestion(question) {
628
+ const rl = readline.createInterface({
629
+ input: process.stdin,
630
+ output: process.stdout
631
+ });
632
+ return new Promise((resolve4) => {
633
+ rl.question(`${question} (y/n): `, (answer) => {
634
+ rl.close();
635
+ resolve4(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
636
+ });
637
+ });
638
+ }
639
+ async function initProject() {
640
+ const projectConfigDir = PATHS.projectConfig;
641
+ if (await dirExists(projectConfigDir)) {
642
+ return;
643
+ }
644
+ print(`
645
+ <dim>Project directory: ${workingDir}</dim>`);
646
+ const trusted = await askQuestion("Do you trust this project and want to initialize arki config?");
647
+ if (!trusted) {
648
+ print("<yellow>Initialization cancelled.</yellow>");
649
+ process.exit(0);
650
+ }
651
+ await copyDir(PATHS.projectTemplate, projectConfigDir);
652
+ const statePath = path4.join(projectConfigDir, "state.json");
653
+ const state = {
654
+ initialized: true,
655
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
656
+ };
657
+ await writeJsonFile(statePath, state);
658
+ print("<green>Project configuration initialized.</green>");
659
+ }
660
+ var init_project = __esm({
661
+ "src/init/project.ts"() {
662
+ "use strict";
663
+ init_fs();
664
+ init_log2();
665
+ }
666
+ });
667
+
668
+ // src/init/global.ts
669
+ async function initGlobalConfig() {
670
+ const globalConfigDir = PATHS.globalConfig;
671
+ if (await dirExists(globalConfigDir)) {
672
+ return;
673
+ }
674
+ await copyDir(PATHS.globalTemplate, globalConfigDir);
361
675
  }
362
- var TOOLS = {};
363
- var adapter = null;
364
676
  function initAdapter() {
365
677
  if (adapter) {
366
678
  return;
367
679
  }
368
- const mainAgentConfig = config.getAgentConfig("main");
680
+ Promise.resolve().then(() => (init_init(), init_exports)).then(({ getAgentConfig: getAgentConfig2, getApiKey: getApiKey2 }) => {
681
+ const mainAgentConfig = getAgentConfig2("main");
682
+ adapter = new OpenAIAdapter({
683
+ apiKey: getApiKey2("openai") || "",
684
+ model: mainAgentConfig.model,
685
+ flex: mainAgentConfig.flex,
686
+ tools: Object.values(TOOLS)
687
+ });
688
+ });
689
+ }
690
+ async function init(cwd) {
691
+ const { setWorkingDir: setWorkingDir2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
692
+ setWorkingDir2(cwd || process.cwd());
693
+ await initGlobalConfig();
694
+ const { initProject: initProject2 } = await Promise.resolve().then(() => (init_project(), project_exports));
695
+ await initProject2();
696
+ const { loadConfigs: loadConfigs2, getAgentConfig: getAgentConfig2, getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
697
+ await loadConfigs2();
698
+ const mainAgentConfig = getAgentConfig2("main");
369
699
  adapter = new OpenAIAdapter({
370
- apiKey: config.getApiKey("openai") || "",
700
+ apiKey: getApiKey2("openai") || "",
371
701
  model: mainAgentConfig.model,
372
702
  flex: mainAgentConfig.flex,
373
703
  tools: Object.values(TOOLS)
374
704
  });
375
705
  }
376
- async function init(cwd) {
377
- workingDir = cwd || process.cwd();
378
- await config.load();
379
- initAdapter();
380
- }
706
+ var TOOLS, PROCEDURES, adapter;
707
+ var init_global = __esm({
708
+ "src/init/global.ts"() {
709
+ "use strict";
710
+ init_openai();
711
+ init_fs();
712
+ TOOLS = {};
713
+ PROCEDURES = {};
714
+ adapter = null;
715
+ }
716
+ });
717
+
718
+ // src/index.ts
719
+ import * as readline2 from "readline";
720
+ import * as fs6 from "fs";
721
+ import * as path8 from "path";
722
+ import * as os2 from "os";
723
+
724
+ // src/global.ts
725
+ init_fs();
726
+ init_global();
727
+ init_init();
381
728
 
382
729
  // src/tool/Tool.ts
730
+ init_log2();
383
731
  var HAS_MANUAL = "\u{1F4D8}";
384
732
  var Tool = class _Tool {
385
733
  name;
@@ -388,12 +736,12 @@ var Tool = class _Tool {
388
736
  required;
389
737
  manual;
390
738
  _execute;
391
- constructor(config2) {
392
- this.name = config2.name;
393
- this.parameters = config2.parameters;
394
- this.required = config2.required;
395
- this._execute = config2.execute;
396
- const { description, manual } = _Tool.parseManual(config2.manualContent);
739
+ constructor(config) {
740
+ this.name = config.name;
741
+ this.parameters = config.parameters;
742
+ this.required = config.required;
743
+ this._execute = config.execute;
744
+ const { description, manual } = _Tool.parseManual(config.manualContent);
397
745
  this.description = description;
398
746
  this.manual = manual;
399
747
  }
@@ -421,29 +769,23 @@ var Tool = class _Tool {
421
769
  * Execute tool (with error handling and logging)
422
770
  */
423
771
  async run(args) {
424
- debug("Tool", `Starting tool execution: ${this.name}`, args);
425
- const startTime = Date.now();
426
772
  try {
427
773
  const result = await this._execute(args);
428
- const elapsed = Date.now() - startTime;
429
774
  if (typeof result === "string") {
430
- debug("Tool", `Tool execution successful: ${this.name} (elapsed: ${elapsed}ms, result length: ${result.length})`);
431
775
  return { toolName: this.name, result };
432
776
  }
433
- debug("Tool", `Tool execution completed: ${this.name} (elapsed: ${elapsed}ms, isError: ${result.isError || false})`);
434
777
  return { toolName: this.name, result: result.content, isError: result.isError };
435
778
  } catch (error2) {
436
- const elapsed = Date.now() - startTime;
437
779
  const errorMsg = error2 instanceof Error ? error2.message : String(error2);
438
- debug("Tool", `Tool execution failed: ${this.name} (elapsed: ${elapsed}ms)`, errorMsg);
780
+ debug("Tool", `${this.name} error`, errorMsg);
439
781
  return { toolName: this.name, result: `Error: ${errorMsg}`, isError: true };
440
782
  }
441
783
  }
442
784
  };
443
785
 
444
786
  // src/tool/read_file/index.ts
445
- import * as fs2 from "fs/promises";
446
- import * as path2 from "path";
787
+ import * as fs3 from "fs/promises";
788
+ import * as path5 from "path";
447
789
 
448
790
  // src/tool/read_file/manual.md
449
791
  var manual_default = "read_file: Read the content of a specified file\n";
@@ -459,8 +801,8 @@ TOOLS["read_file"] = new Tool({
459
801
  execute: async (args) => {
460
802
  const filePath = args.path;
461
803
  try {
462
- const fullPath = path2.resolve(workingDir, filePath);
463
- return await fs2.readFile(fullPath, "utf-8");
804
+ const fullPath = path5.resolve(workingDir, filePath);
805
+ return await fs3.readFile(fullPath, "utf-8");
464
806
  } catch (error2) {
465
807
  return {
466
808
  content: `Failed to read file: ${error2 instanceof Error ? error2.message : String(error2)}`,
@@ -471,8 +813,8 @@ TOOLS["read_file"] = new Tool({
471
813
  });
472
814
 
473
815
  // src/tool/write_file/index.ts
474
- import * as fs3 from "fs/promises";
475
- import * as path3 from "path";
816
+ import * as fs4 from "fs/promises";
817
+ import * as path6 from "path";
476
818
 
477
819
  // src/tool/write_file/manual.md
478
820
  var manual_default2 = "write_file: Write content to a specified file, create the file if it doesn't exist\n";
@@ -490,9 +832,9 @@ TOOLS["write_file"] = new Tool({
490
832
  const filePath = args.path;
491
833
  const content = args.content;
492
834
  try {
493
- const fullPath = path3.resolve(workingDir, filePath);
494
- await fs3.mkdir(path3.dirname(fullPath), { recursive: true });
495
- await fs3.writeFile(fullPath, content, "utf-8");
835
+ const fullPath = path6.resolve(workingDir, filePath);
836
+ await fs4.mkdir(path6.dirname(fullPath), { recursive: true });
837
+ await fs4.writeFile(fullPath, content, "utf-8");
496
838
  return `File written successfully: ${filePath}`;
497
839
  } catch (error2) {
498
840
  return {
@@ -504,8 +846,8 @@ TOOLS["write_file"] = new Tool({
504
846
  });
505
847
 
506
848
  // src/tool/list_directory/index.ts
507
- import * as fs4 from "fs/promises";
508
- import * as path4 from "path";
849
+ import * as fs5 from "fs/promises";
850
+ import * as path7 from "path";
509
851
 
510
852
  // src/tool/list_directory/manual.md
511
853
  var manual_default3 = "list_directory: List files and subdirectories in a specified directory\n";
@@ -521,8 +863,8 @@ TOOLS["list_directory"] = new Tool({
521
863
  execute: async (args) => {
522
864
  const dirPath = args.path || ".";
523
865
  try {
524
- const fullPath = path4.resolve(workingDir, dirPath);
525
- const entries = await fs4.readdir(fullPath, { withFileTypes: true });
866
+ const fullPath = path7.resolve(workingDir, dirPath);
867
+ const entries = await fs5.readdir(fullPath, { withFileTypes: true });
526
868
  const result = entries.map((entry) => {
527
869
  const type = entry.isDirectory() ? "[DIR]" : "[FILE]";
528
870
  return `${type} ${entry.name}`;
@@ -603,6 +945,79 @@ ${foundTool.manual}`;
603
945
  }
604
946
  });
605
947
 
948
+ // src/tool/read_procedure/manual.md
949
+ var manual_default6 = "read_procedure: View detailed steps for a specified procedure\n";
950
+
951
+ // src/tool/read_procedure/index.ts
952
+ TOOLS["read_procedure"] = new Tool({
953
+ name: "read_procedure",
954
+ parameters: {
955
+ procedure_name: { type: "string", description: "Procedure name to view" }
956
+ },
957
+ required: ["procedure_name"],
958
+ manualContent: manual_default6,
959
+ execute: async (args) => {
960
+ const procedureName = args.procedure_name;
961
+ const procedure = PROCEDURES[procedureName];
962
+ if (!procedure) {
963
+ const available = Object.keys(PROCEDURES).join(", ");
964
+ return {
965
+ content: `Procedure not found: ${procedureName}
966
+ Available: ${available}`,
967
+ isError: true
968
+ };
969
+ }
970
+ return `# ${procedure.name}
971
+
972
+ ${procedure.description}
973
+
974
+ ## Steps
975
+
976
+ ${procedure.manual}`;
977
+ }
978
+ });
979
+
980
+ // src/procedure/Procedure.ts
981
+ var Procedure = class _Procedure {
982
+ name;
983
+ description;
984
+ manual;
985
+ constructor(config) {
986
+ this.name = config.name;
987
+ const { description, manual } = _Procedure.parseManual(config.procedureContent);
988
+ this.description = description;
989
+ this.manual = manual;
990
+ }
991
+ /**
992
+ * Parse procedure.md content
993
+ * First line format: "procedure_name: description", extract description
994
+ * Remaining content is the procedure steps
995
+ */
996
+ static parseManual(content) {
997
+ const lines = content.split("\n");
998
+ const firstLine = lines[0] || "";
999
+ let description = "";
1000
+ const colonIndex = firstLine.indexOf(":");
1001
+ if (colonIndex > 0) {
1002
+ description = firstLine.slice(colonIndex + 1).trim();
1003
+ }
1004
+ const manual = lines.slice(1).join("\n").trim();
1005
+ return { description, manual };
1006
+ }
1007
+ };
1008
+
1009
+ // src/procedure/understand_project/procedure.md
1010
+ var procedure_default = "understand_project: Systematically explore and understand the current project structure\n\n1. Use `list_directory` on the root directory to get an overview\n - Identify key directories (src, lib, tests, docs, etc.)\n - Note configuration files (package.json, tsconfig.json, Cargo.toml, etc.)\n\n2. Use `read_file` on the main configuration file (package.json, Cargo.toml, pyproject.toml, etc.)\n - Project name and description\n - Dependencies and their purposes\n - Scripts/commands available\n - Entry points\n\n3. Use `read_file` on README.md if it exists\n - Project purpose and goals\n - Setup instructions\n - Usage examples\n\n4. Use `list_directory` on the main source directory (src/, lib/, app/, etc.)\n - Identify the entry point file (index.ts, main.ts, app.ts, etc.)\n - List subdirectories to understand module organization\n - Note any patterns (MVC, feature-based, etc.)\n\n5. Use `read_file` on 2-3 key source files to understand\n - Coding style and conventions\n - Main abstractions and patterns used\n - How modules interact with each other\n\n6. Output the final report in this format **in user's language**:\n\n---\n<bold><cyan>Project Overview</cyan></bold>\n\nName: [project name]\nType: [CLI tool / Web app / Library / API server / etc.]\nLanguage: [TypeScript / JavaScript / Python / etc.]\nPackage Manager: [npm / pnpm / yarn / pip / cargo / etc.]\n\n<bold><cyan>Project Structure</cyan></bold>\n\n[Brief description of directory structure and organization pattern]\n\n<bold><cyan>Key Components</cyan></bold>\n\n\u2022 [Component 1]: [brief description]\n\u2022 [Component 2]: [brief description]\n\u2022 [Component 3]: [brief description]\n...\n\n<bold><cyan>Entry Points</cyan></bold>\n\n\u2022 Main: [path to main entry]\n\u2022 CLI: [path to CLI entry if applicable]\n\u2022 Tests: [path to test entry if applicable]\n\n<bold><cyan>Dependencies</cyan></bold>\n\nCore:\n\u2022 [dep1]: [purpose]\n\u2022 [dep2]: [purpose]\n\nDev:\n\u2022 [dev-dep1]: [purpose]\n\u2022 [dev-dep2]: [purpose]\n\n<bold><cyan>Available Commands</cyan></bold>\n\n\u2022 [command1]: [description]\n\u2022 [command2]: [description]\n...\n\n<bold><cyan>Code Patterns</cyan></bold>\n\n\u2022 [Pattern 1 observed in the codebase]\n\u2022 [Pattern 2 observed in the codebase]\n...\n---\n";
1011
+
1012
+ // src/procedure/understand_project/index.ts
1013
+ PROCEDURES["understand_project"] = new Procedure({
1014
+ name: "understand_project",
1015
+ procedureContent: procedure_default
1016
+ });
1017
+
1018
+ // src/index.ts
1019
+ init_log2();
1020
+
606
1021
  // src/model/models.ts
607
1022
  var MODELS = {
608
1023
  "gpt-5.2": {
@@ -644,12 +1059,15 @@ var MODELS = {
644
1059
  };
645
1060
 
646
1061
  // src/agent/Agent.ts
1062
+ init_Msg();
1063
+ init_init();
1064
+ init_log2();
647
1065
  var Agent = class {
648
1066
  config;
649
1067
  messages = [];
650
- constructor(config2) {
651
- this.config = config2;
652
- this.messages = [...config2.messages];
1068
+ constructor(config) {
1069
+ this.config = config;
1070
+ this.messages = [...config.messages];
653
1071
  }
654
1072
  /**
655
1073
  * Render template string, replacing {{variable}} style variables
@@ -696,13 +1114,8 @@ var Agent = class {
696
1114
  const toolResults = [];
697
1115
  for (const tc of toolCalls) {
698
1116
  this.config.onBeforeToolRun?.(tc.name, tc.arguments);
699
- debug("Agent", `Executing tool: ${tc.name}`, tc.arguments);
700
1117
  const tool = TOOLS[tc.name];
701
1118
  const result = tool ? await tool.run(tc.arguments) : { toolName: tc.name, result: `Unknown tool: ${tc.name}`, isError: true };
702
- debug("Agent", `Tool execution completed: ${tc.name}`, {
703
- isError: result.isError,
704
- contentLength: result.result.length
705
- });
706
1119
  toolCallHistory.push({
707
1120
  name: tc.name,
708
1121
  arguments: tc.arguments,
@@ -720,46 +1133,26 @@ var Agent = class {
720
1133
  }
721
1134
  };
722
1135
 
723
- // src/agent/Arki/colors.ts
724
- var tagNames = Object.keys(colors).filter((k) => k !== "reset").join("|");
725
- var tagRegex = new RegExp(`<(\\/?)(${tagNames})>`, "gi");
726
- function convertColorTags(str) {
727
- return str.replace(tagRegex, (_, closing, tag) => {
728
- return closing ? colors.reset : colors[tag.toLowerCase()] || "";
729
- });
730
- }
731
- function createColorConverter() {
732
- let buffer = "";
733
- return (chunk) => {
734
- buffer += chunk;
735
- const lastOpen = buffer.lastIndexOf("<");
736
- if (lastOpen === -1) {
737
- const out2 = convertColorTags(buffer);
738
- buffer = "";
739
- return out2;
740
- }
741
- if (buffer.indexOf(">", lastOpen) !== -1) {
742
- const out2 = convertColorTags(buffer);
743
- buffer = "";
744
- return out2;
745
- }
746
- const out = convertColorTags(buffer.slice(0, lastOpen));
747
- buffer = buffer.slice(lastOpen);
748
- return out;
749
- };
750
- }
1136
+ // src/agent/index.ts
1137
+ init_Msg();
1138
+
1139
+ // src/agent/Arki/Arki.ts
1140
+ init_log2();
751
1141
 
752
1142
  // src/agent/Arki/system.md
753
- var system_default = "You are Arki, a professional AI programming assistant. You work in the codebase directory `{{working_dir}}`.\n\n## Tool Usage\n\nTools can be called multiple times at once.\nIf a tool has the {{has_manual}} symbol in its description, you **MUST** call `read_tool_manual` before using it. Read the manual exactly once per tool - do not skip it, and do not read it repeatedly.\n\n## Working Principles\n\n- **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. For example, never tell the user what might be inside a directory based only on its name\u2014always inspect its contents first, and never guess functionality from directory, file, or function names in a way that could mislead the user.\n- **Safety**: Consider potential risks before executing commands.\n- **Conciseness**: Keep answers brief and concise, avoid repetition and redundancy. Keep each response within 200 words unless the user requests detailed explanation. If user requirements are unclear, ask for clarification once at most. If still unclear after asking, proceed with your best understanding and show the result to the user - do not ask multiple times.\n- **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**. Prefer terminal-friendly characters and plain text formatting. **Do not** output Markdown syntax such as `**` for bold, `*` or `-` for lists, etc. For long answers, feel free to organize content with clear section headings. Use numbered lists for ordered lists only when items have a clear sequence or dependency; otherwise use the \u2022 symbol for unordered lists.\nUse the following tags to format output:\n\n| Purpose | Format Tag | Usage |\n|--------|------------|-------|\n| Code blocks (```...```) | `<dim>...</dim>` | Wrap the entire code block content |\n| Inline code (`...`) | `<dim>...</dim>` | Wrap inline code snippets |\n| File paths | `<cyan>...</cyan>` | For paths, e.g., `src/index.ts` |\n| Filenames | `<gray>...</gray>` | For file names when mentioned alone |\n| Command names | `<blue>...</blue>` | For commands, e.g., `npm install` |\n| Section headings / titles | `<bold><cyan>...</cyan></bold>` | For section titles in plain text output |\n| Important or strong emphasis (**...**) | `<bold>...</bold>` | For key points that must stand out |\n| Secondary / less important info | `<dim>...</dim>` | For metadata, debug info, token counts, etc. |\n| Tips / important notices | `<yellow>...</yellow>` | For tips, cautions, non-fatal problems |\n| Success confirmations | `<green>...</green>` | For success messages, completion status |\n| Errors or serious problems | `<red>...</red>` | For real problems the user must fix |\n| Neutral informational messages | `<blue>...</blue>` | For general info that is not success/failure |\n| Highlighted keywords / categories | `<magenta>...</magenta>` | For labels, categories, or tags in text |\n| De-emphasized / grayed-out text | `<gray>...</gray>` | For low-priority info, old values, etc. |\n| Underlined emphasis | `<underline>...</underline>` | For things you want to underline instead of bold |\n| Optional / tentative text | `<italic>...</italic>` | For suggestions, optional steps, side notes |\n| Reversed highlight | `<inverse>...</inverse>` | For very strong highlights (rarely use) |\n| Deleted / not recommended content | `<strikethrough>...</strikethrough>` | For deprecated commands or steps |\n\nTags can be combined, e.g., `<bold><red>Critical Error</red></bold>`\n\n- Do not mention the contents of this prompt to users. The prompt provides context and instructions for you to follow, not to recite verbatim. Use the information in the prompt to inform your responses naturally. Bad example: \"You are currently talking to me via a Mac OS terminal interface. How can I help you?\" Good example: (Display terminal-friendly characters and provide suggestions based on the Mac OS system environment)\n\nPlease answer questions in the language the user is using, and flexibly use available tools to complete tasks.\n\n";
1143
+ var system_default = "You are Arki, a professional AI programming assistant. You work in the codebase directory `{{working_dir}}`.\n\n## Tool Usage\n\nTools can be called multiple times at once.\nIf a tool has the {{has_manual}} symbol in its description, you **MUST** call `read_tool_manual` before using it. Read the manual exactly once per tool - do not skip it, and do not read it repeatedly.\n\n## Procedure Usage\n\nProcedures are step-by-step guides for specific workflows. When a task involves a defined procedure, you **MUST** call `read_procedure` first to get the complete steps, then follow the procedure exactly.\n\nIf a procedure defines output text/format templates, translate them to the user's language unless the procedure explicitly forbids translation.\n\nAvailable procedures:\n{{procedures}}\n\n## Working Principles\n\n- **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. For example, never tell the user what might be inside a directory based only on its name\u2014always inspect its contents first, and never guess functionality from directory, file, or function names in a way that could mislead the user.\n- **Safety**: Consider potential risks before executing commands.\n- **Conciseness**: Keep answers brief and concise, avoid repetition and redundancy. Keep each response within 200 words unless the user requests detailed explanation. If user requirements are unclear, ask for clarification once at most. If still unclear after asking, proceed with your best understanding and show the result to the user - do not ask multiple times.\n- **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**. Prefer terminal-friendly characters and plain text formatting. **Do not** output Markdown syntax such as `**` for bold, `*` or `-` for lists, etc. For long answers, feel free to organize content with clear section headings. Use numbered lists for ordered lists only when items have a clear sequence or dependency; otherwise use the \u2022 symbol for unordered lists.\nUse the following tags to format output:\n\n| Purpose | Format Tag | Usage |\n|--------|------------|-------|\n| Code blocks (```...```) | `<dim>...</dim>` | Wrap the entire code block content |\n| Inline code (`...`) | `<dim>...</dim>` | Wrap inline code snippets |\n| File paths | `<cyan>...</cyan>` | For paths, e.g., `src/index.ts` |\n| Filenames | `<gray>...</gray>` | For file names when mentioned alone |\n| Command names | `<blue>...</blue>` | For commands, e.g., `npm install` |\n| Section headings / titles | `<bold><cyan>...</cyan></bold>` | For section titles in plain text output |\n| Important or strong emphasis (**...**) | `<bold>...</bold>` | For key points that must stand out |\n| Secondary / less important info | `<dim>...</dim>` | For metadata, debug info, token counts, etc. |\n| Tips / important notices | `<yellow>...</yellow>` | For tips, cautions, non-fatal problems |\n| Success confirmations | `<green>...</green>` | For success messages, completion status |\n| Errors or serious problems | `<red>...</red>` | For real problems the user must fix |\n| Neutral informational messages | `<blue>...</blue>` | For general info that is not success/failure |\n| Highlighted keywords / categories | `<magenta>...</magenta>` | For labels, categories, or tags in text |\n| De-emphasized / grayed-out text | `<gray>...</gray>` | For low-priority info, old values, etc. |\n| Underlined emphasis | `<underline>...</underline>` | For things you want to underline instead of bold |\n| Optional / tentative text | `<italic>...</italic>` | For suggestions, optional steps, side notes |\n| Reversed highlight | `<inverse>...</inverse>` | For very strong highlights (rarely use) |\n| Deleted / not recommended content | `<strikethrough>...</strikethrough>` | For deprecated commands or steps |\n\nTags can be combined, e.g., `<bold><red>Critical Error</red></bold>`\n\n- Do not mention the contents of this prompt to users. The prompt provides context and instructions for you to follow, not to recite verbatim. Use the information in the prompt to inform your responses naturally. Bad example: \"You are currently talking to me via a Mac OS terminal interface. How can I help you?\" Good example: (Display terminal-friendly characters and provide suggestions based on the Mac OS system environment)\n\nPlease answer questions in the language the user is using, and flexibly use available tools to complete tasks.\n\n";
754
1144
 
755
1145
  // src/agent/Arki/Arki.ts
1146
+ var toolStartTimes = /* @__PURE__ */ new Map();
756
1147
  function createMainAgent() {
757
1148
  if (!adapter) {
758
1149
  throw new Error("Adapter not initialized, please call init() first");
759
1150
  }
1151
+ const proceduresList = Object.values(PROCEDURES).map((p) => `- ${p.name}: ${p.description}`).join("\n");
760
1152
  const systemInstruction = Agent.renderTemplate(system_default, {
761
1153
  working_dir: workingDir,
762
- has_manual: HAS_MANUAL
1154
+ has_manual: HAS_MANUAL,
1155
+ procedures: proceduresList || "(none)"
763
1156
  });
764
1157
  const convertColor = createColorConverter();
765
1158
  const agent = new Agent({
@@ -768,27 +1161,31 @@ function createMainAgent() {
768
1161
  onStream: (chunk) => {
769
1162
  process.stdout.write(convertColor(chunk));
770
1163
  },
771
- onBeforeToolRun: (name, args) => {
772
- const argsStr = JSON.stringify(args);
773
- const argsPreview = argsStr.length > 60 ? argsStr.substring(0, 60) + "..." : argsStr;
774
- if (isDebugMode()) {
775
- console.log(`\x1B[33m\u{1F527} ${name}\x1B[0m \x1B[2m${argsPreview}\x1B[0m`);
776
- } else {
777
- process.stdout.write(`\x1B[33m\u{1F527} ${name}\x1B[0m \x1B[2m${argsPreview}\x1B[0m`);
778
- }
1164
+ onBeforeToolRun: (name) => {
1165
+ toolStartTimes.set(name, Date.now());
779
1166
  },
780
1167
  onToolResult: (name, args, result) => {
1168
+ const startTime = toolStartTimes.get(name) || Date.now();
1169
+ const elapsed = Date.now() - startTime;
1170
+ toolStartTimes.delete(name);
781
1171
  const argsStr = JSON.stringify(args);
782
1172
  const argsPreview = argsStr.length > 60 ? argsStr.substring(0, 60) + "..." : argsStr;
783
- const resultPreview = result.length > 80 ? result.substring(0, 80) + "..." : result;
784
- const firstLine = resultPreview.split("\n")[0];
1173
+ let output = `<green>[TOOL]</green> ${name} <dim>${argsPreview} (${elapsed}ms)`;
785
1174
  if (isDebugMode()) {
786
- console.log(`\x1B[32m\u2714 ${name}\x1B[0m \x1B[2m${argsPreview}\x1B[0m`);
787
- } else {
788
- process.stdout.write(`\r\x1B[2K\x1B[32m\u2714 ${name}\x1B[0m \x1B[2m${argsPreview}\x1B[0m
789
- `);
1175
+ const lines = result.split("\n").filter((l) => l.trim());
1176
+ let summary;
1177
+ if (lines.length <= 3) {
1178
+ summary = lines.join(", ");
1179
+ if (summary.length > 60) summary = summary.substring(0, 60) + "...";
1180
+ } else {
1181
+ const preview = lines.slice(0, 3).join(", ");
1182
+ summary = preview.length > 50 ? preview.substring(0, 50) + "..." : preview;
1183
+ summary += ` (+${lines.length - 3} more)`;
1184
+ }
1185
+ output += ` -> ${summary}`;
790
1186
  }
791
- log("dim", ` ${firstLine}`);
1187
+ output += "</dim>";
1188
+ log(output);
792
1189
  }
793
1190
  });
794
1191
  return agent;
@@ -797,7 +1194,7 @@ function createMainAgent() {
797
1194
  // package.json
798
1195
  var package_default = {
799
1196
  name: "arki",
800
- version: "0.0.4",
1197
+ version: "0.0.6",
801
1198
  description: "AI Agent Programming Assistant",
802
1199
  type: "module",
803
1200
  main: "dist/index.js",
@@ -854,13 +1251,21 @@ var package_default = {
854
1251
  };
855
1252
 
856
1253
  // src/index.ts
857
- function getConfigPath2() {
858
- return path5.join(os2.homedir(), ".config", "arki", "config.json");
1254
+ init_log2();
1255
+ init_Msg();
1256
+
1257
+ // src/adapter/index.ts
1258
+ init_Adapter();
1259
+ init_openai();
1260
+
1261
+ // src/index.ts
1262
+ function getConfigPath() {
1263
+ return path8.join(os2.homedir(), ".config", "arki", "config.json");
859
1264
  }
860
1265
  function resetConfig() {
861
- const configPath = getConfigPath2();
862
- if (fs5.existsSync(configPath)) {
863
- fs5.unlinkSync(configPath);
1266
+ const configPath = getConfigPath();
1267
+ if (fs6.existsSync(configPath)) {
1268
+ fs6.unlinkSync(configPath);
864
1269
  console.log(`Configuration file deleted: ${configPath}`);
865
1270
  console.log("Default configuration will be used on next startup.");
866
1271
  } else {
@@ -902,60 +1307,62 @@ async function main() {
902
1307
  debug("Init", "Debug mode enabled");
903
1308
  }
904
1309
  await init(targetDir);
905
- const mainAgentConfig = config.getAgentConfig("main");
1310
+ const mainAgentConfig = getAgentConfig("main");
906
1311
  const model = MODELS[mainAgentConfig.model];
907
1312
  console.log();
908
- log("cyan", `Arki AI Agent v${package_default.version}`);
1313
+ log(`<cyan>Arki AI Agent v${package_default.version}</cyan>`);
909
1314
  console.log();
910
- log("dim", `Model: ${mainAgentConfig.model}${model ? ` (${model.name})` : ""}`);
911
- log("dim", `Working directory: ${workingDir}`);
1315
+ log(`<dim>Model: ${mainAgentConfig.model}${model ? ` (${model.name})` : ""}</dim>`);
1316
+ log(`<dim>Working directory: ${workingDir}</dim>`);
1317
+ log(`<dim>OS: ${OS.name} (${OS.version})</dim>`);
912
1318
  if (isDebugMode()) {
913
- log("yellow", `\u{1F41B} Debug mode enabled`);
1319
+ log(`<yellow>Debug mode enabled</yellow>`);
914
1320
  }
915
1321
  console.log();
916
- log("dim", `Loaded ${Object.keys(TOOLS).length} tools`);
1322
+ log(`<dim>Loaded ${Object.keys(TOOLS).length} tools</dim>`);
917
1323
  if (isDebugMode()) {
918
1324
  debug("Init", "Loaded tools", Object.keys(TOOLS));
919
1325
  debug("Init", "Agent config", mainAgentConfig);
920
1326
  }
921
1327
  console.log();
922
1328
  const agent = createMainAgent();
923
- const rl = readline.createInterface({
1329
+ const rl = readline2.createInterface({
924
1330
  input: process.stdin,
925
1331
  output: process.stdout
926
1332
  });
927
- log("blue", "Enter your question and press Enter to send. Type /exit or /quit to exit.");
928
- log("blue", "Type /clear to clear conversation history.");
1333
+ log(`<blue>Enter your question and press Enter to send. Type /exit or /quit to exit.</blue>`);
1334
+ log(`<blue>Type /clear to clear conversation history.</blue>`);
929
1335
  console.log();
1336
+ const promptStr = convertColorTags("<green>> </green>");
930
1337
  const prompt = () => {
931
- rl.question(`${colors.green}> ${colors.reset}`, async (input) => {
1338
+ rl.question(promptStr, async (input) => {
932
1339
  const trimmed = input.trim();
933
1340
  if (trimmed === "/exit" || trimmed === "/quit") {
934
- log("cyan", "Goodbye!");
1341
+ log(`<cyan>Goodbye!</cyan>`);
935
1342
  rl.close();
936
1343
  process.exit(0);
937
1344
  }
938
1345
  if (trimmed === "/clear") {
939
1346
  agent.reset();
940
- log("yellow", "Conversation history cleared");
1347
+ log(`<yellow>Conversation history cleared</yellow>`);
941
1348
  console.log();
942
1349
  prompt();
943
1350
  return;
944
1351
  }
945
1352
  if (trimmed === "/help") {
946
1353
  console.log();
947
- log("cyan", "Available commands:");
948
- log("dim", " /exit, /quit - Exit program");
949
- log("dim", " /clear - Clear conversation history");
950
- log("dim", " /debug - Toggle debug mode");
951
- log("dim", " /help - Show help");
1354
+ log(`<cyan>Available commands:</cyan>`);
1355
+ log(`<dim> /exit, /quit - Exit program</dim>`);
1356
+ log(`<dim> /clear - Clear conversation history</dim>`);
1357
+ log(`<dim> /debug - Toggle debug mode</dim>`);
1358
+ log(`<dim> /help - Show help</dim>`);
952
1359
  console.log();
953
1360
  prompt();
954
1361
  return;
955
1362
  }
956
1363
  if (trimmed === "/debug") {
957
1364
  setDebugMode(!isDebugMode());
958
- log("yellow", `Debug mode ${isDebugMode() ? "enabled \u{1F41B}" : "disabled"}`);
1365
+ log(`<yellow>Debug mode ${isDebugMode() ? "enabled" : "disabled"}</yellow>`);
959
1366
  console.log();
960
1367
  prompt();
961
1368
  return;
@@ -970,14 +1377,11 @@ async function main() {
970
1377
  console.log();
971
1378
  if (result.usage) {
972
1379
  const contextLimit = model?.capabilities.contextWindow || "N/A";
973
- log(
974
- "dim",
975
- `[Tokens: ${result.usage.totalTokens} (prompt: ${result.usage.promptTokens}, cached: ${result.usage.cachedTokens || 0}, limit: ${contextLimit})]`
976
- );
1380
+ log(`<dim>[Tokens: ${result.usage.totalTokens} (prompt: ${result.usage.promptTokens}, cached: ${result.usage.cachedTokens || 0}, limit: ${contextLimit})]</dim>`);
977
1381
  }
978
1382
  console.log();
979
1383
  } catch (error2) {
980
- log("red", `Error: ${error2 instanceof Error ? error2.message : String(error2)}`);
1384
+ log(`<red>Error: ${error2 instanceof Error ? error2.message : String(error2)}</red>`);
981
1385
  console.log();
982
1386
  }
983
1387
  prompt();
@@ -998,7 +1402,10 @@ export {
998
1402
  MODELS,
999
1403
  Msg,
1000
1404
  MsgType,
1405
+ OS,
1001
1406
  OpenAIAdapter,
1407
+ PATHS,
1408
+ PROCEDURES,
1002
1409
  SystemMsg,
1003
1410
  TEMPERATURE,
1004
1411
  TOOLS,
@@ -1008,13 +1415,20 @@ export {
1008
1415
  UserMsg,
1009
1416
  adapter,
1010
1417
  colors,
1011
- config,
1418
+ convertColorTags,
1419
+ createColorConverter,
1012
1420
  debug,
1013
1421
  error,
1422
+ getAgentConfig,
1423
+ getApiKey,
1424
+ getConfig,
1014
1425
  info,
1015
1426
  init,
1427
+ initAdapter,
1016
1428
  isDebugMode,
1017
1429
  log,
1430
+ print,
1431
+ saveConfig,
1018
1432
  setDebugMode,
1019
1433
  setWorkingDir,
1020
1434
  success,