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