@agentskit/cli 0.5.3 → 0.6.0
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/bin.cjs +570 -57
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-CCPJYGHP.js → chunk-V7E4HWTG.js} +565 -51
- package/dist/chunk-V7E4HWTG.js.map +1 -0
- package/dist/index.cjs +571 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -6
- package/dist/index.d.ts +61 -6
- package/dist/index.js +1 -1
- package/package.json +8 -8
- package/dist/chunk-CCPJYGHP.js.map +0 -1
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { mkdir, writeFile, readFile } from 'fs/promises';
|
|
3
|
-
import
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import path3, { join, resolve, basename } from 'path';
|
|
4
5
|
import { kimi, grok, deepseek, ollama, gemini, anthropic, openai } from '@agentskit/adapters';
|
|
5
|
-
import React3, { useMemo,
|
|
6
|
+
import React3, { useState, useMemo, useRef, useEffect } from 'react';
|
|
6
7
|
import { Box, Text, render } from 'ink';
|
|
7
|
-
import { useChat, StatusHeader, ChatContainer, Message, ToolCallView, ThinkingIndicator, InputBar } from '@agentskit/ink';
|
|
8
|
-
import { shell, filesystem, webSearch } from '@agentskit/tools';
|
|
8
|
+
import { useChat, StatusHeader, ChatContainer, Message, ToolCallView, ToolConfirmation, ThinkingIndicator, InputBar } from '@agentskit/ink';
|
|
9
|
+
import { shell, filesystem, fetchUrl, webSearch } from '@agentskit/tools';
|
|
9
10
|
import { summarizer, critic, planner, coder, researcher, composeSkills } from '@agentskit/skills';
|
|
10
11
|
import { fileChatMemory, sqliteChatMemory } from '@agentskit/memory';
|
|
12
|
+
import { randomBytes, createHash } from 'crypto';
|
|
13
|
+
import { existsSync, mkdirSync, writeFileSync, readdirSync, statSync, readFileSync } from 'fs';
|
|
11
14
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
12
15
|
import { createRuntime } from '@agentskit/runtime';
|
|
13
|
-
import { existsSync } from 'fs';
|
|
14
16
|
import { spawn } from 'child_process';
|
|
15
17
|
import chokidar from 'chokidar';
|
|
16
18
|
import kleur3 from 'kleur';
|
|
@@ -45,16 +47,39 @@ async function loadPackageJsonConfig(dir) {
|
|
|
45
47
|
return void 0;
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
function mergeConfigs(base, override) {
|
|
51
|
+
if (!base && !override) return void 0;
|
|
52
|
+
if (!base) return override;
|
|
53
|
+
if (!override) return base;
|
|
54
|
+
return {
|
|
55
|
+
...base,
|
|
56
|
+
...override,
|
|
57
|
+
tools: { ...base.tools, ...override.tools },
|
|
58
|
+
defaults: { ...base.defaults, ...override.defaults },
|
|
59
|
+
runtime: { ...base.runtime, ...override.runtime },
|
|
60
|
+
observability: { ...base.observability, ...override.observability }
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function loadLocalConfig(cwd) {
|
|
64
|
+
const tsConfig = await loadTsConfig(join(cwd, ".agentskit.config.ts"));
|
|
52
65
|
if (tsConfig) return tsConfig;
|
|
53
|
-
const
|
|
54
|
-
const jsonConfig = await loadJsonConfig(jsonPath);
|
|
66
|
+
const jsonConfig = await loadJsonConfig(join(cwd, ".agentskit.config.json"));
|
|
55
67
|
if (jsonConfig) return jsonConfig;
|
|
56
68
|
return await loadPackageJsonConfig(cwd);
|
|
57
69
|
}
|
|
70
|
+
async function loadGlobalConfig(home) {
|
|
71
|
+
if (home === null) return void 0;
|
|
72
|
+
const globalDir = join(home ?? homedir(), ".agentskit");
|
|
73
|
+
const tsConfig = await loadTsConfig(join(globalDir, "config.ts"));
|
|
74
|
+
if (tsConfig) return tsConfig;
|
|
75
|
+
return await loadJsonConfig(join(globalDir, "config.json"));
|
|
76
|
+
}
|
|
77
|
+
async function loadConfig(options) {
|
|
78
|
+
const cwd = resolve(options?.cwd ?? process.cwd());
|
|
79
|
+
const global = await loadGlobalConfig(options?.home);
|
|
80
|
+
const local = await loadLocalConfig(cwd);
|
|
81
|
+
return mergeConfigs(global, local);
|
|
82
|
+
}
|
|
58
83
|
var providers = {
|
|
59
84
|
openai: {
|
|
60
85
|
label: "OpenAI",
|
|
@@ -172,19 +197,34 @@ var skillRegistry = {
|
|
|
172
197
|
critic,
|
|
173
198
|
summarizer
|
|
174
199
|
};
|
|
200
|
+
function instantiate(kind) {
|
|
201
|
+
switch (kind) {
|
|
202
|
+
case "web_search":
|
|
203
|
+
return [webSearch()];
|
|
204
|
+
case "fetch_url":
|
|
205
|
+
return [fetchUrl()];
|
|
206
|
+
case "filesystem":
|
|
207
|
+
return filesystem({ basePath: process.cwd() });
|
|
208
|
+
case "shell":
|
|
209
|
+
return [shell({ timeout: 3e4 })];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function gateTool(tool) {
|
|
213
|
+
if (tool.requiresConfirmation === false) return tool;
|
|
214
|
+
return { ...tool, requiresConfirmation: true };
|
|
215
|
+
}
|
|
175
216
|
function resolveTools(toolNames) {
|
|
176
|
-
if (!toolNames)
|
|
217
|
+
if (!toolNames) {
|
|
218
|
+
return [...instantiate("web_search"), ...instantiate("fetch_url")].map(gateTool);
|
|
219
|
+
}
|
|
177
220
|
const tools = [];
|
|
178
|
-
for (const name of toolNames.split(",").map((s) => s.trim())) {
|
|
221
|
+
for (const name of toolNames.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
179
222
|
switch (name) {
|
|
180
223
|
case "web_search":
|
|
181
|
-
|
|
182
|
-
break;
|
|
224
|
+
case "fetch_url":
|
|
183
225
|
case "filesystem":
|
|
184
|
-
tools.push(...filesystem({ basePath: process.cwd() }));
|
|
185
|
-
break;
|
|
186
226
|
case "shell":
|
|
187
|
-
tools.push(
|
|
227
|
+
tools.push(...instantiate(name));
|
|
188
228
|
break;
|
|
189
229
|
default:
|
|
190
230
|
process.stderr.write(`Unknown tool: ${name}
|
|
@@ -224,6 +264,244 @@ function resolveMemory(backend, memoryPath) {
|
|
|
224
264
|
return fileChatMemory(memoryPath);
|
|
225
265
|
}
|
|
226
266
|
}
|
|
267
|
+
var ROOT = join(homedir(), ".agentskit", "sessions");
|
|
268
|
+
var META_SUFFIX = ".meta.json";
|
|
269
|
+
function cwdHash(cwd = process.cwd()) {
|
|
270
|
+
return createHash("sha256").update(cwd).digest("hex").slice(0, 12);
|
|
271
|
+
}
|
|
272
|
+
function dirFor(cwd = process.cwd()) {
|
|
273
|
+
return join(ROOT, cwdHash(cwd));
|
|
274
|
+
}
|
|
275
|
+
function ensureDir(dir) {
|
|
276
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
277
|
+
}
|
|
278
|
+
function generateSessionId() {
|
|
279
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
280
|
+
const suffix = randomBytes(3).toString("hex");
|
|
281
|
+
return `${ts}-${suffix}`;
|
|
282
|
+
}
|
|
283
|
+
function sessionFilePath(id, cwd = process.cwd()) {
|
|
284
|
+
ensureDir(dirFor(cwd));
|
|
285
|
+
return join(dirFor(cwd), `${id}.json`);
|
|
286
|
+
}
|
|
287
|
+
function metaPath(id, cwd = process.cwd()) {
|
|
288
|
+
return join(dirFor(cwd), `${id}${META_SUFFIX}`);
|
|
289
|
+
}
|
|
290
|
+
function readMeta(id, cwd = process.cwd()) {
|
|
291
|
+
const path4 = metaPath(id, cwd);
|
|
292
|
+
if (!existsSync(path4)) return null;
|
|
293
|
+
try {
|
|
294
|
+
return JSON.parse(readFileSync(path4, "utf8"));
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function writeSessionMeta(meta, cwd = process.cwd()) {
|
|
300
|
+
ensureDir(dirFor(cwd));
|
|
301
|
+
writeFileSync(metaPath(meta.id, cwd), JSON.stringify(meta, null, 2));
|
|
302
|
+
}
|
|
303
|
+
function derivePreview(messages) {
|
|
304
|
+
const firstUser = messages.find((m) => m.role === "user" && m.content.trim());
|
|
305
|
+
if (!firstUser) return "(empty)";
|
|
306
|
+
const single = firstUser.content.replace(/\s+/g, " ").trim();
|
|
307
|
+
return single.length > 80 ? `${single.slice(0, 80)}\u2026` : single;
|
|
308
|
+
}
|
|
309
|
+
function listSessions(cwd = process.cwd()) {
|
|
310
|
+
const dir = dirFor(cwd);
|
|
311
|
+
if (!existsSync(dir)) return [];
|
|
312
|
+
const entries = readdirSync(dir);
|
|
313
|
+
const records = [];
|
|
314
|
+
for (const entry of entries) {
|
|
315
|
+
if (!entry.endsWith(".json") || entry.endsWith(META_SUFFIX)) continue;
|
|
316
|
+
const id = entry.replace(/\.json$/, "");
|
|
317
|
+
const meta = readMeta(id, cwd);
|
|
318
|
+
const file = join(dir, entry);
|
|
319
|
+
if (meta) {
|
|
320
|
+
records.push({ metadata: meta, file });
|
|
321
|
+
} else {
|
|
322
|
+
const stats = statSync(file);
|
|
323
|
+
records.push({
|
|
324
|
+
metadata: {
|
|
325
|
+
id,
|
|
326
|
+
cwd,
|
|
327
|
+
createdAt: stats.birthtime.toISOString(),
|
|
328
|
+
updatedAt: stats.mtime.toISOString(),
|
|
329
|
+
messageCount: 0,
|
|
330
|
+
preview: "(legacy session)"
|
|
331
|
+
},
|
|
332
|
+
file
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
records.sort((a, b) => b.metadata.updatedAt.localeCompare(a.metadata.updatedAt));
|
|
337
|
+
return records;
|
|
338
|
+
}
|
|
339
|
+
function findLatestSession(cwd = process.cwd()) {
|
|
340
|
+
const all = listSessions(cwd);
|
|
341
|
+
return all[0] ?? null;
|
|
342
|
+
}
|
|
343
|
+
function findSession(id, cwd = process.cwd()) {
|
|
344
|
+
const exact = listSessions(cwd).find((s) => s.metadata.id === id);
|
|
345
|
+
if (exact) return exact;
|
|
346
|
+
const prefix = listSessions(cwd).find((s) => s.metadata.id.startsWith(id));
|
|
347
|
+
return prefix ?? null;
|
|
348
|
+
}
|
|
349
|
+
function resolveSession(input2) {
|
|
350
|
+
const cwd = input2.cwd ?? process.cwd();
|
|
351
|
+
if (input2.explicitPath) {
|
|
352
|
+
return { id: "custom", file: input2.explicitPath, isNew: !existsSync(input2.explicitPath) };
|
|
353
|
+
}
|
|
354
|
+
if (input2.forceNew) {
|
|
355
|
+
const id2 = generateSessionId();
|
|
356
|
+
return { id: id2, file: sessionFilePath(id2, cwd), isNew: true };
|
|
357
|
+
}
|
|
358
|
+
if (input2.resumeId) {
|
|
359
|
+
const target = input2.resumeId === true ? findLatestSession(cwd) : findSession(input2.resumeId, cwd);
|
|
360
|
+
if (target) {
|
|
361
|
+
return { id: target.metadata.id, file: target.file, isNew: false };
|
|
362
|
+
}
|
|
363
|
+
process.stderr.write(
|
|
364
|
+
`No session matching "${String(input2.resumeId)}" \u2014 starting a new one.
|
|
365
|
+
`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
const latest = findLatestSession(cwd);
|
|
369
|
+
if (latest) return { id: latest.metadata.id, file: latest.file, isNew: false };
|
|
370
|
+
const id = generateSessionId();
|
|
371
|
+
return { id, file: sessionFilePath(id, cwd), isNew: true };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/slash-commands.ts
|
|
375
|
+
function parseSlashCommand(input2) {
|
|
376
|
+
if (!input2.startsWith("/")) return null;
|
|
377
|
+
const match = input2.slice(1).match(/^(\S+)\s*([\s\S]*)$/);
|
|
378
|
+
if (!match) return null;
|
|
379
|
+
return { name: match[1], args: match[2] ?? "" };
|
|
380
|
+
}
|
|
381
|
+
function createSlashRegistry(commands) {
|
|
382
|
+
const map = /* @__PURE__ */ new Map();
|
|
383
|
+
for (const cmd of commands) {
|
|
384
|
+
map.set(cmd.name, cmd);
|
|
385
|
+
for (const alias of cmd.aliases ?? []) map.set(alias, cmd);
|
|
386
|
+
}
|
|
387
|
+
return map;
|
|
388
|
+
}
|
|
389
|
+
var builtinSlashCommands = [
|
|
390
|
+
{
|
|
391
|
+
name: "help",
|
|
392
|
+
aliases: ["?"],
|
|
393
|
+
description: "List available slash commands.",
|
|
394
|
+
run(ctx) {
|
|
395
|
+
const seen = /* @__PURE__ */ new Set();
|
|
396
|
+
const lines = [];
|
|
397
|
+
for (const cmd of ctx.commands) {
|
|
398
|
+
if (seen.has(cmd.name)) continue;
|
|
399
|
+
seen.add(cmd.name);
|
|
400
|
+
const suffix = cmd.usage ? ` (${cmd.usage})` : "";
|
|
401
|
+
lines.push(` /${cmd.name.padEnd(10)} ${cmd.description}${suffix}`);
|
|
402
|
+
}
|
|
403
|
+
ctx.feedback(`Slash commands:
|
|
404
|
+
${lines.join("\n")}`, "info");
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
name: "model",
|
|
409
|
+
description: "Switch the active model.",
|
|
410
|
+
usage: "/model <name>",
|
|
411
|
+
run(ctx, args) {
|
|
412
|
+
const value = args.trim();
|
|
413
|
+
if (!value) {
|
|
414
|
+
ctx.feedback(
|
|
415
|
+
`Current model: ${ctx.runtime.model ?? "unset"}. Usage: /model <name>`,
|
|
416
|
+
"warn"
|
|
417
|
+
);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
ctx.setModel(value);
|
|
421
|
+
ctx.feedback(`Model \u2192 ${value}`, "success");
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
name: "provider",
|
|
426
|
+
description: "Switch the adapter provider.",
|
|
427
|
+
usage: "/provider openai|anthropic|gemini|ollama|deepseek|grok|kimi|demo",
|
|
428
|
+
run(ctx, args) {
|
|
429
|
+
const value = args.trim();
|
|
430
|
+
if (!value) {
|
|
431
|
+
ctx.feedback(
|
|
432
|
+
`Current provider: ${ctx.runtime.provider}. Usage: /provider <name>`,
|
|
433
|
+
"warn"
|
|
434
|
+
);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
ctx.setProvider(value);
|
|
438
|
+
ctx.feedback(`Provider \u2192 ${value}`, "success");
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: "base-url",
|
|
443
|
+
aliases: ["baseurl"],
|
|
444
|
+
description: "Override provider base URL.",
|
|
445
|
+
usage: "/base-url <url|clear>",
|
|
446
|
+
run(ctx, args) {
|
|
447
|
+
const value = args.trim();
|
|
448
|
+
if (!value || value === "clear") {
|
|
449
|
+
ctx.setBaseUrl(void 0);
|
|
450
|
+
ctx.feedback("Base URL cleared.", "success");
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
ctx.setBaseUrl(value);
|
|
454
|
+
ctx.feedback(`Base URL \u2192 ${value}`, "success");
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
name: "tools",
|
|
459
|
+
description: "Set active tools (comma-separated) or clear them.",
|
|
460
|
+
usage: "/tools web_search,fetch_url | /tools clear",
|
|
461
|
+
run(ctx, args) {
|
|
462
|
+
const value = args.trim();
|
|
463
|
+
if (!value || value === "clear") {
|
|
464
|
+
ctx.setTools(void 0);
|
|
465
|
+
ctx.feedback("Tools reset to defaults.", "success");
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
ctx.setTools(value);
|
|
469
|
+
ctx.feedback(`Tools \u2192 ${value}`, "success");
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
name: "skill",
|
|
474
|
+
description: "Set active skill(s) (comma-separated) or clear them.",
|
|
475
|
+
usage: "/skill researcher,coder | /skill clear",
|
|
476
|
+
run(ctx, args) {
|
|
477
|
+
const value = args.trim();
|
|
478
|
+
if (!value || value === "clear") {
|
|
479
|
+
ctx.setSkill(void 0);
|
|
480
|
+
ctx.feedback("Skills cleared.", "success");
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
ctx.setSkill(value);
|
|
484
|
+
ctx.feedback(`Skills \u2192 ${value}`, "success");
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
name: "clear",
|
|
489
|
+
aliases: ["reset"],
|
|
490
|
+
description: "Clear the conversation history in this session.",
|
|
491
|
+
async run(ctx) {
|
|
492
|
+
await ctx.chat.clear();
|
|
493
|
+
ctx.feedback("History cleared.", "success");
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
name: "exit",
|
|
498
|
+
aliases: ["quit", "q"],
|
|
499
|
+
description: "Exit the chat.",
|
|
500
|
+
run() {
|
|
501
|
+
process.exit(0);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
];
|
|
227
505
|
function groupIntoTurns(messages) {
|
|
228
506
|
const turns = [];
|
|
229
507
|
let current = [];
|
|
@@ -243,24 +521,28 @@ function groupIntoTurns(messages) {
|
|
|
243
521
|
return turns;
|
|
244
522
|
}
|
|
245
523
|
function ChatApp(options) {
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
]);
|
|
524
|
+
const [provider, setProvider] = useState(options.provider);
|
|
525
|
+
const [model, setModel] = useState(options.model);
|
|
526
|
+
const [apiKey, setApiKey] = useState(options.apiKey);
|
|
527
|
+
const [baseUrl, setBaseUrl] = useState(options.baseUrl);
|
|
528
|
+
const [toolsFlag, setToolsFlag] = useState(options.tools);
|
|
529
|
+
const [skillFlag, setSkillFlag] = useState(options.skill);
|
|
530
|
+
const runtime = useMemo(
|
|
531
|
+
() => resolveChatProvider({ provider, model, apiKey, baseUrl }),
|
|
532
|
+
[provider, model, apiKey, baseUrl]
|
|
533
|
+
);
|
|
252
534
|
const memory = useMemo(
|
|
253
535
|
() => resolveMemory(options.memoryBackend, options.memoryPath ?? ".agentskit-history.json"),
|
|
254
536
|
[options.memoryPath, options.memoryBackend]
|
|
255
537
|
);
|
|
256
|
-
const tools = useMemo(() => resolveTools(
|
|
538
|
+
const tools = useMemo(() => resolveTools(toolsFlag), [toolsFlag]);
|
|
257
539
|
const skills = useMemo(() => {
|
|
258
|
-
if (!
|
|
259
|
-
const names =
|
|
540
|
+
if (!skillFlag) return void 0;
|
|
541
|
+
const names = skillFlag.split(",").map((s) => s.trim());
|
|
260
542
|
const resolved = names.map((n) => skillRegistry[n]).filter(Boolean);
|
|
261
543
|
if (resolved.length === 0) return void 0;
|
|
262
544
|
return resolved;
|
|
263
|
-
}, [
|
|
545
|
+
}, [skillFlag]);
|
|
264
546
|
const chat = useChat({
|
|
265
547
|
adapter: runtime.adapter,
|
|
266
548
|
memory,
|
|
@@ -268,8 +550,109 @@ function ChatApp(options) {
|
|
|
268
550
|
tools: tools.length > 0 ? tools : void 0,
|
|
269
551
|
skills
|
|
270
552
|
});
|
|
553
|
+
const [sessionAllowed, setSessionAllowed] = useState(/* @__PURE__ */ new Set());
|
|
554
|
+
const autoApprovedRef = useRef(/* @__PURE__ */ new Set());
|
|
555
|
+
useEffect(() => {
|
|
556
|
+
if (sessionAllowed.size === 0) return;
|
|
557
|
+
for (const message of chat.messages) {
|
|
558
|
+
for (const call of message.toolCalls ?? []) {
|
|
559
|
+
if (call.status === "requires_confirmation" && sessionAllowed.has(call.name) && !autoApprovedRef.current.has(call.id)) {
|
|
560
|
+
autoApprovedRef.current.add(call.id);
|
|
561
|
+
void chat.approve(call.id);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}, [chat.messages, sessionAllowed, chat.approve]);
|
|
566
|
+
const handleApproveAlways = (toolCallId, toolName) => {
|
|
567
|
+
setSessionAllowed((prev) => {
|
|
568
|
+
if (prev.has(toolName)) return prev;
|
|
569
|
+
const next = new Set(prev);
|
|
570
|
+
next.add(toolName);
|
|
571
|
+
return next;
|
|
572
|
+
});
|
|
573
|
+
autoApprovedRef.current.add(toolCallId);
|
|
574
|
+
void chat.approve(toolCallId);
|
|
575
|
+
};
|
|
576
|
+
const sessionCreatedAtRef = useRef(void 0);
|
|
577
|
+
const messageCount = chat.messages.length;
|
|
578
|
+
const firstUserContent = chat.messages.find((m) => m.role === "user")?.content ?? "";
|
|
579
|
+
useEffect(() => {
|
|
580
|
+
const sessionId = options.sessionId;
|
|
581
|
+
if (!sessionId || sessionId === "custom") return;
|
|
582
|
+
if (!sessionCreatedAtRef.current) {
|
|
583
|
+
sessionCreatedAtRef.current = (/* @__PURE__ */ new Date()).toISOString();
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
writeSessionMeta({
|
|
587
|
+
id: sessionId,
|
|
588
|
+
cwd: process.cwd(),
|
|
589
|
+
createdAt: sessionCreatedAtRef.current,
|
|
590
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
591
|
+
messageCount,
|
|
592
|
+
preview: derivePreview(chat.messages),
|
|
593
|
+
provider: runtime.provider,
|
|
594
|
+
model: runtime.model
|
|
595
|
+
});
|
|
596
|
+
} catch {
|
|
597
|
+
}
|
|
598
|
+
}, [options.sessionId, messageCount, firstUserContent, runtime.provider, runtime.model]);
|
|
271
599
|
const turns = useMemo(() => groupIntoTurns(chat.messages), [chat.messages]);
|
|
272
|
-
const toolNames =
|
|
600
|
+
const toolNames = toolsFlag ? toolsFlag.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
601
|
+
const [feedback, setFeedback] = useState(null);
|
|
602
|
+
const slashCommands = useMemo(
|
|
603
|
+
() => [...builtinSlashCommands, ...options.slashCommands ?? []],
|
|
604
|
+
[options.slashCommands]
|
|
605
|
+
);
|
|
606
|
+
const slashRegistry = useMemo(() => createSlashRegistry(slashCommands), [slashCommands]);
|
|
607
|
+
const handleSubmitInput = async (raw) => {
|
|
608
|
+
const parsed = parseSlashCommand(raw);
|
|
609
|
+
if (!parsed) {
|
|
610
|
+
setFeedback(null);
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
const cmd = slashRegistry.get(parsed.name);
|
|
614
|
+
if (!cmd) {
|
|
615
|
+
setFeedback({
|
|
616
|
+
message: `Unknown command: /${parsed.name}. Type /help for the list.`,
|
|
617
|
+
kind: "error"
|
|
618
|
+
});
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
const ctx = {
|
|
622
|
+
chat,
|
|
623
|
+
runtime: {
|
|
624
|
+
provider: runtime.provider,
|
|
625
|
+
model: runtime.model,
|
|
626
|
+
mode: runtime.mode,
|
|
627
|
+
baseUrl,
|
|
628
|
+
tools: toolsFlag,
|
|
629
|
+
skill: skillFlag
|
|
630
|
+
},
|
|
631
|
+
setProvider,
|
|
632
|
+
setModel,
|
|
633
|
+
setApiKey,
|
|
634
|
+
setBaseUrl,
|
|
635
|
+
setTools: setToolsFlag,
|
|
636
|
+
setSkill: setSkillFlag,
|
|
637
|
+
feedback: (message, kind = "info") => setFeedback({ message, kind }),
|
|
638
|
+
commands: slashCommands
|
|
639
|
+
};
|
|
640
|
+
try {
|
|
641
|
+
await cmd.run(ctx, parsed.args);
|
|
642
|
+
} catch (err) {
|
|
643
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
644
|
+
setFeedback({ message: `/${parsed.name} failed: ${message}`, kind: "error" });
|
|
645
|
+
}
|
|
646
|
+
return true;
|
|
647
|
+
};
|
|
648
|
+
const awaitingConfirmation = useMemo(
|
|
649
|
+
() => chat.messages.some(
|
|
650
|
+
(message) => message.toolCalls?.some(
|
|
651
|
+
(call) => call.status === "requires_confirmation" && !sessionAllowed.has(call.name)
|
|
652
|
+
)
|
|
653
|
+
),
|
|
654
|
+
[chat.messages, sessionAllowed]
|
|
655
|
+
);
|
|
273
656
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, children: [
|
|
274
657
|
/* @__PURE__ */ jsx(
|
|
275
658
|
StatusHeader,
|
|
@@ -278,7 +661,8 @@ function ChatApp(options) {
|
|
|
278
661
|
model: runtime.model,
|
|
279
662
|
mode: runtime.mode,
|
|
280
663
|
tools: toolNames,
|
|
281
|
-
messageCount: chat.messages.length
|
|
664
|
+
messageCount: chat.messages.length,
|
|
665
|
+
sessionId: options.sessionId
|
|
282
666
|
}
|
|
283
667
|
),
|
|
284
668
|
/* @__PURE__ */ jsx(ChatContainer, { children: turns.map((turn, turnIdx) => {
|
|
@@ -295,7 +679,18 @@ function ChatApp(options) {
|
|
|
295
679
|
assistantSteps
|
|
296
680
|
] }) : null,
|
|
297
681
|
/* @__PURE__ */ jsx(Message, { message }),
|
|
298
|
-
message.toolCalls?.map((toolCall) => /* @__PURE__ */
|
|
682
|
+
message.toolCalls?.map((toolCall) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
683
|
+
/* @__PURE__ */ jsx(ToolCallView, { toolCall, expanded: true }),
|
|
684
|
+
toolCall.status === "requires_confirmation" && !sessionAllowed.has(toolCall.name) ? /* @__PURE__ */ jsx(
|
|
685
|
+
ToolConfirmation,
|
|
686
|
+
{
|
|
687
|
+
toolCall,
|
|
688
|
+
onApprove: chat.approve,
|
|
689
|
+
onDeny: chat.deny,
|
|
690
|
+
onApproveAlways: handleApproveAlways
|
|
691
|
+
}
|
|
692
|
+
) : null
|
|
693
|
+
] }, toolCall.id))
|
|
299
694
|
] }, message.id);
|
|
300
695
|
}) }, `turn-${turnIdx}`);
|
|
301
696
|
}) }),
|
|
@@ -306,9 +701,38 @@ function ChatApp(options) {
|
|
|
306
701
|
label: toolNames.length > 0 ? "agent working" : "thinking"
|
|
307
702
|
}
|
|
308
703
|
),
|
|
309
|
-
/* @__PURE__ */
|
|
704
|
+
chat.error ? /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: "red", paddingX: 1, flexDirection: "column", children: [
|
|
705
|
+
/* @__PURE__ */ jsxs(Text, { color: "red", bold: true, children: [
|
|
706
|
+
"\u2717 ",
|
|
707
|
+
chat.error.name || "Error"
|
|
708
|
+
] }),
|
|
709
|
+
/* @__PURE__ */ jsx(Text, { color: "red", children: chat.error.message })
|
|
710
|
+
] }) : null,
|
|
711
|
+
feedback ? /* @__PURE__ */ jsx(Box, { borderStyle: "round", borderColor: feedbackBorder(feedback.kind), paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: feedbackBorder(feedback.kind), children: feedback.message }) }) : null,
|
|
712
|
+
/* @__PURE__ */ jsx(
|
|
713
|
+
InputBar,
|
|
714
|
+
{
|
|
715
|
+
chat,
|
|
716
|
+
placeholder: "Type a message or /help for commands",
|
|
717
|
+
disabled: awaitingConfirmation,
|
|
718
|
+
onSubmitInput: handleSubmitInput
|
|
719
|
+
}
|
|
720
|
+
)
|
|
310
721
|
] });
|
|
311
722
|
}
|
|
723
|
+
function feedbackBorder(kind) {
|
|
724
|
+
switch (kind) {
|
|
725
|
+
case "error":
|
|
726
|
+
return "red";
|
|
727
|
+
case "warn":
|
|
728
|
+
return "yellow";
|
|
729
|
+
case "success":
|
|
730
|
+
return "green";
|
|
731
|
+
case "info":
|
|
732
|
+
default:
|
|
733
|
+
return "cyan";
|
|
734
|
+
}
|
|
735
|
+
}
|
|
312
736
|
function renderChatHeader(options) {
|
|
313
737
|
const runtime = resolveChatProvider(options);
|
|
314
738
|
const parts = [`provider=${runtime.provider}`];
|
|
@@ -417,7 +841,7 @@ function reactStarter(ctx) {
|
|
|
417
841
|
return {
|
|
418
842
|
"package.json": JSON.stringify(
|
|
419
843
|
{
|
|
420
|
-
name:
|
|
844
|
+
name: path3.basename(ctx.template === "react" ? "agentskit-react-app" : "agentskit-app"),
|
|
421
845
|
private: true,
|
|
422
846
|
type: "module",
|
|
423
847
|
scripts: {
|
|
@@ -786,8 +1210,8 @@ async function writeStarterProject(options) {
|
|
|
786
1210
|
await mkdir(options.targetDir, { recursive: true });
|
|
787
1211
|
await Promise.all(
|
|
788
1212
|
Object.entries(files).map(async ([relativePath, content]) => {
|
|
789
|
-
const absolutePath =
|
|
790
|
-
await mkdir(
|
|
1213
|
+
const absolutePath = path3.join(options.targetDir, relativePath);
|
|
1214
|
+
await mkdir(path3.dirname(absolutePath), { recursive: true });
|
|
791
1215
|
await writeFile(absolutePath, content, "utf8");
|
|
792
1216
|
})
|
|
793
1217
|
);
|
|
@@ -1295,7 +1719,7 @@ ${kleur3.bold().green("\u25B2")} ${kleur3.bold("agentskit init")}
|
|
|
1295
1719
|
default: defaults.dir ?? "agentskit-app",
|
|
1296
1720
|
validate: (value) => {
|
|
1297
1721
|
if (!value.trim()) return "A directory name is required.";
|
|
1298
|
-
const abs =
|
|
1722
|
+
const abs = path3.resolve(process.cwd(), value);
|
|
1299
1723
|
if (existsSync(abs)) return `${value} already exists. Pick a different name.`;
|
|
1300
1724
|
return true;
|
|
1301
1725
|
}
|
|
@@ -1373,7 +1797,7 @@ ${kleur3.bold().green("\u25B2")} ${kleur3.bold("agentskit init")}
|
|
|
1373
1797
|
return {
|
|
1374
1798
|
cancelled: false,
|
|
1375
1799
|
options: {
|
|
1376
|
-
targetDir:
|
|
1800
|
+
targetDir: path3.resolve(process.cwd(), targetDir),
|
|
1377
1801
|
template,
|
|
1378
1802
|
provider,
|
|
1379
1803
|
tools,
|
|
@@ -1390,7 +1814,7 @@ ${kleur3.bold().green("\u25B2")} ${kleur3.bold("agentskit init")}
|
|
|
1390
1814
|
}
|
|
1391
1815
|
}
|
|
1392
1816
|
function printNextSteps(options) {
|
|
1393
|
-
const dir =
|
|
1817
|
+
const dir = path3.relative(process.cwd(), options.targetDir) || ".";
|
|
1394
1818
|
const pm = options.packageManager ?? "pnpm";
|
|
1395
1819
|
const installCmd = pm === "npm" ? "npm install" : `${pm} install`;
|
|
1396
1820
|
const runCmd = pm === "npm" ? "npm run dev" : `${pm} dev`;
|
|
@@ -1513,34 +1937,87 @@ function RunApp({ task, options }) {
|
|
|
1513
1937
|
// src/commands.ts
|
|
1514
1938
|
function mergeWithConfig(options, config) {
|
|
1515
1939
|
if (!config) return options;
|
|
1940
|
+
const d = config.defaults ?? {};
|
|
1941
|
+
const resolvedApiKey = options.apiKey ?? (d.apiKeyEnv ? process.env[d.apiKeyEnv] : void 0) ?? d.apiKey;
|
|
1516
1942
|
return {
|
|
1517
1943
|
...options,
|
|
1518
1944
|
// Config defaults — only apply if CLI flag wasn't set
|
|
1519
|
-
provider: options.provider !== "demo" ? options.provider :
|
|
1520
|
-
model: options.model ??
|
|
1945
|
+
provider: options.provider !== "demo" ? options.provider : d.provider ?? options.provider,
|
|
1946
|
+
model: options.model ?? d.model,
|
|
1947
|
+
apiKey: resolvedApiKey,
|
|
1948
|
+
baseUrl: options.baseUrl ?? d.baseUrl,
|
|
1949
|
+
tools: options.tools ?? d.tools,
|
|
1950
|
+
skill: options.skill ?? d.skill,
|
|
1951
|
+
system: options.system ?? d.system,
|
|
1952
|
+
memoryBackend: options.memoryBackend ?? d.memoryBackend
|
|
1521
1953
|
};
|
|
1522
1954
|
}
|
|
1523
1955
|
function createCli() {
|
|
1524
1956
|
const program = new Command();
|
|
1525
1957
|
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
|
|
1526
|
-
program.command("chat").description("Start a terminal chat session.").option("--provider <provider>", "Provider to use", "demo").option("--model <model>", "Model name").option("--api-key <key>", "API key for the selected provider").option("--base-url <url>", "Override provider base URL").option("--system <prompt>", "System prompt").option("--memory <path>", "
|
|
1958
|
+
program.command("chat").description("Start a terminal chat session.").option("--provider <provider>", "Provider to use", "demo").option("--model <model>", "Model name").option("--api-key <key>", "API key for the selected provider").option("--base-url <url>", "Override provider base URL").option("--system <prompt>", "System prompt").option("--memory <path>", "Explicit memory file path (overrides session management)").option("--tools <tools>", "Comma-separated tools: web_search,fetch_url,filesystem,shell").option("--skill <skills>", "Comma-separated skills: researcher,coder,planner,critic,summarizer").option("--memory-backend <backend>", "Memory backend: file (default), sqlite").option("--new", "Start a fresh chat session (ignore previous conversations in this directory)").option("--resume [id]", "Resume a prior session by id; omit id to resume the latest").option("--list-sessions", "List saved sessions for this directory and exit").option("--no-config", "Skip loading .agentskit.config.json").action(async (options) => {
|
|
1959
|
+
if (options.listSessions) {
|
|
1960
|
+
const sessions = listSessions();
|
|
1961
|
+
if (sessions.length === 0) {
|
|
1962
|
+
process.stdout.write("No saved sessions for this directory.\n");
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
for (const s of sessions) {
|
|
1966
|
+
const { id, updatedAt, messageCount, preview, model } = s.metadata;
|
|
1967
|
+
process.stdout.write(
|
|
1968
|
+
`${id} ${updatedAt} msgs=${messageCount}${model ? ` model=${model}` : ""}
|
|
1969
|
+
${preview}
|
|
1970
|
+
`
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1527
1975
|
const config = options.config !== false ? await loadConfig() : void 0;
|
|
1528
1976
|
const merged = mergeWithConfig(options, config);
|
|
1977
|
+
const session = resolveSession({
|
|
1978
|
+
explicitPath: options.memory,
|
|
1979
|
+
forceNew: Boolean(options.new),
|
|
1980
|
+
resumeId: options.resume
|
|
1981
|
+
});
|
|
1982
|
+
if (!session.isNew && !options.memory) {
|
|
1983
|
+
process.stdout.write(
|
|
1984
|
+
`Resuming session ${session.id}. Start fresh with --new or list with --list-sessions.
|
|
1985
|
+
`
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1529
1988
|
const chatOptions = {
|
|
1530
1989
|
apiKey: merged.apiKey ?? options.apiKey,
|
|
1531
1990
|
baseUrl: merged.baseUrl ?? options.baseUrl,
|
|
1532
1991
|
provider: merged.provider,
|
|
1533
1992
|
model: merged.model,
|
|
1534
|
-
system: options.system,
|
|
1535
|
-
memoryPath:
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1993
|
+
system: merged.system ?? options.system,
|
|
1994
|
+
memoryPath: session.file,
|
|
1995
|
+
sessionId: session.id,
|
|
1996
|
+
tools: merged.tools ?? options.tools,
|
|
1997
|
+
skill: merged.skill ?? options.skill,
|
|
1998
|
+
memoryBackend: merged.memoryBackend ?? options.memoryBackend,
|
|
1539
1999
|
agentsKitConfig: config
|
|
1540
2000
|
};
|
|
1541
2001
|
process.stdout.write(`${renderChatHeader(chatOptions)}
|
|
1542
2002
|
`);
|
|
1543
|
-
render(React3.createElement(ChatApp, chatOptions));
|
|
2003
|
+
const instance = render(React3.createElement(ChatApp, chatOptions));
|
|
2004
|
+
await instance.waitUntilExit();
|
|
2005
|
+
if (options.memory) {
|
|
2006
|
+
process.stdout.write(
|
|
2007
|
+
`
|
|
2008
|
+
Session saved to ${session.file}. Resume with --memory ${session.file}
|
|
2009
|
+
`
|
|
2010
|
+
);
|
|
2011
|
+
} else {
|
|
2012
|
+
process.stdout.write(
|
|
2013
|
+
`
|
|
2014
|
+
Session saved. Resume with:
|
|
2015
|
+
agentskit chat --resume ${session.id}
|
|
2016
|
+
Or start fresh with:
|
|
2017
|
+
agentskit chat --new
|
|
2018
|
+
`
|
|
2019
|
+
);
|
|
2020
|
+
}
|
|
1544
2021
|
});
|
|
1545
2022
|
program.command("run [task]").description("Execute an agent task and output the result.").option("--task <task>", "Task string (alternative to positional argument)").option("--provider <provider>", "Provider to use", "demo").option("--model <model>", "Model name").option("--api-key <key>", "API key for the selected provider").option("--base-url <url>", "Override provider base URL").option("--skill <skill>", "Single skill to use").option("--skills <skills>", "Comma-separated skills (composed together)").option("--tools <tools>", "Comma-separated tools: web_search,filesystem,shell").option("--memory <path>", "Path for memory persistence").option("--memory-backend <backend>", "Memory backend: file (default), sqlite").option("--system-prompt <prompt>", "System prompt").option("--max-steps <steps>", "Maximum agent steps", "10").option("--verbose", "Stream agent steps to stderr").option("--pretty", "Use rich Ink-based output").option("--no-config", "Skip loading .agentskit.config.json").action(async (positionalTask, options) => {
|
|
1546
2023
|
const task = options.task ?? positionalTask;
|
|
@@ -1568,7 +2045,7 @@ function createCli() {
|
|
|
1568
2045
|
if (isCi) {
|
|
1569
2046
|
const template = rawOptions.template ?? "react";
|
|
1570
2047
|
resolved = {
|
|
1571
|
-
targetDir:
|
|
2048
|
+
targetDir: path3.resolve(process.cwd(), rawOptions.dir),
|
|
1572
2049
|
template,
|
|
1573
2050
|
provider: rawOptions.provider ?? "demo",
|
|
1574
2051
|
tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
|
|
@@ -1588,7 +2065,7 @@ function createCli() {
|
|
|
1588
2065
|
await writeStarterProject(resolved);
|
|
1589
2066
|
if (isCi) {
|
|
1590
2067
|
process.stdout.write(
|
|
1591
|
-
`Created ${resolved.template} starter in ${
|
|
2068
|
+
`Created ${resolved.template} starter in ${path3.relative(process.cwd(), resolved.targetDir) || "."}
|
|
1592
2069
|
`
|
|
1593
2070
|
);
|
|
1594
2071
|
} else {
|
|
@@ -1629,6 +2106,43 @@ function createCli() {
|
|
|
1629
2106
|
process.exit(1);
|
|
1630
2107
|
}
|
|
1631
2108
|
});
|
|
2109
|
+
program.command("config").description("Show or scaffold the AgentsKit config.").argument("[action]", 'Action: "init" to create a template, "show" to print the merged config.', "show").option("--global", "Write/read the global config at ~/.agentskit/config.json (default)").option("--local", "Write/read a project-level .agentskit.config.json in the current directory").option("--force", "Overwrite an existing config file").action(async (action, options) => {
|
|
2110
|
+
const isLocal = Boolean(options.local);
|
|
2111
|
+
const targetPath = isLocal ? path3.join(process.cwd(), ".agentskit.config.json") : path3.join(homedir(), ".agentskit", "config.json");
|
|
2112
|
+
if (action === "show") {
|
|
2113
|
+
const config = await loadConfig();
|
|
2114
|
+
process.stdout.write(JSON.stringify(config ?? {}, null, 2) + "\n");
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
if (action !== "init") {
|
|
2118
|
+
process.stderr.write(`Unknown action: ${action}. Use "init" or "show".
|
|
2119
|
+
`);
|
|
2120
|
+
process.exit(2);
|
|
2121
|
+
}
|
|
2122
|
+
if (existsSync(targetPath) && !options.force) {
|
|
2123
|
+
process.stderr.write(`Config already exists at ${targetPath}. Re-run with --force to overwrite.
|
|
2124
|
+
`);
|
|
2125
|
+
process.exit(1);
|
|
2126
|
+
}
|
|
2127
|
+
const template = {
|
|
2128
|
+
defaults: {
|
|
2129
|
+
provider: "openai",
|
|
2130
|
+
baseUrl: "https://openrouter.ai/api",
|
|
2131
|
+
apiKeyEnv: "OPENROUTER_API_KEY",
|
|
2132
|
+
model: "openai/gpt-oss-120b:free",
|
|
2133
|
+
tools: "web_search,fetch_url"
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
mkdirSync(path3.dirname(targetPath), { recursive: true });
|
|
2137
|
+
writeFileSync(targetPath, JSON.stringify(template, null, 2) + "\n");
|
|
2138
|
+
process.stdout.write(
|
|
2139
|
+
`Wrote ${targetPath}
|
|
2140
|
+
Edit it to taste, then run:
|
|
2141
|
+
agentskit chat
|
|
2142
|
+
(flags on the CLI still win over config values.)
|
|
2143
|
+
`
|
|
2144
|
+
);
|
|
2145
|
+
});
|
|
1632
2146
|
program.command("tunnel <port>").description("Open a public URL pointing to a local port (great for webhooks).").option("--subdomain <name>", "Hint for a stable subdomain (provider may decline)").option("--host <host>", "Local hostname", "localhost").action(async (port, options) => {
|
|
1633
2147
|
const portNum = Number(port);
|
|
1634
2148
|
if (Number.isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
@@ -1653,5 +2167,5 @@ function createCli() {
|
|
|
1653
2167
|
}
|
|
1654
2168
|
|
|
1655
2169
|
export { ChatApp, createCli, loadConfig, renderChatHeader, renderReport, resolveChatProvider, runAgent, runDoctor, startDev, startTunnel, writeStarterProject };
|
|
1656
|
-
//# sourceMappingURL=chunk-
|
|
1657
|
-
//# sourceMappingURL=chunk-
|
|
2170
|
+
//# sourceMappingURL=chunk-V7E4HWTG.js.map
|
|
2171
|
+
//# sourceMappingURL=chunk-V7E4HWTG.js.map
|