@agentskit/cli 0.6.0 → 0.7.1
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 +6 -1
- package/dist/bin.cjs +2063 -1271
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-V7E4HWTG.js → chunk-72XFU2X2.js} +1232 -433
- package/dist/chunk-72XFU2X2.js.map +1 -0
- package/dist/index.cjs +2096 -1270
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +420 -2
- package/dist/index.d.ts +420 -2
- package/dist/index.js +1 -1
- package/package.json +11 -10
- package/dist/chunk-V7E4HWTG.js.map +0 -1
package/dist/bin.cjs
CHANGED
|
@@ -1,44 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var React3 = require('react');
|
|
5
|
-
var ink = require('ink');
|
|
6
4
|
var commander = require('commander');
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var path3 = require('path');
|
|
5
|
+
var React2 = require('react');
|
|
6
|
+
var ink = require('ink');
|
|
10
7
|
var promises = require('fs/promises');
|
|
8
|
+
var os = require('os');
|
|
9
|
+
var path = require('path');
|
|
11
10
|
var ink$1 = require('@agentskit/ink');
|
|
12
11
|
var adapters = require('@agentskit/adapters');
|
|
12
|
+
var crypto = require('crypto');
|
|
13
|
+
var fs = require('fs');
|
|
13
14
|
var tools = require('@agentskit/tools');
|
|
14
15
|
var skills = require('@agentskit/skills');
|
|
15
16
|
var memory = require('@agentskit/memory');
|
|
16
|
-
var
|
|
17
|
+
var child_process = require('child_process');
|
|
17
18
|
var jsxRuntime = require('react/jsx-runtime');
|
|
19
|
+
var url = require('url');
|
|
20
|
+
var runtime = require('@agentskit/runtime');
|
|
18
21
|
var prompts = require('@inquirer/prompts');
|
|
19
22
|
var kleur = require('kleur');
|
|
20
|
-
var runtime = require('@agentskit/runtime');
|
|
21
|
-
var child_process = require('child_process');
|
|
22
23
|
var chokidar = require('chokidar');
|
|
24
|
+
var rag = require('@agentskit/rag');
|
|
23
25
|
|
|
24
26
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
25
27
|
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
+
var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
29
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
28
30
|
var kleur__default = /*#__PURE__*/_interopDefault(kleur);
|
|
29
31
|
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
30
32
|
|
|
31
|
-
async function loadJsonConfig(
|
|
33
|
+
async function loadJsonConfig(path5) {
|
|
32
34
|
try {
|
|
33
|
-
const raw = await promises.readFile(
|
|
35
|
+
const raw = await promises.readFile(path5, "utf8");
|
|
34
36
|
return JSON.parse(raw);
|
|
35
37
|
} catch {
|
|
36
38
|
return void 0;
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
|
-
async function loadTsConfig(
|
|
41
|
+
async function loadTsConfig(path5) {
|
|
40
42
|
try {
|
|
41
|
-
const mod = await import(
|
|
43
|
+
const mod = await import(path5);
|
|
42
44
|
return mod.default ?? mod;
|
|
43
45
|
} catch {
|
|
44
46
|
return void 0;
|
|
@@ -46,7 +48,7 @@ async function loadTsConfig(path4) {
|
|
|
46
48
|
}
|
|
47
49
|
async function loadPackageJsonConfig(dir) {
|
|
48
50
|
try {
|
|
49
|
-
const raw = await promises.readFile(
|
|
51
|
+
const raw = await promises.readFile(path.join(dir, "package.json"), "utf8");
|
|
50
52
|
const pkg = JSON.parse(raw);
|
|
51
53
|
if (pkg.agentskit && typeof pkg.agentskit === "object") {
|
|
52
54
|
return pkg.agentskit;
|
|
@@ -70,20 +72,20 @@ function mergeConfigs(base, override) {
|
|
|
70
72
|
};
|
|
71
73
|
}
|
|
72
74
|
async function loadLocalConfig(cwd) {
|
|
73
|
-
const tsConfig = await loadTsConfig(
|
|
75
|
+
const tsConfig = await loadTsConfig(path.join(cwd, ".agentskit.config.ts"));
|
|
74
76
|
if (tsConfig) return tsConfig;
|
|
75
|
-
const jsonConfig = await loadJsonConfig(
|
|
77
|
+
const jsonConfig = await loadJsonConfig(path.join(cwd, ".agentskit.config.json"));
|
|
76
78
|
if (jsonConfig) return jsonConfig;
|
|
77
79
|
return await loadPackageJsonConfig(cwd);
|
|
78
80
|
}
|
|
79
81
|
async function loadGlobalConfig(home) {
|
|
80
|
-
const globalDir =
|
|
81
|
-
const tsConfig = await loadTsConfig(
|
|
82
|
+
const globalDir = path.join(os.homedir(), ".agentskit");
|
|
83
|
+
const tsConfig = await loadTsConfig(path.join(globalDir, "config.ts"));
|
|
82
84
|
if (tsConfig) return tsConfig;
|
|
83
|
-
return await loadJsonConfig(
|
|
85
|
+
return await loadJsonConfig(path.join(globalDir, "config.json"));
|
|
84
86
|
}
|
|
85
87
|
async function loadConfig(options) {
|
|
86
|
-
const cwd =
|
|
88
|
+
const cwd = path.resolve(process.cwd());
|
|
87
89
|
const global = await loadGlobalConfig();
|
|
88
90
|
const local = await loadLocalConfig(cwd);
|
|
89
91
|
return mergeConfigs(global, local);
|
|
@@ -147,7 +149,7 @@ function createDemoAdapter(provider, model) {
|
|
|
147
149
|
].join(" ");
|
|
148
150
|
for (const chunk of reply.match(/.{1,18}/g) ?? []) {
|
|
149
151
|
if (cancelled) return;
|
|
150
|
-
await new Promise((
|
|
152
|
+
await new Promise((resolve4) => setTimeout(resolve4, 35));
|
|
151
153
|
yield { type: "text", content: chunk };
|
|
152
154
|
}
|
|
153
155
|
yield { type: "done" };
|
|
@@ -198,87 +200,13 @@ function resolveChatProvider(options) {
|
|
|
198
200
|
summary: `${entry.label} live adapter`
|
|
199
201
|
};
|
|
200
202
|
}
|
|
201
|
-
var
|
|
202
|
-
researcher: skills.researcher,
|
|
203
|
-
coder: skills.coder,
|
|
204
|
-
planner: skills.planner,
|
|
205
|
-
critic: skills.critic,
|
|
206
|
-
summarizer: skills.summarizer
|
|
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
|
-
}
|
|
224
|
-
function resolveTools(toolNames) {
|
|
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)) {
|
|
230
|
-
switch (name) {
|
|
231
|
-
case "web_search":
|
|
232
|
-
case "fetch_url":
|
|
233
|
-
case "filesystem":
|
|
234
|
-
case "shell":
|
|
235
|
-
tools.push(...instantiate(name));
|
|
236
|
-
break;
|
|
237
|
-
default:
|
|
238
|
-
process.stderr.write(`Unknown tool: ${name}
|
|
239
|
-
`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return tools;
|
|
243
|
-
}
|
|
244
|
-
function resolveSkill(skillName) {
|
|
245
|
-
if (!skillName) return void 0;
|
|
246
|
-
const skill = skillRegistry[skillName.trim()];
|
|
247
|
-
if (!skill) {
|
|
248
|
-
process.stderr.write(`Unknown skill: ${skillName}
|
|
249
|
-
`);
|
|
250
|
-
return void 0;
|
|
251
|
-
}
|
|
252
|
-
return skill;
|
|
253
|
-
}
|
|
254
|
-
function resolveSkills(skillNames) {
|
|
255
|
-
if (!skillNames) return void 0;
|
|
256
|
-
const names = skillNames.split(",").map((s) => s.trim());
|
|
257
|
-
const resolved = names.map((n) => skillRegistry[n]).filter(Boolean);
|
|
258
|
-
if (resolved.length === 0) {
|
|
259
|
-
process.stderr.write(`No valid skills found in: ${skillNames}
|
|
260
|
-
`);
|
|
261
|
-
return void 0;
|
|
262
|
-
}
|
|
263
|
-
if (resolved.length === 1) return resolved[0];
|
|
264
|
-
return skills.composeSkills(...resolved);
|
|
265
|
-
}
|
|
266
|
-
function resolveMemory(backend, memoryPath) {
|
|
267
|
-
switch (backend) {
|
|
268
|
-
case "sqlite":
|
|
269
|
-
return memory.sqliteChatMemory({ path: memoryPath.replace(/\.json$/, ".db") });
|
|
270
|
-
case "file":
|
|
271
|
-
default:
|
|
272
|
-
return memory.fileChatMemory(memoryPath);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
var ROOT = path3.join(os.homedir(), ".agentskit", "sessions");
|
|
203
|
+
var ROOT = path.join(os.homedir(), ".agentskit", "sessions");
|
|
276
204
|
var META_SUFFIX = ".meta.json";
|
|
277
205
|
function cwdHash(cwd = process.cwd()) {
|
|
278
206
|
return crypto.createHash("sha256").update(cwd).digest("hex").slice(0, 12);
|
|
279
207
|
}
|
|
280
208
|
function dirFor(cwd = process.cwd()) {
|
|
281
|
-
return
|
|
209
|
+
return path.join(ROOT, cwdHash(cwd));
|
|
282
210
|
}
|
|
283
211
|
function ensureDir(dir) {
|
|
284
212
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
@@ -290,16 +218,16 @@ function generateSessionId() {
|
|
|
290
218
|
}
|
|
291
219
|
function sessionFilePath(id, cwd = process.cwd()) {
|
|
292
220
|
ensureDir(dirFor(cwd));
|
|
293
|
-
return
|
|
221
|
+
return path.join(dirFor(cwd), `${id}.json`);
|
|
294
222
|
}
|
|
295
223
|
function metaPath(id, cwd = process.cwd()) {
|
|
296
|
-
return
|
|
224
|
+
return path.join(dirFor(cwd), `${id}${META_SUFFIX}`);
|
|
297
225
|
}
|
|
298
226
|
function readMeta(id, cwd = process.cwd()) {
|
|
299
|
-
const
|
|
300
|
-
if (!fs.existsSync(
|
|
227
|
+
const path5 = metaPath(id, cwd);
|
|
228
|
+
if (!fs.existsSync(path5)) return null;
|
|
301
229
|
try {
|
|
302
|
-
return JSON.parse(fs.readFileSync(
|
|
230
|
+
return JSON.parse(fs.readFileSync(path5, "utf8"));
|
|
303
231
|
} catch {
|
|
304
232
|
return null;
|
|
305
233
|
}
|
|
@@ -323,7 +251,7 @@ function listSessions(cwd = process.cwd()) {
|
|
|
323
251
|
if (!entry.endsWith(".json") || entry.endsWith(META_SUFFIX)) continue;
|
|
324
252
|
const id = entry.replace(/\.json$/, "");
|
|
325
253
|
const meta = readMeta(id, cwd);
|
|
326
|
-
const file =
|
|
254
|
+
const file = path.join(dir, entry);
|
|
327
255
|
if (meta) {
|
|
328
256
|
records.push({ metadata: meta, file });
|
|
329
257
|
} else {
|
|
@@ -349,11 +277,41 @@ function findLatestSession(cwd = process.cwd()) {
|
|
|
349
277
|
return all[0] ?? null;
|
|
350
278
|
}
|
|
351
279
|
function findSession(id, cwd = process.cwd()) {
|
|
352
|
-
const
|
|
280
|
+
const all = listSessions(cwd);
|
|
281
|
+
const exact = all.find((s) => s.metadata.id === id || s.metadata.label === id);
|
|
353
282
|
if (exact) return exact;
|
|
354
|
-
const prefix =
|
|
283
|
+
const prefix = all.find((s) => s.metadata.id.startsWith(id));
|
|
355
284
|
return prefix ?? null;
|
|
356
285
|
}
|
|
286
|
+
function renameSession(id, label, cwd = process.cwd()) {
|
|
287
|
+
const record = findSession(id, cwd);
|
|
288
|
+
if (!record) throw new Error(`No session matching "${id}".`);
|
|
289
|
+
const next = { ...record.metadata, label, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
290
|
+
writeSessionMeta(next, cwd);
|
|
291
|
+
return next;
|
|
292
|
+
}
|
|
293
|
+
function forkSession(id, cwd = process.cwd()) {
|
|
294
|
+
const record = findSession(id, cwd);
|
|
295
|
+
if (!record) throw new Error(`No session matching "${id}".`);
|
|
296
|
+
const newId = generateSessionId();
|
|
297
|
+
const newFile = sessionFilePath(newId, cwd);
|
|
298
|
+
if (fs.existsSync(record.file)) {
|
|
299
|
+
fs.writeFileSync(newFile, fs.readFileSync(record.file, "utf8"));
|
|
300
|
+
}
|
|
301
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
302
|
+
writeSessionMeta(
|
|
303
|
+
{
|
|
304
|
+
...record.metadata,
|
|
305
|
+
id: newId,
|
|
306
|
+
createdAt: now,
|
|
307
|
+
updatedAt: now,
|
|
308
|
+
forkedFrom: record.metadata.id,
|
|
309
|
+
label: void 0
|
|
310
|
+
},
|
|
311
|
+
cwd
|
|
312
|
+
);
|
|
313
|
+
return { id: newId, file: newFile, isNew: true };
|
|
314
|
+
}
|
|
357
315
|
function resolveSession(input2) {
|
|
358
316
|
const cwd = input2.cwd ?? process.cwd();
|
|
359
317
|
if (input2.explicitPath) {
|
|
@@ -379,6 +337,40 @@ function resolveSession(input2) {
|
|
|
379
337
|
return { id, file: sessionFilePath(id, cwd), isNew: true };
|
|
380
338
|
}
|
|
381
339
|
|
|
340
|
+
// src/extensibility/telemetry/pricing.ts
|
|
341
|
+
var builtinPricing = {
|
|
342
|
+
"gpt-4o": { inputPerM: 2.5, outputPerM: 10 },
|
|
343
|
+
"gpt-4o-mini": { inputPerM: 0.15, outputPerM: 0.6 },
|
|
344
|
+
"gpt-4.1": { inputPerM: 2, outputPerM: 8 },
|
|
345
|
+
"gpt-4.1-mini": { inputPerM: 0.4, outputPerM: 1.6 },
|
|
346
|
+
"claude-opus-4": { inputPerM: 15, outputPerM: 75 },
|
|
347
|
+
"claude-sonnet-4": { inputPerM: 3, outputPerM: 15 },
|
|
348
|
+
"claude-haiku-4": { inputPerM: 0.8, outputPerM: 4 },
|
|
349
|
+
"gemini-2.5-pro": { inputPerM: 1.25, outputPerM: 10 },
|
|
350
|
+
"gemini-2.5-flash": { inputPerM: 0.3, outputPerM: 2.5 }
|
|
351
|
+
};
|
|
352
|
+
var customPricing = {};
|
|
353
|
+
function getPricing(model) {
|
|
354
|
+
if (!model) return void 0;
|
|
355
|
+
if (customPricing[model]) return customPricing[model];
|
|
356
|
+
if (builtinPricing[model]) return builtinPricing[model];
|
|
357
|
+
const short = model.includes("/") ? model.split("/").pop() : model;
|
|
358
|
+
return customPricing[short] ?? builtinPricing[short];
|
|
359
|
+
}
|
|
360
|
+
function computeCost(model, usage) {
|
|
361
|
+
if (!model) return void 0;
|
|
362
|
+
const pricing = getPricing(model);
|
|
363
|
+
if (!pricing) return void 0;
|
|
364
|
+
const inputUsd = usage.promptTokens / 1e6 * pricing.inputPerM;
|
|
365
|
+
const outputUsd = usage.completionTokens / 1e6 * pricing.outputPerM;
|
|
366
|
+
return {
|
|
367
|
+
model,
|
|
368
|
+
inputUsd,
|
|
369
|
+
outputUsd,
|
|
370
|
+
totalUsd: inputUsd + outputUsd
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
382
374
|
// src/slash-commands.ts
|
|
383
375
|
function parseSlashCommand(input2) {
|
|
384
376
|
if (!input2.startsWith("/")) return null;
|
|
@@ -501,6 +493,89 @@ ${lines.join("\n")}`, "info");
|
|
|
501
493
|
ctx.feedback("History cleared.", "success");
|
|
502
494
|
}
|
|
503
495
|
},
|
|
496
|
+
{
|
|
497
|
+
name: "usage",
|
|
498
|
+
description: "Show the cumulative token usage for this session.",
|
|
499
|
+
run(ctx) {
|
|
500
|
+
const usage = ctx.chat.usage;
|
|
501
|
+
if (!usage || usage.totalTokens === 0) {
|
|
502
|
+
ctx.feedback("No usage reported yet for this session.", "info");
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
ctx.feedback(
|
|
506
|
+
`Tokens \u2014 prompt=${usage.promptTokens} completion=${usage.completionTokens} total=${usage.totalTokens}`,
|
|
507
|
+
"info"
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
name: "cost",
|
|
513
|
+
description: "Estimate the cost so far for the current model.",
|
|
514
|
+
run(ctx) {
|
|
515
|
+
const usage = ctx.chat.usage;
|
|
516
|
+
const model = ctx.runtime.model;
|
|
517
|
+
if (!usage || usage.totalTokens === 0) {
|
|
518
|
+
ctx.feedback("No usage reported yet for this session.", "info");
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const cost = computeCost(model, usage);
|
|
522
|
+
if (!cost) {
|
|
523
|
+
ctx.feedback(
|
|
524
|
+
`No pricing registered for model "${model ?? "unset"}". Register with registerPricing() or provide a known model name.`,
|
|
525
|
+
"warn"
|
|
526
|
+
);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
ctx.feedback(
|
|
530
|
+
`$${cost.totalUsd.toFixed(4)} total (in=$${cost.inputUsd.toFixed(4)} out=$${cost.outputUsd.toFixed(4)} model=${cost.model})`,
|
|
531
|
+
"info"
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
name: "rename",
|
|
537
|
+
description: "Attach a human-readable label to the current session.",
|
|
538
|
+
usage: "/rename <label>",
|
|
539
|
+
run(ctx, args) {
|
|
540
|
+
const label = args.trim();
|
|
541
|
+
const sessionId = ctx.runtime.sessionId;
|
|
542
|
+
if (!sessionId || sessionId === "custom") {
|
|
543
|
+
ctx.feedback("Rename is only available for managed sessions.", "warn");
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (!label) {
|
|
547
|
+
ctx.feedback("Usage: /rename <label>", "warn");
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
try {
|
|
551
|
+
renameSession(sessionId, label);
|
|
552
|
+
ctx.feedback(`Session labeled "${label}".`, "success");
|
|
553
|
+
} catch (err) {
|
|
554
|
+
ctx.feedback(`/rename failed: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
name: "fork",
|
|
560
|
+
description: "Branch a copy of the current session. Does not switch to it.",
|
|
561
|
+
run(ctx) {
|
|
562
|
+
const sessionId = ctx.runtime.sessionId;
|
|
563
|
+
if (!sessionId || sessionId === "custom") {
|
|
564
|
+
ctx.feedback("Fork is only available for managed sessions.", "warn");
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
try {
|
|
568
|
+
const result = forkSession(sessionId);
|
|
569
|
+
ctx.feedback(
|
|
570
|
+
`Forked into ${result.id}. Resume with:
|
|
571
|
+
agentskit chat --resume ${result.id}`,
|
|
572
|
+
"success"
|
|
573
|
+
);
|
|
574
|
+
} catch (err) {
|
|
575
|
+
ctx.feedback(`/fork failed: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
},
|
|
504
579
|
{
|
|
505
580
|
name: "exit",
|
|
506
581
|
aliases: ["quit", "q"],
|
|
@@ -510,57 +585,160 @@ ${lines.join("\n")}`, "info");
|
|
|
510
585
|
}
|
|
511
586
|
}
|
|
512
587
|
];
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
588
|
+
var skillRegistry = {
|
|
589
|
+
researcher: skills.researcher,
|
|
590
|
+
coder: skills.coder,
|
|
591
|
+
planner: skills.planner,
|
|
592
|
+
critic: skills.critic,
|
|
593
|
+
summarizer: skills.summarizer
|
|
594
|
+
};
|
|
595
|
+
function instantiate(kind) {
|
|
596
|
+
switch (kind) {
|
|
597
|
+
case "web_search":
|
|
598
|
+
return [tools.webSearch()];
|
|
599
|
+
case "fetch_url":
|
|
600
|
+
return [tools.fetchUrl()];
|
|
601
|
+
case "filesystem":
|
|
602
|
+
return tools.filesystem({ basePath: process.cwd() });
|
|
603
|
+
case "shell":
|
|
604
|
+
return [tools.shell({ timeout: 3e4 })];
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function gateTool(tool) {
|
|
608
|
+
if (tool.requiresConfirmation === false) return tool;
|
|
609
|
+
return { ...tool, requiresConfirmation: true };
|
|
610
|
+
}
|
|
611
|
+
function resolveTools(toolNames) {
|
|
612
|
+
if (!toolNames) {
|
|
613
|
+
return [...instantiate("web_search"), ...instantiate("fetch_url")].map(gateTool);
|
|
614
|
+
}
|
|
615
|
+
const tools = [];
|
|
616
|
+
for (const name of toolNames.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
617
|
+
switch (name) {
|
|
618
|
+
case "web_search":
|
|
619
|
+
case "fetch_url":
|
|
620
|
+
case "filesystem":
|
|
621
|
+
case "shell":
|
|
622
|
+
tools.push(...instantiate(name));
|
|
623
|
+
break;
|
|
624
|
+
default:
|
|
625
|
+
process.stderr.write(`Unknown tool: ${name}
|
|
626
|
+
`);
|
|
526
627
|
}
|
|
527
628
|
}
|
|
528
|
-
|
|
529
|
-
return turns;
|
|
629
|
+
return tools;
|
|
530
630
|
}
|
|
531
|
-
function
|
|
532
|
-
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
631
|
+
function resolveSkill(skillName) {
|
|
632
|
+
if (!skillName) return void 0;
|
|
633
|
+
const skill = skillRegistry[skillName.trim()];
|
|
634
|
+
if (!skill) {
|
|
635
|
+
process.stderr.write(`Unknown skill: ${skillName}
|
|
636
|
+
`);
|
|
637
|
+
return void 0;
|
|
638
|
+
}
|
|
639
|
+
return skill;
|
|
640
|
+
}
|
|
641
|
+
function resolveSkills(skillNames) {
|
|
642
|
+
if (!skillNames) return void 0;
|
|
643
|
+
const names = skillNames.split(",").map((s) => s.trim());
|
|
644
|
+
const resolved = names.map((n) => skillRegistry[n]).filter(Boolean);
|
|
645
|
+
if (resolved.length === 0) {
|
|
646
|
+
process.stderr.write(`No valid skills found in: ${skillNames}
|
|
647
|
+
`);
|
|
648
|
+
return void 0;
|
|
649
|
+
}
|
|
650
|
+
if (resolved.length === 1) return resolved[0];
|
|
651
|
+
return skills.composeSkills(...resolved);
|
|
652
|
+
}
|
|
653
|
+
function resolveMemory(backend, memoryPath) {
|
|
654
|
+
switch (backend) {
|
|
655
|
+
case "sqlite":
|
|
656
|
+
return memory.sqliteChatMemory({ path: memoryPath.replace(/\.json$/, ".db") });
|
|
657
|
+
case "file":
|
|
658
|
+
default:
|
|
659
|
+
return memory.fileChatMemory(memoryPath);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/extensibility/permissions/policy.ts
|
|
664
|
+
function evaluatePolicy(policy, toolName) {
|
|
665
|
+
if (policy.mode === "bypassPermissions") return "allow";
|
|
666
|
+
if (policy.mode === "plan") return "ask";
|
|
667
|
+
for (const rule of policy.rules) {
|
|
668
|
+
if (matchesRule(rule, toolName)) return rule.action;
|
|
669
|
+
}
|
|
670
|
+
if (policy.mode === "acceptEdits" && /^(fs_write|edit|write_file)/.test(toolName)) {
|
|
671
|
+
return "allow";
|
|
672
|
+
}
|
|
673
|
+
return "ask";
|
|
674
|
+
}
|
|
675
|
+
function matchesRule(rule, toolName) {
|
|
676
|
+
if (rule.tool instanceof RegExp) return rule.tool.test(toolName);
|
|
677
|
+
const str = rule.tool;
|
|
678
|
+
if (str.startsWith("re:")) return new RegExp(str.slice(3)).test(toolName);
|
|
679
|
+
return str === toolName;
|
|
680
|
+
}
|
|
681
|
+
function applyPolicyToTool(policy, tool) {
|
|
682
|
+
const action = evaluatePolicy(policy, tool.name);
|
|
683
|
+
if (action === "deny") return null;
|
|
684
|
+
if (action === "allow") return { ...tool, requiresConfirmation: false };
|
|
685
|
+
return { ...tool, requiresConfirmation: true };
|
|
686
|
+
}
|
|
687
|
+
function applyPolicyToTools(policy, tools) {
|
|
688
|
+
const out = [];
|
|
689
|
+
for (const tool of tools) {
|
|
690
|
+
const gated = applyPolicyToTool(policy, tool);
|
|
691
|
+
if (gated) out.push(gated);
|
|
692
|
+
}
|
|
693
|
+
return out;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// src/runtime/use-runtime.ts
|
|
697
|
+
function useRuntime(options) {
|
|
698
|
+
const [provider, setProvider] = React2.useState(options.provider);
|
|
699
|
+
const [model, setModel] = React2.useState(options.model);
|
|
700
|
+
const [apiKey, setApiKey] = React2.useState(options.apiKey);
|
|
701
|
+
const [baseUrl, setBaseUrl] = React2.useState(options.baseUrl);
|
|
702
|
+
const [toolsFlag, setToolsFlag] = React2.useState(options.tools);
|
|
703
|
+
const [skillFlag, setSkillFlag] = React2.useState(options.skill);
|
|
704
|
+
const runtime = React2.useMemo(
|
|
539
705
|
() => resolveChatProvider({ provider, model, apiKey, baseUrl }),
|
|
540
706
|
[provider, model, apiKey, baseUrl]
|
|
541
707
|
);
|
|
542
|
-
const memory =
|
|
708
|
+
const memory = React2.useMemo(
|
|
543
709
|
() => resolveMemory(options.memoryBackend, options.memoryPath ?? ".agentskit-history.json"),
|
|
544
710
|
[options.memoryPath, options.memoryBackend]
|
|
545
711
|
);
|
|
546
|
-
const tools =
|
|
547
|
-
|
|
712
|
+
const tools = React2.useMemo(() => {
|
|
713
|
+
const resolved = resolveTools(toolsFlag);
|
|
714
|
+
if (!options.permissionPolicy) return resolved;
|
|
715
|
+
return applyPolicyToTools(options.permissionPolicy, resolved);
|
|
716
|
+
}, [toolsFlag, options.permissionPolicy]);
|
|
717
|
+
const skills = React2.useMemo(() => {
|
|
548
718
|
if (!skillFlag) return void 0;
|
|
549
719
|
const names = skillFlag.split(",").map((s) => s.trim());
|
|
550
720
|
const resolved = names.map((n) => skillRegistry[n]).filter(Boolean);
|
|
551
721
|
if (resolved.length === 0) return void 0;
|
|
552
722
|
return resolved;
|
|
553
723
|
}, [skillFlag]);
|
|
554
|
-
|
|
555
|
-
|
|
724
|
+
return {
|
|
725
|
+
runtime,
|
|
556
726
|
memory,
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
727
|
+
tools,
|
|
728
|
+
skills,
|
|
729
|
+
state: { provider, model, apiKey, baseUrl, toolsFlag, skillFlag },
|
|
730
|
+
setProvider,
|
|
731
|
+
setModel,
|
|
732
|
+
setApiKey,
|
|
733
|
+
setBaseUrl,
|
|
734
|
+
setToolsFlag,
|
|
735
|
+
setSkillFlag
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
function useToolPermissions(chat) {
|
|
739
|
+
const [sessionAllowed, setSessionAllowed] = React2.useState(/* @__PURE__ */ new Set());
|
|
740
|
+
const autoApprovedRef = React2.useRef(/* @__PURE__ */ new Set());
|
|
741
|
+
React2.useEffect(() => {
|
|
564
742
|
if (sessionAllowed.size === 0) return;
|
|
565
743
|
for (const message of chat.messages) {
|
|
566
744
|
for (const call of message.toolCalls ?? []) {
|
|
@@ -581,10 +759,21 @@ function ChatApp(options) {
|
|
|
581
759
|
autoApprovedRef.current.add(toolCallId);
|
|
582
760
|
void chat.approve(toolCallId);
|
|
583
761
|
};
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
762
|
+
const awaitingConfirmation = React2.useMemo(
|
|
763
|
+
() => chat.messages.some(
|
|
764
|
+
(message) => message.toolCalls?.some(
|
|
765
|
+
(call) => call.status === "requires_confirmation" && !sessionAllowed.has(call.name)
|
|
766
|
+
)
|
|
767
|
+
),
|
|
768
|
+
[chat.messages, sessionAllowed]
|
|
769
|
+
);
|
|
770
|
+
return { sessionAllowed, handleApproveAlways, awaitingConfirmation };
|
|
771
|
+
}
|
|
772
|
+
function useSessionMeta(options) {
|
|
773
|
+
const sessionCreatedAtRef = React2.useRef(void 0);
|
|
774
|
+
const messageCount = options.messages.length;
|
|
775
|
+
const firstUserContent = options.messages.find((m) => m.role === "user")?.content ?? "";
|
|
776
|
+
React2.useEffect(() => {
|
|
588
777
|
const sessionId = options.sessionId;
|
|
589
778
|
if (!sessionId || sessionId === "custom") return;
|
|
590
779
|
if (!sessionCreatedAtRef.current) {
|
|
@@ -597,24 +786,211 @@ function ChatApp(options) {
|
|
|
597
786
|
createdAt: sessionCreatedAtRef.current,
|
|
598
787
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
599
788
|
messageCount,
|
|
600
|
-
preview: derivePreview(
|
|
601
|
-
provider:
|
|
602
|
-
model:
|
|
789
|
+
preview: derivePreview(options.messages),
|
|
790
|
+
provider: options.provider,
|
|
791
|
+
model: options.model
|
|
603
792
|
});
|
|
604
793
|
} catch {
|
|
605
794
|
}
|
|
606
|
-
}, [options.sessionId, messageCount, firstUserContent,
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
[
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
795
|
+
}, [options.sessionId, messageCount, firstUserContent, options.provider, options.model]);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// src/extensibility/hooks/runner.ts
|
|
799
|
+
var HookDispatcher = class {
|
|
800
|
+
constructor(handlers = [], onError = (_h, err) => process.stderr.write(
|
|
801
|
+
`[agentskit] hook error: ${err instanceof Error ? err.message : String(err)}
|
|
802
|
+
`
|
|
803
|
+
)) {
|
|
804
|
+
this.onError = onError;
|
|
805
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
806
|
+
for (const handler of handlers) this.register(handler);
|
|
807
|
+
}
|
|
808
|
+
register(handler) {
|
|
809
|
+
const list = this.handlers.get(handler.event) ?? [];
|
|
810
|
+
list.push(handler);
|
|
811
|
+
this.handlers.set(handler.event, list);
|
|
812
|
+
}
|
|
813
|
+
async dispatch(event, payload) {
|
|
814
|
+
const list = this.handlers.get(event) ?? [];
|
|
815
|
+
let current = { ...payload, event };
|
|
816
|
+
for (const handler of list) {
|
|
817
|
+
if (!this.matches(handler, current)) continue;
|
|
818
|
+
let result;
|
|
819
|
+
try {
|
|
820
|
+
result = await handler.run(current);
|
|
821
|
+
} catch (err) {
|
|
822
|
+
this.onError(handler, err);
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
if (result.decision === "block") {
|
|
826
|
+
return { payload: current, blocked: true, reason: result.reason };
|
|
827
|
+
}
|
|
828
|
+
if (result.decision === "modify") {
|
|
829
|
+
current = { ...result.payload, event };
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return { payload: current, blocked: false };
|
|
833
|
+
}
|
|
834
|
+
matches(handler, payload) {
|
|
835
|
+
if (!handler.matcher) return true;
|
|
836
|
+
if (typeof handler.matcher === "function") return handler.matcher(payload);
|
|
837
|
+
return handler.matcher.test(String(payload.tool ?? payload.prompt ?? ""));
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
function configHooksToHandlers(config) {
|
|
841
|
+
if (!config) return [];
|
|
842
|
+
const handlers = [];
|
|
843
|
+
for (const [event, entries] of Object.entries(config)) {
|
|
844
|
+
for (const entry of entries) {
|
|
845
|
+
handlers.push({
|
|
846
|
+
event,
|
|
847
|
+
matcher: entry.matcher ? new RegExp(entry.matcher) : void 0,
|
|
848
|
+
run: (payload) => runShellHook(entry, payload)
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return handlers;
|
|
853
|
+
}
|
|
854
|
+
function runShellHook(entry, payload) {
|
|
855
|
+
return new Promise((resolvePromise) => {
|
|
856
|
+
const timeoutMs = entry.timeout ?? 5e3;
|
|
857
|
+
const child = child_process.spawn("sh", ["-c", entry.run], {
|
|
858
|
+
stdio: ["pipe", "pipe", "inherit"]
|
|
859
|
+
});
|
|
860
|
+
let stdout = "";
|
|
861
|
+
child.stdout.on("data", (chunk) => {
|
|
862
|
+
stdout += chunk.toString();
|
|
863
|
+
});
|
|
864
|
+
const timer = setTimeout(() => {
|
|
865
|
+
child.kill("SIGTERM");
|
|
866
|
+
}, timeoutMs);
|
|
867
|
+
child.on("close", (code) => {
|
|
868
|
+
clearTimeout(timer);
|
|
869
|
+
if (code !== 0) {
|
|
870
|
+
resolvePromise({
|
|
871
|
+
decision: "block",
|
|
872
|
+
reason: `shell hook exited with code ${code}`
|
|
873
|
+
});
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
const trimmed = stdout.trim();
|
|
877
|
+
if (!trimmed) {
|
|
878
|
+
resolvePromise({ decision: "continue" });
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
try {
|
|
882
|
+
const parsed = JSON.parse(trimmed);
|
|
883
|
+
resolvePromise(parsed);
|
|
884
|
+
} catch {
|
|
885
|
+
resolvePromise({ decision: "continue" });
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
child.on("error", (err) => {
|
|
889
|
+
clearTimeout(timer);
|
|
890
|
+
resolvePromise({ decision: "block", reason: err.message });
|
|
891
|
+
});
|
|
892
|
+
try {
|
|
893
|
+
child.stdin.write(JSON.stringify(payload));
|
|
894
|
+
child.stdin.end();
|
|
895
|
+
} catch {
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
function groupIntoTurns(messages) {
|
|
900
|
+
const turns = [];
|
|
901
|
+
let current = [];
|
|
902
|
+
for (const message of messages) {
|
|
903
|
+
if (message.role === "user") {
|
|
904
|
+
if (current.length > 0) turns.push(current);
|
|
905
|
+
current = [message];
|
|
906
|
+
} else if (message.role === "system") {
|
|
907
|
+
if (current.length > 0) turns.push(current);
|
|
908
|
+
turns.push([message]);
|
|
909
|
+
current = [];
|
|
910
|
+
} else {
|
|
911
|
+
current.push(message);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
if (current.length > 0) turns.push(current);
|
|
915
|
+
return turns;
|
|
916
|
+
}
|
|
917
|
+
function ChatApp(options) {
|
|
918
|
+
const {
|
|
919
|
+
runtime,
|
|
920
|
+
memory,
|
|
921
|
+
tools,
|
|
922
|
+
skills,
|
|
923
|
+
state: { baseUrl, toolsFlag, skillFlag },
|
|
924
|
+
setProvider,
|
|
925
|
+
setModel,
|
|
926
|
+
setApiKey,
|
|
927
|
+
setBaseUrl,
|
|
928
|
+
setToolsFlag,
|
|
929
|
+
setSkillFlag
|
|
930
|
+
} = useRuntime(options);
|
|
931
|
+
const mergedTools = React2.useMemo(() => {
|
|
932
|
+
const extra = options.extraTools ?? [];
|
|
933
|
+
return [...tools, ...extra];
|
|
934
|
+
}, [tools, options.extraTools]);
|
|
935
|
+
const mergedSkills = React2.useMemo(() => {
|
|
936
|
+
const extra = options.extraSkills ?? [];
|
|
937
|
+
if (!skills && extra.length === 0) return void 0;
|
|
938
|
+
return [...skills ?? [], ...extra];
|
|
939
|
+
}, [skills, options.extraSkills]);
|
|
940
|
+
const chat = ink$1.useChat({
|
|
941
|
+
adapter: runtime.adapter,
|
|
942
|
+
memory,
|
|
943
|
+
systemPrompt: options.system,
|
|
944
|
+
tools: mergedTools.length > 0 ? mergedTools : void 0,
|
|
945
|
+
skills: mergedSkills
|
|
946
|
+
});
|
|
947
|
+
const { sessionAllowed, handleApproveAlways, awaitingConfirmation } = useToolPermissions(chat);
|
|
948
|
+
useSessionMeta({
|
|
949
|
+
sessionId: options.sessionId,
|
|
950
|
+
messages: chat.messages,
|
|
951
|
+
provider: runtime.provider,
|
|
952
|
+
model: runtime.model
|
|
953
|
+
});
|
|
954
|
+
const turns = React2.useMemo(() => groupIntoTurns(chat.messages), [chat.messages]);
|
|
955
|
+
const toolNames = toolsFlag ? toolsFlag.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
956
|
+
const [feedback, setFeedback] = React2.useState(null);
|
|
957
|
+
const hookDispatcher = React2.useMemo(
|
|
958
|
+
() => new HookDispatcher(options.hookHandlers ?? []),
|
|
959
|
+
[options.hookHandlers]
|
|
960
|
+
);
|
|
961
|
+
React2.useEffect(() => {
|
|
962
|
+
void hookDispatcher.dispatch("SessionStart", {
|
|
963
|
+
event: "SessionStart",
|
|
964
|
+
sessionId: options.sessionId,
|
|
965
|
+
provider: runtime.provider,
|
|
966
|
+
model: runtime.model
|
|
967
|
+
});
|
|
968
|
+
return () => {
|
|
969
|
+
void hookDispatcher.dispatch("SessionEnd", {
|
|
970
|
+
event: "SessionEnd",
|
|
971
|
+
sessionId: options.sessionId
|
|
972
|
+
});
|
|
973
|
+
};
|
|
974
|
+
}, [hookDispatcher]);
|
|
975
|
+
const slashCommands = React2.useMemo(
|
|
976
|
+
() => [...builtinSlashCommands, ...options.slashCommands ?? []],
|
|
977
|
+
[options.slashCommands]
|
|
978
|
+
);
|
|
979
|
+
const slashRegistry = React2.useMemo(() => createSlashRegistry(slashCommands), [slashCommands]);
|
|
980
|
+
const handleSubmitInput = async (raw) => {
|
|
616
981
|
const parsed = parseSlashCommand(raw);
|
|
617
982
|
if (!parsed) {
|
|
983
|
+
const hookResult = await hookDispatcher.dispatch("UserPromptSubmit", {
|
|
984
|
+
event: "UserPromptSubmit",
|
|
985
|
+
prompt: raw
|
|
986
|
+
});
|
|
987
|
+
if (hookResult.blocked) {
|
|
988
|
+
setFeedback({
|
|
989
|
+
message: `Prompt blocked: ${hookResult.reason ?? "hook refused"}`,
|
|
990
|
+
kind: "warn"
|
|
991
|
+
});
|
|
992
|
+
return true;
|
|
993
|
+
}
|
|
618
994
|
setFeedback(null);
|
|
619
995
|
return false;
|
|
620
996
|
}
|
|
@@ -634,7 +1010,8 @@ function ChatApp(options) {
|
|
|
634
1010
|
mode: runtime.mode,
|
|
635
1011
|
baseUrl,
|
|
636
1012
|
tools: toolsFlag,
|
|
637
|
-
skill: skillFlag
|
|
1013
|
+
skill: skillFlag,
|
|
1014
|
+
sessionId: options.sessionId
|
|
638
1015
|
},
|
|
639
1016
|
setProvider,
|
|
640
1017
|
setModel,
|
|
@@ -653,14 +1030,6 @@ function ChatApp(options) {
|
|
|
653
1030
|
}
|
|
654
1031
|
return true;
|
|
655
1032
|
};
|
|
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
|
-
);
|
|
664
1033
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", gap: 1, children: [
|
|
665
1034
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
666
1035
|
ink$1.StatusHeader,
|
|
@@ -751,605 +1120,363 @@ function renderChatHeader(options) {
|
|
|
751
1120
|
if (options.memoryBackend) parts.push(`memory=${options.memoryBackend}`);
|
|
752
1121
|
return parts.join(" ");
|
|
753
1122
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
ollama: null,
|
|
772
|
-
demo: null
|
|
773
|
-
};
|
|
774
|
-
function adapterCall(provider, prefix = "process.env") {
|
|
775
|
-
const model = PROVIDER_DEFAULT_MODEL[provider];
|
|
776
|
-
if (provider === "demo") return `demoAdapter()`;
|
|
777
|
-
if (provider === "ollama") return `ollama({ model: '${model}' })`;
|
|
778
|
-
const envKey = PROVIDER_ENV_KEY[provider];
|
|
779
|
-
return `${PROVIDER_IMPORT[provider]}({ apiKey: ${prefix}.${envKey} ?? '', model: '${model}' })`;
|
|
780
|
-
}
|
|
781
|
-
function viteAdapterCall(provider) {
|
|
782
|
-
if (provider === "demo") return `demoAdapter()`;
|
|
783
|
-
if (provider === "ollama") return `ollama({ model: '${PROVIDER_DEFAULT_MODEL[provider]}' })`;
|
|
784
|
-
const envKey = PROVIDER_ENV_KEY[provider];
|
|
785
|
-
return `${PROVIDER_IMPORT[provider]}({ apiKey: import.meta.env.VITE_${envKey} ?? '', model: '${PROVIDER_DEFAULT_MODEL[provider]}' })`;
|
|
786
|
-
}
|
|
787
|
-
function adapterImport(provider) {
|
|
788
|
-
if (provider === "demo") return "";
|
|
789
|
-
return `import { ${PROVIDER_IMPORT[provider]} } from '@agentskit/adapters'
|
|
790
|
-
`;
|
|
791
|
-
}
|
|
792
|
-
function toolImports(tools) {
|
|
793
|
-
if (tools.length === 0) return "";
|
|
794
|
-
return `import { ${tools.map((t) => t === "web_search" ? "webSearch" : t).join(", ")} } from '@agentskit/tools'
|
|
795
|
-
`;
|
|
1123
|
+
|
|
1124
|
+
// src/commands/shared.ts
|
|
1125
|
+
function mergeWithConfig(options, config) {
|
|
1126
|
+
if (!config) return options;
|
|
1127
|
+
const d = config.defaults ?? {};
|
|
1128
|
+
const resolvedApiKey = options.apiKey ?? (d.apiKeyEnv ? process.env[d.apiKeyEnv] : void 0) ?? d.apiKey;
|
|
1129
|
+
return {
|
|
1130
|
+
...options,
|
|
1131
|
+
provider: options.provider !== "demo" ? options.provider : d.provider ?? options.provider,
|
|
1132
|
+
model: options.model ?? d.model,
|
|
1133
|
+
apiKey: resolvedApiKey,
|
|
1134
|
+
baseUrl: options.baseUrl ?? d.baseUrl,
|
|
1135
|
+
tools: options.tools ?? d.tools,
|
|
1136
|
+
skill: options.skill ?? d.skill,
|
|
1137
|
+
system: options.system ?? d.system,
|
|
1138
|
+
memoryBackend: options.memoryBackend ?? d.memoryBackend
|
|
1139
|
+
};
|
|
796
1140
|
}
|
|
797
|
-
function
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1141
|
+
async function loadPlugins(options = {}) {
|
|
1142
|
+
const {
|
|
1143
|
+
specs = [],
|
|
1144
|
+
pluginDirs = [],
|
|
1145
|
+
cwd = process.cwd(),
|
|
1146
|
+
autoDiscoverUserDir = true,
|
|
1147
|
+
onError = (spec, err) => process.stderr.write(
|
|
1148
|
+
`[agentskit] plugin "${spec}" failed to load: ${err instanceof Error ? err.message : String(err)}
|
|
1149
|
+
`
|
|
1150
|
+
),
|
|
1151
|
+
log = () => {
|
|
1152
|
+
}
|
|
1153
|
+
} = options;
|
|
1154
|
+
const resolvedSpecs = [...specs];
|
|
1155
|
+
const discoveryDirs = [...pluginDirs];
|
|
1156
|
+
if (autoDiscoverUserDir) discoveryDirs.push(path.join(os.homedir(), ".agentskit", "plugins"));
|
|
1157
|
+
for (const dir of discoveryDirs) {
|
|
1158
|
+
const discovered = await discoverPluginsInDir(dir);
|
|
1159
|
+
resolvedSpecs.push(...discovered);
|
|
1160
|
+
}
|
|
1161
|
+
const plugins = [];
|
|
1162
|
+
for (const spec of resolvedSpecs) {
|
|
1163
|
+
try {
|
|
1164
|
+
const plugin = await loadPluginFromSpec(spec, cwd, log);
|
|
1165
|
+
if (plugin) plugins.push(plugin);
|
|
1166
|
+
} catch (err) {
|
|
1167
|
+
onError(spec, err);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return mergePluginsIntoBundle(plugins);
|
|
806
1171
|
}
|
|
807
|
-
function
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
1172
|
+
async function discoverPluginsInDir(dir) {
|
|
1173
|
+
try {
|
|
1174
|
+
const entries = await promises.readdir(dir);
|
|
1175
|
+
const absolutes = entries.filter((name) => /\.(m?js|ts)$/i.test(name)).map((name) => path.join(dir, name));
|
|
1176
|
+
const validated = [];
|
|
1177
|
+
for (const p of absolutes) {
|
|
1178
|
+
try {
|
|
1179
|
+
const s = await promises.stat(p);
|
|
1180
|
+
if (s.isFile()) validated.push(p);
|
|
1181
|
+
} catch {
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
return validated;
|
|
1185
|
+
} catch {
|
|
1186
|
+
return [];
|
|
1187
|
+
}
|
|
813
1188
|
}
|
|
814
|
-
function
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1189
|
+
async function loadPluginFromSpec(spec, cwd, log) {
|
|
1190
|
+
const isPath = spec.startsWith("./") || spec.startsWith("../") || path.isAbsolute(spec);
|
|
1191
|
+
const importTarget = isPath ? url.pathToFileURL(path.resolve(cwd, spec)).href : spec;
|
|
1192
|
+
const mod = await import(importTarget);
|
|
1193
|
+
const exported = mod.default ?? mod.plugin ?? mod;
|
|
1194
|
+
const sourcePath = isPath ? path.resolve(cwd, spec) : void 0;
|
|
1195
|
+
const ctx = {
|
|
1196
|
+
cwd,
|
|
1197
|
+
sourcePath,
|
|
1198
|
+
log: (msg) => log(`[${spec}] ${msg}`)
|
|
1199
|
+
};
|
|
1200
|
+
if (typeof exported === "function") {
|
|
1201
|
+
const factory = exported;
|
|
1202
|
+
return await factory(ctx);
|
|
1203
|
+
}
|
|
1204
|
+
if (exported && typeof exported === "object" && "name" in exported) {
|
|
1205
|
+
const plugin = exported;
|
|
1206
|
+
if (plugin.init) await plugin.init(ctx);
|
|
1207
|
+
return plugin;
|
|
1208
|
+
}
|
|
1209
|
+
throw new Error(
|
|
1210
|
+
"Module did not export a Plugin \u2014 expected default export to be a Plugin object or a PluginFactory function."
|
|
1211
|
+
);
|
|
818
1212
|
}
|
|
819
|
-
function
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
1213
|
+
function mergePluginsIntoBundle(plugins) {
|
|
1214
|
+
const bundle = {
|
|
1215
|
+
plugins,
|
|
1216
|
+
slashCommands: [],
|
|
1217
|
+
tools: [],
|
|
1218
|
+
skills: [],
|
|
1219
|
+
providers: {},
|
|
1220
|
+
hooks: [],
|
|
1221
|
+
mcpServers: []
|
|
1222
|
+
};
|
|
1223
|
+
for (const plugin of plugins) {
|
|
1224
|
+
if (plugin.slashCommands) bundle.slashCommands.push(...plugin.slashCommands);
|
|
1225
|
+
if (plugin.tools) bundle.tools.push(...plugin.tools);
|
|
1226
|
+
if (plugin.skills) bundle.skills.push(...plugin.skills);
|
|
1227
|
+
if (plugin.hooks) bundle.hooks.push(...plugin.hooks);
|
|
1228
|
+
if (plugin.mcpServers) bundle.mcpServers.push(...plugin.mcpServers);
|
|
1229
|
+
if (plugin.providers) {
|
|
1230
|
+
for (const [name, factory] of Object.entries(plugin.providers)) {
|
|
1231
|
+
bundle.providers[name] = factory;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
830
1234
|
}
|
|
1235
|
+
return bundle;
|
|
831
1236
|
}
|
|
1237
|
+
var McpClient = class {
|
|
1238
|
+
constructor(spec, onError = (err) => process.stderr.write(
|
|
1239
|
+
`[agentskit] mcp[${spec.name}] error: ${err instanceof Error ? err.message : String(err)}
|
|
1240
|
+
`
|
|
1241
|
+
)) {
|
|
1242
|
+
this.spec = spec;
|
|
1243
|
+
this.onError = onError;
|
|
1244
|
+
this.buffer = "";
|
|
1245
|
+
this.nextId = 1;
|
|
1246
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
1247
|
+
this.disposed = false;
|
|
1248
|
+
}
|
|
1249
|
+
async start() {
|
|
1250
|
+
if (this.child) return;
|
|
1251
|
+
const child = child_process.spawn(this.spec.command, this.spec.args ?? [], {
|
|
1252
|
+
env: { ...process.env, ...this.spec.env ?? {} },
|
|
1253
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1254
|
+
});
|
|
1255
|
+
this.child = child;
|
|
1256
|
+
child.stdout.on("data", (chunk) => this.onStdout(chunk.toString()));
|
|
1257
|
+
child.stderr.on("data", (chunk) => {
|
|
1258
|
+
process.stderr.write(`[mcp ${this.spec.name}] ${chunk}`);
|
|
1259
|
+
});
|
|
1260
|
+
child.on("error", (err) => this.onError(err));
|
|
1261
|
+
child.on("close", () => {
|
|
1262
|
+
this.disposed = true;
|
|
1263
|
+
for (const pending of this.pending.values()) {
|
|
1264
|
+
pending({ jsonrpc: "2.0", id: 0, error: { code: -1, message: "server closed" } });
|
|
1265
|
+
}
|
|
1266
|
+
this.pending.clear();
|
|
1267
|
+
});
|
|
1268
|
+
await this.request("initialize", {
|
|
1269
|
+
protocolVersion: "2024-11-05",
|
|
1270
|
+
capabilities: {},
|
|
1271
|
+
clientInfo: { name: "agentskit", version: "0" }
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
async listTools() {
|
|
1275
|
+
const res = await this.request("tools/list", {});
|
|
1276
|
+
const tools = res.tools ?? [];
|
|
1277
|
+
return tools;
|
|
1278
|
+
}
|
|
1279
|
+
async callTool(name, args) {
|
|
1280
|
+
return await this.request("tools/call", { name, arguments: args });
|
|
1281
|
+
}
|
|
1282
|
+
dispose() {
|
|
1283
|
+
if (this.disposed) return;
|
|
1284
|
+
this.disposed = true;
|
|
1285
|
+
this.child?.kill("SIGTERM");
|
|
1286
|
+
}
|
|
1287
|
+
request(method, params) {
|
|
1288
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
1289
|
+
if (!this.child || this.disposed) {
|
|
1290
|
+
rejectPromise(new Error(`mcp server ${this.spec.name} not running`));
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
const id = this.nextId++;
|
|
1294
|
+
const timeoutMs = this.spec.timeout ?? 1e4;
|
|
1295
|
+
const timer = setTimeout(() => {
|
|
1296
|
+
this.pending.delete(id);
|
|
1297
|
+
rejectPromise(new Error(`mcp ${this.spec.name}.${method} timed out`));
|
|
1298
|
+
}, timeoutMs);
|
|
1299
|
+
this.pending.set(id, (res) => {
|
|
1300
|
+
clearTimeout(timer);
|
|
1301
|
+
if (res.error) {
|
|
1302
|
+
rejectPromise(new Error(`mcp ${method} failed: ${res.error.message}`));
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
resolvePromise(res.result);
|
|
1306
|
+
});
|
|
1307
|
+
const message = JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n";
|
|
1308
|
+
this.child.stdin.write(message);
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
onStdout(chunk) {
|
|
1312
|
+
this.buffer += chunk;
|
|
1313
|
+
let newlineIndex = this.buffer.indexOf("\n");
|
|
1314
|
+
while (newlineIndex !== -1) {
|
|
1315
|
+
const line = this.buffer.slice(0, newlineIndex).trim();
|
|
1316
|
+
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
1317
|
+
if (line) {
|
|
1318
|
+
try {
|
|
1319
|
+
const parsed = JSON.parse(line);
|
|
1320
|
+
const pending = this.pending.get(Number(parsed.id));
|
|
1321
|
+
if (pending) {
|
|
1322
|
+
this.pending.delete(Number(parsed.id));
|
|
1323
|
+
pending(parsed);
|
|
1324
|
+
}
|
|
1325
|
+
} catch (err) {
|
|
1326
|
+
this.onError(err);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
newlineIndex = this.buffer.indexOf("\n");
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
832
1333
|
|
|
833
|
-
|
|
1334
|
+
// src/extensibility/mcp/bridge.ts
|
|
1335
|
+
async function bridgeMcpServers(specs) {
|
|
1336
|
+
const clients = [];
|
|
1337
|
+
const tools = [];
|
|
1338
|
+
for (const spec of specs) {
|
|
1339
|
+
const client = new McpClient(spec);
|
|
1340
|
+
try {
|
|
1341
|
+
await client.start();
|
|
1342
|
+
const mcpTools = await client.listTools();
|
|
1343
|
+
for (const mcpTool of mcpTools) {
|
|
1344
|
+
tools.push(mcpToolToDefinition(spec.name, client, mcpTool));
|
|
1345
|
+
}
|
|
1346
|
+
clients.push(client);
|
|
1347
|
+
} catch (err) {
|
|
1348
|
+
process.stderr.write(
|
|
1349
|
+
`[agentskit] mcp server "${spec.name}" failed: ${err instanceof Error ? err.message : String(err)}
|
|
1350
|
+
`
|
|
1351
|
+
);
|
|
1352
|
+
client.dispose();
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
return { clients, tools };
|
|
834
1356
|
}
|
|
835
|
-
function
|
|
836
|
-
const deps = {
|
|
837
|
-
"@agentskit/react": "^0.4.0",
|
|
838
|
-
react: "^19.0.0",
|
|
839
|
-
"react-dom": "^19.0.0"
|
|
840
|
-
};
|
|
841
|
-
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
842
|
-
if (ctx.tools.length > 0) deps["@agentskit/tools"] = "^0.4.0";
|
|
843
|
-
if (ctx.memory !== "none") deps["@agentskit/memory"] = "^0.4.0";
|
|
844
|
-
const includesDemo = ctx.provider === "demo";
|
|
845
|
-
const adapter = ctx.provider === "demo" ? viteAdapterCall(ctx.provider) : viteAdapterCall(ctx.provider);
|
|
846
|
-
const envKey = PROVIDER_ENV_KEY[ctx.provider];
|
|
847
|
-
const envContent = envKey ? `VITE_${envKey}=
|
|
848
|
-
` : "# No API key required for the local provider\n";
|
|
1357
|
+
function mcpToolToDefinition(serverName, client, tool) {
|
|
849
1358
|
return {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
dev: "vite",
|
|
857
|
-
build: "vite build",
|
|
858
|
-
preview: "vite preview"
|
|
859
|
-
},
|
|
860
|
-
dependencies: deps,
|
|
861
|
-
devDependencies: {
|
|
862
|
-
"@types/react": "^19.0.0",
|
|
863
|
-
"@types/react-dom": "^19.0.0",
|
|
864
|
-
"@vitejs/plugin-react": "^5.0.0",
|
|
865
|
-
typescript: "^5.5.0",
|
|
866
|
-
vite: "^7.0.0"
|
|
867
|
-
}
|
|
868
|
-
},
|
|
869
|
-
null,
|
|
870
|
-
2
|
|
871
|
-
) + "\n",
|
|
872
|
-
"index.html": `<!doctype html>
|
|
873
|
-
<html lang="en">
|
|
874
|
-
<head>
|
|
875
|
-
<meta charset="UTF-8" />
|
|
876
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
877
|
-
<title>AgentsKit React Starter</title>
|
|
878
|
-
</head>
|
|
879
|
-
<body>
|
|
880
|
-
<div id="root"></div>
|
|
881
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
882
|
-
</body>
|
|
883
|
-
</html>
|
|
884
|
-
`,
|
|
885
|
-
"vite.config.ts": `import { defineConfig } from 'vite'
|
|
886
|
-
import react from '@vitejs/plugin-react'
|
|
887
|
-
|
|
888
|
-
export default defineConfig({ plugins: [react()] })
|
|
889
|
-
`,
|
|
890
|
-
"tsconfig.json": JSON.stringify(
|
|
891
|
-
{
|
|
892
|
-
compilerOptions: {
|
|
893
|
-
target: "ES2022",
|
|
894
|
-
lib: ["ES2022", "DOM"],
|
|
895
|
-
module: "ESNext",
|
|
896
|
-
moduleResolution: "bundler",
|
|
897
|
-
jsx: "react-jsx",
|
|
898
|
-
strict: true,
|
|
899
|
-
noEmit: true,
|
|
900
|
-
skipLibCheck: true
|
|
901
|
-
},
|
|
902
|
-
include: ["src"]
|
|
903
|
-
},
|
|
904
|
-
null,
|
|
905
|
-
2
|
|
906
|
-
) + "\n",
|
|
907
|
-
"src/main.tsx": `import React from 'react'
|
|
908
|
-
import ReactDOM from 'react-dom/client'
|
|
909
|
-
import App from './App'
|
|
910
|
-
|
|
911
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
912
|
-
<React.StrictMode>
|
|
913
|
-
<App />
|
|
914
|
-
</React.StrictMode>,
|
|
915
|
-
)
|
|
916
|
-
`,
|
|
917
|
-
"src/App.tsx": `import { ChatContainer, InputBar, Message, useChat } from '@agentskit/react'
|
|
918
|
-
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}${memoryImport(ctx.memory)}import '@agentskit/react/theme'
|
|
919
|
-
|
|
920
|
-
${includesDemo ? demoAdapterSnippet() : ""}export default function App() {
|
|
921
|
-
const chat = useChat({
|
|
922
|
-
adapter: ${adapter},${ctx.tools.length > 0 ? `
|
|
923
|
-
tools: ${toolList(ctx.tools)},` : ""}${ctx.memory !== "none" ? `
|
|
924
|
-
memory: ${memoryCall(ctx.memory)},` : ""}
|
|
925
|
-
})
|
|
926
|
-
|
|
927
|
-
return (
|
|
928
|
-
<ChatContainer>
|
|
929
|
-
{chat.messages.map(message => (
|
|
930
|
-
<Message key={message.id} message={message} />
|
|
931
|
-
))}
|
|
932
|
-
<InputBar chat={chat} />
|
|
933
|
-
</ChatContainer>
|
|
934
|
-
)
|
|
935
|
-
}
|
|
936
|
-
`,
|
|
937
|
-
".env.example": envContent,
|
|
938
|
-
".gitignore": `node_modules
|
|
939
|
-
dist
|
|
940
|
-
.env
|
|
941
|
-
.env.local
|
|
942
|
-
.agentskit-history.*
|
|
943
|
-
`,
|
|
944
|
-
"README.md": readmeFor(ctx)
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
function inkStarter(ctx) {
|
|
948
|
-
const deps = {
|
|
949
|
-
"@agentskit/ink": "^0.4.0",
|
|
950
|
-
ink: "^7.0.0",
|
|
951
|
-
react: "^19.0.0"
|
|
952
|
-
};
|
|
953
|
-
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
954
|
-
if (ctx.tools.length > 0) deps["@agentskit/tools"] = "^0.4.0";
|
|
955
|
-
if (ctx.memory !== "none") deps["@agentskit/memory"] = "^0.4.0";
|
|
956
|
-
return {
|
|
957
|
-
"package.json": JSON.stringify(
|
|
958
|
-
{
|
|
959
|
-
name: "agentskit-ink-app",
|
|
960
|
-
private: true,
|
|
961
|
-
type: "module",
|
|
962
|
-
scripts: {
|
|
963
|
-
dev: "tsx src/index.tsx",
|
|
964
|
-
start: "tsx src/index.tsx"
|
|
965
|
-
},
|
|
966
|
-
dependencies: deps,
|
|
967
|
-
devDependencies: {
|
|
968
|
-
"@types/react": "^19.0.0",
|
|
969
|
-
"@types/react-dom": "^19.0.0",
|
|
970
|
-
tsx: "^4.20.0",
|
|
971
|
-
typescript: "^5.5.0"
|
|
972
|
-
}
|
|
973
|
-
},
|
|
974
|
-
null,
|
|
975
|
-
2
|
|
976
|
-
) + "\n",
|
|
977
|
-
"tsconfig.json": JSON.stringify(
|
|
978
|
-
{
|
|
979
|
-
compilerOptions: {
|
|
980
|
-
target: "ES2022",
|
|
981
|
-
module: "ESNext",
|
|
982
|
-
moduleResolution: "bundler",
|
|
983
|
-
jsx: "react-jsx",
|
|
984
|
-
strict: true,
|
|
985
|
-
noEmit: true,
|
|
986
|
-
skipLibCheck: true
|
|
987
|
-
},
|
|
988
|
-
include: ["src"]
|
|
989
|
-
},
|
|
990
|
-
null,
|
|
991
|
-
2
|
|
992
|
-
) + "\n",
|
|
993
|
-
"src/index.tsx": `import React from 'react'
|
|
994
|
-
import { render } from 'ink'
|
|
995
|
-
import { ChatContainer, InputBar, Message, useChat } from '@agentskit/ink'
|
|
996
|
-
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}${memoryImport(ctx.memory)}
|
|
997
|
-
${ctx.provider === "demo" ? demoAdapterSnippet() : ""}function App() {
|
|
998
|
-
const chat = useChat({
|
|
999
|
-
adapter: ${adapterCall(ctx.provider)},${ctx.tools.length > 0 ? `
|
|
1000
|
-
tools: ${toolList(ctx.tools)},` : ""}${ctx.memory !== "none" ? `
|
|
1001
|
-
memory: ${memoryCall(ctx.memory)},` : ""}
|
|
1002
|
-
})
|
|
1003
|
-
|
|
1004
|
-
return (
|
|
1005
|
-
<ChatContainer>
|
|
1006
|
-
{chat.messages.map(message => (
|
|
1007
|
-
<Message key={message.id} message={message} />
|
|
1008
|
-
))}
|
|
1009
|
-
<InputBar chat={chat} />
|
|
1010
|
-
</ChatContainer>
|
|
1011
|
-
)
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
render(<App />)
|
|
1015
|
-
`,
|
|
1016
|
-
".env.example": PROVIDER_ENV_KEY[ctx.provider] ? `${PROVIDER_ENV_KEY[ctx.provider]}=
|
|
1017
|
-
` : "# No API key required for the local provider\n",
|
|
1018
|
-
".gitignore": `node_modules
|
|
1019
|
-
.env
|
|
1020
|
-
.env.local
|
|
1021
|
-
.agentskit-history.*
|
|
1022
|
-
`,
|
|
1023
|
-
"README.md": readmeFor(ctx)
|
|
1024
|
-
};
|
|
1025
|
-
}
|
|
1026
|
-
function runtimeStarter(ctx) {
|
|
1027
|
-
const deps = {
|
|
1028
|
-
"@agentskit/runtime": "^0.4.0"
|
|
1029
|
-
};
|
|
1030
|
-
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
1031
|
-
if (ctx.tools.length > 0) deps["@agentskit/tools"] = "^0.4.0";
|
|
1032
|
-
if (ctx.memory !== "none") deps["@agentskit/memory"] = "^0.4.0";
|
|
1033
|
-
return {
|
|
1034
|
-
"package.json": JSON.stringify(
|
|
1035
|
-
{
|
|
1036
|
-
name: "agentskit-runtime-app",
|
|
1037
|
-
private: true,
|
|
1038
|
-
type: "module",
|
|
1039
|
-
scripts: {
|
|
1040
|
-
start: "tsx src/index.ts",
|
|
1041
|
-
dev: "tsx src/index.ts"
|
|
1042
|
-
},
|
|
1043
|
-
dependencies: deps,
|
|
1044
|
-
devDependencies: {
|
|
1045
|
-
tsx: "^4.20.0",
|
|
1046
|
-
typescript: "^5.5.0"
|
|
1047
|
-
}
|
|
1048
|
-
},
|
|
1049
|
-
null,
|
|
1050
|
-
2
|
|
1051
|
-
) + "\n",
|
|
1052
|
-
"tsconfig.json": JSON.stringify(
|
|
1053
|
-
{
|
|
1054
|
-
compilerOptions: {
|
|
1055
|
-
target: "ES2022",
|
|
1056
|
-
module: "ESNext",
|
|
1057
|
-
moduleResolution: "bundler",
|
|
1058
|
-
strict: true,
|
|
1059
|
-
noEmit: true,
|
|
1060
|
-
skipLibCheck: true
|
|
1061
|
-
},
|
|
1062
|
-
include: ["src"]
|
|
1063
|
-
},
|
|
1064
|
-
null,
|
|
1065
|
-
2
|
|
1066
|
-
) + "\n",
|
|
1067
|
-
"src/index.ts": `import { createRuntime } from '@agentskit/runtime'
|
|
1068
|
-
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}${memoryImport(ctx.memory)}
|
|
1069
|
-
${ctx.provider === "demo" ? demoAdapterSnippet() : ""}const runtime = createRuntime({
|
|
1070
|
-
adapter: ${adapterCall(ctx.provider)},${ctx.tools.length > 0 ? `
|
|
1071
|
-
tools: ${toolList(ctx.tools)},` : ""}${ctx.memory !== "none" ? `
|
|
1072
|
-
memory: ${memoryCall(ctx.memory)},` : ""}
|
|
1073
|
-
maxSteps: 10,
|
|
1074
|
-
})
|
|
1075
|
-
|
|
1076
|
-
const task = process.argv.slice(2).join(' ') || 'Say hello and tell me one fact about TypeScript.'
|
|
1077
|
-
const result = await runtime.run(task)
|
|
1078
|
-
|
|
1079
|
-
console.log(result.content)
|
|
1080
|
-
console.log(\`\\n\u2014 \${result.steps} steps \xB7 \${result.toolCalls.length} tool calls \xB7 \${result.durationMs}ms\`)
|
|
1081
|
-
`,
|
|
1082
|
-
".env.example": PROVIDER_ENV_KEY[ctx.provider] ? `${PROVIDER_ENV_KEY[ctx.provider]}=
|
|
1083
|
-
` : "# No API key required for the local provider\n",
|
|
1084
|
-
".gitignore": `node_modules
|
|
1085
|
-
.env
|
|
1086
|
-
.env.local
|
|
1087
|
-
.agentskit-history.*
|
|
1088
|
-
`,
|
|
1089
|
-
"README.md": readmeFor(ctx)
|
|
1090
|
-
};
|
|
1091
|
-
}
|
|
1092
|
-
function multiAgentStarter(ctx) {
|
|
1093
|
-
const deps = {
|
|
1094
|
-
"@agentskit/runtime": "^0.4.0",
|
|
1095
|
-
"@agentskit/skills": "^0.4.0"
|
|
1096
|
-
};
|
|
1097
|
-
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
1098
|
-
if (ctx.tools.length === 0) ctx.tools = ["web_search"];
|
|
1099
|
-
deps["@agentskit/tools"] = "^0.4.0";
|
|
1100
|
-
return {
|
|
1101
|
-
"package.json": JSON.stringify(
|
|
1102
|
-
{
|
|
1103
|
-
name: "agentskit-multi-agent",
|
|
1104
|
-
private: true,
|
|
1105
|
-
type: "module",
|
|
1106
|
-
scripts: {
|
|
1107
|
-
start: "tsx src/index.ts",
|
|
1108
|
-
dev: "tsx src/index.ts"
|
|
1109
|
-
},
|
|
1110
|
-
dependencies: deps,
|
|
1111
|
-
devDependencies: {
|
|
1112
|
-
tsx: "^4.20.0",
|
|
1113
|
-
typescript: "^5.5.0"
|
|
1114
|
-
}
|
|
1115
|
-
},
|
|
1116
|
-
null,
|
|
1117
|
-
2
|
|
1118
|
-
) + "\n",
|
|
1119
|
-
"tsconfig.json": JSON.stringify(
|
|
1120
|
-
{
|
|
1121
|
-
compilerOptions: {
|
|
1122
|
-
target: "ES2022",
|
|
1123
|
-
module: "ESNext",
|
|
1124
|
-
moduleResolution: "bundler",
|
|
1125
|
-
strict: true,
|
|
1126
|
-
noEmit: true,
|
|
1127
|
-
skipLibCheck: true
|
|
1128
|
-
},
|
|
1129
|
-
include: ["src"]
|
|
1130
|
-
},
|
|
1131
|
-
null,
|
|
1132
|
-
2
|
|
1133
|
-
) + "\n",
|
|
1134
|
-
"src/index.ts": `import { createRuntime } from '@agentskit/runtime'
|
|
1135
|
-
import { planner, researcher } from '@agentskit/skills'
|
|
1136
|
-
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}
|
|
1137
|
-
${ctx.provider === "demo" ? demoAdapterSnippet() : ""}const runtime = createRuntime({
|
|
1138
|
-
adapter: ${adapterCall(ctx.provider)},
|
|
1139
|
-
maxSteps: 10,
|
|
1140
|
-
maxDelegationDepth: 2,
|
|
1141
|
-
})
|
|
1142
|
-
|
|
1143
|
-
const task = process.argv.slice(2).join(' ') || 'Research the current state of WebGPU and summarize.'
|
|
1144
|
-
|
|
1145
|
-
const result = await runtime.run(task, {
|
|
1146
|
-
skill: planner,
|
|
1147
|
-
delegates: {
|
|
1148
|
-
researcher: {
|
|
1149
|
-
skill: researcher,
|
|
1150
|
-
tools: ${toolList(ctx.tools)},
|
|
1151
|
-
maxSteps: 5,
|
|
1152
|
-
},
|
|
1153
|
-
},
|
|
1154
|
-
})
|
|
1155
|
-
|
|
1156
|
-
console.log(result.content)
|
|
1157
|
-
console.log(\`\\n\u2014 \${result.steps} steps \xB7 \${result.toolCalls.length} tool calls\`)
|
|
1158
|
-
`,
|
|
1159
|
-
".env.example": PROVIDER_ENV_KEY[ctx.provider] ? `${PROVIDER_ENV_KEY[ctx.provider]}=
|
|
1160
|
-
` : "# No API key required for the local provider\n",
|
|
1161
|
-
".gitignore": `node_modules
|
|
1162
|
-
.env
|
|
1163
|
-
.env.local
|
|
1164
|
-
`,
|
|
1165
|
-
"README.md": readmeFor(ctx)
|
|
1359
|
+
name: `${serverName}__${tool.name}`,
|
|
1360
|
+
description: tool.description ?? `MCP tool ${tool.name} from ${serverName}`,
|
|
1361
|
+
schema: tool.inputSchema ?? { type: "object", properties: {} },
|
|
1362
|
+
execute: async (args) => {
|
|
1363
|
+
return await client.callTool(tool.name, args);
|
|
1364
|
+
}
|
|
1166
1365
|
};
|
|
1167
1366
|
}
|
|
1168
|
-
function
|
|
1169
|
-
const
|
|
1170
|
-
const runCmd = ctx.pm === "npm" ? "npm run dev" : `${ctx.pm} dev`;
|
|
1171
|
-
const envKey = PROVIDER_ENV_KEY[ctx.provider];
|
|
1172
|
-
return `# AgentsKit ${ctx.template} starter
|
|
1173
|
-
|
|
1174
|
-
Generated by \`agentskit init\`.
|
|
1175
|
-
|
|
1176
|
-
## Stack
|
|
1177
|
-
|
|
1178
|
-
- **Template**: \`${ctx.template}\`
|
|
1179
|
-
- **Provider**: \`${ctx.provider}\`${ctx.tools.length ? `
|
|
1180
|
-
- **Tools**: ${ctx.tools.map((t) => `\`${t}\``).join(", ")}` : ""}${ctx.memory !== "none" ? `
|
|
1181
|
-
- **Memory**: \`${ctx.memory}\`` : ""}
|
|
1182
|
-
|
|
1183
|
-
## Run
|
|
1184
|
-
|
|
1185
|
-
\`\`\`bash
|
|
1186
|
-
${installCmd}
|
|
1187
|
-
${envKey ? `cp .env.example .env
|
|
1188
|
-
# add ${envKey}=...` : "# No API key required"}
|
|
1189
|
-
${runCmd}
|
|
1190
|
-
\`\`\`
|
|
1191
|
-
|
|
1192
|
-
## Next steps
|
|
1193
|
-
|
|
1194
|
-
- Open the AgentsKit docs at https://www.agentskit.io/docs
|
|
1195
|
-
- Add a custom skill: https://www.agentskit.io/docs/concepts/skill
|
|
1196
|
-
- Wire up RAG: https://www.agentskit.io/docs/recipes/rag-chat
|
|
1197
|
-
|
|
1198
|
-
## License
|
|
1199
|
-
|
|
1200
|
-
ISC
|
|
1201
|
-
`;
|
|
1202
|
-
}
|
|
1203
|
-
var TEMPLATE_FN = {
|
|
1204
|
-
react: reactStarter,
|
|
1205
|
-
ink: inkStarter,
|
|
1206
|
-
runtime: runtimeStarter,
|
|
1207
|
-
"multi-agent": multiAgentStarter
|
|
1208
|
-
};
|
|
1209
|
-
async function writeStarterProject(options) {
|
|
1210
|
-
const ctx = {
|
|
1211
|
-
template: options.template,
|
|
1212
|
-
provider: options.provider ?? "demo",
|
|
1213
|
-
tools: options.tools ?? [],
|
|
1214
|
-
memory: options.memory ?? "none",
|
|
1215
|
-
pm: options.packageManager ?? "pnpm"
|
|
1216
|
-
};
|
|
1217
|
-
const files = TEMPLATE_FN[ctx.template](ctx);
|
|
1218
|
-
await promises.mkdir(options.targetDir, { recursive: true });
|
|
1219
|
-
await Promise.all(
|
|
1220
|
-
Object.entries(files).map(async ([relativePath, content]) => {
|
|
1221
|
-
const absolutePath = path3__default.default.join(options.targetDir, relativePath);
|
|
1222
|
-
await promises.mkdir(path3__default.default.dirname(absolutePath), { recursive: true });
|
|
1223
|
-
await promises.writeFile(absolutePath, content, "utf8");
|
|
1224
|
-
})
|
|
1225
|
-
);
|
|
1367
|
+
function disposeMcpClients(clients) {
|
|
1368
|
+
for (const client of clients) client.dispose();
|
|
1226
1369
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1370
|
+
|
|
1371
|
+
// src/commands/chat.ts
|
|
1372
|
+
function registerChatCommand(program) {
|
|
1373
|
+
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").option(
|
|
1374
|
+
"--plugin-dir <dir>",
|
|
1375
|
+
"Extra directory to auto-discover plugin modules from (repeatable)",
|
|
1376
|
+
(value, prev = []) => [...prev, value],
|
|
1377
|
+
[]
|
|
1378
|
+
).option(
|
|
1379
|
+
"--mode <mode>",
|
|
1380
|
+
"Permission mode: default | plan | acceptEdits | bypassPermissions"
|
|
1381
|
+
).action(async (options) => {
|
|
1382
|
+
if (options.listSessions) {
|
|
1383
|
+
const sessions = listSessions();
|
|
1384
|
+
if (sessions.length === 0) {
|
|
1385
|
+
process.stdout.write("No saved sessions for this directory.\n");
|
|
1386
|
+
return;
|
|
1241
1387
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
const provider = await prompts.select({
|
|
1254
|
-
message: "LLM provider:",
|
|
1255
|
-
default: "demo",
|
|
1256
|
-
choices: [
|
|
1257
|
-
{ name: "Demo (no API key \u2014 deterministic stub)", value: "demo" },
|
|
1258
|
-
{ name: "OpenAI", value: "openai" },
|
|
1259
|
-
{ name: "Anthropic", value: "anthropic" },
|
|
1260
|
-
{ name: "Gemini", value: "gemini" },
|
|
1261
|
-
{ name: "Ollama (local, no key)", value: "ollama" }
|
|
1262
|
-
]
|
|
1263
|
-
});
|
|
1264
|
-
let tools = [];
|
|
1265
|
-
if (template !== "react") {
|
|
1266
|
-
tools = await prompts.checkbox({
|
|
1267
|
-
message: "Tools (space to toggle, enter to confirm):",
|
|
1268
|
-
choices: [
|
|
1269
|
-
{ name: "web_search", value: "web_search" },
|
|
1270
|
-
{ name: "filesystem", value: "filesystem" },
|
|
1271
|
-
{ name: "shell", value: "shell" }
|
|
1272
|
-
]
|
|
1273
|
-
});
|
|
1388
|
+
for (const s of sessions) {
|
|
1389
|
+
const { id, updatedAt, messageCount, preview, model, label, forkedFrom } = s.metadata;
|
|
1390
|
+
const display = label ? `${label} (${id})` : id;
|
|
1391
|
+
const forkNote = forkedFrom ? ` \u2190 fork ${forkedFrom}` : "";
|
|
1392
|
+
process.stdout.write(
|
|
1393
|
+
`${display} ${updatedAt} msgs=${messageCount}${model ? ` model=${model}` : ""}${forkNote}
|
|
1394
|
+
${preview}
|
|
1395
|
+
`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
return;
|
|
1274
1399
|
}
|
|
1275
|
-
const
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
{ name: "SQLite (better-sqlite3)", value: "sqlite" }
|
|
1282
|
-
]
|
|
1283
|
-
});
|
|
1284
|
-
const packageManager = await prompts.select({
|
|
1285
|
-
message: "Package manager:",
|
|
1286
|
-
default: "pnpm",
|
|
1287
|
-
choices: [
|
|
1288
|
-
{ name: "pnpm", value: "pnpm" },
|
|
1289
|
-
{ name: "npm", value: "npm" },
|
|
1290
|
-
{ name: "yarn", value: "yarn" },
|
|
1291
|
-
{ name: "bun", value: "bun" }
|
|
1292
|
-
]
|
|
1400
|
+
const config = options.config !== false ? await loadConfig() : void 0;
|
|
1401
|
+
const merged = mergeWithConfig(options, config);
|
|
1402
|
+
const session = resolveSession({
|
|
1403
|
+
explicitPath: options.memory,
|
|
1404
|
+
forceNew: Boolean(options.new),
|
|
1405
|
+
resumeId: options.resume
|
|
1293
1406
|
});
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
`
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
process.stdout.write(kleur__default.default.dim(` provider ${provider}
|
|
1300
|
-
`));
|
|
1301
|
-
if (tools.length) process.stdout.write(kleur__default.default.dim(` tools ${tools.join(", ")}
|
|
1302
|
-
`));
|
|
1303
|
-
process.stdout.write(kleur__default.default.dim(` memory ${memory}
|
|
1304
|
-
`));
|
|
1305
|
-
process.stdout.write(kleur__default.default.dim(` pm ${packageManager}
|
|
1306
|
-
|
|
1307
|
-
`));
|
|
1308
|
-
const proceed = await prompts.confirm({ message: "Generate?", default: true });
|
|
1309
|
-
if (!proceed) {
|
|
1310
|
-
process.stdout.write(kleur__default.default.yellow("Cancelled.\n"));
|
|
1311
|
-
return { cancelled: true, options: { targetDir, template, provider, tools, memory, packageManager } };
|
|
1407
|
+
if (!session.isNew && !options.memory) {
|
|
1408
|
+
process.stdout.write(
|
|
1409
|
+
`Resuming session ${session.id}. Start fresh with --new or list with --list-sessions.
|
|
1410
|
+
`
|
|
1411
|
+
);
|
|
1312
1412
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1413
|
+
const pluginBundle = await loadPlugins({
|
|
1414
|
+
specs: config?.plugins ?? [],
|
|
1415
|
+
pluginDirs: options.pluginDir ?? []
|
|
1416
|
+
});
|
|
1417
|
+
const configHooks = configHooksToHandlers(config?.hooks);
|
|
1418
|
+
const hookHandlers = [...configHooks, ...pluginBundle.hooks];
|
|
1419
|
+
const configMcpSpecs = Object.entries(config?.mcp?.servers ?? {}).map(([name, spec]) => ({
|
|
1420
|
+
name,
|
|
1421
|
+
command: spec.command,
|
|
1422
|
+
args: spec.args,
|
|
1423
|
+
env: spec.env,
|
|
1424
|
+
timeout: spec.timeout
|
|
1425
|
+
}));
|
|
1426
|
+
const allMcpSpecs = [...configMcpSpecs, ...pluginBundle.mcpServers];
|
|
1427
|
+
const { clients: mcpClients, tools: mcpTools } = await bridgeMcpServers(allMcpSpecs);
|
|
1428
|
+
const policyMode = options.mode ?? config?.permissions?.mode ?? "default";
|
|
1429
|
+
const permissionPolicy = {
|
|
1430
|
+
mode: policyMode,
|
|
1431
|
+
rules: (config?.permissions?.rules ?? []).map((r) => ({
|
|
1432
|
+
tool: r.tool,
|
|
1433
|
+
action: r.action,
|
|
1434
|
+
scope: r.scope
|
|
1435
|
+
}))
|
|
1323
1436
|
};
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1437
|
+
const chatOptions = {
|
|
1438
|
+
apiKey: merged.apiKey ?? options.apiKey,
|
|
1439
|
+
baseUrl: merged.baseUrl ?? options.baseUrl,
|
|
1440
|
+
provider: merged.provider,
|
|
1441
|
+
model: merged.model,
|
|
1442
|
+
system: merged.system ?? options.system,
|
|
1443
|
+
memoryPath: session.file,
|
|
1444
|
+
sessionId: session.id,
|
|
1445
|
+
tools: merged.tools ?? options.tools,
|
|
1446
|
+
skill: merged.skill ?? options.skill,
|
|
1447
|
+
memoryBackend: merged.memoryBackend ?? options.memoryBackend,
|
|
1448
|
+
agentsKitConfig: config,
|
|
1449
|
+
slashCommands: pluginBundle.slashCommands,
|
|
1450
|
+
extraTools: [...pluginBundle.tools, ...mcpTools],
|
|
1451
|
+
extraSkills: pluginBundle.skills,
|
|
1452
|
+
hookHandlers,
|
|
1453
|
+
permissionPolicy
|
|
1454
|
+
};
|
|
1455
|
+
process.stdout.write(`${renderChatHeader(chatOptions)}
|
|
1342
1456
|
`);
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1457
|
+
const instance = ink.render(React2__default.default.createElement(ChatApp, chatOptions));
|
|
1458
|
+
try {
|
|
1459
|
+
await instance.waitUntilExit();
|
|
1460
|
+
} finally {
|
|
1461
|
+
disposeMcpClients(mcpClients);
|
|
1462
|
+
}
|
|
1463
|
+
if (options.memory) {
|
|
1464
|
+
process.stdout.write(
|
|
1465
|
+
`
|
|
1466
|
+
Session saved to ${session.file}. Resume with --memory ${session.file}
|
|
1346
1467
|
`
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1468
|
+
);
|
|
1469
|
+
} else {
|
|
1470
|
+
process.stdout.write(
|
|
1471
|
+
`
|
|
1472
|
+
Session saved. Resume with:
|
|
1473
|
+
agentskit chat --resume ${session.id}
|
|
1474
|
+
Or start fresh with:
|
|
1475
|
+
agentskit chat --new
|
|
1476
|
+
`
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1353
1480
|
}
|
|
1354
1481
|
function formatEvent(event) {
|
|
1355
1482
|
switch (event.type) {
|
|
@@ -1408,13 +1535,13 @@ async function runAgent(task, options) {
|
|
|
1408
1535
|
process.stdout.write(result.content + "\n");
|
|
1409
1536
|
}
|
|
1410
1537
|
function RunApp({ task, options }) {
|
|
1411
|
-
const [status, setStatus] =
|
|
1412
|
-
const [currentStep, setCurrentStep] =
|
|
1413
|
-
const [toolCalls, setToolCalls] =
|
|
1414
|
-
const [result, setResult] =
|
|
1415
|
-
const [error, setError] =
|
|
1416
|
-
const [durationMs, setDurationMs] =
|
|
1417
|
-
|
|
1538
|
+
const [status, setStatus] = React2.useState("running");
|
|
1539
|
+
const [currentStep, setCurrentStep] = React2.useState(0);
|
|
1540
|
+
const [toolCalls, setToolCalls] = React2.useState([]);
|
|
1541
|
+
const [result, setResult] = React2.useState("");
|
|
1542
|
+
const [error, setError] = React2.useState("");
|
|
1543
|
+
const [durationMs, setDurationMs] = React2.useState(0);
|
|
1544
|
+
React2.useEffect(() => {
|
|
1418
1545
|
async function execute() {
|
|
1419
1546
|
if (options.skill && options.skills) {
|
|
1420
1547
|
setError("--skill and --skills are mutually exclusive.");
|
|
@@ -1505,555 +1632,640 @@ function RunApp({ task, options }) {
|
|
|
1505
1632
|
] })
|
|
1506
1633
|
] });
|
|
1507
1634
|
}
|
|
1508
|
-
|
|
1635
|
+
|
|
1636
|
+
// src/commands/run.ts
|
|
1637
|
+
function registerRunCommand(program) {
|
|
1638
|
+
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) => {
|
|
1639
|
+
const task = options.task ?? positionalTask;
|
|
1640
|
+
if (!task) {
|
|
1641
|
+
process.stderr.write("Error: task is required. Pass as argument or use --task.\n");
|
|
1642
|
+
process.exit(1);
|
|
1643
|
+
}
|
|
1644
|
+
const config = options.config !== false ? await loadConfig() : void 0;
|
|
1645
|
+
const merged = mergeWithConfig(options, config);
|
|
1646
|
+
if (options.pretty) {
|
|
1647
|
+
ink.render(React2__default.default.createElement(RunApp, { task, options }));
|
|
1648
|
+
} else {
|
|
1649
|
+
try {
|
|
1650
|
+
await runAgent(task, { ...options, provider: merged.provider, model: merged.model });
|
|
1651
|
+
} catch (err) {
|
|
1652
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
1653
|
+
`);
|
|
1654
|
+
process.exit(1);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
var PROVIDER_IMPORT = {
|
|
1660
|
+
openai: "openai",
|
|
1661
|
+
anthropic: "anthropic",
|
|
1662
|
+
gemini: "gemini",
|
|
1663
|
+
ollama: "ollama"
|
|
1664
|
+
};
|
|
1665
|
+
var PROVIDER_DEFAULT_MODEL = {
|
|
1666
|
+
openai: "gpt-4o-mini",
|
|
1667
|
+
anthropic: "claude-sonnet-4-6",
|
|
1668
|
+
gemini: "gemini-2.5-flash",
|
|
1669
|
+
ollama: "llama3.1",
|
|
1670
|
+
demo: "demo"
|
|
1671
|
+
};
|
|
1672
|
+
var PROVIDER_ENV_KEY = {
|
|
1509
1673
|
openai: "OPENAI_API_KEY",
|
|
1510
1674
|
anthropic: "ANTHROPIC_API_KEY",
|
|
1511
1675
|
gemini: "GEMINI_API_KEY",
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
kimi: "KIMI_API_KEY"
|
|
1515
|
-
};
|
|
1516
|
-
var PROVIDER_REACH_URLS = {
|
|
1517
|
-
openai: "https://api.openai.com/v1/models",
|
|
1518
|
-
anthropic: "https://api.anthropic.com/v1/messages",
|
|
1519
|
-
gemini: "https://generativelanguage.googleapis.com/v1beta/models",
|
|
1520
|
-
ollama: "http://localhost:11434/api/tags"
|
|
1676
|
+
ollama: null,
|
|
1677
|
+
demo: null
|
|
1521
1678
|
};
|
|
1522
|
-
|
|
1523
|
-
const
|
|
1524
|
-
if (
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
return {
|
|
1529
|
-
status: "fail",
|
|
1530
|
-
name: "Node version",
|
|
1531
|
-
detail: `Node ${process.versions.node} (need 22+)`,
|
|
1532
|
-
fix: "Install Node 22 LTS or newer (https://nodejs.org)"
|
|
1533
|
-
};
|
|
1534
|
-
}
|
|
1535
|
-
if (major === 25) {
|
|
1536
|
-
return {
|
|
1537
|
-
status: "warn",
|
|
1538
|
-
name: "Node version",
|
|
1539
|
-
detail: `Node ${process.versions.node} \u2014 Docusaurus apps may break here`,
|
|
1540
|
-
fix: "Use Node 22 LTS for the legacy docs app, or stay on 25 for everything else"
|
|
1541
|
-
};
|
|
1542
|
-
}
|
|
1543
|
-
return { status: "pass", name: "Node version", detail: `Node ${process.versions.node}` };
|
|
1679
|
+
function adapterCall(provider, prefix = "process.env") {
|
|
1680
|
+
const model = PROVIDER_DEFAULT_MODEL[provider];
|
|
1681
|
+
if (provider === "demo") return `demoAdapter()`;
|
|
1682
|
+
if (provider === "ollama") return `ollama({ model: '${model}' })`;
|
|
1683
|
+
const envKey = PROVIDER_ENV_KEY[provider];
|
|
1684
|
+
return `${PROVIDER_IMPORT[provider]}({ apiKey: ${prefix}.${envKey} ?? '', model: '${model}' })`;
|
|
1544
1685
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
}
|
|
1551
|
-
if (fs.existsSync(path3.join(cwd, "package-lock.json"))) {
|
|
1552
|
-
return { status: "warn", name: "Package manager", detail: "npm detected \u2014 pnpm recommended for monorepo workflows" };
|
|
1553
|
-
}
|
|
1554
|
-
if (fs.existsSync(path3.join(cwd, "yarn.lock"))) {
|
|
1555
|
-
return { status: "pass", name: "Package manager", detail: "yarn detected" };
|
|
1556
|
-
}
|
|
1557
|
-
if (fs.existsSync(path3.join(cwd, "bun.lock")) || fs.existsSync(path3.join(cwd, "bun.lockb"))) {
|
|
1558
|
-
return { status: "pass", name: "Package manager", detail: "bun detected" };
|
|
1559
|
-
}
|
|
1560
|
-
return {
|
|
1561
|
-
status: "skip",
|
|
1562
|
-
name: "Package manager",
|
|
1563
|
-
detail: "No lockfile found in cwd"
|
|
1564
|
-
};
|
|
1686
|
+
function viteAdapterCall(provider) {
|
|
1687
|
+
if (provider === "demo") return `demoAdapter()`;
|
|
1688
|
+
if (provider === "ollama") return `ollama({ model: '${PROVIDER_DEFAULT_MODEL[provider]}' })`;
|
|
1689
|
+
const envKey = PROVIDER_ENV_KEY[provider];
|
|
1690
|
+
return `${PROVIDER_IMPORT[provider]}({ apiKey: import.meta.env.VITE_${envKey} ?? '', model: '${PROVIDER_DEFAULT_MODEL[provider]}' })`;
|
|
1565
1691
|
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
status: "warn",
|
|
1571
|
-
name: "package.json",
|
|
1572
|
-
detail: "No package.json in cwd",
|
|
1573
|
-
fix: "Run from a project directory (or use `agentskit init` to create one)"
|
|
1574
|
-
};
|
|
1575
|
-
}
|
|
1576
|
-
try {
|
|
1577
|
-
const pkg = JSON.parse(await promises.readFile(path4, "utf8"));
|
|
1578
|
-
const deps = {
|
|
1579
|
-
...pkg.dependencies ?? {},
|
|
1580
|
-
...pkg.devDependencies ?? {}
|
|
1581
|
-
};
|
|
1582
|
-
const akDeps = Object.entries(deps).filter(([name]) => name.startsWith("@agentskit/"));
|
|
1583
|
-
if (akDeps.length === 0) {
|
|
1584
|
-
return { status: "skip", name: "AgentsKit packages", detail: "No @agentskit/* deps found in package.json" };
|
|
1585
|
-
}
|
|
1586
|
-
return {
|
|
1587
|
-
status: "pass",
|
|
1588
|
-
name: "AgentsKit packages",
|
|
1589
|
-
detail: `${akDeps.length} installed: ${akDeps.map(([n]) => n.replace("@agentskit/", "")).join(", ")}`
|
|
1590
|
-
};
|
|
1591
|
-
} catch (err) {
|
|
1592
|
-
return {
|
|
1593
|
-
status: "fail",
|
|
1594
|
-
name: "package.json",
|
|
1595
|
-
detail: `Could not parse: ${err.message}`
|
|
1596
|
-
};
|
|
1597
|
-
}
|
|
1692
|
+
function adapterImport(provider) {
|
|
1693
|
+
if (provider === "demo") return "";
|
|
1694
|
+
return `import { ${PROVIDER_IMPORT[provider]} } from '@agentskit/adapters'
|
|
1695
|
+
`;
|
|
1598
1696
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
}
|
|
1604
|
-
const value = process.env[envKey];
|
|
1605
|
-
if (!value) {
|
|
1606
|
-
return {
|
|
1607
|
-
status: "skip",
|
|
1608
|
-
name: `${provider} API key`,
|
|
1609
|
-
detail: `${envKey} not set`,
|
|
1610
|
-
fix: `export ${envKey}=... (only needed if you use ${provider})`
|
|
1611
|
-
};
|
|
1612
|
-
}
|
|
1613
|
-
if (value.length < 16) {
|
|
1614
|
-
return {
|
|
1615
|
-
status: "warn",
|
|
1616
|
-
name: `${provider} API key`,
|
|
1617
|
-
detail: `${envKey} looks too short (${value.length} chars)`,
|
|
1618
|
-
fix: "Verify the key is complete and not truncated"
|
|
1619
|
-
};
|
|
1620
|
-
}
|
|
1621
|
-
return { status: "pass", name: `${provider} API key`, detail: `${envKey} set (${value.length} chars)` };
|
|
1697
|
+
function toolImports(tools) {
|
|
1698
|
+
if (tools.length === 0) return "";
|
|
1699
|
+
return `import { ${tools.map((t) => t === "web_search" ? "webSearch" : t).join(", ")} } from '@agentskit/tools'
|
|
1700
|
+
`;
|
|
1622
1701
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
}
|
|
1632
|
-
const controller = new AbortController();
|
|
1633
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1634
|
-
try {
|
|
1635
|
-
const res = await fetchImpl(url, {
|
|
1636
|
-
method: "GET",
|
|
1637
|
-
signal: controller.signal
|
|
1638
|
-
});
|
|
1639
|
-
if (res.status >= 200 && res.status < 400) {
|
|
1640
|
-
return { status: "pass", name: `${provider} reachable`, detail: `${url} \u2192 ${res.status} OK` };
|
|
1641
|
-
}
|
|
1642
|
-
if (res.status === 401 || res.status === 403 || res.status === 405) {
|
|
1643
|
-
return { status: "pass", name: `${provider} reachable`, detail: `${url} \u2192 ${res.status} (host reachable)` };
|
|
1644
|
-
}
|
|
1645
|
-
return {
|
|
1646
|
-
status: "warn",
|
|
1647
|
-
name: `${provider} reachable`,
|
|
1648
|
-
detail: `${url} \u2192 HTTP ${res.status}`,
|
|
1649
|
-
fix: "Host reachable but returned unexpected status \u2014 check provider docs"
|
|
1650
|
-
};
|
|
1651
|
-
} catch (err) {
|
|
1652
|
-
const reason = err.name === "AbortError" ? `timeout after ${timeoutMs}ms` : err.message;
|
|
1653
|
-
return {
|
|
1654
|
-
status: "fail",
|
|
1655
|
-
name: `${provider} reachable`,
|
|
1656
|
-
detail: `${url} \u2192 ${reason}`,
|
|
1657
|
-
fix: provider === "ollama" ? "Start Ollama: `ollama serve` (or install from https://ollama.com)" : "Check network / firewall / VPN settings"
|
|
1658
|
-
};
|
|
1659
|
-
} finally {
|
|
1660
|
-
clearTimeout(timer);
|
|
1661
|
-
}
|
|
1702
|
+
function toolList(tools) {
|
|
1703
|
+
if (tools.length === 0) return "[]";
|
|
1704
|
+
const calls = tools.map((t) => {
|
|
1705
|
+
if (t === "web_search") return "webSearch()";
|
|
1706
|
+
if (t === "filesystem") return `...filesystem({ basePath: './workspace' })`;
|
|
1707
|
+
if (t === "shell") return `shell({ allowedCommands: ['ls', 'cat'] })`;
|
|
1708
|
+
return "";
|
|
1709
|
+
});
|
|
1710
|
+
return `[${calls.join(", ")}]`;
|
|
1662
1711
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
return {
|
|
1670
|
-
status: "pass",
|
|
1671
|
-
name: "AgentsKit config",
|
|
1672
|
-
detail: `loaded \u2014 defaults: ${JSON.stringify(config.defaults ?? {})}`
|
|
1673
|
-
};
|
|
1674
|
-
} catch (err) {
|
|
1675
|
-
return {
|
|
1676
|
-
status: "warn",
|
|
1677
|
-
name: "AgentsKit config",
|
|
1678
|
-
detail: `Could not load: ${err.message}`
|
|
1679
|
-
};
|
|
1680
|
-
}
|
|
1712
|
+
function memoryImport(memory) {
|
|
1713
|
+
if (memory === "file") return `import { fileChatMemory } from '@agentskit/memory'
|
|
1714
|
+
`;
|
|
1715
|
+
if (memory === "sqlite") return `import { sqliteChatMemory } from '@agentskit/memory'
|
|
1716
|
+
`;
|
|
1717
|
+
return "";
|
|
1681
1718
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
checkConfig()
|
|
1690
|
-
];
|
|
1691
|
-
for (const provider of providers2) {
|
|
1692
|
-
checks.push(checkProviderEnv(provider));
|
|
1693
|
-
if (!options.noNetwork) {
|
|
1694
|
-
checks.push(checkProviderReachable(provider, fetchImpl));
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
const results = await Promise.all(checks);
|
|
1719
|
+
function memoryCall(memory) {
|
|
1720
|
+
if (memory === "file") return `fileChatMemory('./.agentskit-history.json')`;
|
|
1721
|
+
if (memory === "sqlite") return `sqliteChatMemory({ path: './.agentskit-history.db' })`;
|
|
1722
|
+
return "undefined";
|
|
1723
|
+
}
|
|
1724
|
+
function demoAdapterSnippet() {
|
|
1725
|
+
return `function demoAdapter() {
|
|
1698
1726
|
return {
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1727
|
+
createSource: () => ({
|
|
1728
|
+
stream: async function* () {
|
|
1729
|
+
yield { type: 'text' as const, content: 'Hello from your AgentsKit starter. ' }
|
|
1730
|
+
yield { type: 'text' as const, content: 'Configure a real adapter to talk to a model.' }
|
|
1731
|
+
yield { type: 'done' as const }
|
|
1732
|
+
},
|
|
1733
|
+
abort: () => {},
|
|
1734
|
+
}),
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
`;
|
|
1705
1739
|
}
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
};
|
|
1712
|
-
function renderReport(report, opts = {}) {
|
|
1713
|
-
const color = opts.color ?? true;
|
|
1714
|
-
const c = (code, text) => color ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
1715
|
-
const colorFor = {
|
|
1716
|
-
pass: (t) => c("32", t),
|
|
1717
|
-
// green
|
|
1718
|
-
warn: (t) => c("33", t),
|
|
1719
|
-
// yellow
|
|
1720
|
-
fail: (t) => c("31", t),
|
|
1721
|
-
// red
|
|
1722
|
-
skip: (t) => c("90", t)
|
|
1723
|
-
// dim
|
|
1724
|
-
};
|
|
1725
|
-
const lines = [];
|
|
1726
|
-
lines.push("");
|
|
1727
|
-
lines.push(` ${c("1;36", "\u26A1 AgentsKit Doctor")}`);
|
|
1728
|
-
lines.push(` ${c("90", "\u2500".repeat(50))}`);
|
|
1729
|
-
lines.push("");
|
|
1730
|
-
const groups = {
|
|
1731
|
-
"Environment": [],
|
|
1732
|
-
"Providers": [],
|
|
1733
|
-
"Network": []
|
|
1740
|
+
function reactStarter(ctx) {
|
|
1741
|
+
const deps = {
|
|
1742
|
+
"@agentskit/react": "^0.4.0",
|
|
1743
|
+
react: "^19.0.0",
|
|
1744
|
+
"react-dom": "^19.0.0"
|
|
1734
1745
|
};
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1746
|
+
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
1747
|
+
if (ctx.tools.length > 0) deps["@agentskit/tools"] = "^0.4.0";
|
|
1748
|
+
if (ctx.memory !== "none") deps["@agentskit/memory"] = "^0.4.0";
|
|
1749
|
+
const includesDemo = ctx.provider === "demo";
|
|
1750
|
+
const adapter = ctx.provider === "demo" ? viteAdapterCall(ctx.provider) : viteAdapterCall(ctx.provider);
|
|
1751
|
+
const envKey = PROVIDER_ENV_KEY[ctx.provider];
|
|
1752
|
+
const envContent = envKey ? `VITE_${envKey}=
|
|
1753
|
+
` : "# No API key required for the local provider\n";
|
|
1754
|
+
return {
|
|
1755
|
+
"package.json": JSON.stringify(
|
|
1756
|
+
{
|
|
1757
|
+
name: path__default.default.basename(ctx.template === "react" ? "agentskit-react-app" : "agentskit-app"),
|
|
1758
|
+
private: true,
|
|
1759
|
+
type: "module",
|
|
1760
|
+
scripts: {
|
|
1761
|
+
dev: "vite",
|
|
1762
|
+
build: "vite build",
|
|
1763
|
+
preview: "vite preview"
|
|
1764
|
+
},
|
|
1765
|
+
dependencies: deps,
|
|
1766
|
+
devDependencies: {
|
|
1767
|
+
"@types/react": "^19.0.0",
|
|
1768
|
+
"@types/react-dom": "^19.0.0",
|
|
1769
|
+
"@vitejs/plugin-react": "^5.0.0",
|
|
1770
|
+
typescript: "^5.5.0",
|
|
1771
|
+
vite: "^7.0.0"
|
|
1772
|
+
}
|
|
1773
|
+
},
|
|
1774
|
+
null,
|
|
1775
|
+
2
|
|
1776
|
+
) + "\n",
|
|
1777
|
+
"index.html": `<!doctype html>
|
|
1778
|
+
<html lang="en">
|
|
1779
|
+
<head>
|
|
1780
|
+
<meta charset="UTF-8" />
|
|
1781
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1782
|
+
<title>AgentsKit React Starter</title>
|
|
1783
|
+
</head>
|
|
1784
|
+
<body>
|
|
1785
|
+
<div id="root"></div>
|
|
1786
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
1787
|
+
</body>
|
|
1788
|
+
</html>
|
|
1789
|
+
`,
|
|
1790
|
+
"vite.config.ts": `import { defineConfig } from 'vite'
|
|
1791
|
+
import react from '@vitejs/plugin-react'
|
|
1792
|
+
|
|
1793
|
+
export default defineConfig({ plugins: [react()] })
|
|
1794
|
+
`,
|
|
1795
|
+
"tsconfig.json": JSON.stringify(
|
|
1796
|
+
{
|
|
1797
|
+
compilerOptions: {
|
|
1798
|
+
target: "ES2022",
|
|
1799
|
+
lib: ["ES2022", "DOM"],
|
|
1800
|
+
module: "ESNext",
|
|
1801
|
+
moduleResolution: "bundler",
|
|
1802
|
+
jsx: "react-jsx",
|
|
1803
|
+
strict: true,
|
|
1804
|
+
noEmit: true,
|
|
1805
|
+
skipLibCheck: true
|
|
1806
|
+
},
|
|
1807
|
+
include: ["src"]
|
|
1808
|
+
},
|
|
1809
|
+
null,
|
|
1810
|
+
2
|
|
1811
|
+
) + "\n",
|
|
1812
|
+
"src/main.tsx": `import React from 'react'
|
|
1813
|
+
import ReactDOM from 'react-dom/client'
|
|
1814
|
+
import App from './App'
|
|
1815
|
+
|
|
1816
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
1817
|
+
<React.StrictMode>
|
|
1818
|
+
<App />
|
|
1819
|
+
</React.StrictMode>,
|
|
1820
|
+
)
|
|
1821
|
+
`,
|
|
1822
|
+
"src/App.tsx": `import { ChatContainer, InputBar, Message, useChat } from '@agentskit/react'
|
|
1823
|
+
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}${memoryImport(ctx.memory)}import '@agentskit/react/theme'
|
|
1824
|
+
|
|
1825
|
+
${includesDemo ? demoAdapterSnippet() : ""}export default function App() {
|
|
1826
|
+
const chat = useChat({
|
|
1827
|
+
adapter: ${adapter},${ctx.tools.length > 0 ? `
|
|
1828
|
+
tools: ${toolList(ctx.tools)},` : ""}${ctx.memory !== "none" ? `
|
|
1829
|
+
memory: ${memoryCall(ctx.memory)},` : ""}
|
|
1830
|
+
})
|
|
1831
|
+
|
|
1832
|
+
return (
|
|
1833
|
+
<ChatContainer>
|
|
1834
|
+
{chat.messages.map(message => (
|
|
1835
|
+
<Message key={message.id} message={message} />
|
|
1836
|
+
))}
|
|
1837
|
+
<InputBar chat={chat} />
|
|
1838
|
+
</ChatContainer>
|
|
1839
|
+
)
|
|
1772
1840
|
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
"**/dist/**",
|
|
1783
|
-
"**/build/**",
|
|
1784
|
-
"**/.next/**",
|
|
1785
|
-
"**/.turbo/**",
|
|
1786
|
-
"**/.git/**",
|
|
1787
|
-
"**/coverage/**",
|
|
1788
|
-
"**/*.test.ts",
|
|
1789
|
-
"**/*.spec.ts"
|
|
1790
|
-
];
|
|
1791
|
-
function startDev(options) {
|
|
1792
|
-
const entry = path3.resolve(process.cwd(), options.entry);
|
|
1793
|
-
if (!fs.existsSync(entry)) {
|
|
1794
|
-
throw new Error(`Entry file not found: ${entry}`);
|
|
1795
|
-
}
|
|
1796
|
-
const stdout = options.stdout ?? process.stdout;
|
|
1797
|
-
const stderr = options.stderr ?? process.stderr;
|
|
1798
|
-
const debounceMs = options.debounceMs ?? 200;
|
|
1799
|
-
const isTs = entry.endsWith(".ts") || entry.endsWith(".tsx");
|
|
1800
|
-
const cmd = isTs ? "tsx" : "node";
|
|
1801
|
-
const baseArgs = [entry, ...options.scriptArgs ?? []];
|
|
1802
|
-
const spawnFn = options.spawn ?? ((c, a) => child_process.spawn(c, a, {
|
|
1803
|
-
stdio: ["inherit", "pipe", "pipe"],
|
|
1804
|
-
env: { ...process.env, FORCE_COLOR: "1" }
|
|
1805
|
-
}));
|
|
1806
|
-
const watchPaths = options.watch ?? DEFAULT_WATCH;
|
|
1807
|
-
const ignorePaths = [...DEFAULT_IGNORE, ...options.ignore ?? []];
|
|
1808
|
-
const watcherFactory = options.watcher ?? ((paths, opts) => chokidar__default.default.watch(paths, { ignored: opts.ignored, ignoreInitial: true }));
|
|
1809
|
-
const watcher = watcherFactory(watchPaths, { ignored: ignorePaths });
|
|
1810
|
-
let child;
|
|
1811
|
-
let restartCount = 0;
|
|
1812
|
-
let restartTimer;
|
|
1813
|
-
let stopped = false;
|
|
1814
|
-
let resolveDone;
|
|
1815
|
-
const done = new Promise((r) => {
|
|
1816
|
-
resolveDone = r;
|
|
1817
|
-
});
|
|
1818
|
-
const banner = (msg, color = "green") => {
|
|
1819
|
-
const time = (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8);
|
|
1820
|
-
stdout.write(kleur__default.default[color](`[agentskit dev ${time}] `) + msg + "\n");
|
|
1841
|
+
`,
|
|
1842
|
+
".env.example": envContent,
|
|
1843
|
+
".gitignore": `node_modules
|
|
1844
|
+
dist
|
|
1845
|
+
.env
|
|
1846
|
+
.env.local
|
|
1847
|
+
.agentskit-history.*
|
|
1848
|
+
`,
|
|
1849
|
+
"README.md": readmeFor(ctx)
|
|
1821
1850
|
};
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
c.stderr?.on("data", (d) => stderr.write(d));
|
|
1829
|
-
c.on("exit", (code, signal) => {
|
|
1830
|
-
if (stopped) return;
|
|
1831
|
-
if (signal === "SIGTERM" || signal === "SIGINT") return;
|
|
1832
|
-
if (code === 0) {
|
|
1833
|
-
banner(`\u2713 exited cleanly \u2014 waiting for changes`, "green");
|
|
1834
|
-
} else {
|
|
1835
|
-
banner(`\u2717 exited with code ${code} \u2014 waiting for changes`, "red");
|
|
1836
|
-
}
|
|
1837
|
-
});
|
|
1851
|
+
}
|
|
1852
|
+
function inkStarter(ctx) {
|
|
1853
|
+
const deps = {
|
|
1854
|
+
"@agentskit/ink": "^0.4.0",
|
|
1855
|
+
ink: "^7.0.0",
|
|
1856
|
+
react: "^19.0.0"
|
|
1838
1857
|
};
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1858
|
+
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
1859
|
+
if (ctx.tools.length > 0) deps["@agentskit/tools"] = "^0.4.0";
|
|
1860
|
+
if (ctx.memory !== "none") deps["@agentskit/memory"] = "^0.4.0";
|
|
1861
|
+
return {
|
|
1862
|
+
"package.json": JSON.stringify(
|
|
1863
|
+
{
|
|
1864
|
+
name: "agentskit-ink-app",
|
|
1865
|
+
private: true,
|
|
1866
|
+
type: "module",
|
|
1867
|
+
scripts: {
|
|
1868
|
+
dev: "tsx src/index.tsx",
|
|
1869
|
+
start: "tsx src/index.tsx"
|
|
1870
|
+
},
|
|
1871
|
+
dependencies: deps,
|
|
1872
|
+
devDependencies: {
|
|
1873
|
+
"@types/react": "^19.0.0",
|
|
1874
|
+
"@types/react-dom": "^19.0.0",
|
|
1875
|
+
tsx: "^4.20.0",
|
|
1876
|
+
typescript: "^5.5.0"
|
|
1877
|
+
}
|
|
1878
|
+
},
|
|
1879
|
+
null,
|
|
1880
|
+
2
|
|
1881
|
+
) + "\n",
|
|
1882
|
+
"tsconfig.json": JSON.stringify(
|
|
1883
|
+
{
|
|
1884
|
+
compilerOptions: {
|
|
1885
|
+
target: "ES2022",
|
|
1886
|
+
module: "ESNext",
|
|
1887
|
+
moduleResolution: "bundler",
|
|
1888
|
+
jsx: "react-jsx",
|
|
1889
|
+
strict: true,
|
|
1890
|
+
noEmit: true,
|
|
1891
|
+
skipLibCheck: true
|
|
1892
|
+
},
|
|
1893
|
+
include: ["src"]
|
|
1894
|
+
},
|
|
1895
|
+
null,
|
|
1896
|
+
2
|
|
1897
|
+
) + "\n",
|
|
1898
|
+
"src/index.tsx": `import React from 'react'
|
|
1899
|
+
import { render } from 'ink'
|
|
1900
|
+
import { ChatContainer, InputBar, Message, useChat } from '@agentskit/ink'
|
|
1901
|
+
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}${memoryImport(ctx.memory)}
|
|
1902
|
+
${ctx.provider === "demo" ? demoAdapterSnippet() : ""}function App() {
|
|
1903
|
+
const chat = useChat({
|
|
1904
|
+
adapter: ${adapterCall(ctx.provider)},${ctx.tools.length > 0 ? `
|
|
1905
|
+
tools: ${toolList(ctx.tools)},` : ""}${ctx.memory !== "none" ? `
|
|
1906
|
+
memory: ${memoryCall(ctx.memory)},` : ""}
|
|
1907
|
+
})
|
|
1908
|
+
|
|
1909
|
+
return (
|
|
1910
|
+
<ChatContainer>
|
|
1911
|
+
{chat.messages.map(message => (
|
|
1912
|
+
<Message key={message.id} message={message} />
|
|
1913
|
+
))}
|
|
1914
|
+
<InputBar chat={chat} />
|
|
1915
|
+
</ChatContainer>
|
|
1916
|
+
)
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
render(<App />)
|
|
1920
|
+
`,
|
|
1921
|
+
".env.example": PROVIDER_ENV_KEY[ctx.provider] ? `${PROVIDER_ENV_KEY[ctx.provider]}=
|
|
1922
|
+
` : "# No API key required for the local provider\n",
|
|
1923
|
+
".gitignore": `node_modules
|
|
1924
|
+
.env
|
|
1925
|
+
.env.local
|
|
1926
|
+
.agentskit-history.*
|
|
1927
|
+
`,
|
|
1928
|
+
"README.md": readmeFor(ctx)
|
|
1849
1929
|
};
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
const stop = async () => {
|
|
1855
|
-
if (stopped) return;
|
|
1856
|
-
stopped = true;
|
|
1857
|
-
if (restartTimer) clearTimeout(restartTimer);
|
|
1858
|
-
if (child && !child.killed && child.exitCode === null) {
|
|
1859
|
-
child.kill("SIGTERM");
|
|
1860
|
-
}
|
|
1861
|
-
await watcher.close();
|
|
1862
|
-
banner(`stopped`, "cyan");
|
|
1863
|
-
resolveDone();
|
|
1930
|
+
}
|
|
1931
|
+
function runtimeStarter(ctx) {
|
|
1932
|
+
const deps = {
|
|
1933
|
+
"@agentskit/runtime": "^0.4.0"
|
|
1864
1934
|
};
|
|
1865
|
-
if (
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
process.stdin.on("data", (data) => {
|
|
1869
|
-
const key = data.toString();
|
|
1870
|
-
if (key === "r") restart("manual");
|
|
1871
|
-
if (key === "q" || key === "") void stop();
|
|
1872
|
-
});
|
|
1873
|
-
}
|
|
1935
|
+
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
1936
|
+
if (ctx.tools.length > 0) deps["@agentskit/tools"] = "^0.4.0";
|
|
1937
|
+
if (ctx.memory !== "none") deps["@agentskit/memory"] = "^0.4.0";
|
|
1874
1938
|
return {
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1939
|
+
"package.json": JSON.stringify(
|
|
1940
|
+
{
|
|
1941
|
+
name: "agentskit-runtime-app",
|
|
1942
|
+
private: true,
|
|
1943
|
+
type: "module",
|
|
1944
|
+
scripts: {
|
|
1945
|
+
start: "tsx src/index.ts",
|
|
1946
|
+
dev: "tsx src/index.ts"
|
|
1947
|
+
},
|
|
1948
|
+
dependencies: deps,
|
|
1949
|
+
devDependencies: {
|
|
1950
|
+
tsx: "^4.20.0",
|
|
1951
|
+
typescript: "^5.5.0"
|
|
1952
|
+
}
|
|
1953
|
+
},
|
|
1954
|
+
null,
|
|
1955
|
+
2
|
|
1956
|
+
) + "\n",
|
|
1957
|
+
"tsconfig.json": JSON.stringify(
|
|
1958
|
+
{
|
|
1959
|
+
compilerOptions: {
|
|
1960
|
+
target: "ES2022",
|
|
1961
|
+
module: "ESNext",
|
|
1962
|
+
moduleResolution: "bundler",
|
|
1963
|
+
strict: true,
|
|
1964
|
+
noEmit: true,
|
|
1965
|
+
skipLibCheck: true
|
|
1966
|
+
},
|
|
1967
|
+
include: ["src"]
|
|
1968
|
+
},
|
|
1969
|
+
null,
|
|
1970
|
+
2
|
|
1971
|
+
) + "\n",
|
|
1972
|
+
"src/index.ts": `import { createRuntime } from '@agentskit/runtime'
|
|
1973
|
+
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}${memoryImport(ctx.memory)}
|
|
1974
|
+
${ctx.provider === "demo" ? demoAdapterSnippet() : ""}const runtime = createRuntime({
|
|
1975
|
+
adapter: ${adapterCall(ctx.provider)},${ctx.tools.length > 0 ? `
|
|
1976
|
+
tools: ${toolList(ctx.tools)},` : ""}${ctx.memory !== "none" ? `
|
|
1977
|
+
memory: ${memoryCall(ctx.memory)},` : ""}
|
|
1978
|
+
maxSteps: 10,
|
|
1979
|
+
})
|
|
1980
|
+
|
|
1981
|
+
const task = process.argv.slice(2).join(' ') || 'Say hello and tell me one fact about TypeScript.'
|
|
1982
|
+
const result = await runtime.run(task)
|
|
1983
|
+
|
|
1984
|
+
console.log(result.content)
|
|
1985
|
+
console.log(\`\\n\u2014 \${result.steps} steps \xB7 \${result.toolCalls.length} tool calls \xB7 \${result.durationMs}ms\`)
|
|
1986
|
+
`,
|
|
1987
|
+
".env.example": PROVIDER_ENV_KEY[ctx.provider] ? `${PROVIDER_ENV_KEY[ctx.provider]}=
|
|
1988
|
+
` : "# No API key required for the local provider\n",
|
|
1989
|
+
".gitignore": `node_modules
|
|
1990
|
+
.env
|
|
1991
|
+
.env.local
|
|
1992
|
+
.agentskit-history.*
|
|
1993
|
+
`,
|
|
1994
|
+
"README.md": readmeFor(ctx)
|
|
1878
1995
|
};
|
|
1879
1996
|
}
|
|
1880
|
-
|
|
1881
|
-
const
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
return await lt(opts);
|
|
1885
|
-
});
|
|
1886
|
-
const banner = (msg, color = "green") => {
|
|
1887
|
-
const time = (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8);
|
|
1888
|
-
stdout.write(kleur__default.default[color](`[agentskit tunnel ${time}] `) + msg + "\n");
|
|
1997
|
+
function multiAgentStarter(ctx) {
|
|
1998
|
+
const deps = {
|
|
1999
|
+
"@agentskit/runtime": "^0.4.0",
|
|
2000
|
+
"@agentskit/skills": "^0.4.0"
|
|
1889
2001
|
};
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
2002
|
+
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
2003
|
+
if (ctx.tools.length === 0) ctx.tools = ["web_search"];
|
|
2004
|
+
deps["@agentskit/tools"] = "^0.4.0";
|
|
2005
|
+
return {
|
|
2006
|
+
"package.json": JSON.stringify(
|
|
2007
|
+
{
|
|
2008
|
+
name: "agentskit-multi-agent",
|
|
2009
|
+
private: true,
|
|
2010
|
+
type: "module",
|
|
2011
|
+
scripts: {
|
|
2012
|
+
start: "tsx src/index.ts",
|
|
2013
|
+
dev: "tsx src/index.ts"
|
|
2014
|
+
},
|
|
2015
|
+
dependencies: deps,
|
|
2016
|
+
devDependencies: {
|
|
2017
|
+
tsx: "^4.20.0",
|
|
2018
|
+
typescript: "^5.5.0"
|
|
2019
|
+
}
|
|
2020
|
+
},
|
|
2021
|
+
null,
|
|
2022
|
+
2
|
|
2023
|
+
) + "\n",
|
|
2024
|
+
"tsconfig.json": JSON.stringify(
|
|
2025
|
+
{
|
|
2026
|
+
compilerOptions: {
|
|
2027
|
+
target: "ES2022",
|
|
2028
|
+
module: "ESNext",
|
|
2029
|
+
moduleResolution: "bundler",
|
|
2030
|
+
strict: true,
|
|
2031
|
+
noEmit: true,
|
|
2032
|
+
skipLibCheck: true
|
|
2033
|
+
},
|
|
2034
|
+
include: ["src"]
|
|
2035
|
+
},
|
|
2036
|
+
null,
|
|
2037
|
+
2
|
|
2038
|
+
) + "\n",
|
|
2039
|
+
"src/index.ts": `import { createRuntime } from '@agentskit/runtime'
|
|
2040
|
+
import { planner, researcher } from '@agentskit/skills'
|
|
2041
|
+
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}
|
|
2042
|
+
${ctx.provider === "demo" ? demoAdapterSnippet() : ""}const runtime = createRuntime({
|
|
2043
|
+
adapter: ${adapterCall(ctx.provider)},
|
|
2044
|
+
maxSteps: 10,
|
|
2045
|
+
maxDelegationDepth: 2,
|
|
2046
|
+
})
|
|
2047
|
+
|
|
2048
|
+
const task = process.argv.slice(2).join(' ') || 'Research the current state of WebGPU and summarize.'
|
|
2049
|
+
|
|
2050
|
+
const result = await runtime.run(task, {
|
|
2051
|
+
skill: planner,
|
|
2052
|
+
delegates: {
|
|
2053
|
+
researcher: {
|
|
2054
|
+
skill: researcher,
|
|
2055
|
+
tools: ${toolList(ctx.tools)},
|
|
2056
|
+
maxSteps: 5,
|
|
2057
|
+
},
|
|
2058
|
+
},
|
|
2059
|
+
})
|
|
1922
2060
|
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
process.on("SIGINT", () => {
|
|
1934
|
-
void stop();
|
|
1935
|
-
});
|
|
1936
|
-
}
|
|
1937
|
-
return {
|
|
1938
|
-
url: tunnel.url,
|
|
1939
|
-
done,
|
|
1940
|
-
stop,
|
|
1941
|
-
requests: () => requests
|
|
2061
|
+
console.log(result.content)
|
|
2062
|
+
console.log(\`\\n\u2014 \${result.steps} steps \xB7 \${result.toolCalls.length} tool calls\`)
|
|
2063
|
+
`,
|
|
2064
|
+
".env.example": PROVIDER_ENV_KEY[ctx.provider] ? `${PROVIDER_ENV_KEY[ctx.provider]}=
|
|
2065
|
+
` : "# No API key required for the local provider\n",
|
|
2066
|
+
".gitignore": `node_modules
|
|
2067
|
+
.env
|
|
2068
|
+
.env.local
|
|
2069
|
+
`,
|
|
2070
|
+
"README.md": readmeFor(ctx)
|
|
1942
2071
|
};
|
|
1943
2072
|
}
|
|
2073
|
+
function readmeFor(ctx) {
|
|
2074
|
+
const installCmd = ctx.pm === "npm" ? "npm install" : `${ctx.pm} install`;
|
|
2075
|
+
const runCmd = ctx.pm === "npm" ? "npm run dev" : `${ctx.pm} dev`;
|
|
2076
|
+
const envKey = PROVIDER_ENV_KEY[ctx.provider];
|
|
2077
|
+
return `# AgentsKit ${ctx.template} starter
|
|
1944
2078
|
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2079
|
+
Generated by \`agentskit init\`.
|
|
2080
|
+
|
|
2081
|
+
## Stack
|
|
2082
|
+
|
|
2083
|
+
- **Template**: \`${ctx.template}\`
|
|
2084
|
+
- **Provider**: \`${ctx.provider}\`${ctx.tools.length ? `
|
|
2085
|
+
- **Tools**: ${ctx.tools.map((t) => `\`${t}\``).join(", ")}` : ""}${ctx.memory !== "none" ? `
|
|
2086
|
+
- **Memory**: \`${ctx.memory}\`` : ""}
|
|
2087
|
+
|
|
2088
|
+
## Run
|
|
2089
|
+
|
|
2090
|
+
\`\`\`bash
|
|
2091
|
+
${installCmd}
|
|
2092
|
+
${envKey ? `cp .env.example .env
|
|
2093
|
+
# add ${envKey}=...` : "# No API key required"}
|
|
2094
|
+
${runCmd}
|
|
2095
|
+
\`\`\`
|
|
2096
|
+
|
|
2097
|
+
## Next steps
|
|
2098
|
+
|
|
2099
|
+
- Open the AgentsKit docs at https://www.agentskit.io/docs
|
|
2100
|
+
- Add a custom skill: https://www.agentskit.io/docs/concepts/skill
|
|
2101
|
+
- Wire up RAG: https://www.agentskit.io/docs/recipes/rag-chat
|
|
2102
|
+
|
|
2103
|
+
## License
|
|
2104
|
+
|
|
2105
|
+
ISC
|
|
2106
|
+
`;
|
|
2107
|
+
}
|
|
2108
|
+
var TEMPLATE_FN = {
|
|
2109
|
+
react: reactStarter,
|
|
2110
|
+
ink: inkStarter,
|
|
2111
|
+
runtime: runtimeStarter,
|
|
2112
|
+
"multi-agent": multiAgentStarter
|
|
2113
|
+
};
|
|
2114
|
+
async function writeStarterProject(options) {
|
|
2115
|
+
const ctx = {
|
|
2116
|
+
template: options.template,
|
|
2117
|
+
provider: options.provider ?? "demo",
|
|
2118
|
+
tools: options.tools ?? [],
|
|
2119
|
+
memory: options.memory ?? "none",
|
|
2120
|
+
pm: options.packageManager ?? "pnpm"
|
|
1961
2121
|
};
|
|
2122
|
+
const files = TEMPLATE_FN[ctx.template](ctx);
|
|
2123
|
+
await promises.mkdir(options.targetDir, { recursive: true });
|
|
2124
|
+
await Promise.all(
|
|
2125
|
+
Object.entries(files).map(async ([relativePath, content]) => {
|
|
2126
|
+
const absolutePath = path__default.default.join(options.targetDir, relativePath);
|
|
2127
|
+
await promises.mkdir(path__default.default.dirname(absolutePath), { recursive: true });
|
|
2128
|
+
await promises.writeFile(absolutePath, content, "utf8");
|
|
2129
|
+
})
|
|
2130
|
+
);
|
|
1962
2131
|
}
|
|
1963
|
-
function
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
const
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
${preview}
|
|
1978
|
-
`
|
|
1979
|
-
);
|
|
2132
|
+
async function runInteractiveInit(defaults = {}) {
|
|
2133
|
+
process.stdout.write(`
|
|
2134
|
+
${kleur__default.default.bold().green("\u25B2")} ${kleur__default.default.bold("agentskit init")}
|
|
2135
|
+
`);
|
|
2136
|
+
process.stdout.write(kleur__default.default.dim(" Generate a starter project \u2014 answer five questions.\n\n"));
|
|
2137
|
+
try {
|
|
2138
|
+
const targetDir = await prompts.input({
|
|
2139
|
+
message: "Project directory:",
|
|
2140
|
+
default: defaults.dir ?? "agentskit-app",
|
|
2141
|
+
validate: (value) => {
|
|
2142
|
+
if (!value.trim()) return "A directory name is required.";
|
|
2143
|
+
const abs = path__default.default.resolve(process.cwd(), value);
|
|
2144
|
+
if (fs.existsSync(abs)) return `${value} already exists. Pick a different name.`;
|
|
2145
|
+
return true;
|
|
1980
2146
|
}
|
|
1981
|
-
return;
|
|
1982
|
-
}
|
|
1983
|
-
const config = options.config !== false ? await loadConfig() : void 0;
|
|
1984
|
-
const merged = mergeWithConfig(options, config);
|
|
1985
|
-
const session = resolveSession({
|
|
1986
|
-
explicitPath: options.memory,
|
|
1987
|
-
forceNew: Boolean(options.new),
|
|
1988
|
-
resumeId: options.resume
|
|
1989
2147
|
});
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
`
|
|
2022
|
-
Session saved. Resume with:
|
|
2023
|
-
agentskit chat --resume ${session.id}
|
|
2024
|
-
Or start fresh with:
|
|
2025
|
-
agentskit chat --new
|
|
2026
|
-
`
|
|
2027
|
-
);
|
|
2148
|
+
const template = await prompts.select({
|
|
2149
|
+
message: "Template:",
|
|
2150
|
+
default: defaults.template ?? "react",
|
|
2151
|
+
choices: [
|
|
2152
|
+
{ name: "React chat (Vite + browser)", value: "react", description: "Streaming UI with @agentskit/react" },
|
|
2153
|
+
{ name: "Ink chat (terminal UI)", value: "ink", description: "Same chat but in your terminal" },
|
|
2154
|
+
{ name: "Runtime (headless agent, no UI)", value: "runtime", description: "Autonomous task \u2192 result" },
|
|
2155
|
+
{ name: "Multi-agent (planner + delegates)", value: "multi-agent", description: "Supervisor pattern, ready to extend" }
|
|
2156
|
+
]
|
|
2157
|
+
});
|
|
2158
|
+
const provider = await prompts.select({
|
|
2159
|
+
message: "LLM provider:",
|
|
2160
|
+
default: "demo",
|
|
2161
|
+
choices: [
|
|
2162
|
+
{ name: "Demo (no API key \u2014 deterministic stub)", value: "demo" },
|
|
2163
|
+
{ name: "OpenAI", value: "openai" },
|
|
2164
|
+
{ name: "Anthropic", value: "anthropic" },
|
|
2165
|
+
{ name: "Gemini", value: "gemini" },
|
|
2166
|
+
{ name: "Ollama (local, no key)", value: "ollama" }
|
|
2167
|
+
]
|
|
2168
|
+
});
|
|
2169
|
+
let tools = [];
|
|
2170
|
+
if (template !== "react") {
|
|
2171
|
+
tools = await prompts.checkbox({
|
|
2172
|
+
message: "Tools (space to toggle, enter to confirm):",
|
|
2173
|
+
choices: [
|
|
2174
|
+
{ name: "web_search", value: "web_search" },
|
|
2175
|
+
{ name: "filesystem", value: "filesystem" },
|
|
2176
|
+
{ name: "shell", value: "shell" }
|
|
2177
|
+
]
|
|
2178
|
+
});
|
|
2028
2179
|
}
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2180
|
+
const memory = await prompts.select({
|
|
2181
|
+
message: "Memory backend:",
|
|
2182
|
+
default: "none",
|
|
2183
|
+
choices: [
|
|
2184
|
+
{ name: "None (stateless)", value: "none" },
|
|
2185
|
+
{ name: "File (JSON on disk)", value: "file" },
|
|
2186
|
+
{ name: "SQLite (better-sqlite3)", value: "sqlite" }
|
|
2187
|
+
]
|
|
2188
|
+
});
|
|
2189
|
+
const packageManager = await prompts.select({
|
|
2190
|
+
message: "Package manager:",
|
|
2191
|
+
default: "pnpm",
|
|
2192
|
+
choices: [
|
|
2193
|
+
{ name: "pnpm", value: "pnpm" },
|
|
2194
|
+
{ name: "npm", value: "npm" },
|
|
2195
|
+
{ name: "yarn", value: "yarn" },
|
|
2196
|
+
{ name: "bun", value: "bun" }
|
|
2197
|
+
]
|
|
2198
|
+
});
|
|
2199
|
+
process.stdout.write("\n" + kleur__default.default.dim(" Summary:\n"));
|
|
2200
|
+
process.stdout.write(kleur__default.default.dim(` dir ${targetDir}
|
|
2201
|
+
`));
|
|
2202
|
+
process.stdout.write(kleur__default.default.dim(` template ${template}
|
|
2203
|
+
`));
|
|
2204
|
+
process.stdout.write(kleur__default.default.dim(` provider ${provider}
|
|
2205
|
+
`));
|
|
2206
|
+
if (tools.length) process.stdout.write(kleur__default.default.dim(` tools ${tools.join(", ")}
|
|
2207
|
+
`));
|
|
2208
|
+
process.stdout.write(kleur__default.default.dim(` memory ${memory}
|
|
2209
|
+
`));
|
|
2210
|
+
process.stdout.write(kleur__default.default.dim(` pm ${packageManager}
|
|
2211
|
+
|
|
2212
|
+
`));
|
|
2213
|
+
const proceed = await prompts.confirm({ message: "Generate?", default: true });
|
|
2214
|
+
if (!proceed) {
|
|
2215
|
+
process.stdout.write(kleur__default.default.yellow("Cancelled.\n"));
|
|
2216
|
+
return { cancelled: true, options: { targetDir, template, provider, tools, memory, packageManager } };
|
|
2035
2217
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
`);
|
|
2046
|
-
process.exit(1);
|
|
2218
|
+
return {
|
|
2219
|
+
cancelled: false,
|
|
2220
|
+
options: {
|
|
2221
|
+
targetDir: path__default.default.resolve(process.cwd(), targetDir),
|
|
2222
|
+
template,
|
|
2223
|
+
provider,
|
|
2224
|
+
tools,
|
|
2225
|
+
memory,
|
|
2226
|
+
packageManager
|
|
2047
2227
|
}
|
|
2228
|
+
};
|
|
2229
|
+
} catch (err) {
|
|
2230
|
+
if (err.name === "ExitPromptError") {
|
|
2231
|
+
process.stdout.write(kleur__default.default.yellow("\nCancelled.\n"));
|
|
2232
|
+
return { cancelled: true, options: { targetDir: "", template: "react" } };
|
|
2048
2233
|
}
|
|
2049
|
-
|
|
2234
|
+
throw err;
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
function printNextSteps(options) {
|
|
2238
|
+
const dir = path__default.default.relative(process.cwd(), options.targetDir) || ".";
|
|
2239
|
+
const pm = options.packageManager ?? "pnpm";
|
|
2240
|
+
const installCmd = pm === "npm" ? "npm install" : `${pm} install`;
|
|
2241
|
+
const runCmd = pm === "npm" ? "npm run dev" : `${pm} dev`;
|
|
2242
|
+
process.stdout.write("\n" + kleur__default.default.green("\u2713 Created starter at ") + kleur__default.default.bold(dir) + "\n\n");
|
|
2243
|
+
process.stdout.write(kleur__default.default.bold("Next steps:\n\n"));
|
|
2244
|
+
process.stdout.write(` ${kleur__default.default.cyan("cd")} ${dir}
|
|
2245
|
+
`);
|
|
2246
|
+
process.stdout.write(` ${kleur__default.default.cyan(installCmd)}
|
|
2247
|
+
`);
|
|
2248
|
+
if (options.provider && options.provider !== "demo" && options.provider !== "ollama") {
|
|
2249
|
+
process.stdout.write(
|
|
2250
|
+
` ${kleur__default.default.cyan("cp")} .env.example .env ${kleur__default.default.dim("# add your API key")}
|
|
2251
|
+
`
|
|
2252
|
+
);
|
|
2253
|
+
}
|
|
2254
|
+
process.stdout.write(` ${kleur__default.default.cyan(runCmd)}
|
|
2255
|
+
|
|
2256
|
+
`);
|
|
2257
|
+
process.stdout.write(kleur__default.default.dim(" Docs: https://www.agentskit.io/docs\n\n"));
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
// src/commands/init.ts
|
|
2261
|
+
function registerInitCommand(program) {
|
|
2050
2262
|
program.command("init").description("Generate a starter project. Run with no flags for interactive mode.").option("--template <template>", "Starter template (react|ink|runtime|multi-agent)").option("--dir <directory>", "Target directory", "agentskit-app").option("--provider <provider>", "LLM provider (openai|anthropic|gemini|ollama|demo)").option("--tools <tools>", "Comma-separated tools (web_search,filesystem,shell)").option("--memory <backend>", "Memory backend (none|file|sqlite)").option("--pm <packageManager>", "Package manager (pnpm|npm|yarn|bun)").option("-y, --yes", "Skip interactive prompts; use flag values + defaults").action(async (rawOptions) => {
|
|
2051
2263
|
const isCi = !process.stdout.isTTY || rawOptions.yes || rawOptions.template;
|
|
2052
2264
|
let resolved;
|
|
2053
2265
|
if (isCi) {
|
|
2054
2266
|
const template = rawOptions.template ?? "react";
|
|
2055
2267
|
resolved = {
|
|
2056
|
-
targetDir:
|
|
2268
|
+
targetDir: path__default.default.resolve(process.cwd(), rawOptions.dir),
|
|
2057
2269
|
template,
|
|
2058
2270
|
provider: rawOptions.provider ?? "demo",
|
|
2059
2271
|
tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
|
|
@@ -2073,13 +2285,282 @@ Or start fresh with:
|
|
|
2073
2285
|
await writeStarterProject(resolved);
|
|
2074
2286
|
if (isCi) {
|
|
2075
2287
|
process.stdout.write(
|
|
2076
|
-
`Created ${resolved.template} starter in ${
|
|
2288
|
+
`Created ${resolved.template} starter in ${path__default.default.relative(process.cwd(), resolved.targetDir) || "."}
|
|
2077
2289
|
`
|
|
2078
2290
|
);
|
|
2079
2291
|
} else {
|
|
2080
|
-
printNextSteps(resolved);
|
|
2292
|
+
printNextSteps(resolved);
|
|
2293
|
+
}
|
|
2294
|
+
});
|
|
2295
|
+
}
|
|
2296
|
+
var PROVIDER_ENV_KEYS = {
|
|
2297
|
+
openai: "OPENAI_API_KEY",
|
|
2298
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
2299
|
+
gemini: "GEMINI_API_KEY",
|
|
2300
|
+
deepseek: "DEEPSEEK_API_KEY",
|
|
2301
|
+
grok: "XAI_API_KEY",
|
|
2302
|
+
kimi: "KIMI_API_KEY"
|
|
2303
|
+
};
|
|
2304
|
+
var PROVIDER_REACH_URLS = {
|
|
2305
|
+
openai: "https://api.openai.com/v1/models",
|
|
2306
|
+
anthropic: "https://api.anthropic.com/v1/messages",
|
|
2307
|
+
gemini: "https://generativelanguage.googleapis.com/v1beta/models",
|
|
2308
|
+
ollama: "http://localhost:11434/api/tags"
|
|
2309
|
+
};
|
|
2310
|
+
async function checkNodeVersion() {
|
|
2311
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
2312
|
+
if (Number.isNaN(major)) {
|
|
2313
|
+
return { status: "fail", name: "Node version", detail: "Could not parse process.versions.node" };
|
|
2314
|
+
}
|
|
2315
|
+
if (major < 22) {
|
|
2316
|
+
return {
|
|
2317
|
+
status: "fail",
|
|
2318
|
+
name: "Node version",
|
|
2319
|
+
detail: `Node ${process.versions.node} (need 22+)`,
|
|
2320
|
+
fix: "Install Node 22 LTS or newer (https://nodejs.org)"
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
if (major === 25) {
|
|
2324
|
+
return {
|
|
2325
|
+
status: "warn",
|
|
2326
|
+
name: "Node version",
|
|
2327
|
+
detail: `Node ${process.versions.node} \u2014 Docusaurus apps may break here`,
|
|
2328
|
+
fix: "Use Node 22 LTS for the legacy docs app, or stay on 25 for everything else"
|
|
2329
|
+
};
|
|
2330
|
+
}
|
|
2331
|
+
return { status: "pass", name: "Node version", detail: `Node ${process.versions.node}` };
|
|
2332
|
+
}
|
|
2333
|
+
async function checkPnpm() {
|
|
2334
|
+
const cwd = process.cwd();
|
|
2335
|
+
const hasPnpm = fs.existsSync(path.join(cwd, "pnpm-lock.yaml")) || fs.existsSync(path.join(cwd, "pnpm-workspace.yaml"));
|
|
2336
|
+
if (hasPnpm) {
|
|
2337
|
+
return { status: "pass", name: "Package manager", detail: "pnpm detected (lockfile)" };
|
|
2338
|
+
}
|
|
2339
|
+
if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
|
|
2340
|
+
return { status: "warn", name: "Package manager", detail: "npm detected \u2014 pnpm recommended for monorepo workflows" };
|
|
2341
|
+
}
|
|
2342
|
+
if (fs.existsSync(path.join(cwd, "yarn.lock"))) {
|
|
2343
|
+
return { status: "pass", name: "Package manager", detail: "yarn detected" };
|
|
2344
|
+
}
|
|
2345
|
+
if (fs.existsSync(path.join(cwd, "bun.lock")) || fs.existsSync(path.join(cwd, "bun.lockb"))) {
|
|
2346
|
+
return { status: "pass", name: "Package manager", detail: "bun detected" };
|
|
2347
|
+
}
|
|
2348
|
+
return {
|
|
2349
|
+
status: "skip",
|
|
2350
|
+
name: "Package manager",
|
|
2351
|
+
detail: "No lockfile found in cwd"
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2354
|
+
async function checkPackageJson() {
|
|
2355
|
+
const path5 = path.join(process.cwd(), "package.json");
|
|
2356
|
+
if (!fs.existsSync(path5)) {
|
|
2357
|
+
return {
|
|
2358
|
+
status: "warn",
|
|
2359
|
+
name: "package.json",
|
|
2360
|
+
detail: "No package.json in cwd",
|
|
2361
|
+
fix: "Run from a project directory (or use `agentskit init` to create one)"
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
try {
|
|
2365
|
+
const pkg = JSON.parse(await promises.readFile(path5, "utf8"));
|
|
2366
|
+
const deps = {
|
|
2367
|
+
...pkg.dependencies ?? {},
|
|
2368
|
+
...pkg.devDependencies ?? {}
|
|
2369
|
+
};
|
|
2370
|
+
const akDeps = Object.entries(deps).filter(([name]) => name.startsWith("@agentskit/"));
|
|
2371
|
+
if (akDeps.length === 0) {
|
|
2372
|
+
return { status: "skip", name: "AgentsKit packages", detail: "No @agentskit/* deps found in package.json" };
|
|
2373
|
+
}
|
|
2374
|
+
return {
|
|
2375
|
+
status: "pass",
|
|
2376
|
+
name: "AgentsKit packages",
|
|
2377
|
+
detail: `${akDeps.length} installed: ${akDeps.map(([n]) => n.replace("@agentskit/", "")).join(", ")}`
|
|
2378
|
+
};
|
|
2379
|
+
} catch (err) {
|
|
2380
|
+
return {
|
|
2381
|
+
status: "fail",
|
|
2382
|
+
name: "package.json",
|
|
2383
|
+
detail: `Could not parse: ${err.message}`
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
async function checkProviderEnv(provider) {
|
|
2388
|
+
const envKey = PROVIDER_ENV_KEYS[provider];
|
|
2389
|
+
if (!envKey) {
|
|
2390
|
+
return { status: "skip", name: `${provider} API key`, detail: "No env-key requirement for this provider" };
|
|
2391
|
+
}
|
|
2392
|
+
const value = process.env[envKey];
|
|
2393
|
+
if (!value) {
|
|
2394
|
+
return {
|
|
2395
|
+
status: "skip",
|
|
2396
|
+
name: `${provider} API key`,
|
|
2397
|
+
detail: `${envKey} not set`,
|
|
2398
|
+
fix: `export ${envKey}=... (only needed if you use ${provider})`
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
if (value.length < 16) {
|
|
2402
|
+
return {
|
|
2403
|
+
status: "warn",
|
|
2404
|
+
name: `${provider} API key`,
|
|
2405
|
+
detail: `${envKey} looks too short (${value.length} chars)`,
|
|
2406
|
+
fix: "Verify the key is complete and not truncated"
|
|
2407
|
+
};
|
|
2408
|
+
}
|
|
2409
|
+
return { status: "pass", name: `${provider} API key`, detail: `${envKey} set (${value.length} chars)` };
|
|
2410
|
+
}
|
|
2411
|
+
async function checkProviderReachable(provider, fetchImpl = fetch, timeoutMs = 4e3) {
|
|
2412
|
+
const url = PROVIDER_REACH_URLS[provider];
|
|
2413
|
+
if (!url) {
|
|
2414
|
+
return { status: "skip", name: `${provider} reachable`, detail: "No reachability check configured" };
|
|
2415
|
+
}
|
|
2416
|
+
const envKey = PROVIDER_ENV_KEYS[provider];
|
|
2417
|
+
if (envKey && !process.env[envKey]) {
|
|
2418
|
+
return { status: "skip", name: `${provider} reachable`, detail: "Skipped \u2014 no API key configured" };
|
|
2419
|
+
}
|
|
2420
|
+
const controller = new AbortController();
|
|
2421
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2422
|
+
try {
|
|
2423
|
+
const res = await fetchImpl(url, {
|
|
2424
|
+
method: "GET",
|
|
2425
|
+
signal: controller.signal
|
|
2426
|
+
});
|
|
2427
|
+
if (res.status >= 200 && res.status < 400) {
|
|
2428
|
+
return { status: "pass", name: `${provider} reachable`, detail: `${url} \u2192 ${res.status} OK` };
|
|
2429
|
+
}
|
|
2430
|
+
if (res.status === 401 || res.status === 403 || res.status === 405) {
|
|
2431
|
+
return { status: "pass", name: `${provider} reachable`, detail: `${url} \u2192 ${res.status} (host reachable)` };
|
|
2432
|
+
}
|
|
2433
|
+
return {
|
|
2434
|
+
status: "warn",
|
|
2435
|
+
name: `${provider} reachable`,
|
|
2436
|
+
detail: `${url} \u2192 HTTP ${res.status}`,
|
|
2437
|
+
fix: "Host reachable but returned unexpected status \u2014 check provider docs"
|
|
2438
|
+
};
|
|
2439
|
+
} catch (err) {
|
|
2440
|
+
const reason = err.name === "AbortError" ? `timeout after ${timeoutMs}ms` : err.message;
|
|
2441
|
+
return {
|
|
2442
|
+
status: "fail",
|
|
2443
|
+
name: `${provider} reachable`,
|
|
2444
|
+
detail: `${url} \u2192 ${reason}`,
|
|
2445
|
+
fix: provider === "ollama" ? "Start Ollama: `ollama serve` (or install from https://ollama.com)" : "Check network / firewall / VPN settings"
|
|
2446
|
+
};
|
|
2447
|
+
} finally {
|
|
2448
|
+
clearTimeout(timer);
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
async function checkConfig() {
|
|
2452
|
+
try {
|
|
2453
|
+
const config = await loadConfig();
|
|
2454
|
+
if (!config) {
|
|
2455
|
+
return { status: "skip", name: "AgentsKit config", detail: "No .agentskit.config or package.json#agentskit found" };
|
|
2456
|
+
}
|
|
2457
|
+
return {
|
|
2458
|
+
status: "pass",
|
|
2459
|
+
name: "AgentsKit config",
|
|
2460
|
+
detail: `loaded \u2014 defaults: ${JSON.stringify(config.defaults ?? {})}`
|
|
2461
|
+
};
|
|
2462
|
+
} catch (err) {
|
|
2463
|
+
return {
|
|
2464
|
+
status: "warn",
|
|
2465
|
+
name: "AgentsKit config",
|
|
2466
|
+
detail: `Could not load: ${err.message}`
|
|
2467
|
+
};
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
async function runDoctor(options = {}) {
|
|
2471
|
+
const providers2 = options.providers ?? ["openai", "anthropic", "gemini", "ollama"];
|
|
2472
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
2473
|
+
const checks = [
|
|
2474
|
+
checkNodeVersion(),
|
|
2475
|
+
checkPnpm(),
|
|
2476
|
+
checkPackageJson(),
|
|
2477
|
+
checkConfig()
|
|
2478
|
+
];
|
|
2479
|
+
for (const provider of providers2) {
|
|
2480
|
+
checks.push(checkProviderEnv(provider));
|
|
2481
|
+
if (!options.noNetwork) {
|
|
2482
|
+
checks.push(checkProviderReachable(provider, fetchImpl));
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
const results = await Promise.all(checks);
|
|
2486
|
+
return {
|
|
2487
|
+
results,
|
|
2488
|
+
pass: results.filter((r) => r.status === "pass").length,
|
|
2489
|
+
warn: results.filter((r) => r.status === "warn").length,
|
|
2490
|
+
fail: results.filter((r) => r.status === "fail").length,
|
|
2491
|
+
skip: results.filter((r) => r.status === "skip").length
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
var ICON = {
|
|
2495
|
+
pass: "\u2714",
|
|
2496
|
+
warn: "\u26A0",
|
|
2497
|
+
fail: "\u2718",
|
|
2498
|
+
skip: "\u25CB"
|
|
2499
|
+
};
|
|
2500
|
+
function renderReport(report, opts = {}) {
|
|
2501
|
+
const color = opts.color ?? true;
|
|
2502
|
+
const c = (code, text) => color ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
2503
|
+
const colorFor = {
|
|
2504
|
+
pass: (t) => c("32", t),
|
|
2505
|
+
// green
|
|
2506
|
+
warn: (t) => c("33", t),
|
|
2507
|
+
// yellow
|
|
2508
|
+
fail: (t) => c("31", t),
|
|
2509
|
+
// red
|
|
2510
|
+
skip: (t) => c("90", t)
|
|
2511
|
+
// dim
|
|
2512
|
+
};
|
|
2513
|
+
const lines = [];
|
|
2514
|
+
lines.push("");
|
|
2515
|
+
lines.push(` ${c("1;36", "\u26A1 AgentsKit Doctor")}`);
|
|
2516
|
+
lines.push(` ${c("90", "\u2500".repeat(50))}`);
|
|
2517
|
+
lines.push("");
|
|
2518
|
+
const groups = {
|
|
2519
|
+
"Environment": [],
|
|
2520
|
+
"Providers": [],
|
|
2521
|
+
"Network": []
|
|
2522
|
+
};
|
|
2523
|
+
for (const r of report.results) {
|
|
2524
|
+
if (r.name.includes("reachable")) {
|
|
2525
|
+
groups["Network"].push(r);
|
|
2526
|
+
} else if (r.name.includes("API key")) {
|
|
2527
|
+
groups["Providers"].push(r);
|
|
2528
|
+
} else {
|
|
2529
|
+
groups["Environment"].push(r);
|
|
2081
2530
|
}
|
|
2082
|
-
}
|
|
2531
|
+
}
|
|
2532
|
+
for (const [group, results] of Object.entries(groups)) {
|
|
2533
|
+
if (results.length === 0) continue;
|
|
2534
|
+
lines.push(` ${c("1", group)}`);
|
|
2535
|
+
for (const r of results) {
|
|
2536
|
+
const icon = colorFor[r.status](ICON[r.status]);
|
|
2537
|
+
const name = r.name.padEnd(28);
|
|
2538
|
+
const detail = r.detail ? c("90", r.detail) : "";
|
|
2539
|
+
lines.push(` ${icon} ${name} ${detail}`);
|
|
2540
|
+
if (r.fix && r.status !== "pass") {
|
|
2541
|
+
lines.push(` ${c("90", "\u21B3 " + r.fix)}`);
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
lines.push("");
|
|
2545
|
+
}
|
|
2546
|
+
lines.push(` ${c("90", "\u2500".repeat(50))}`);
|
|
2547
|
+
const parts = [];
|
|
2548
|
+
if (report.pass > 0) parts.push(colorFor.pass(`${report.pass} passed`));
|
|
2549
|
+
if (report.warn > 0) parts.push(colorFor.warn(`${report.warn} warnings`));
|
|
2550
|
+
if (report.fail > 0) parts.push(colorFor.fail(`${report.fail} failed`));
|
|
2551
|
+
if (report.skip > 0) parts.push(colorFor.skip(`${report.skip} skipped`));
|
|
2552
|
+
lines.push(` ${parts.join(" \xB7 ")}`);
|
|
2553
|
+
if (report.fail === 0) {
|
|
2554
|
+
lines.push(` ${c("32", "\u2714 Ready to build agents.")}`);
|
|
2555
|
+
} else {
|
|
2556
|
+
lines.push(` ${c("31", "\u2718 Fix the issues above before continuing.")}`);
|
|
2557
|
+
}
|
|
2558
|
+
lines.push("");
|
|
2559
|
+
return lines.join("\n");
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
// src/commands/doctor.ts
|
|
2563
|
+
function registerDoctorCommand(program) {
|
|
2083
2564
|
program.command("doctor").description("Diagnose your AgentsKit environment.").option("--no-network", "Skip provider reachability checks").option(
|
|
2084
2565
|
"--providers <providers>",
|
|
2085
2566
|
"Comma-separated providers to check (default: openai,anthropic,gemini,ollama)"
|
|
@@ -2096,6 +2577,117 @@ Or start fresh with:
|
|
|
2096
2577
|
}
|
|
2097
2578
|
if (report.fail > 0) process.exit(1);
|
|
2098
2579
|
});
|
|
2580
|
+
}
|
|
2581
|
+
var DEFAULT_WATCH = [
|
|
2582
|
+
"**/*.ts",
|
|
2583
|
+
"**/*.tsx",
|
|
2584
|
+
"**/*.mjs",
|
|
2585
|
+
"**/*.json",
|
|
2586
|
+
".agentskit.config.*"
|
|
2587
|
+
];
|
|
2588
|
+
var DEFAULT_IGNORE = [
|
|
2589
|
+
"**/node_modules/**",
|
|
2590
|
+
"**/dist/**",
|
|
2591
|
+
"**/build/**",
|
|
2592
|
+
"**/.next/**",
|
|
2593
|
+
"**/.turbo/**",
|
|
2594
|
+
"**/.git/**",
|
|
2595
|
+
"**/coverage/**",
|
|
2596
|
+
"**/*.test.ts",
|
|
2597
|
+
"**/*.spec.ts"
|
|
2598
|
+
];
|
|
2599
|
+
function startDev(options) {
|
|
2600
|
+
const entry = path.resolve(process.cwd(), options.entry);
|
|
2601
|
+
if (!fs.existsSync(entry)) {
|
|
2602
|
+
throw new Error(`Entry file not found: ${entry}`);
|
|
2603
|
+
}
|
|
2604
|
+
const stdout = options.stdout ?? process.stdout;
|
|
2605
|
+
const stderr = options.stderr ?? process.stderr;
|
|
2606
|
+
const debounceMs = options.debounceMs ?? 200;
|
|
2607
|
+
const isTs = entry.endsWith(".ts") || entry.endsWith(".tsx");
|
|
2608
|
+
const cmd = isTs ? "tsx" : "node";
|
|
2609
|
+
const baseArgs = [entry, ...options.scriptArgs ?? []];
|
|
2610
|
+
const spawnFn = options.spawn ?? ((c, a) => child_process.spawn(c, a, {
|
|
2611
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
2612
|
+
env: { ...process.env, FORCE_COLOR: "1" }
|
|
2613
|
+
}));
|
|
2614
|
+
const watchPaths = options.watch ?? DEFAULT_WATCH;
|
|
2615
|
+
const ignorePaths = [...DEFAULT_IGNORE, ...options.ignore ?? []];
|
|
2616
|
+
const watcherFactory = options.watcher ?? ((paths, opts) => chokidar__default.default.watch(paths, { ignored: opts.ignored, ignoreInitial: true }));
|
|
2617
|
+
const watcher = watcherFactory(watchPaths, { ignored: ignorePaths });
|
|
2618
|
+
let child;
|
|
2619
|
+
let restartCount = 0;
|
|
2620
|
+
let restartTimer;
|
|
2621
|
+
let stopped = false;
|
|
2622
|
+
let resolveDone;
|
|
2623
|
+
const done = new Promise((r) => {
|
|
2624
|
+
resolveDone = r;
|
|
2625
|
+
});
|
|
2626
|
+
const banner = (msg, color = "green") => {
|
|
2627
|
+
const time = (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8);
|
|
2628
|
+
stdout.write(kleur__default.default[color](`[agentskit dev ${time}] `) + msg + "\n");
|
|
2629
|
+
};
|
|
2630
|
+
const startChild = () => {
|
|
2631
|
+
restartCount++;
|
|
2632
|
+
banner(`\u25B8 starting ${kleur__default.default.bold(path.basename(entry))} (restart #${restartCount - 1})`, "cyan");
|
|
2633
|
+
const c = spawnFn(cmd, baseArgs);
|
|
2634
|
+
child = c;
|
|
2635
|
+
c.stdout?.on("data", (d) => stdout.write(d));
|
|
2636
|
+
c.stderr?.on("data", (d) => stderr.write(d));
|
|
2637
|
+
c.on("exit", (code, signal) => {
|
|
2638
|
+
if (stopped) return;
|
|
2639
|
+
if (signal === "SIGTERM" || signal === "SIGINT") return;
|
|
2640
|
+
if (code === 0) {
|
|
2641
|
+
banner(`\u2713 exited cleanly \u2014 waiting for changes`, "green");
|
|
2642
|
+
} else {
|
|
2643
|
+
banner(`\u2717 exited with code ${code} \u2014 waiting for changes`, "red");
|
|
2644
|
+
}
|
|
2645
|
+
});
|
|
2646
|
+
};
|
|
2647
|
+
const restart = (path5) => {
|
|
2648
|
+
if (restartTimer) clearTimeout(restartTimer);
|
|
2649
|
+
restartTimer = setTimeout(() => {
|
|
2650
|
+
restartTimer = void 0;
|
|
2651
|
+
banner(`\u21BB change detected \u2014 ${path5}`, "yellow");
|
|
2652
|
+
if (child && !child.killed && child.exitCode === null) {
|
|
2653
|
+
child.kill("SIGTERM");
|
|
2654
|
+
}
|
|
2655
|
+
setTimeout(startChild, 80);
|
|
2656
|
+
}, debounceMs);
|
|
2657
|
+
};
|
|
2658
|
+
watcher.on("change", restart);
|
|
2659
|
+
watcher.on("add", restart);
|
|
2660
|
+
watcher.on("unlink", restart);
|
|
2661
|
+
startChild();
|
|
2662
|
+
const stop = async () => {
|
|
2663
|
+
if (stopped) return;
|
|
2664
|
+
stopped = true;
|
|
2665
|
+
if (restartTimer) clearTimeout(restartTimer);
|
|
2666
|
+
if (child && !child.killed && child.exitCode === null) {
|
|
2667
|
+
child.kill("SIGTERM");
|
|
2668
|
+
}
|
|
2669
|
+
await watcher.close();
|
|
2670
|
+
banner(`stopped`, "cyan");
|
|
2671
|
+
resolveDone();
|
|
2672
|
+
};
|
|
2673
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
2674
|
+
process.stdin.setRawMode(true);
|
|
2675
|
+
process.stdin.resume();
|
|
2676
|
+
process.stdin.on("data", (data) => {
|
|
2677
|
+
const key = data.toString();
|
|
2678
|
+
if (key === "r") restart("manual");
|
|
2679
|
+
if (key === "q" || key === "") void stop();
|
|
2680
|
+
});
|
|
2681
|
+
}
|
|
2682
|
+
return {
|
|
2683
|
+
done,
|
|
2684
|
+
stop,
|
|
2685
|
+
restarts: () => restartCount
|
|
2686
|
+
};
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
// src/commands/dev.ts
|
|
2690
|
+
function registerDevCommand(program) {
|
|
2099
2691
|
program.command("dev [entry]").description("Run an entry file with hot-reload on file changes.").option("--watch <globs>", "Comma-separated glob patterns to watch").option("--ignore <globs>", "Comma-separated glob patterns to ignore").option("--debounce <ms>", "Debounce window before restart", "200").action(async (positional, options) => {
|
|
2100
2692
|
const entry = positional ?? "src/index.ts";
|
|
2101
2693
|
const watch = options.watch ? options.watch.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
@@ -2114,9 +2706,15 @@ Or start fresh with:
|
|
|
2114
2706
|
process.exit(1);
|
|
2115
2707
|
}
|
|
2116
2708
|
});
|
|
2117
|
-
|
|
2709
|
+
}
|
|
2710
|
+
function registerConfigCommand(program) {
|
|
2711
|
+
program.command("config").description("Show or scaffold the AgentsKit config.").argument(
|
|
2712
|
+
"[action]",
|
|
2713
|
+
'Action: "init" to create a template, "show" to print the merged config.',
|
|
2714
|
+
"show"
|
|
2715
|
+
).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
2716
|
const isLocal = Boolean(options.local);
|
|
2119
|
-
const targetPath = isLocal ?
|
|
2717
|
+
const targetPath = isLocal ? path__default.default.join(process.cwd(), ".agentskit.config.json") : path__default.default.join(os.homedir(), ".agentskit", "config.json");
|
|
2120
2718
|
if (action === "show") {
|
|
2121
2719
|
const config = await loadConfig();
|
|
2122
2720
|
process.stdout.write(JSON.stringify(config ?? {}, null, 2) + "\n");
|
|
@@ -2128,8 +2726,10 @@ Or start fresh with:
|
|
|
2128
2726
|
process.exit(2);
|
|
2129
2727
|
}
|
|
2130
2728
|
if (fs.existsSync(targetPath) && !options.force) {
|
|
2131
|
-
process.stderr.write(
|
|
2132
|
-
`
|
|
2729
|
+
process.stderr.write(
|
|
2730
|
+
`Config already exists at ${targetPath}. Re-run with --force to overwrite.
|
|
2731
|
+
`
|
|
2732
|
+
);
|
|
2133
2733
|
process.exit(1);
|
|
2134
2734
|
}
|
|
2135
2735
|
const template = {
|
|
@@ -2141,7 +2741,7 @@ Or start fresh with:
|
|
|
2141
2741
|
tools: "web_search,fetch_url"
|
|
2142
2742
|
}
|
|
2143
2743
|
};
|
|
2144
|
-
fs.mkdirSync(
|
|
2744
|
+
fs.mkdirSync(path__default.default.dirname(targetPath), { recursive: true });
|
|
2145
2745
|
fs.writeFileSync(targetPath, JSON.stringify(template, null, 2) + "\n");
|
|
2146
2746
|
process.stdout.write(
|
|
2147
2747
|
`Wrote ${targetPath}
|
|
@@ -2151,6 +2751,74 @@ Edit it to taste, then run:
|
|
|
2151
2751
|
`
|
|
2152
2752
|
);
|
|
2153
2753
|
});
|
|
2754
|
+
}
|
|
2755
|
+
async function startTunnel(options) {
|
|
2756
|
+
const stdout = options.stdout ?? process.stdout;
|
|
2757
|
+
const open = options.open ?? (async (opts) => {
|
|
2758
|
+
const lt = (await import('localtunnel')).default;
|
|
2759
|
+
return await lt(opts);
|
|
2760
|
+
});
|
|
2761
|
+
const banner = (msg, color = "green") => {
|
|
2762
|
+
const time = (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8);
|
|
2763
|
+
stdout.write(kleur__default.default[color](`[agentskit tunnel ${time}] `) + msg + "\n");
|
|
2764
|
+
};
|
|
2765
|
+
banner(`opening tunnel to ${options.host ?? "localhost"}:${options.port}...`, "cyan");
|
|
2766
|
+
const tunnel = await open({
|
|
2767
|
+
port: options.port,
|
|
2768
|
+
subdomain: options.subdomain,
|
|
2769
|
+
local_host: options.host
|
|
2770
|
+
});
|
|
2771
|
+
let requests = 0;
|
|
2772
|
+
let stopped = false;
|
|
2773
|
+
let resolveDone;
|
|
2774
|
+
const done = new Promise((r) => {
|
|
2775
|
+
resolveDone = r;
|
|
2776
|
+
});
|
|
2777
|
+
tunnel.on("request", () => {
|
|
2778
|
+
requests++;
|
|
2779
|
+
});
|
|
2780
|
+
tunnel.on("close", () => {
|
|
2781
|
+
if (stopped) return;
|
|
2782
|
+
banner(`tunnel closed by remote`, "yellow");
|
|
2783
|
+
resolveDone();
|
|
2784
|
+
});
|
|
2785
|
+
tunnel.on("error", (...args) => {
|
|
2786
|
+
const err = args[0];
|
|
2787
|
+
banner(`error: ${err?.message ?? "unknown"}`, "red");
|
|
2788
|
+
});
|
|
2789
|
+
banner(`\u2713 ready`, "green");
|
|
2790
|
+
stdout.write("\n");
|
|
2791
|
+
stdout.write(` ${kleur__default.default.bold("Public URL:")} ${kleur__default.default.cyan(tunnel.url)}
|
|
2792
|
+
`);
|
|
2793
|
+
stdout.write(` ${kleur__default.default.bold("Local:")} http://${options.host ?? "localhost"}:${options.port}
|
|
2794
|
+
`);
|
|
2795
|
+
stdout.write("\n");
|
|
2796
|
+
stdout.write(kleur__default.default.dim(` Forward webhooks here, then ${kleur__default.default.bold("Ctrl+C")} to stop.
|
|
2797
|
+
|
|
2798
|
+
`));
|
|
2799
|
+
options.onReady?.(tunnel.url);
|
|
2800
|
+
const stop = async () => {
|
|
2801
|
+
if (stopped) return;
|
|
2802
|
+
stopped = true;
|
|
2803
|
+
tunnel.close();
|
|
2804
|
+
banner(`stopped \u2014 proxied ${requests} request${requests === 1 ? "" : "s"}`, "cyan");
|
|
2805
|
+
resolveDone();
|
|
2806
|
+
};
|
|
2807
|
+
if (process.stdin.isTTY) {
|
|
2808
|
+
process.on("SIGINT", () => {
|
|
2809
|
+
void stop();
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
return {
|
|
2813
|
+
url: tunnel.url,
|
|
2814
|
+
done,
|
|
2815
|
+
stop,
|
|
2816
|
+
requests: () => requests
|
|
2817
|
+
};
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
// src/commands/tunnel.ts
|
|
2821
|
+
function registerTunnelCommand(program) {
|
|
2154
2822
|
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) => {
|
|
2155
2823
|
const portNum = Number(port);
|
|
2156
2824
|
if (Number.isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
@@ -2171,6 +2839,130 @@ Edit it to taste, then run:
|
|
|
2171
2839
|
process.exit(1);
|
|
2172
2840
|
}
|
|
2173
2841
|
});
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
// src/extensibility/rag/embedders.ts
|
|
2845
|
+
function createOpenAiEmbedder(config) {
|
|
2846
|
+
const model = config.model ?? "text-embedding-3-small";
|
|
2847
|
+
const baseUrl = (config.baseUrl ?? "https://api.openai.com").replace(/\/$/, "");
|
|
2848
|
+
return async (text) => {
|
|
2849
|
+
const res = await fetch(`${baseUrl}/v1/embeddings`, {
|
|
2850
|
+
method: "POST",
|
|
2851
|
+
headers: {
|
|
2852
|
+
"content-type": "application/json",
|
|
2853
|
+
authorization: `Bearer ${config.apiKey}`
|
|
2854
|
+
},
|
|
2855
|
+
body: JSON.stringify({ model, input: text })
|
|
2856
|
+
});
|
|
2857
|
+
if (!res.ok) {
|
|
2858
|
+
const body = await res.text().catch(() => "");
|
|
2859
|
+
throw new Error(`embedder ${model} HTTP ${res.status}: ${body}`);
|
|
2860
|
+
}
|
|
2861
|
+
const json = await res.json();
|
|
2862
|
+
const first = json.data?.[0]?.embedding;
|
|
2863
|
+
if (!first) throw new Error(`embedder ${model}: response missing data[0].embedding`);
|
|
2864
|
+
return first;
|
|
2865
|
+
};
|
|
2866
|
+
}
|
|
2867
|
+
function resolveEmbedder(config) {
|
|
2868
|
+
const embedder = config.embedder;
|
|
2869
|
+
const provider = embedder?.provider ?? "openai";
|
|
2870
|
+
if (provider !== "openai") {
|
|
2871
|
+
throw new Error(`Unsupported RAG embedder provider: ${provider}. Only "openai" is built-in.`);
|
|
2872
|
+
}
|
|
2873
|
+
const apiKey = embedder?.apiKey ?? process.env.OPENAI_API_KEY ?? process.env.OPENROUTER_API_KEY;
|
|
2874
|
+
if (!apiKey) {
|
|
2875
|
+
throw new Error("RAG embedder needs an API key (config.rag.embedder.apiKey or OPENAI_API_KEY env).");
|
|
2876
|
+
}
|
|
2877
|
+
return createOpenAiEmbedder({
|
|
2878
|
+
apiKey,
|
|
2879
|
+
model: embedder?.model,
|
|
2880
|
+
baseUrl: embedder?.baseUrl
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
function buildRagFromConfig(options) {
|
|
2884
|
+
const cwd = options.cwd ?? process.cwd();
|
|
2885
|
+
const dir = path.resolve(cwd, options.config.dir ?? "./.agentskit-rag");
|
|
2886
|
+
const store = memory.fileVectorMemory({ path: `${dir}/store.json` });
|
|
2887
|
+
const embed = options.embedder ?? resolveEmbedder(options.config);
|
|
2888
|
+
return rag.createRAG({
|
|
2889
|
+
embed,
|
|
2890
|
+
store,
|
|
2891
|
+
chunkSize: options.config.chunkSize,
|
|
2892
|
+
topK: options.config.topK
|
|
2893
|
+
});
|
|
2894
|
+
}
|
|
2895
|
+
async function indexSources(rag, config, cwd) {
|
|
2896
|
+
const root = process.cwd();
|
|
2897
|
+
const sources = config.sources ?? [];
|
|
2898
|
+
const absolutePaths = [];
|
|
2899
|
+
for (const pattern of sources) {
|
|
2900
|
+
for await (const match of promises.glob(pattern, { cwd: root })) {
|
|
2901
|
+
absolutePaths.push(path.resolve(root, match));
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
const documents = await Promise.all(
|
|
2905
|
+
absolutePaths.map(async (path5) => ({
|
|
2906
|
+
id: path5,
|
|
2907
|
+
content: await promises.readFile(path5, "utf8"),
|
|
2908
|
+
source: path5
|
|
2909
|
+
}))
|
|
2910
|
+
);
|
|
2911
|
+
if (documents.length > 0) await rag.ingest(documents);
|
|
2912
|
+
return { documentCount: documents.length, sources: absolutePaths };
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
// src/commands/rag.ts
|
|
2916
|
+
function registerRagCommand(program) {
|
|
2917
|
+
const rag = program.command("rag").description("Retrieval-augmented generation utilities.");
|
|
2918
|
+
rag.command("index").description("Index files referenced by config.rag.sources into the vector store.").option(
|
|
2919
|
+
"--source <glob>",
|
|
2920
|
+
"Glob to index (overrides config.rag.sources; repeatable)",
|
|
2921
|
+
(value, prev = []) => [...prev, value],
|
|
2922
|
+
[]
|
|
2923
|
+
).action(async (options) => {
|
|
2924
|
+
const config = await loadConfig();
|
|
2925
|
+
const rawConfig = config?.rag;
|
|
2926
|
+
const overrideSources = options.source;
|
|
2927
|
+
const ragConfig = {
|
|
2928
|
+
...rawConfig ?? {},
|
|
2929
|
+
sources: overrideSources.length > 0 ? overrideSources : rawConfig?.sources ?? []
|
|
2930
|
+
};
|
|
2931
|
+
if (!ragConfig.sources || ragConfig.sources.length === 0) {
|
|
2932
|
+
process.stderr.write("No RAG sources configured. Set config.rag.sources or pass --source <glob>.\n");
|
|
2933
|
+
process.exit(1);
|
|
2934
|
+
}
|
|
2935
|
+
try {
|
|
2936
|
+
const instance = buildRagFromConfig({ config: ragConfig });
|
|
2937
|
+
const result = await indexSources(instance, ragConfig);
|
|
2938
|
+
process.stdout.write(
|
|
2939
|
+
`Indexed ${result.documentCount} document${result.documentCount === 1 ? "" : "s"}.
|
|
2940
|
+
`
|
|
2941
|
+
);
|
|
2942
|
+
for (const source of result.sources) {
|
|
2943
|
+
process.stdout.write(` \u2022 ${source}
|
|
2944
|
+
`);
|
|
2945
|
+
}
|
|
2946
|
+
} catch (err) {
|
|
2947
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2948
|
+
`);
|
|
2949
|
+
process.exit(1);
|
|
2950
|
+
}
|
|
2951
|
+
});
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
// src/commands/index.ts
|
|
2955
|
+
function createCli() {
|
|
2956
|
+
const program = new commander.Command();
|
|
2957
|
+
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
|
|
2958
|
+
registerChatCommand(program);
|
|
2959
|
+
registerRunCommand(program);
|
|
2960
|
+
registerInitCommand(program);
|
|
2961
|
+
registerDoctorCommand(program);
|
|
2962
|
+
registerDevCommand(program);
|
|
2963
|
+
registerConfigCommand(program);
|
|
2964
|
+
registerTunnelCommand(program);
|
|
2965
|
+
registerRagCommand(program);
|
|
2174
2966
|
return program;
|
|
2175
2967
|
}
|
|
2176
2968
|
|