@agentskit/cli 0.5.2 → 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/README.md +11 -0
- package/dist/bin.cjs +622 -59
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-KXI7545I.js → chunk-V7E4HWTG.js} +617 -53
- package/dist/chunk-V7E4HWTG.js.map +1 -0
- package/dist/index.cjs +623 -59
- 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-KXI7545I.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
var React3 = require('react');
|
|
5
5
|
var ink$1 = require('ink');
|
|
6
6
|
var commander = require('commander');
|
|
7
|
-
var
|
|
7
|
+
var fs = require('fs');
|
|
8
|
+
var os = require('os');
|
|
9
|
+
var path3 = require('path');
|
|
8
10
|
var promises = require('fs/promises');
|
|
9
11
|
var ink = require('@agentskit/ink');
|
|
10
12
|
var adapters = require('@agentskit/adapters');
|
|
11
13
|
var tools = require('@agentskit/tools');
|
|
12
14
|
var skills = require('@agentskit/skills');
|
|
13
15
|
var memory = require('@agentskit/memory');
|
|
16
|
+
var crypto = require('crypto');
|
|
14
17
|
var jsxRuntime = require('react/jsx-runtime');
|
|
15
18
|
var prompts = require('@inquirer/prompts');
|
|
16
19
|
var kleur = require('kleur');
|
|
17
|
-
var fs = require('fs');
|
|
18
20
|
var runtime = require('@agentskit/runtime');
|
|
19
21
|
var child_process = require('child_process');
|
|
20
22
|
var chokidar = require('chokidar');
|
|
@@ -22,7 +24,7 @@ var chokidar = require('chokidar');
|
|
|
22
24
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
23
25
|
|
|
24
26
|
var React3__default = /*#__PURE__*/_interopDefault(React3);
|
|
25
|
-
var
|
|
27
|
+
var path3__default = /*#__PURE__*/_interopDefault(path3);
|
|
26
28
|
var kleur__default = /*#__PURE__*/_interopDefault(kleur);
|
|
27
29
|
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
28
30
|
|
|
@@ -44,7 +46,7 @@ async function loadTsConfig(path4) {
|
|
|
44
46
|
}
|
|
45
47
|
async function loadPackageJsonConfig(dir) {
|
|
46
48
|
try {
|
|
47
|
-
const raw = await promises.readFile(
|
|
49
|
+
const raw = await promises.readFile(path3.join(dir, "package.json"), "utf8");
|
|
48
50
|
const pkg = JSON.parse(raw);
|
|
49
51
|
if (pkg.agentskit && typeof pkg.agentskit === "object") {
|
|
50
52
|
return pkg.agentskit;
|
|
@@ -54,16 +56,39 @@ async function loadPackageJsonConfig(dir) {
|
|
|
54
56
|
return void 0;
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
function mergeConfigs(base, override) {
|
|
60
|
+
if (!base && !override) return void 0;
|
|
61
|
+
if (!base) return override;
|
|
62
|
+
if (!override) return base;
|
|
63
|
+
return {
|
|
64
|
+
...base,
|
|
65
|
+
...override,
|
|
66
|
+
tools: { ...base.tools, ...override.tools },
|
|
67
|
+
defaults: { ...base.defaults, ...override.defaults },
|
|
68
|
+
runtime: { ...base.runtime, ...override.runtime },
|
|
69
|
+
observability: { ...base.observability, ...override.observability }
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async function loadLocalConfig(cwd) {
|
|
73
|
+
const tsConfig = await loadTsConfig(path3.join(cwd, ".agentskit.config.ts"));
|
|
61
74
|
if (tsConfig) return tsConfig;
|
|
62
|
-
const
|
|
63
|
-
const jsonConfig = await loadJsonConfig(jsonPath);
|
|
75
|
+
const jsonConfig = await loadJsonConfig(path3.join(cwd, ".agentskit.config.json"));
|
|
64
76
|
if (jsonConfig) return jsonConfig;
|
|
65
77
|
return await loadPackageJsonConfig(cwd);
|
|
66
78
|
}
|
|
79
|
+
async function loadGlobalConfig(home) {
|
|
80
|
+
if (home === null) return void 0;
|
|
81
|
+
const globalDir = path3.join(home ?? os.homedir(), ".agentskit");
|
|
82
|
+
const tsConfig = await loadTsConfig(path3.join(globalDir, "config.ts"));
|
|
83
|
+
if (tsConfig) return tsConfig;
|
|
84
|
+
return await loadJsonConfig(path3.join(globalDir, "config.json"));
|
|
85
|
+
}
|
|
86
|
+
async function loadConfig(options) {
|
|
87
|
+
const cwd = path3.resolve(options?.cwd ?? process.cwd());
|
|
88
|
+
const global = await loadGlobalConfig(options?.home);
|
|
89
|
+
const local = await loadLocalConfig(cwd);
|
|
90
|
+
return mergeConfigs(global, local);
|
|
91
|
+
}
|
|
67
92
|
var providers = {
|
|
68
93
|
openai: {
|
|
69
94
|
label: "OpenAI",
|
|
@@ -181,26 +206,41 @@ var skillRegistry = {
|
|
|
181
206
|
critic: skills.critic,
|
|
182
207
|
summarizer: skills.summarizer
|
|
183
208
|
};
|
|
209
|
+
function instantiate(kind) {
|
|
210
|
+
switch (kind) {
|
|
211
|
+
case "web_search":
|
|
212
|
+
return [tools.webSearch()];
|
|
213
|
+
case "fetch_url":
|
|
214
|
+
return [tools.fetchUrl()];
|
|
215
|
+
case "filesystem":
|
|
216
|
+
return tools.filesystem({ basePath: process.cwd() });
|
|
217
|
+
case "shell":
|
|
218
|
+
return [tools.shell({ timeout: 3e4 })];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function gateTool(tool) {
|
|
222
|
+
if (tool.requiresConfirmation === false) return tool;
|
|
223
|
+
return { ...tool, requiresConfirmation: true };
|
|
224
|
+
}
|
|
184
225
|
function resolveTools(toolNames) {
|
|
185
|
-
if (!toolNames)
|
|
186
|
-
|
|
187
|
-
|
|
226
|
+
if (!toolNames) {
|
|
227
|
+
return [...instantiate("web_search"), ...instantiate("fetch_url")].map(gateTool);
|
|
228
|
+
}
|
|
229
|
+
const tools = [];
|
|
230
|
+
for (const name of toolNames.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
188
231
|
switch (name) {
|
|
189
232
|
case "web_search":
|
|
190
|
-
|
|
191
|
-
break;
|
|
233
|
+
case "fetch_url":
|
|
192
234
|
case "filesystem":
|
|
193
|
-
tools$1.push(...tools.filesystem({ basePath: process.cwd() }));
|
|
194
|
-
break;
|
|
195
235
|
case "shell":
|
|
196
|
-
tools
|
|
236
|
+
tools.push(...instantiate(name));
|
|
197
237
|
break;
|
|
198
238
|
default:
|
|
199
239
|
process.stderr.write(`Unknown tool: ${name}
|
|
200
240
|
`);
|
|
201
241
|
}
|
|
202
242
|
}
|
|
203
|
-
return tools
|
|
243
|
+
return tools;
|
|
204
244
|
}
|
|
205
245
|
function resolveSkill(skillName) {
|
|
206
246
|
if (!skillName) return void 0;
|
|
@@ -233,41 +273,475 @@ function resolveMemory(backend, memoryPath) {
|
|
|
233
273
|
return memory.fileChatMemory(memoryPath);
|
|
234
274
|
}
|
|
235
275
|
}
|
|
276
|
+
var ROOT = path3.join(os.homedir(), ".agentskit", "sessions");
|
|
277
|
+
var META_SUFFIX = ".meta.json";
|
|
278
|
+
function cwdHash(cwd = process.cwd()) {
|
|
279
|
+
return crypto.createHash("sha256").update(cwd).digest("hex").slice(0, 12);
|
|
280
|
+
}
|
|
281
|
+
function dirFor(cwd = process.cwd()) {
|
|
282
|
+
return path3.join(ROOT, cwdHash(cwd));
|
|
283
|
+
}
|
|
284
|
+
function ensureDir(dir) {
|
|
285
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
286
|
+
}
|
|
287
|
+
function generateSessionId() {
|
|
288
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
289
|
+
const suffix = crypto.randomBytes(3).toString("hex");
|
|
290
|
+
return `${ts}-${suffix}`;
|
|
291
|
+
}
|
|
292
|
+
function sessionFilePath(id, cwd = process.cwd()) {
|
|
293
|
+
ensureDir(dirFor(cwd));
|
|
294
|
+
return path3.join(dirFor(cwd), `${id}.json`);
|
|
295
|
+
}
|
|
296
|
+
function metaPath(id, cwd = process.cwd()) {
|
|
297
|
+
return path3.join(dirFor(cwd), `${id}${META_SUFFIX}`);
|
|
298
|
+
}
|
|
299
|
+
function readMeta(id, cwd = process.cwd()) {
|
|
300
|
+
const path4 = metaPath(id, cwd);
|
|
301
|
+
if (!fs.existsSync(path4)) return null;
|
|
302
|
+
try {
|
|
303
|
+
return JSON.parse(fs.readFileSync(path4, "utf8"));
|
|
304
|
+
} catch {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function writeSessionMeta(meta, cwd = process.cwd()) {
|
|
309
|
+
ensureDir(dirFor(cwd));
|
|
310
|
+
fs.writeFileSync(metaPath(meta.id, cwd), JSON.stringify(meta, null, 2));
|
|
311
|
+
}
|
|
312
|
+
function derivePreview(messages) {
|
|
313
|
+
const firstUser = messages.find((m) => m.role === "user" && m.content.trim());
|
|
314
|
+
if (!firstUser) return "(empty)";
|
|
315
|
+
const single = firstUser.content.replace(/\s+/g, " ").trim();
|
|
316
|
+
return single.length > 80 ? `${single.slice(0, 80)}\u2026` : single;
|
|
317
|
+
}
|
|
318
|
+
function listSessions(cwd = process.cwd()) {
|
|
319
|
+
const dir = dirFor(cwd);
|
|
320
|
+
if (!fs.existsSync(dir)) return [];
|
|
321
|
+
const entries = fs.readdirSync(dir);
|
|
322
|
+
const records = [];
|
|
323
|
+
for (const entry of entries) {
|
|
324
|
+
if (!entry.endsWith(".json") || entry.endsWith(META_SUFFIX)) continue;
|
|
325
|
+
const id = entry.replace(/\.json$/, "");
|
|
326
|
+
const meta = readMeta(id, cwd);
|
|
327
|
+
const file = path3.join(dir, entry);
|
|
328
|
+
if (meta) {
|
|
329
|
+
records.push({ metadata: meta, file });
|
|
330
|
+
} else {
|
|
331
|
+
const stats = fs.statSync(file);
|
|
332
|
+
records.push({
|
|
333
|
+
metadata: {
|
|
334
|
+
id,
|
|
335
|
+
cwd,
|
|
336
|
+
createdAt: stats.birthtime.toISOString(),
|
|
337
|
+
updatedAt: stats.mtime.toISOString(),
|
|
338
|
+
messageCount: 0,
|
|
339
|
+
preview: "(legacy session)"
|
|
340
|
+
},
|
|
341
|
+
file
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
records.sort((a, b) => b.metadata.updatedAt.localeCompare(a.metadata.updatedAt));
|
|
346
|
+
return records;
|
|
347
|
+
}
|
|
348
|
+
function findLatestSession(cwd = process.cwd()) {
|
|
349
|
+
const all = listSessions(cwd);
|
|
350
|
+
return all[0] ?? null;
|
|
351
|
+
}
|
|
352
|
+
function findSession(id, cwd = process.cwd()) {
|
|
353
|
+
const exact = listSessions(cwd).find((s) => s.metadata.id === id);
|
|
354
|
+
if (exact) return exact;
|
|
355
|
+
const prefix = listSessions(cwd).find((s) => s.metadata.id.startsWith(id));
|
|
356
|
+
return prefix ?? null;
|
|
357
|
+
}
|
|
358
|
+
function resolveSession(input2) {
|
|
359
|
+
const cwd = input2.cwd ?? process.cwd();
|
|
360
|
+
if (input2.explicitPath) {
|
|
361
|
+
return { id: "custom", file: input2.explicitPath, isNew: !fs.existsSync(input2.explicitPath) };
|
|
362
|
+
}
|
|
363
|
+
if (input2.forceNew) {
|
|
364
|
+
const id2 = generateSessionId();
|
|
365
|
+
return { id: id2, file: sessionFilePath(id2, cwd), isNew: true };
|
|
366
|
+
}
|
|
367
|
+
if (input2.resumeId) {
|
|
368
|
+
const target = input2.resumeId === true ? findLatestSession(cwd) : findSession(input2.resumeId, cwd);
|
|
369
|
+
if (target) {
|
|
370
|
+
return { id: target.metadata.id, file: target.file, isNew: false };
|
|
371
|
+
}
|
|
372
|
+
process.stderr.write(
|
|
373
|
+
`No session matching "${String(input2.resumeId)}" \u2014 starting a new one.
|
|
374
|
+
`
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
const latest = findLatestSession(cwd);
|
|
378
|
+
if (latest) return { id: latest.metadata.id, file: latest.file, isNew: false };
|
|
379
|
+
const id = generateSessionId();
|
|
380
|
+
return { id, file: sessionFilePath(id, cwd), isNew: true };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/slash-commands.ts
|
|
384
|
+
function parseSlashCommand(input2) {
|
|
385
|
+
if (!input2.startsWith("/")) return null;
|
|
386
|
+
const match = input2.slice(1).match(/^(\S+)\s*([\s\S]*)$/);
|
|
387
|
+
if (!match) return null;
|
|
388
|
+
return { name: match[1], args: match[2] ?? "" };
|
|
389
|
+
}
|
|
390
|
+
function createSlashRegistry(commands) {
|
|
391
|
+
const map = /* @__PURE__ */ new Map();
|
|
392
|
+
for (const cmd of commands) {
|
|
393
|
+
map.set(cmd.name, cmd);
|
|
394
|
+
for (const alias of cmd.aliases ?? []) map.set(alias, cmd);
|
|
395
|
+
}
|
|
396
|
+
return map;
|
|
397
|
+
}
|
|
398
|
+
var builtinSlashCommands = [
|
|
399
|
+
{
|
|
400
|
+
name: "help",
|
|
401
|
+
aliases: ["?"],
|
|
402
|
+
description: "List available slash commands.",
|
|
403
|
+
run(ctx) {
|
|
404
|
+
const seen = /* @__PURE__ */ new Set();
|
|
405
|
+
const lines = [];
|
|
406
|
+
for (const cmd of ctx.commands) {
|
|
407
|
+
if (seen.has(cmd.name)) continue;
|
|
408
|
+
seen.add(cmd.name);
|
|
409
|
+
const suffix = cmd.usage ? ` (${cmd.usage})` : "";
|
|
410
|
+
lines.push(` /${cmd.name.padEnd(10)} ${cmd.description}${suffix}`);
|
|
411
|
+
}
|
|
412
|
+
ctx.feedback(`Slash commands:
|
|
413
|
+
${lines.join("\n")}`, "info");
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "model",
|
|
418
|
+
description: "Switch the active model.",
|
|
419
|
+
usage: "/model <name>",
|
|
420
|
+
run(ctx, args) {
|
|
421
|
+
const value = args.trim();
|
|
422
|
+
if (!value) {
|
|
423
|
+
ctx.feedback(
|
|
424
|
+
`Current model: ${ctx.runtime.model ?? "unset"}. Usage: /model <name>`,
|
|
425
|
+
"warn"
|
|
426
|
+
);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
ctx.setModel(value);
|
|
430
|
+
ctx.feedback(`Model \u2192 ${value}`, "success");
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
name: "provider",
|
|
435
|
+
description: "Switch the adapter provider.",
|
|
436
|
+
usage: "/provider openai|anthropic|gemini|ollama|deepseek|grok|kimi|demo",
|
|
437
|
+
run(ctx, args) {
|
|
438
|
+
const value = args.trim();
|
|
439
|
+
if (!value) {
|
|
440
|
+
ctx.feedback(
|
|
441
|
+
`Current provider: ${ctx.runtime.provider}. Usage: /provider <name>`,
|
|
442
|
+
"warn"
|
|
443
|
+
);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
ctx.setProvider(value);
|
|
447
|
+
ctx.feedback(`Provider \u2192 ${value}`, "success");
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
name: "base-url",
|
|
452
|
+
aliases: ["baseurl"],
|
|
453
|
+
description: "Override provider base URL.",
|
|
454
|
+
usage: "/base-url <url|clear>",
|
|
455
|
+
run(ctx, args) {
|
|
456
|
+
const value = args.trim();
|
|
457
|
+
if (!value || value === "clear") {
|
|
458
|
+
ctx.setBaseUrl(void 0);
|
|
459
|
+
ctx.feedback("Base URL cleared.", "success");
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
ctx.setBaseUrl(value);
|
|
463
|
+
ctx.feedback(`Base URL \u2192 ${value}`, "success");
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: "tools",
|
|
468
|
+
description: "Set active tools (comma-separated) or clear them.",
|
|
469
|
+
usage: "/tools web_search,fetch_url | /tools clear",
|
|
470
|
+
run(ctx, args) {
|
|
471
|
+
const value = args.trim();
|
|
472
|
+
if (!value || value === "clear") {
|
|
473
|
+
ctx.setTools(void 0);
|
|
474
|
+
ctx.feedback("Tools reset to defaults.", "success");
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
ctx.setTools(value);
|
|
478
|
+
ctx.feedback(`Tools \u2192 ${value}`, "success");
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
name: "skill",
|
|
483
|
+
description: "Set active skill(s) (comma-separated) or clear them.",
|
|
484
|
+
usage: "/skill researcher,coder | /skill clear",
|
|
485
|
+
run(ctx, args) {
|
|
486
|
+
const value = args.trim();
|
|
487
|
+
if (!value || value === "clear") {
|
|
488
|
+
ctx.setSkill(void 0);
|
|
489
|
+
ctx.feedback("Skills cleared.", "success");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
ctx.setSkill(value);
|
|
493
|
+
ctx.feedback(`Skills \u2192 ${value}`, "success");
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
name: "clear",
|
|
498
|
+
aliases: ["reset"],
|
|
499
|
+
description: "Clear the conversation history in this session.",
|
|
500
|
+
async run(ctx) {
|
|
501
|
+
await ctx.chat.clear();
|
|
502
|
+
ctx.feedback("History cleared.", "success");
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
name: "exit",
|
|
507
|
+
aliases: ["quit", "q"],
|
|
508
|
+
description: "Exit the chat.",
|
|
509
|
+
run() {
|
|
510
|
+
process.exit(0);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
];
|
|
514
|
+
function groupIntoTurns(messages) {
|
|
515
|
+
const turns = [];
|
|
516
|
+
let current = [];
|
|
517
|
+
for (const message of messages) {
|
|
518
|
+
if (message.role === "user") {
|
|
519
|
+
if (current.length > 0) turns.push(current);
|
|
520
|
+
current = [message];
|
|
521
|
+
} else if (message.role === "system") {
|
|
522
|
+
if (current.length > 0) turns.push(current);
|
|
523
|
+
turns.push([message]);
|
|
524
|
+
current = [];
|
|
525
|
+
} else {
|
|
526
|
+
current.push(message);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (current.length > 0) turns.push(current);
|
|
530
|
+
return turns;
|
|
531
|
+
}
|
|
236
532
|
function ChatApp(options) {
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
533
|
+
const [provider, setProvider] = React3.useState(options.provider);
|
|
534
|
+
const [model, setModel] = React3.useState(options.model);
|
|
535
|
+
const [apiKey, setApiKey] = React3.useState(options.apiKey);
|
|
536
|
+
const [baseUrl, setBaseUrl] = React3.useState(options.baseUrl);
|
|
537
|
+
const [toolsFlag, setToolsFlag] = React3.useState(options.tools);
|
|
538
|
+
const [skillFlag, setSkillFlag] = React3.useState(options.skill);
|
|
539
|
+
const runtime = React3.useMemo(
|
|
540
|
+
() => resolveChatProvider({ provider, model, apiKey, baseUrl }),
|
|
541
|
+
[provider, model, apiKey, baseUrl]
|
|
240
542
|
);
|
|
241
543
|
const memory = React3.useMemo(
|
|
242
544
|
() => resolveMemory(options.memoryBackend, options.memoryPath ?? ".agentskit-history.json"),
|
|
243
545
|
[options.memoryPath, options.memoryBackend]
|
|
244
546
|
);
|
|
245
|
-
const tools = React3.useMemo(() => resolveTools(
|
|
547
|
+
const tools = React3.useMemo(() => resolveTools(toolsFlag), [toolsFlag]);
|
|
246
548
|
const skills = React3.useMemo(() => {
|
|
247
|
-
if (!
|
|
248
|
-
const names =
|
|
549
|
+
if (!skillFlag) return void 0;
|
|
550
|
+
const names = skillFlag.split(",").map((s) => s.trim());
|
|
249
551
|
const resolved = names.map((n) => skillRegistry[n]).filter(Boolean);
|
|
250
552
|
if (resolved.length === 0) return void 0;
|
|
251
553
|
return resolved;
|
|
252
|
-
}, [
|
|
554
|
+
}, [skillFlag]);
|
|
253
555
|
const chat = ink.useChat({
|
|
254
|
-
adapter,
|
|
556
|
+
adapter: runtime.adapter,
|
|
255
557
|
memory,
|
|
256
558
|
systemPrompt: options.system,
|
|
257
559
|
tools: tools.length > 0 ? tools : void 0,
|
|
258
560
|
skills
|
|
259
561
|
});
|
|
562
|
+
const [sessionAllowed, setSessionAllowed] = React3.useState(/* @__PURE__ */ new Set());
|
|
563
|
+
const autoApprovedRef = React3.useRef(/* @__PURE__ */ new Set());
|
|
564
|
+
React3.useEffect(() => {
|
|
565
|
+
if (sessionAllowed.size === 0) return;
|
|
566
|
+
for (const message of chat.messages) {
|
|
567
|
+
for (const call of message.toolCalls ?? []) {
|
|
568
|
+
if (call.status === "requires_confirmation" && sessionAllowed.has(call.name) && !autoApprovedRef.current.has(call.id)) {
|
|
569
|
+
autoApprovedRef.current.add(call.id);
|
|
570
|
+
void chat.approve(call.id);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}, [chat.messages, sessionAllowed, chat.approve]);
|
|
575
|
+
const handleApproveAlways = (toolCallId, toolName) => {
|
|
576
|
+
setSessionAllowed((prev) => {
|
|
577
|
+
if (prev.has(toolName)) return prev;
|
|
578
|
+
const next = new Set(prev);
|
|
579
|
+
next.add(toolName);
|
|
580
|
+
return next;
|
|
581
|
+
});
|
|
582
|
+
autoApprovedRef.current.add(toolCallId);
|
|
583
|
+
void chat.approve(toolCallId);
|
|
584
|
+
};
|
|
585
|
+
const sessionCreatedAtRef = React3.useRef(void 0);
|
|
586
|
+
const messageCount = chat.messages.length;
|
|
587
|
+
const firstUserContent = chat.messages.find((m) => m.role === "user")?.content ?? "";
|
|
588
|
+
React3.useEffect(() => {
|
|
589
|
+
const sessionId = options.sessionId;
|
|
590
|
+
if (!sessionId || sessionId === "custom") return;
|
|
591
|
+
if (!sessionCreatedAtRef.current) {
|
|
592
|
+
sessionCreatedAtRef.current = (/* @__PURE__ */ new Date()).toISOString();
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
writeSessionMeta({
|
|
596
|
+
id: sessionId,
|
|
597
|
+
cwd: process.cwd(),
|
|
598
|
+
createdAt: sessionCreatedAtRef.current,
|
|
599
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
600
|
+
messageCount,
|
|
601
|
+
preview: derivePreview(chat.messages),
|
|
602
|
+
provider: runtime.provider,
|
|
603
|
+
model: runtime.model
|
|
604
|
+
});
|
|
605
|
+
} catch {
|
|
606
|
+
}
|
|
607
|
+
}, [options.sessionId, messageCount, firstUserContent, runtime.provider, runtime.model]);
|
|
608
|
+
const turns = React3.useMemo(() => groupIntoTurns(chat.messages), [chat.messages]);
|
|
609
|
+
const toolNames = toolsFlag ? toolsFlag.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
610
|
+
const [feedback, setFeedback] = React3.useState(null);
|
|
611
|
+
const slashCommands = React3.useMemo(
|
|
612
|
+
() => [...builtinSlashCommands, ...options.slashCommands ?? []],
|
|
613
|
+
[options.slashCommands]
|
|
614
|
+
);
|
|
615
|
+
const slashRegistry = React3.useMemo(() => createSlashRegistry(slashCommands), [slashCommands]);
|
|
616
|
+
const handleSubmitInput = async (raw) => {
|
|
617
|
+
const parsed = parseSlashCommand(raw);
|
|
618
|
+
if (!parsed) {
|
|
619
|
+
setFeedback(null);
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
622
|
+
const cmd = slashRegistry.get(parsed.name);
|
|
623
|
+
if (!cmd) {
|
|
624
|
+
setFeedback({
|
|
625
|
+
message: `Unknown command: /${parsed.name}. Type /help for the list.`,
|
|
626
|
+
kind: "error"
|
|
627
|
+
});
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
const ctx = {
|
|
631
|
+
chat,
|
|
632
|
+
runtime: {
|
|
633
|
+
provider: runtime.provider,
|
|
634
|
+
model: runtime.model,
|
|
635
|
+
mode: runtime.mode,
|
|
636
|
+
baseUrl,
|
|
637
|
+
tools: toolsFlag,
|
|
638
|
+
skill: skillFlag
|
|
639
|
+
},
|
|
640
|
+
setProvider,
|
|
641
|
+
setModel,
|
|
642
|
+
setApiKey,
|
|
643
|
+
setBaseUrl,
|
|
644
|
+
setTools: setToolsFlag,
|
|
645
|
+
setSkill: setSkillFlag,
|
|
646
|
+
feedback: (message, kind = "info") => setFeedback({ message, kind }),
|
|
647
|
+
commands: slashCommands
|
|
648
|
+
};
|
|
649
|
+
try {
|
|
650
|
+
await cmd.run(ctx, parsed.args);
|
|
651
|
+
} catch (err) {
|
|
652
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
653
|
+
setFeedback({ message: `/${parsed.name} failed: ${message}`, kind: "error" });
|
|
654
|
+
}
|
|
655
|
+
return true;
|
|
656
|
+
};
|
|
657
|
+
const awaitingConfirmation = React3.useMemo(
|
|
658
|
+
() => chat.messages.some(
|
|
659
|
+
(message) => message.toolCalls?.some(
|
|
660
|
+
(call) => call.status === "requires_confirmation" && !sessionAllowed.has(call.name)
|
|
661
|
+
)
|
|
662
|
+
),
|
|
663
|
+
[chat.messages, sessionAllowed]
|
|
664
|
+
);
|
|
260
665
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink$1.Box, { flexDirection: "column", gap: 1, children: [
|
|
261
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
666
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
667
|
+
ink.StatusHeader,
|
|
668
|
+
{
|
|
669
|
+
provider: runtime.provider,
|
|
670
|
+
model: runtime.model,
|
|
671
|
+
mode: runtime.mode,
|
|
672
|
+
tools: toolNames,
|
|
673
|
+
messageCount: chat.messages.length,
|
|
674
|
+
sessionId: options.sessionId
|
|
675
|
+
}
|
|
676
|
+
),
|
|
677
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.ChatContainer, { children: turns.map((turn, turnIdx) => {
|
|
678
|
+
const assistantSteps = turn.filter((m) => m.role === "assistant").length;
|
|
679
|
+
let stepIndex = 0;
|
|
680
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ink$1.Box, { flexDirection: "column", gap: 1, children: turn.map((message) => {
|
|
681
|
+
const showStep = message.role === "assistant" && assistantSteps > 1;
|
|
682
|
+
if (showStep) stepIndex++;
|
|
683
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink$1.Box, { flexDirection: "column", children: [
|
|
684
|
+
showStep ? /* @__PURE__ */ jsxRuntime.jsxs(ink$1.Text, { dimColor: true, children: [
|
|
685
|
+
"\u21BB step ",
|
|
686
|
+
stepIndex,
|
|
687
|
+
"/",
|
|
688
|
+
assistantSteps
|
|
689
|
+
] }) : null,
|
|
690
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Message, { message }),
|
|
691
|
+
message.toolCalls?.map((toolCall) => /* @__PURE__ */ jsxRuntime.jsxs(ink$1.Box, { flexDirection: "column", children: [
|
|
692
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.ToolCallView, { toolCall, expanded: true }),
|
|
693
|
+
toolCall.status === "requires_confirmation" && !sessionAllowed.has(toolCall.name) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
694
|
+
ink.ToolConfirmation,
|
|
695
|
+
{
|
|
696
|
+
toolCall,
|
|
697
|
+
onApprove: chat.approve,
|
|
698
|
+
onDeny: chat.deny,
|
|
699
|
+
onApproveAlways: handleApproveAlways
|
|
700
|
+
}
|
|
701
|
+
) : null
|
|
702
|
+
] }, toolCall.id))
|
|
703
|
+
] }, message.id);
|
|
704
|
+
}) }, `turn-${turnIdx}`);
|
|
705
|
+
}) }),
|
|
706
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
707
|
+
ink.ThinkingIndicator,
|
|
708
|
+
{
|
|
709
|
+
visible: chat.status === "streaming",
|
|
710
|
+
label: toolNames.length > 0 ? "agent working" : "thinking"
|
|
711
|
+
}
|
|
712
|
+
),
|
|
713
|
+
chat.error ? /* @__PURE__ */ jsxRuntime.jsxs(ink$1.Box, { borderStyle: "round", borderColor: "red", paddingX: 1, flexDirection: "column", children: [
|
|
714
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink$1.Text, { color: "red", bold: true, children: [
|
|
715
|
+
"\u2717 ",
|
|
716
|
+
chat.error.name || "Error"
|
|
717
|
+
] }),
|
|
718
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink$1.Text, { color: "red", children: chat.error.message })
|
|
719
|
+
] }) : null,
|
|
720
|
+
feedback ? /* @__PURE__ */ jsxRuntime.jsx(ink$1.Box, { borderStyle: "round", borderColor: feedbackBorder(feedback.kind), paddingX: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ink$1.Text, { color: feedbackBorder(feedback.kind), children: feedback.message }) }) : null,
|
|
721
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
722
|
+
ink.InputBar,
|
|
723
|
+
{
|
|
724
|
+
chat,
|
|
725
|
+
placeholder: "Type a message or /help for commands",
|
|
726
|
+
disabled: awaitingConfirmation,
|
|
727
|
+
onSubmitInput: handleSubmitInput
|
|
728
|
+
}
|
|
729
|
+
)
|
|
269
730
|
] });
|
|
270
731
|
}
|
|
732
|
+
function feedbackBorder(kind) {
|
|
733
|
+
switch (kind) {
|
|
734
|
+
case "error":
|
|
735
|
+
return "red";
|
|
736
|
+
case "warn":
|
|
737
|
+
return "yellow";
|
|
738
|
+
case "success":
|
|
739
|
+
return "green";
|
|
740
|
+
case "info":
|
|
741
|
+
default:
|
|
742
|
+
return "cyan";
|
|
743
|
+
}
|
|
744
|
+
}
|
|
271
745
|
function renderChatHeader(options) {
|
|
272
746
|
const runtime = resolveChatProvider(options);
|
|
273
747
|
const parts = [`provider=${runtime.provider}`];
|
|
@@ -376,7 +850,7 @@ function reactStarter(ctx) {
|
|
|
376
850
|
return {
|
|
377
851
|
"package.json": JSON.stringify(
|
|
378
852
|
{
|
|
379
|
-
name:
|
|
853
|
+
name: path3__default.default.basename(ctx.template === "react" ? "agentskit-react-app" : "agentskit-app"),
|
|
380
854
|
private: true,
|
|
381
855
|
type: "module",
|
|
382
856
|
scripts: {
|
|
@@ -745,8 +1219,8 @@ async function writeStarterProject(options) {
|
|
|
745
1219
|
await promises.mkdir(options.targetDir, { recursive: true });
|
|
746
1220
|
await Promise.all(
|
|
747
1221
|
Object.entries(files).map(async ([relativePath, content]) => {
|
|
748
|
-
const absolutePath =
|
|
749
|
-
await promises.mkdir(
|
|
1222
|
+
const absolutePath = path3__default.default.join(options.targetDir, relativePath);
|
|
1223
|
+
await promises.mkdir(path3__default.default.dirname(absolutePath), { recursive: true });
|
|
750
1224
|
await promises.writeFile(absolutePath, content, "utf8");
|
|
751
1225
|
})
|
|
752
1226
|
);
|
|
@@ -762,7 +1236,7 @@ ${kleur__default.default.bold().green("\u25B2")} ${kleur__default.default.bold("
|
|
|
762
1236
|
default: defaults.dir ?? "agentskit-app",
|
|
763
1237
|
validate: (value) => {
|
|
764
1238
|
if (!value.trim()) return "A directory name is required.";
|
|
765
|
-
const abs =
|
|
1239
|
+
const abs = path3__default.default.resolve(process.cwd(), value);
|
|
766
1240
|
if (fs.existsSync(abs)) return `${value} already exists. Pick a different name.`;
|
|
767
1241
|
return true;
|
|
768
1242
|
}
|
|
@@ -840,7 +1314,7 @@ ${kleur__default.default.bold().green("\u25B2")} ${kleur__default.default.bold("
|
|
|
840
1314
|
return {
|
|
841
1315
|
cancelled: false,
|
|
842
1316
|
options: {
|
|
843
|
-
targetDir:
|
|
1317
|
+
targetDir: path3__default.default.resolve(process.cwd(), targetDir),
|
|
844
1318
|
template,
|
|
845
1319
|
provider,
|
|
846
1320
|
tools,
|
|
@@ -857,7 +1331,7 @@ ${kleur__default.default.bold().green("\u25B2")} ${kleur__default.default.bold("
|
|
|
857
1331
|
}
|
|
858
1332
|
}
|
|
859
1333
|
function printNextSteps(options) {
|
|
860
|
-
const dir =
|
|
1334
|
+
const dir = path3__default.default.relative(process.cwd(), options.targetDir) || ".";
|
|
861
1335
|
const pm = options.packageManager ?? "pnpm";
|
|
862
1336
|
const installCmd = pm === "npm" ? "npm install" : `${pm} install`;
|
|
863
1337
|
const runCmd = pm === "npm" ? "npm run dev" : `${pm} dev`;
|
|
@@ -1071,17 +1545,17 @@ async function checkNodeVersion() {
|
|
|
1071
1545
|
}
|
|
1072
1546
|
async function checkPnpm() {
|
|
1073
1547
|
const cwd = process.cwd();
|
|
1074
|
-
const hasPnpm = fs.existsSync(
|
|
1548
|
+
const hasPnpm = fs.existsSync(path3.join(cwd, "pnpm-lock.yaml")) || fs.existsSync(path3.join(cwd, "pnpm-workspace.yaml"));
|
|
1075
1549
|
if (hasPnpm) {
|
|
1076
1550
|
return { status: "pass", name: "Package manager", detail: "pnpm detected (lockfile)" };
|
|
1077
1551
|
}
|
|
1078
|
-
if (fs.existsSync(
|
|
1552
|
+
if (fs.existsSync(path3.join(cwd, "package-lock.json"))) {
|
|
1079
1553
|
return { status: "warn", name: "Package manager", detail: "npm detected \u2014 pnpm recommended for monorepo workflows" };
|
|
1080
1554
|
}
|
|
1081
|
-
if (fs.existsSync(
|
|
1555
|
+
if (fs.existsSync(path3.join(cwd, "yarn.lock"))) {
|
|
1082
1556
|
return { status: "pass", name: "Package manager", detail: "yarn detected" };
|
|
1083
1557
|
}
|
|
1084
|
-
if (fs.existsSync(
|
|
1558
|
+
if (fs.existsSync(path3.join(cwd, "bun.lock")) || fs.existsSync(path3.join(cwd, "bun.lockb"))) {
|
|
1085
1559
|
return { status: "pass", name: "Package manager", detail: "bun detected" };
|
|
1086
1560
|
}
|
|
1087
1561
|
return {
|
|
@@ -1091,7 +1565,7 @@ async function checkPnpm() {
|
|
|
1091
1565
|
};
|
|
1092
1566
|
}
|
|
1093
1567
|
async function checkPackageJson() {
|
|
1094
|
-
const path4 =
|
|
1568
|
+
const path4 = path3.join(process.cwd(), "package.json");
|
|
1095
1569
|
if (!fs.existsSync(path4)) {
|
|
1096
1570
|
return {
|
|
1097
1571
|
status: "warn",
|
|
@@ -1316,7 +1790,7 @@ var DEFAULT_IGNORE = [
|
|
|
1316
1790
|
"**/*.spec.ts"
|
|
1317
1791
|
];
|
|
1318
1792
|
function startDev(options) {
|
|
1319
|
-
const entry =
|
|
1793
|
+
const entry = path3.resolve(process.cwd(), options.entry);
|
|
1320
1794
|
if (!fs.existsSync(entry)) {
|
|
1321
1795
|
throw new Error(`Entry file not found: ${entry}`);
|
|
1322
1796
|
}
|
|
@@ -1348,7 +1822,7 @@ function startDev(options) {
|
|
|
1348
1822
|
};
|
|
1349
1823
|
const startChild = () => {
|
|
1350
1824
|
restartCount++;
|
|
1351
|
-
banner(`\u25B8 starting ${kleur__default.default.bold(
|
|
1825
|
+
banner(`\u25B8 starting ${kleur__default.default.bold(path3.basename(entry))} (restart #${restartCount - 1})`, "cyan");
|
|
1352
1826
|
const c = spawnFn(cmd, baseArgs);
|
|
1353
1827
|
child = c;
|
|
1354
1828
|
c.stdout?.on("data", (d) => stdout.write(d));
|
|
@@ -1472,34 +1946,87 @@ async function startTunnel(options) {
|
|
|
1472
1946
|
// src/commands.ts
|
|
1473
1947
|
function mergeWithConfig(options, config) {
|
|
1474
1948
|
if (!config) return options;
|
|
1949
|
+
const d = config.defaults ?? {};
|
|
1950
|
+
const resolvedApiKey = options.apiKey ?? (d.apiKeyEnv ? process.env[d.apiKeyEnv] : void 0) ?? d.apiKey;
|
|
1475
1951
|
return {
|
|
1476
1952
|
...options,
|
|
1477
1953
|
// Config defaults — only apply if CLI flag wasn't set
|
|
1478
|
-
provider: options.provider !== "demo" ? options.provider :
|
|
1479
|
-
model: options.model ??
|
|
1954
|
+
provider: options.provider !== "demo" ? options.provider : d.provider ?? options.provider,
|
|
1955
|
+
model: options.model ?? d.model,
|
|
1956
|
+
apiKey: resolvedApiKey,
|
|
1957
|
+
baseUrl: options.baseUrl ?? d.baseUrl,
|
|
1958
|
+
tools: options.tools ?? d.tools,
|
|
1959
|
+
skill: options.skill ?? d.skill,
|
|
1960
|
+
system: options.system ?? d.system,
|
|
1961
|
+
memoryBackend: options.memoryBackend ?? d.memoryBackend
|
|
1480
1962
|
};
|
|
1481
1963
|
}
|
|
1482
1964
|
function createCli() {
|
|
1483
1965
|
const program = new commander.Command();
|
|
1484
1966
|
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
|
|
1485
|
-
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>", "
|
|
1967
|
+
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) => {
|
|
1968
|
+
if (options.listSessions) {
|
|
1969
|
+
const sessions = listSessions();
|
|
1970
|
+
if (sessions.length === 0) {
|
|
1971
|
+
process.stdout.write("No saved sessions for this directory.\n");
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
for (const s of sessions) {
|
|
1975
|
+
const { id, updatedAt, messageCount, preview, model } = s.metadata;
|
|
1976
|
+
process.stdout.write(
|
|
1977
|
+
`${id} ${updatedAt} msgs=${messageCount}${model ? ` model=${model}` : ""}
|
|
1978
|
+
${preview}
|
|
1979
|
+
`
|
|
1980
|
+
);
|
|
1981
|
+
}
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1486
1984
|
const config = options.config !== false ? await loadConfig() : void 0;
|
|
1487
1985
|
const merged = mergeWithConfig(options, config);
|
|
1986
|
+
const session = resolveSession({
|
|
1987
|
+
explicitPath: options.memory,
|
|
1988
|
+
forceNew: Boolean(options.new),
|
|
1989
|
+
resumeId: options.resume
|
|
1990
|
+
});
|
|
1991
|
+
if (!session.isNew && !options.memory) {
|
|
1992
|
+
process.stdout.write(
|
|
1993
|
+
`Resuming session ${session.id}. Start fresh with --new or list with --list-sessions.
|
|
1994
|
+
`
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1488
1997
|
const chatOptions = {
|
|
1489
1998
|
apiKey: merged.apiKey ?? options.apiKey,
|
|
1490
1999
|
baseUrl: merged.baseUrl ?? options.baseUrl,
|
|
1491
2000
|
provider: merged.provider,
|
|
1492
2001
|
model: merged.model,
|
|
1493
|
-
system: options.system,
|
|
1494
|
-
memoryPath:
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
2002
|
+
system: merged.system ?? options.system,
|
|
2003
|
+
memoryPath: session.file,
|
|
2004
|
+
sessionId: session.id,
|
|
2005
|
+
tools: merged.tools ?? options.tools,
|
|
2006
|
+
skill: merged.skill ?? options.skill,
|
|
2007
|
+
memoryBackend: merged.memoryBackend ?? options.memoryBackend,
|
|
1498
2008
|
agentsKitConfig: config
|
|
1499
2009
|
};
|
|
1500
2010
|
process.stdout.write(`${renderChatHeader(chatOptions)}
|
|
1501
2011
|
`);
|
|
1502
|
-
ink$1.render(React3__default.default.createElement(ChatApp, chatOptions));
|
|
2012
|
+
const instance = ink$1.render(React3__default.default.createElement(ChatApp, chatOptions));
|
|
2013
|
+
await instance.waitUntilExit();
|
|
2014
|
+
if (options.memory) {
|
|
2015
|
+
process.stdout.write(
|
|
2016
|
+
`
|
|
2017
|
+
Session saved to ${session.file}. Resume with --memory ${session.file}
|
|
2018
|
+
`
|
|
2019
|
+
);
|
|
2020
|
+
} else {
|
|
2021
|
+
process.stdout.write(
|
|
2022
|
+
`
|
|
2023
|
+
Session saved. Resume with:
|
|
2024
|
+
agentskit chat --resume ${session.id}
|
|
2025
|
+
Or start fresh with:
|
|
2026
|
+
agentskit chat --new
|
|
2027
|
+
`
|
|
2028
|
+
);
|
|
2029
|
+
}
|
|
1503
2030
|
});
|
|
1504
2031
|
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) => {
|
|
1505
2032
|
const task = options.task ?? positionalTask;
|
|
@@ -1527,7 +2054,7 @@ function createCli() {
|
|
|
1527
2054
|
if (isCi) {
|
|
1528
2055
|
const template = rawOptions.template ?? "react";
|
|
1529
2056
|
resolved = {
|
|
1530
|
-
targetDir:
|
|
2057
|
+
targetDir: path3__default.default.resolve(process.cwd(), rawOptions.dir),
|
|
1531
2058
|
template,
|
|
1532
2059
|
provider: rawOptions.provider ?? "demo",
|
|
1533
2060
|
tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
|
|
@@ -1547,7 +2074,7 @@ function createCli() {
|
|
|
1547
2074
|
await writeStarterProject(resolved);
|
|
1548
2075
|
if (isCi) {
|
|
1549
2076
|
process.stdout.write(
|
|
1550
|
-
`Created ${resolved.template} starter in ${
|
|
2077
|
+
`Created ${resolved.template} starter in ${path3__default.default.relative(process.cwd(), resolved.targetDir) || "."}
|
|
1551
2078
|
`
|
|
1552
2079
|
);
|
|
1553
2080
|
} else {
|
|
@@ -1588,6 +2115,43 @@ function createCli() {
|
|
|
1588
2115
|
process.exit(1);
|
|
1589
2116
|
}
|
|
1590
2117
|
});
|
|
2118
|
+
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) => {
|
|
2119
|
+
const isLocal = Boolean(options.local);
|
|
2120
|
+
const targetPath = isLocal ? path3__default.default.join(process.cwd(), ".agentskit.config.json") : path3__default.default.join(os.homedir(), ".agentskit", "config.json");
|
|
2121
|
+
if (action === "show") {
|
|
2122
|
+
const config = await loadConfig();
|
|
2123
|
+
process.stdout.write(JSON.stringify(config ?? {}, null, 2) + "\n");
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
2126
|
+
if (action !== "init") {
|
|
2127
|
+
process.stderr.write(`Unknown action: ${action}. Use "init" or "show".
|
|
2128
|
+
`);
|
|
2129
|
+
process.exit(2);
|
|
2130
|
+
}
|
|
2131
|
+
if (fs.existsSync(targetPath) && !options.force) {
|
|
2132
|
+
process.stderr.write(`Config already exists at ${targetPath}. Re-run with --force to overwrite.
|
|
2133
|
+
`);
|
|
2134
|
+
process.exit(1);
|
|
2135
|
+
}
|
|
2136
|
+
const template = {
|
|
2137
|
+
defaults: {
|
|
2138
|
+
provider: "openai",
|
|
2139
|
+
baseUrl: "https://openrouter.ai/api",
|
|
2140
|
+
apiKeyEnv: "OPENROUTER_API_KEY",
|
|
2141
|
+
model: "openai/gpt-oss-120b:free",
|
|
2142
|
+
tools: "web_search,fetch_url"
|
|
2143
|
+
}
|
|
2144
|
+
};
|
|
2145
|
+
fs.mkdirSync(path3__default.default.dirname(targetPath), { recursive: true });
|
|
2146
|
+
fs.writeFileSync(targetPath, JSON.stringify(template, null, 2) + "\n");
|
|
2147
|
+
process.stdout.write(
|
|
2148
|
+
`Wrote ${targetPath}
|
|
2149
|
+
Edit it to taste, then run:
|
|
2150
|
+
agentskit chat
|
|
2151
|
+
(flags on the CLI still win over config values.)
|
|
2152
|
+
`
|
|
2153
|
+
);
|
|
2154
|
+
});
|
|
1591
2155
|
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) => {
|
|
1592
2156
|
const portNum = Number(port);
|
|
1593
2157
|
if (Number.isNaN(portNum) || portNum < 1 || portNum > 65535) {
|