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