@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
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,6 +273,244 @@ 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
|
+
];
|
|
236
514
|
function groupIntoTurns(messages) {
|
|
237
515
|
const turns = [];
|
|
238
516
|
let current = [];
|
|
@@ -252,24 +530,28 @@ function groupIntoTurns(messages) {
|
|
|
252
530
|
return turns;
|
|
253
531
|
}
|
|
254
532
|
function ChatApp(options) {
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
]);
|
|
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]
|
|
542
|
+
);
|
|
261
543
|
const memory = React3.useMemo(
|
|
262
544
|
() => resolveMemory(options.memoryBackend, options.memoryPath ?? ".agentskit-history.json"),
|
|
263
545
|
[options.memoryPath, options.memoryBackend]
|
|
264
546
|
);
|
|
265
|
-
const tools = React3.useMemo(() => resolveTools(
|
|
547
|
+
const tools = React3.useMemo(() => resolveTools(toolsFlag), [toolsFlag]);
|
|
266
548
|
const skills = React3.useMemo(() => {
|
|
267
|
-
if (!
|
|
268
|
-
const names =
|
|
549
|
+
if (!skillFlag) return void 0;
|
|
550
|
+
const names = skillFlag.split(",").map((s) => s.trim());
|
|
269
551
|
const resolved = names.map((n) => skillRegistry[n]).filter(Boolean);
|
|
270
552
|
if (resolved.length === 0) return void 0;
|
|
271
553
|
return resolved;
|
|
272
|
-
}, [
|
|
554
|
+
}, [skillFlag]);
|
|
273
555
|
const chat = ink.useChat({
|
|
274
556
|
adapter: runtime.adapter,
|
|
275
557
|
memory,
|
|
@@ -277,8 +559,109 @@ function ChatApp(options) {
|
|
|
277
559
|
tools: tools.length > 0 ? tools : void 0,
|
|
278
560
|
skills
|
|
279
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]);
|
|
280
608
|
const turns = React3.useMemo(() => groupIntoTurns(chat.messages), [chat.messages]);
|
|
281
|
-
const toolNames =
|
|
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
|
+
);
|
|
282
665
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink$1.Box, { flexDirection: "column", gap: 1, children: [
|
|
283
666
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
284
667
|
ink.StatusHeader,
|
|
@@ -287,7 +670,8 @@ function ChatApp(options) {
|
|
|
287
670
|
model: runtime.model,
|
|
288
671
|
mode: runtime.mode,
|
|
289
672
|
tools: toolNames,
|
|
290
|
-
messageCount: chat.messages.length
|
|
673
|
+
messageCount: chat.messages.length,
|
|
674
|
+
sessionId: options.sessionId
|
|
291
675
|
}
|
|
292
676
|
),
|
|
293
677
|
/* @__PURE__ */ jsxRuntime.jsx(ink.ChatContainer, { children: turns.map((turn, turnIdx) => {
|
|
@@ -304,7 +688,18 @@ function ChatApp(options) {
|
|
|
304
688
|
assistantSteps
|
|
305
689
|
] }) : null,
|
|
306
690
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Message, { message }),
|
|
307
|
-
message.toolCalls?.map((toolCall) => /* @__PURE__ */ jsxRuntime.
|
|
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))
|
|
308
703
|
] }, message.id);
|
|
309
704
|
}) }, `turn-${turnIdx}`);
|
|
310
705
|
}) }),
|
|
@@ -315,9 +710,38 @@ function ChatApp(options) {
|
|
|
315
710
|
label: toolNames.length > 0 ? "agent working" : "thinking"
|
|
316
711
|
}
|
|
317
712
|
),
|
|
318
|
-
/* @__PURE__ */ jsxRuntime.
|
|
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
|
+
)
|
|
319
730
|
] });
|
|
320
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
|
+
}
|
|
321
745
|
function renderChatHeader(options) {
|
|
322
746
|
const runtime = resolveChatProvider(options);
|
|
323
747
|
const parts = [`provider=${runtime.provider}`];
|
|
@@ -426,7 +850,7 @@ function reactStarter(ctx) {
|
|
|
426
850
|
return {
|
|
427
851
|
"package.json": JSON.stringify(
|
|
428
852
|
{
|
|
429
|
-
name:
|
|
853
|
+
name: path3__default.default.basename(ctx.template === "react" ? "agentskit-react-app" : "agentskit-app"),
|
|
430
854
|
private: true,
|
|
431
855
|
type: "module",
|
|
432
856
|
scripts: {
|
|
@@ -795,8 +1219,8 @@ async function writeStarterProject(options) {
|
|
|
795
1219
|
await promises.mkdir(options.targetDir, { recursive: true });
|
|
796
1220
|
await Promise.all(
|
|
797
1221
|
Object.entries(files).map(async ([relativePath, content]) => {
|
|
798
|
-
const absolutePath =
|
|
799
|
-
await promises.mkdir(
|
|
1222
|
+
const absolutePath = path3__default.default.join(options.targetDir, relativePath);
|
|
1223
|
+
await promises.mkdir(path3__default.default.dirname(absolutePath), { recursive: true });
|
|
800
1224
|
await promises.writeFile(absolutePath, content, "utf8");
|
|
801
1225
|
})
|
|
802
1226
|
);
|
|
@@ -812,7 +1236,7 @@ ${kleur__default.default.bold().green("\u25B2")} ${kleur__default.default.bold("
|
|
|
812
1236
|
default: defaults.dir ?? "agentskit-app",
|
|
813
1237
|
validate: (value) => {
|
|
814
1238
|
if (!value.trim()) return "A directory name is required.";
|
|
815
|
-
const abs =
|
|
1239
|
+
const abs = path3__default.default.resolve(process.cwd(), value);
|
|
816
1240
|
if (fs.existsSync(abs)) return `${value} already exists. Pick a different name.`;
|
|
817
1241
|
return true;
|
|
818
1242
|
}
|
|
@@ -890,7 +1314,7 @@ ${kleur__default.default.bold().green("\u25B2")} ${kleur__default.default.bold("
|
|
|
890
1314
|
return {
|
|
891
1315
|
cancelled: false,
|
|
892
1316
|
options: {
|
|
893
|
-
targetDir:
|
|
1317
|
+
targetDir: path3__default.default.resolve(process.cwd(), targetDir),
|
|
894
1318
|
template,
|
|
895
1319
|
provider,
|
|
896
1320
|
tools,
|
|
@@ -907,7 +1331,7 @@ ${kleur__default.default.bold().green("\u25B2")} ${kleur__default.default.bold("
|
|
|
907
1331
|
}
|
|
908
1332
|
}
|
|
909
1333
|
function printNextSteps(options) {
|
|
910
|
-
const dir =
|
|
1334
|
+
const dir = path3__default.default.relative(process.cwd(), options.targetDir) || ".";
|
|
911
1335
|
const pm = options.packageManager ?? "pnpm";
|
|
912
1336
|
const installCmd = pm === "npm" ? "npm install" : `${pm} install`;
|
|
913
1337
|
const runCmd = pm === "npm" ? "npm run dev" : `${pm} dev`;
|
|
@@ -1121,17 +1545,17 @@ async function checkNodeVersion() {
|
|
|
1121
1545
|
}
|
|
1122
1546
|
async function checkPnpm() {
|
|
1123
1547
|
const cwd = process.cwd();
|
|
1124
|
-
const hasPnpm = fs.existsSync(
|
|
1548
|
+
const hasPnpm = fs.existsSync(path3.join(cwd, "pnpm-lock.yaml")) || fs.existsSync(path3.join(cwd, "pnpm-workspace.yaml"));
|
|
1125
1549
|
if (hasPnpm) {
|
|
1126
1550
|
return { status: "pass", name: "Package manager", detail: "pnpm detected (lockfile)" };
|
|
1127
1551
|
}
|
|
1128
|
-
if (fs.existsSync(
|
|
1552
|
+
if (fs.existsSync(path3.join(cwd, "package-lock.json"))) {
|
|
1129
1553
|
return { status: "warn", name: "Package manager", detail: "npm detected \u2014 pnpm recommended for monorepo workflows" };
|
|
1130
1554
|
}
|
|
1131
|
-
if (fs.existsSync(
|
|
1555
|
+
if (fs.existsSync(path3.join(cwd, "yarn.lock"))) {
|
|
1132
1556
|
return { status: "pass", name: "Package manager", detail: "yarn detected" };
|
|
1133
1557
|
}
|
|
1134
|
-
if (fs.existsSync(
|
|
1558
|
+
if (fs.existsSync(path3.join(cwd, "bun.lock")) || fs.existsSync(path3.join(cwd, "bun.lockb"))) {
|
|
1135
1559
|
return { status: "pass", name: "Package manager", detail: "bun detected" };
|
|
1136
1560
|
}
|
|
1137
1561
|
return {
|
|
@@ -1141,7 +1565,7 @@ async function checkPnpm() {
|
|
|
1141
1565
|
};
|
|
1142
1566
|
}
|
|
1143
1567
|
async function checkPackageJson() {
|
|
1144
|
-
const path4 =
|
|
1568
|
+
const path4 = path3.join(process.cwd(), "package.json");
|
|
1145
1569
|
if (!fs.existsSync(path4)) {
|
|
1146
1570
|
return {
|
|
1147
1571
|
status: "warn",
|
|
@@ -1366,7 +1790,7 @@ var DEFAULT_IGNORE = [
|
|
|
1366
1790
|
"**/*.spec.ts"
|
|
1367
1791
|
];
|
|
1368
1792
|
function startDev(options) {
|
|
1369
|
-
const entry =
|
|
1793
|
+
const entry = path3.resolve(process.cwd(), options.entry);
|
|
1370
1794
|
if (!fs.existsSync(entry)) {
|
|
1371
1795
|
throw new Error(`Entry file not found: ${entry}`);
|
|
1372
1796
|
}
|
|
@@ -1398,7 +1822,7 @@ function startDev(options) {
|
|
|
1398
1822
|
};
|
|
1399
1823
|
const startChild = () => {
|
|
1400
1824
|
restartCount++;
|
|
1401
|
-
banner(`\u25B8 starting ${kleur__default.default.bold(
|
|
1825
|
+
banner(`\u25B8 starting ${kleur__default.default.bold(path3.basename(entry))} (restart #${restartCount - 1})`, "cyan");
|
|
1402
1826
|
const c = spawnFn(cmd, baseArgs);
|
|
1403
1827
|
child = c;
|
|
1404
1828
|
c.stdout?.on("data", (d) => stdout.write(d));
|
|
@@ -1522,34 +1946,87 @@ async function startTunnel(options) {
|
|
|
1522
1946
|
// src/commands.ts
|
|
1523
1947
|
function mergeWithConfig(options, config) {
|
|
1524
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;
|
|
1525
1951
|
return {
|
|
1526
1952
|
...options,
|
|
1527
1953
|
// Config defaults — only apply if CLI flag wasn't set
|
|
1528
|
-
provider: options.provider !== "demo" ? options.provider :
|
|
1529
|
-
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
|
|
1530
1962
|
};
|
|
1531
1963
|
}
|
|
1532
1964
|
function createCli() {
|
|
1533
1965
|
const program = new commander.Command();
|
|
1534
1966
|
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
|
|
1535
|
-
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
|
+
}
|
|
1536
1984
|
const config = options.config !== false ? await loadConfig() : void 0;
|
|
1537
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
|
+
}
|
|
1538
1997
|
const chatOptions = {
|
|
1539
1998
|
apiKey: merged.apiKey ?? options.apiKey,
|
|
1540
1999
|
baseUrl: merged.baseUrl ?? options.baseUrl,
|
|
1541
2000
|
provider: merged.provider,
|
|
1542
2001
|
model: merged.model,
|
|
1543
|
-
system: options.system,
|
|
1544
|
-
memoryPath:
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
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,
|
|
1548
2008
|
agentsKitConfig: config
|
|
1549
2009
|
};
|
|
1550
2010
|
process.stdout.write(`${renderChatHeader(chatOptions)}
|
|
1551
2011
|
`);
|
|
1552
|
-
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
|
+
}
|
|
1553
2030
|
});
|
|
1554
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) => {
|
|
1555
2032
|
const task = options.task ?? positionalTask;
|
|
@@ -1577,7 +2054,7 @@ function createCli() {
|
|
|
1577
2054
|
if (isCi) {
|
|
1578
2055
|
const template = rawOptions.template ?? "react";
|
|
1579
2056
|
resolved = {
|
|
1580
|
-
targetDir:
|
|
2057
|
+
targetDir: path3__default.default.resolve(process.cwd(), rawOptions.dir),
|
|
1581
2058
|
template,
|
|
1582
2059
|
provider: rawOptions.provider ?? "demo",
|
|
1583
2060
|
tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
|
|
@@ -1597,7 +2074,7 @@ function createCli() {
|
|
|
1597
2074
|
await writeStarterProject(resolved);
|
|
1598
2075
|
if (isCi) {
|
|
1599
2076
|
process.stdout.write(
|
|
1600
|
-
`Created ${resolved.template} starter in ${
|
|
2077
|
+
`Created ${resolved.template} starter in ${path3__default.default.relative(process.cwd(), resolved.targetDir) || "."}
|
|
1601
2078
|
`
|
|
1602
2079
|
);
|
|
1603
2080
|
} else {
|
|
@@ -1638,6 +2115,43 @@ function createCli() {
|
|
|
1638
2115
|
process.exit(1);
|
|
1639
2116
|
}
|
|
1640
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
|
+
});
|
|
1641
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) => {
|
|
1642
2156
|
const portNum = Number(port);
|
|
1643
2157
|
if (Number.isNaN(portNum) || portNum < 1 || portNum > 65535) {
|