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