@agentskit/cli 0.4.0 → 0.5.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 +53 -0
- package/dist/bin.cjs +1386 -90
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/chunk-YHCQHIPX.js +1566 -0
- package/dist/chunk-YHCQHIPX.js.map +1 -0
- package/dist/index.cjs +1392 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +196 -2
- package/dist/index.d.ts +196 -2
- package/dist/index.js +1 -1
- package/package.json +35 -10
- package/dist/chunk-LVWMW6SW.js +0 -270
- package/dist/chunk-LVWMW6SW.js.map +0 -1
package/dist/bin.cjs
CHANGED
|
@@ -1,61 +1,109 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var React3 = require('react');
|
|
5
5
|
var ink = require('ink');
|
|
6
6
|
var commander = require('commander');
|
|
7
7
|
var path = require('path');
|
|
8
|
-
var
|
|
8
|
+
var promises = require('fs/promises');
|
|
9
9
|
var ink$1 = require('@agentskit/ink');
|
|
10
10
|
var adapters = require('@agentskit/adapters');
|
|
11
|
+
var tools = require('@agentskit/tools');
|
|
12
|
+
var skills = require('@agentskit/skills');
|
|
13
|
+
var memory = require('@agentskit/memory');
|
|
11
14
|
var jsxRuntime = require('react/jsx-runtime');
|
|
12
|
-
var
|
|
15
|
+
var prompts = require('@inquirer/prompts');
|
|
16
|
+
var kleur = require('kleur');
|
|
17
|
+
var fs = require('fs');
|
|
18
|
+
var runtime = require('@agentskit/runtime');
|
|
19
|
+
var child_process = require('child_process');
|
|
20
|
+
var chokidar = require('chokidar');
|
|
13
21
|
|
|
14
22
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
23
|
|
|
16
|
-
var
|
|
24
|
+
var React3__default = /*#__PURE__*/_interopDefault(React3);
|
|
17
25
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
26
|
+
var kleur__default = /*#__PURE__*/_interopDefault(kleur);
|
|
27
|
+
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
18
28
|
|
|
29
|
+
async function loadJsonConfig(path4) {
|
|
30
|
+
try {
|
|
31
|
+
const raw = await promises.readFile(path4, "utf8");
|
|
32
|
+
return JSON.parse(raw);
|
|
33
|
+
} catch {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function loadTsConfig(path4) {
|
|
38
|
+
try {
|
|
39
|
+
const mod = await import(path4);
|
|
40
|
+
return mod.default ?? mod;
|
|
41
|
+
} catch {
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function loadPackageJsonConfig(dir) {
|
|
46
|
+
try {
|
|
47
|
+
const raw = await promises.readFile(path.join(dir, "package.json"), "utf8");
|
|
48
|
+
const pkg = JSON.parse(raw);
|
|
49
|
+
if (pkg.agentskit && typeof pkg.agentskit === "object") {
|
|
50
|
+
return pkg.agentskit;
|
|
51
|
+
}
|
|
52
|
+
return void 0;
|
|
53
|
+
} catch {
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function loadConfig(options) {
|
|
58
|
+
const cwd = path.resolve(process.cwd());
|
|
59
|
+
const tsPath = path.join(cwd, ".agentskit.config.ts");
|
|
60
|
+
const tsConfig = await loadTsConfig(tsPath);
|
|
61
|
+
if (tsConfig) return tsConfig;
|
|
62
|
+
const jsonPath = path.join(cwd, ".agentskit.config.json");
|
|
63
|
+
const jsonConfig = await loadJsonConfig(jsonPath);
|
|
64
|
+
if (jsonConfig) return jsonConfig;
|
|
65
|
+
return await loadPackageJsonConfig(cwd);
|
|
66
|
+
}
|
|
19
67
|
var providers = {
|
|
20
68
|
openai: {
|
|
21
69
|
label: "OpenAI",
|
|
22
|
-
|
|
70
|
+
envKeys: ["OPENAI_API_KEY"],
|
|
23
71
|
defaultModel: "gpt-4o-mini",
|
|
24
72
|
factory: (c) => adapters.openai(c)
|
|
25
73
|
},
|
|
26
74
|
anthropic: {
|
|
27
75
|
label: "Anthropic",
|
|
28
|
-
|
|
76
|
+
envKeys: ["ANTHROPIC_API_KEY"],
|
|
29
77
|
defaultModel: "claude-3-5-sonnet-latest",
|
|
30
78
|
factory: (c) => adapters.anthropic(c)
|
|
31
79
|
},
|
|
32
80
|
gemini: {
|
|
33
81
|
label: "Gemini",
|
|
34
|
-
|
|
82
|
+
envKeys: ["GEMINI_API_KEY"],
|
|
35
83
|
defaultModel: "gemini-2.5-flash",
|
|
36
84
|
factory: (c) => adapters.gemini(c)
|
|
37
85
|
},
|
|
38
86
|
ollama: {
|
|
39
87
|
label: "Ollama",
|
|
40
|
-
|
|
88
|
+
envKeys: [],
|
|
41
89
|
defaultModel: "llama3.1",
|
|
42
90
|
factory: (c) => adapters.ollama({ model: c.model, baseUrl: c.baseUrl })
|
|
43
91
|
},
|
|
44
92
|
deepseek: {
|
|
45
93
|
label: "DeepSeek",
|
|
46
|
-
|
|
94
|
+
envKeys: ["DEEPSEEK_API_KEY"],
|
|
47
95
|
defaultModel: "deepseek-chat",
|
|
48
96
|
factory: (c) => adapters.deepseek(c)
|
|
49
97
|
},
|
|
50
98
|
grok: {
|
|
51
99
|
label: "xAI Grok",
|
|
52
|
-
|
|
100
|
+
envKeys: ["XAI_API_KEY"],
|
|
53
101
|
requiresModel: true,
|
|
54
102
|
factory: (c) => adapters.grok(c)
|
|
55
103
|
},
|
|
56
104
|
kimi: {
|
|
57
105
|
label: "Kimi",
|
|
58
|
-
|
|
106
|
+
envKeys: ["KIMI_API_KEY", "MOONSHOT_API_KEY"],
|
|
59
107
|
requiresModel: true,
|
|
60
108
|
factory: (c) => adapters.kimi(c)
|
|
61
109
|
}
|
|
@@ -75,7 +123,7 @@ function createDemoAdapter(provider, model) {
|
|
|
75
123
|
].join(" ");
|
|
76
124
|
for (const chunk of reply.match(/.{1,18}/g) ?? []) {
|
|
77
125
|
if (cancelled) return;
|
|
78
|
-
await new Promise((
|
|
126
|
+
await new Promise((resolve2) => setTimeout(resolve2, 35));
|
|
79
127
|
yield { type: "text", content: chunk };
|
|
80
128
|
}
|
|
81
129
|
yield { type: "done" };
|
|
@@ -103,12 +151,16 @@ function resolveChatProvider(options) {
|
|
|
103
151
|
const supported = ["demo", ...Object.keys(providers)].join(", ");
|
|
104
152
|
throw new Error(`Unsupported provider "${options.provider}". Try ${supported}.`);
|
|
105
153
|
}
|
|
106
|
-
let apiKey = options.apiKey
|
|
107
|
-
if (
|
|
108
|
-
|
|
154
|
+
let apiKey = options.apiKey;
|
|
155
|
+
if (!apiKey) {
|
|
156
|
+
for (const key of entry.envKeys) {
|
|
157
|
+
apiKey = process.env[key];
|
|
158
|
+
if (apiKey) break;
|
|
159
|
+
}
|
|
109
160
|
}
|
|
110
|
-
if (!apiKey && entry.
|
|
111
|
-
|
|
161
|
+
if (!apiKey && entry.envKeys.length > 0) {
|
|
162
|
+
const keyList = entry.envKeys.join(" or ");
|
|
163
|
+
throw new Error(`${entry.label} requires an API key. Pass --api-key or set ${keyList}.`);
|
|
112
164
|
}
|
|
113
165
|
const model = options.model ?? entry.defaultModel;
|
|
114
166
|
if (!model) {
|
|
@@ -122,19 +174,88 @@ function resolveChatProvider(options) {
|
|
|
122
174
|
summary: `${entry.label} live adapter`
|
|
123
175
|
};
|
|
124
176
|
}
|
|
177
|
+
var skillRegistry = {
|
|
178
|
+
researcher: skills.researcher,
|
|
179
|
+
coder: skills.coder,
|
|
180
|
+
planner: skills.planner,
|
|
181
|
+
critic: skills.critic,
|
|
182
|
+
summarizer: skills.summarizer
|
|
183
|
+
};
|
|
184
|
+
function resolveTools(toolNames) {
|
|
185
|
+
if (!toolNames) return [];
|
|
186
|
+
const tools$1 = [];
|
|
187
|
+
for (const name of toolNames.split(",").map((s) => s.trim())) {
|
|
188
|
+
switch (name) {
|
|
189
|
+
case "web_search":
|
|
190
|
+
tools$1.push(tools.webSearch());
|
|
191
|
+
break;
|
|
192
|
+
case "filesystem":
|
|
193
|
+
tools$1.push(...tools.filesystem({ basePath: process.cwd() }));
|
|
194
|
+
break;
|
|
195
|
+
case "shell":
|
|
196
|
+
tools$1.push(tools.shell({ timeout: 3e4 }));
|
|
197
|
+
break;
|
|
198
|
+
default:
|
|
199
|
+
process.stderr.write(`Unknown tool: ${name}
|
|
200
|
+
`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return tools$1;
|
|
204
|
+
}
|
|
205
|
+
function resolveSkill(skillName) {
|
|
206
|
+
if (!skillName) return void 0;
|
|
207
|
+
const skill = skillRegistry[skillName.trim()];
|
|
208
|
+
if (!skill) {
|
|
209
|
+
process.stderr.write(`Unknown skill: ${skillName}
|
|
210
|
+
`);
|
|
211
|
+
return void 0;
|
|
212
|
+
}
|
|
213
|
+
return skill;
|
|
214
|
+
}
|
|
215
|
+
function resolveSkills(skillNames) {
|
|
216
|
+
if (!skillNames) return void 0;
|
|
217
|
+
const names = skillNames.split(",").map((s) => s.trim());
|
|
218
|
+
const resolved = names.map((n) => skillRegistry[n]).filter(Boolean);
|
|
219
|
+
if (resolved.length === 0) {
|
|
220
|
+
process.stderr.write(`No valid skills found in: ${skillNames}
|
|
221
|
+
`);
|
|
222
|
+
return void 0;
|
|
223
|
+
}
|
|
224
|
+
if (resolved.length === 1) return resolved[0];
|
|
225
|
+
return skills.composeSkills(...resolved);
|
|
226
|
+
}
|
|
227
|
+
function resolveMemory(backend, memoryPath) {
|
|
228
|
+
switch (backend) {
|
|
229
|
+
case "sqlite":
|
|
230
|
+
return memory.sqliteChatMemory({ path: memoryPath.replace(/\.json$/, ".db") });
|
|
231
|
+
case "file":
|
|
232
|
+
default:
|
|
233
|
+
return memory.fileChatMemory(memoryPath);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
125
236
|
function ChatApp(options) {
|
|
126
|
-
const adapter =
|
|
237
|
+
const adapter = React3.useMemo(
|
|
127
238
|
() => resolveChatProvider(options).adapter,
|
|
128
239
|
[options.apiKey, options.baseUrl, options.model, options.provider]
|
|
129
240
|
);
|
|
130
|
-
const memory =
|
|
131
|
-
() =>
|
|
132
|
-
[options.memoryPath]
|
|
241
|
+
const memory = React3.useMemo(
|
|
242
|
+
() => resolveMemory(options.memoryBackend, options.memoryPath ?? ".agentskit-history.json"),
|
|
243
|
+
[options.memoryPath, options.memoryBackend]
|
|
133
244
|
);
|
|
245
|
+
const tools = React3.useMemo(() => resolveTools(options.tools), [options.tools]);
|
|
246
|
+
const skills = React3.useMemo(() => {
|
|
247
|
+
if (!options.skill) return void 0;
|
|
248
|
+
const names = options.skill.split(",").map((s) => s.trim());
|
|
249
|
+
const resolved = names.map((n) => skillRegistry[n]).filter(Boolean);
|
|
250
|
+
if (resolved.length === 0) return void 0;
|
|
251
|
+
return resolved;
|
|
252
|
+
}, [options.skill]);
|
|
134
253
|
const chat = ink$1.useChat({
|
|
135
254
|
adapter,
|
|
136
255
|
memory,
|
|
137
|
-
systemPrompt: options.system
|
|
256
|
+
systemPrompt: options.system,
|
|
257
|
+
tools: tools.length > 0 ? tools : void 0,
|
|
258
|
+
skills
|
|
138
259
|
});
|
|
139
260
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", gap: 1, children: [
|
|
140
261
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, color: "cyan", children: "AgentsKit CLI" }),
|
|
@@ -149,94 +270,475 @@ function ChatApp(options) {
|
|
|
149
270
|
}
|
|
150
271
|
function renderChatHeader(options) {
|
|
151
272
|
const runtime = resolveChatProvider(options);
|
|
152
|
-
|
|
273
|
+
const parts = [`provider=${runtime.provider}`];
|
|
274
|
+
if (runtime.model) parts.push(`model=${runtime.model}`);
|
|
275
|
+
parts.push(`mode=${runtime.mode}`);
|
|
276
|
+
if (options.tools) parts.push(`tools=${options.tools}`);
|
|
277
|
+
if (options.skill) parts.push(`skill=${options.skill}`);
|
|
278
|
+
if (options.memoryBackend) parts.push(`memory=${options.memoryBackend}`);
|
|
279
|
+
return parts.join(" ");
|
|
280
|
+
}
|
|
281
|
+
var PROVIDER_IMPORT = {
|
|
282
|
+
openai: "openai",
|
|
283
|
+
anthropic: "anthropic",
|
|
284
|
+
gemini: "gemini",
|
|
285
|
+
ollama: "ollama"
|
|
286
|
+
};
|
|
287
|
+
var PROVIDER_DEFAULT_MODEL = {
|
|
288
|
+
openai: "gpt-4o-mini",
|
|
289
|
+
anthropic: "claude-sonnet-4-6",
|
|
290
|
+
gemini: "gemini-2.5-flash",
|
|
291
|
+
ollama: "llama3.1",
|
|
292
|
+
demo: "demo"
|
|
293
|
+
};
|
|
294
|
+
var PROVIDER_ENV_KEY = {
|
|
295
|
+
openai: "OPENAI_API_KEY",
|
|
296
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
297
|
+
gemini: "GEMINI_API_KEY",
|
|
298
|
+
ollama: null,
|
|
299
|
+
demo: null
|
|
300
|
+
};
|
|
301
|
+
function adapterCall(provider, prefix = "process.env") {
|
|
302
|
+
const model = PROVIDER_DEFAULT_MODEL[provider];
|
|
303
|
+
if (provider === "demo") return `demoAdapter()`;
|
|
304
|
+
if (provider === "ollama") return `ollama({ model: '${model}' })`;
|
|
305
|
+
const envKey = PROVIDER_ENV_KEY[provider];
|
|
306
|
+
return `${PROVIDER_IMPORT[provider]}({ apiKey: ${prefix}.${envKey} ?? '', model: '${model}' })`;
|
|
307
|
+
}
|
|
308
|
+
function viteAdapterCall(provider) {
|
|
309
|
+
if (provider === "demo") return `demoAdapter()`;
|
|
310
|
+
if (provider === "ollama") return `ollama({ model: '${PROVIDER_DEFAULT_MODEL[provider]}' })`;
|
|
311
|
+
const envKey = PROVIDER_ENV_KEY[provider];
|
|
312
|
+
return `${PROVIDER_IMPORT[provider]}({ apiKey: import.meta.env.VITE_${envKey} ?? '', model: '${PROVIDER_DEFAULT_MODEL[provider]}' })`;
|
|
313
|
+
}
|
|
314
|
+
function adapterImport(provider) {
|
|
315
|
+
if (provider === "demo") return "";
|
|
316
|
+
return `import { ${PROVIDER_IMPORT[provider]} } from '@agentskit/adapters'
|
|
317
|
+
`;
|
|
318
|
+
}
|
|
319
|
+
function toolImports(tools) {
|
|
320
|
+
if (tools.length === 0) return "";
|
|
321
|
+
return `import { ${tools.map((t) => t === "web_search" ? "webSearch" : t).join(", ")} } from '@agentskit/tools'
|
|
322
|
+
`;
|
|
323
|
+
}
|
|
324
|
+
function toolList(tools) {
|
|
325
|
+
if (tools.length === 0) return "[]";
|
|
326
|
+
const calls = tools.map((t) => {
|
|
327
|
+
if (t === "web_search") return "webSearch()";
|
|
328
|
+
if (t === "filesystem") return `...filesystem({ basePath: './workspace' })`;
|
|
329
|
+
if (t === "shell") return `shell({ allowedCommands: ['ls', 'cat'] })`;
|
|
330
|
+
return "";
|
|
331
|
+
});
|
|
332
|
+
return `[${calls.join(", ")}]`;
|
|
333
|
+
}
|
|
334
|
+
function memoryImport(memory) {
|
|
335
|
+
if (memory === "file") return `import { fileChatMemory } from '@agentskit/memory'
|
|
336
|
+
`;
|
|
337
|
+
if (memory === "sqlite") return `import { sqliteChatMemory } from '@agentskit/memory'
|
|
338
|
+
`;
|
|
339
|
+
return "";
|
|
340
|
+
}
|
|
341
|
+
function memoryCall(memory) {
|
|
342
|
+
if (memory === "file") return `fileChatMemory('./.agentskit-history.json')`;
|
|
343
|
+
if (memory === "sqlite") return `sqliteChatMemory({ path: './.agentskit-history.db' })`;
|
|
344
|
+
return "undefined";
|
|
153
345
|
}
|
|
154
|
-
function
|
|
346
|
+
function demoAdapterSnippet() {
|
|
347
|
+
return `function demoAdapter() {
|
|
155
348
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
dev: "vite"
|
|
349
|
+
createSource: () => ({
|
|
350
|
+
stream: async function* () {
|
|
351
|
+
yield { type: 'text' as const, content: 'Hello from your AgentsKit starter. ' }
|
|
352
|
+
yield { type: 'text' as const, content: 'Configure a real adapter to talk to a model.' }
|
|
353
|
+
yield { type: 'done' as const }
|
|
162
354
|
},
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
355
|
+
abort: () => {},
|
|
356
|
+
}),
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
`;
|
|
361
|
+
}
|
|
362
|
+
function reactStarter(ctx) {
|
|
363
|
+
const deps = {
|
|
364
|
+
"@agentskit/react": "^0.4.0",
|
|
365
|
+
react: "^19.0.0",
|
|
366
|
+
"react-dom": "^19.0.0"
|
|
367
|
+
};
|
|
368
|
+
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
369
|
+
if (ctx.tools.length > 0) deps["@agentskit/tools"] = "^0.4.0";
|
|
370
|
+
if (ctx.memory !== "none") deps["@agentskit/memory"] = "^0.4.0";
|
|
371
|
+
const includesDemo = ctx.provider === "demo";
|
|
372
|
+
const adapter = ctx.provider === "demo" ? viteAdapterCall(ctx.provider) : viteAdapterCall(ctx.provider);
|
|
373
|
+
const envKey = PROVIDER_ENV_KEY[ctx.provider];
|
|
374
|
+
const envContent = envKey ? `VITE_${envKey}=
|
|
375
|
+
` : "# No API key required for the local provider\n";
|
|
376
|
+
return {
|
|
377
|
+
"package.json": JSON.stringify(
|
|
378
|
+
{
|
|
379
|
+
name: path__default.default.basename(ctx.template === "react" ? "agentskit-react-app" : "agentskit-app"),
|
|
380
|
+
private: true,
|
|
381
|
+
type: "module",
|
|
382
|
+
scripts: {
|
|
383
|
+
dev: "vite",
|
|
384
|
+
build: "vite build",
|
|
385
|
+
preview: "vite preview"
|
|
386
|
+
},
|
|
387
|
+
dependencies: deps,
|
|
388
|
+
devDependencies: {
|
|
389
|
+
"@vitejs/plugin-react": "^5.0.0",
|
|
390
|
+
typescript: "^5.5.0",
|
|
391
|
+
vite: "^7.0.0"
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
null,
|
|
395
|
+
2
|
|
396
|
+
) + "\n",
|
|
397
|
+
"index.html": `<!doctype html>
|
|
398
|
+
<html lang="en">
|
|
399
|
+
<head>
|
|
400
|
+
<meta charset="UTF-8" />
|
|
401
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
402
|
+
<title>AgentsKit React Starter</title>
|
|
403
|
+
</head>
|
|
404
|
+
<body>
|
|
405
|
+
<div id="root"></div>
|
|
406
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
407
|
+
</body>
|
|
408
|
+
</html>
|
|
409
|
+
`,
|
|
410
|
+
"vite.config.ts": `import { defineConfig } from 'vite'
|
|
411
|
+
import react from '@vitejs/plugin-react'
|
|
412
|
+
|
|
413
|
+
export default defineConfig({ plugins: [react()] })
|
|
414
|
+
`,
|
|
415
|
+
"tsconfig.json": JSON.stringify(
|
|
416
|
+
{
|
|
417
|
+
compilerOptions: {
|
|
418
|
+
target: "ES2022",
|
|
419
|
+
lib: ["ES2022", "DOM"],
|
|
420
|
+
module: "ESNext",
|
|
421
|
+
moduleResolution: "bundler",
|
|
422
|
+
jsx: "react-jsx",
|
|
423
|
+
strict: true,
|
|
424
|
+
noEmit: true,
|
|
425
|
+
skipLibCheck: true
|
|
426
|
+
},
|
|
427
|
+
include: ["src"]
|
|
428
|
+
},
|
|
429
|
+
null,
|
|
430
|
+
2
|
|
431
|
+
) + "\n",
|
|
170
432
|
"src/main.tsx": `import React from 'react'
|
|
171
433
|
import ReactDOM from 'react-dom/client'
|
|
172
|
-
import
|
|
173
|
-
import { openai } from '@agentskit/adapters'
|
|
174
|
-
import '@agentskit/react/theme'
|
|
434
|
+
import App from './App'
|
|
175
435
|
|
|
176
|
-
|
|
436
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
437
|
+
<React.StrictMode>
|
|
438
|
+
<App />
|
|
439
|
+
</React.StrictMode>,
|
|
440
|
+
)
|
|
441
|
+
`,
|
|
442
|
+
"src/App.tsx": `import { ChatContainer, InputBar, Message, useChat } from '@agentskit/react'
|
|
443
|
+
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}${memoryImport(ctx.memory)}import '@agentskit/react/theme'
|
|
444
|
+
|
|
445
|
+
${includesDemo ? demoAdapterSnippet() : ""}export default function App() {
|
|
177
446
|
const chat = useChat({
|
|
178
|
-
adapter:
|
|
447
|
+
adapter: ${adapter},${ctx.tools.length > 0 ? `
|
|
448
|
+
tools: ${toolList(ctx.tools)},` : ""}${ctx.memory !== "none" ? `
|
|
449
|
+
memory: ${memoryCall(ctx.memory)},` : ""}
|
|
179
450
|
})
|
|
180
451
|
|
|
181
452
|
return (
|
|
182
453
|
<ChatContainer>
|
|
183
|
-
{chat.messages.map(message =>
|
|
454
|
+
{chat.messages.map(message => (
|
|
455
|
+
<Message key={message.id} message={message} />
|
|
456
|
+
))}
|
|
184
457
|
<InputBar chat={chat} />
|
|
185
458
|
</ChatContainer>
|
|
186
459
|
)
|
|
187
460
|
}
|
|
188
|
-
|
|
189
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(<App />)
|
|
190
461
|
`,
|
|
191
|
-
".env.example":
|
|
462
|
+
".env.example": envContent,
|
|
463
|
+
".gitignore": `node_modules
|
|
464
|
+
dist
|
|
465
|
+
.env
|
|
466
|
+
.env.local
|
|
467
|
+
.agentskit-history.*
|
|
468
|
+
`,
|
|
469
|
+
"README.md": readmeFor(ctx)
|
|
192
470
|
};
|
|
193
471
|
}
|
|
194
|
-
function inkStarter() {
|
|
472
|
+
function inkStarter(ctx) {
|
|
473
|
+
const deps = {
|
|
474
|
+
"@agentskit/ink": "^0.4.0",
|
|
475
|
+
ink: "^7.0.0",
|
|
476
|
+
react: "^19.0.0"
|
|
477
|
+
};
|
|
478
|
+
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
479
|
+
if (ctx.tools.length > 0) deps["@agentskit/tools"] = "^0.4.0";
|
|
480
|
+
if (ctx.memory !== "none") deps["@agentskit/memory"] = "^0.4.0";
|
|
195
481
|
return {
|
|
196
|
-
"package.json": JSON.stringify(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
482
|
+
"package.json": JSON.stringify(
|
|
483
|
+
{
|
|
484
|
+
name: "agentskit-ink-app",
|
|
485
|
+
private: true,
|
|
486
|
+
type: "module",
|
|
487
|
+
scripts: {
|
|
488
|
+
dev: "tsx src/index.tsx",
|
|
489
|
+
start: "tsx src/index.tsx"
|
|
490
|
+
},
|
|
491
|
+
dependencies: deps,
|
|
492
|
+
devDependencies: {
|
|
493
|
+
"@types/react": "^19.0.0",
|
|
494
|
+
tsx: "^4.20.0",
|
|
495
|
+
typescript: "^5.5.0"
|
|
496
|
+
}
|
|
202
497
|
},
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
498
|
+
null,
|
|
499
|
+
2
|
|
500
|
+
) + "\n",
|
|
501
|
+
"tsconfig.json": JSON.stringify(
|
|
502
|
+
{
|
|
503
|
+
compilerOptions: {
|
|
504
|
+
target: "ES2022",
|
|
505
|
+
module: "ESNext",
|
|
506
|
+
moduleResolution: "bundler",
|
|
507
|
+
jsx: "react-jsx",
|
|
508
|
+
strict: true,
|
|
509
|
+
noEmit: true,
|
|
510
|
+
skipLibCheck: true
|
|
511
|
+
},
|
|
512
|
+
include: ["src"]
|
|
513
|
+
},
|
|
514
|
+
null,
|
|
515
|
+
2
|
|
516
|
+
) + "\n",
|
|
208
517
|
"src/index.tsx": `import React from 'react'
|
|
209
518
|
import { render } from 'ink'
|
|
210
519
|
import { ChatContainer, InputBar, Message, useChat } from '@agentskit/ink'
|
|
520
|
+
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}${memoryImport(ctx.memory)}
|
|
521
|
+
${ctx.provider === "demo" ? demoAdapterSnippet() : ""}function App() {
|
|
522
|
+
const chat = useChat({
|
|
523
|
+
adapter: ${adapterCall(ctx.provider)},${ctx.tools.length > 0 ? `
|
|
524
|
+
tools: ${toolList(ctx.tools)},` : ""}${ctx.memory !== "none" ? `
|
|
525
|
+
memory: ${memoryCall(ctx.memory)},` : ""}
|
|
526
|
+
})
|
|
211
527
|
|
|
212
|
-
function DemoAdapter() {
|
|
213
|
-
return {
|
|
214
|
-
createSource: () => ({
|
|
215
|
-
async *stream() {
|
|
216
|
-
yield { type: 'text', content: 'Hello from AgentsKit Ink.' }
|
|
217
|
-
yield { type: 'done' }
|
|
218
|
-
},
|
|
219
|
-
abort() {},
|
|
220
|
-
}),
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function App() {
|
|
225
|
-
const chat = useChat({ adapter: DemoAdapter() })
|
|
226
528
|
return (
|
|
227
529
|
<ChatContainer>
|
|
228
|
-
{chat.messages.map(message =>
|
|
530
|
+
{chat.messages.map(message => (
|
|
531
|
+
<Message key={message.id} message={message} />
|
|
532
|
+
))}
|
|
229
533
|
<InputBar chat={chat} />
|
|
230
534
|
</ChatContainer>
|
|
231
535
|
)
|
|
232
536
|
}
|
|
233
537
|
|
|
234
538
|
render(<App />)
|
|
235
|
-
|
|
539
|
+
`,
|
|
540
|
+
".env.example": PROVIDER_ENV_KEY[ctx.provider] ? `${PROVIDER_ENV_KEY[ctx.provider]}=
|
|
541
|
+
` : "# No API key required for the local provider\n",
|
|
542
|
+
".gitignore": `node_modules
|
|
543
|
+
.env
|
|
544
|
+
.env.local
|
|
545
|
+
.agentskit-history.*
|
|
546
|
+
`,
|
|
547
|
+
"README.md": readmeFor(ctx)
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function runtimeStarter(ctx) {
|
|
551
|
+
const deps = {
|
|
552
|
+
"@agentskit/runtime": "^0.4.0"
|
|
553
|
+
};
|
|
554
|
+
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
555
|
+
if (ctx.tools.length > 0) deps["@agentskit/tools"] = "^0.4.0";
|
|
556
|
+
if (ctx.memory !== "none") deps["@agentskit/memory"] = "^0.4.0";
|
|
557
|
+
return {
|
|
558
|
+
"package.json": JSON.stringify(
|
|
559
|
+
{
|
|
560
|
+
name: "agentskit-runtime-app",
|
|
561
|
+
private: true,
|
|
562
|
+
type: "module",
|
|
563
|
+
scripts: {
|
|
564
|
+
start: "tsx src/index.ts",
|
|
565
|
+
dev: "tsx src/index.ts"
|
|
566
|
+
},
|
|
567
|
+
dependencies: deps,
|
|
568
|
+
devDependencies: {
|
|
569
|
+
tsx: "^4.20.0",
|
|
570
|
+
typescript: "^5.5.0"
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
null,
|
|
574
|
+
2
|
|
575
|
+
) + "\n",
|
|
576
|
+
"tsconfig.json": JSON.stringify(
|
|
577
|
+
{
|
|
578
|
+
compilerOptions: {
|
|
579
|
+
target: "ES2022",
|
|
580
|
+
module: "ESNext",
|
|
581
|
+
moduleResolution: "bundler",
|
|
582
|
+
strict: true,
|
|
583
|
+
noEmit: true,
|
|
584
|
+
skipLibCheck: true
|
|
585
|
+
},
|
|
586
|
+
include: ["src"]
|
|
587
|
+
},
|
|
588
|
+
null,
|
|
589
|
+
2
|
|
590
|
+
) + "\n",
|
|
591
|
+
"src/index.ts": `import { createRuntime } from '@agentskit/runtime'
|
|
592
|
+
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}${memoryImport(ctx.memory)}
|
|
593
|
+
${ctx.provider === "demo" ? demoAdapterSnippet() : ""}const runtime = createRuntime({
|
|
594
|
+
adapter: ${adapterCall(ctx.provider)},${ctx.tools.length > 0 ? `
|
|
595
|
+
tools: ${toolList(ctx.tools)},` : ""}${ctx.memory !== "none" ? `
|
|
596
|
+
memory: ${memoryCall(ctx.memory)},` : ""}
|
|
597
|
+
maxSteps: 10,
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
const task = process.argv.slice(2).join(' ') || 'Say hello and tell me one fact about TypeScript.'
|
|
601
|
+
const result = await runtime.run(task)
|
|
602
|
+
|
|
603
|
+
console.log(result.content)
|
|
604
|
+
console.log(\`\\n\u2014 \${result.steps} steps \xB7 \${result.toolCalls.length} tool calls \xB7 \${result.durationMs}ms\`)
|
|
605
|
+
`,
|
|
606
|
+
".env.example": PROVIDER_ENV_KEY[ctx.provider] ? `${PROVIDER_ENV_KEY[ctx.provider]}=
|
|
607
|
+
` : "# No API key required for the local provider\n",
|
|
608
|
+
".gitignore": `node_modules
|
|
609
|
+
.env
|
|
610
|
+
.env.local
|
|
611
|
+
.agentskit-history.*
|
|
612
|
+
`,
|
|
613
|
+
"README.md": readmeFor(ctx)
|
|
236
614
|
};
|
|
237
615
|
}
|
|
616
|
+
function multiAgentStarter(ctx) {
|
|
617
|
+
const deps = {
|
|
618
|
+
"@agentskit/runtime": "^0.4.0",
|
|
619
|
+
"@agentskit/skills": "^0.4.0"
|
|
620
|
+
};
|
|
621
|
+
if (ctx.provider !== "demo") deps["@agentskit/adapters"] = "^0.4.0";
|
|
622
|
+
if (ctx.tools.length === 0) ctx.tools = ["web_search"];
|
|
623
|
+
deps["@agentskit/tools"] = "^0.4.0";
|
|
624
|
+
return {
|
|
625
|
+
"package.json": JSON.stringify(
|
|
626
|
+
{
|
|
627
|
+
name: "agentskit-multi-agent",
|
|
628
|
+
private: true,
|
|
629
|
+
type: "module",
|
|
630
|
+
scripts: {
|
|
631
|
+
start: "tsx src/index.ts",
|
|
632
|
+
dev: "tsx src/index.ts"
|
|
633
|
+
},
|
|
634
|
+
dependencies: deps,
|
|
635
|
+
devDependencies: {
|
|
636
|
+
tsx: "^4.20.0",
|
|
637
|
+
typescript: "^5.5.0"
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
null,
|
|
641
|
+
2
|
|
642
|
+
) + "\n",
|
|
643
|
+
"tsconfig.json": JSON.stringify(
|
|
644
|
+
{
|
|
645
|
+
compilerOptions: {
|
|
646
|
+
target: "ES2022",
|
|
647
|
+
module: "ESNext",
|
|
648
|
+
moduleResolution: "bundler",
|
|
649
|
+
strict: true,
|
|
650
|
+
noEmit: true,
|
|
651
|
+
skipLibCheck: true
|
|
652
|
+
},
|
|
653
|
+
include: ["src"]
|
|
654
|
+
},
|
|
655
|
+
null,
|
|
656
|
+
2
|
|
657
|
+
) + "\n",
|
|
658
|
+
"src/index.ts": `import { createRuntime } from '@agentskit/runtime'
|
|
659
|
+
import { planner, researcher } from '@agentskit/skills'
|
|
660
|
+
${adapterImport(ctx.provider)}${toolImports(ctx.tools)}
|
|
661
|
+
${ctx.provider === "demo" ? demoAdapterSnippet() : ""}const runtime = createRuntime({
|
|
662
|
+
adapter: ${adapterCall(ctx.provider)},
|
|
663
|
+
maxSteps: 10,
|
|
664
|
+
maxDelegationDepth: 2,
|
|
665
|
+
})
|
|
666
|
+
|
|
667
|
+
const task = process.argv.slice(2).join(' ') || 'Research the current state of WebGPU and summarize.'
|
|
668
|
+
|
|
669
|
+
const result = await runtime.run(task, {
|
|
670
|
+
skill: planner,
|
|
671
|
+
delegates: {
|
|
672
|
+
researcher: {
|
|
673
|
+
skill: researcher,
|
|
674
|
+
tools: ${toolList(ctx.tools)},
|
|
675
|
+
maxSteps: 5,
|
|
676
|
+
},
|
|
677
|
+
},
|
|
678
|
+
})
|
|
679
|
+
|
|
680
|
+
console.log(result.content)
|
|
681
|
+
console.log(\`\\n\u2014 \${result.steps} steps \xB7 \${result.toolCalls.length} tool calls\`)
|
|
682
|
+
`,
|
|
683
|
+
".env.example": PROVIDER_ENV_KEY[ctx.provider] ? `${PROVIDER_ENV_KEY[ctx.provider]}=
|
|
684
|
+
` : "# No API key required for the local provider\n",
|
|
685
|
+
".gitignore": `node_modules
|
|
686
|
+
.env
|
|
687
|
+
.env.local
|
|
688
|
+
`,
|
|
689
|
+
"README.md": readmeFor(ctx)
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function readmeFor(ctx) {
|
|
693
|
+
const installCmd = ctx.pm === "npm" ? "npm install" : `${ctx.pm} install`;
|
|
694
|
+
const runCmd = ctx.pm === "npm" ? "npm run dev" : `${ctx.pm} dev`;
|
|
695
|
+
const envKey = PROVIDER_ENV_KEY[ctx.provider];
|
|
696
|
+
return `# AgentsKit ${ctx.template} starter
|
|
697
|
+
|
|
698
|
+
Generated by \`agentskit init\`.
|
|
699
|
+
|
|
700
|
+
## Stack
|
|
701
|
+
|
|
702
|
+
- **Template**: \`${ctx.template}\`
|
|
703
|
+
- **Provider**: \`${ctx.provider}\`${ctx.tools.length ? `
|
|
704
|
+
- **Tools**: ${ctx.tools.map((t) => `\`${t}\``).join(", ")}` : ""}${ctx.memory !== "none" ? `
|
|
705
|
+
- **Memory**: \`${ctx.memory}\`` : ""}
|
|
706
|
+
|
|
707
|
+
## Run
|
|
708
|
+
|
|
709
|
+
\`\`\`bash
|
|
710
|
+
${installCmd}
|
|
711
|
+
${envKey ? `cp .env.example .env
|
|
712
|
+
# add ${envKey}=...` : "# No API key required"}
|
|
713
|
+
${runCmd}
|
|
714
|
+
\`\`\`
|
|
715
|
+
|
|
716
|
+
## Next steps
|
|
717
|
+
|
|
718
|
+
- Open the AgentsKit docs at https://agentskit.io/docs
|
|
719
|
+
- Add a custom skill: https://agentskit.io/docs/concepts/skill
|
|
720
|
+
- Wire up RAG: https://agentskit.io/docs/recipes/rag-chat
|
|
721
|
+
|
|
722
|
+
## License
|
|
723
|
+
|
|
724
|
+
ISC
|
|
725
|
+
`;
|
|
726
|
+
}
|
|
727
|
+
var TEMPLATE_FN = {
|
|
728
|
+
react: reactStarter,
|
|
729
|
+
ink: inkStarter,
|
|
730
|
+
runtime: runtimeStarter,
|
|
731
|
+
"multi-agent": multiAgentStarter
|
|
732
|
+
};
|
|
238
733
|
async function writeStarterProject(options) {
|
|
239
|
-
const
|
|
734
|
+
const ctx = {
|
|
735
|
+
template: options.template,
|
|
736
|
+
provider: options.provider ?? "demo",
|
|
737
|
+
tools: options.tools ?? [],
|
|
738
|
+
memory: options.memory ?? "none",
|
|
739
|
+
pm: options.packageManager ?? "pnpm"
|
|
740
|
+
};
|
|
741
|
+
const files = TEMPLATE_FN[ctx.template](ctx);
|
|
240
742
|
await promises.mkdir(options.targetDir, { recursive: true });
|
|
241
743
|
await Promise.all(
|
|
242
744
|
Object.entries(files).map(async ([relativePath, content]) => {
|
|
@@ -246,30 +748,824 @@ async function writeStarterProject(options) {
|
|
|
246
748
|
})
|
|
247
749
|
);
|
|
248
750
|
}
|
|
751
|
+
async function runInteractiveInit(defaults = {}) {
|
|
752
|
+
process.stdout.write(`
|
|
753
|
+
${kleur__default.default.bold().green("\u25B2")} ${kleur__default.default.bold("agentskit init")}
|
|
754
|
+
`);
|
|
755
|
+
process.stdout.write(kleur__default.default.dim(" Generate a starter project \u2014 answer five questions.\n\n"));
|
|
756
|
+
try {
|
|
757
|
+
const targetDir = await prompts.input({
|
|
758
|
+
message: "Project directory:",
|
|
759
|
+
default: defaults.dir ?? "agentskit-app",
|
|
760
|
+
validate: (value) => {
|
|
761
|
+
if (!value.trim()) return "A directory name is required.";
|
|
762
|
+
const abs = path__default.default.resolve(process.cwd(), value);
|
|
763
|
+
if (fs.existsSync(abs)) return `${value} already exists. Pick a different name.`;
|
|
764
|
+
return true;
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
const template = await prompts.select({
|
|
768
|
+
message: "Template:",
|
|
769
|
+
default: defaults.template ?? "react",
|
|
770
|
+
choices: [
|
|
771
|
+
{ name: "React chat (Vite + browser)", value: "react", description: "Streaming UI with @agentskit/react" },
|
|
772
|
+
{ name: "Ink chat (terminal UI)", value: "ink", description: "Same chat but in your terminal" },
|
|
773
|
+
{ name: "Runtime (headless agent, no UI)", value: "runtime", description: "Autonomous task \u2192 result" },
|
|
774
|
+
{ name: "Multi-agent (planner + delegates)", value: "multi-agent", description: "Supervisor pattern, ready to extend" }
|
|
775
|
+
]
|
|
776
|
+
});
|
|
777
|
+
const provider = await prompts.select({
|
|
778
|
+
message: "LLM provider:",
|
|
779
|
+
default: "demo",
|
|
780
|
+
choices: [
|
|
781
|
+
{ name: "Demo (no API key \u2014 deterministic stub)", value: "demo" },
|
|
782
|
+
{ name: "OpenAI", value: "openai" },
|
|
783
|
+
{ name: "Anthropic", value: "anthropic" },
|
|
784
|
+
{ name: "Gemini", value: "gemini" },
|
|
785
|
+
{ name: "Ollama (local, no key)", value: "ollama" }
|
|
786
|
+
]
|
|
787
|
+
});
|
|
788
|
+
let tools = [];
|
|
789
|
+
if (template !== "react") {
|
|
790
|
+
tools = await prompts.checkbox({
|
|
791
|
+
message: "Tools (space to toggle, enter to confirm):",
|
|
792
|
+
choices: [
|
|
793
|
+
{ name: "web_search", value: "web_search" },
|
|
794
|
+
{ name: "filesystem", value: "filesystem" },
|
|
795
|
+
{ name: "shell", value: "shell" }
|
|
796
|
+
]
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
const memory = await prompts.select({
|
|
800
|
+
message: "Memory backend:",
|
|
801
|
+
default: "none",
|
|
802
|
+
choices: [
|
|
803
|
+
{ name: "None (stateless)", value: "none" },
|
|
804
|
+
{ name: "File (JSON on disk)", value: "file" },
|
|
805
|
+
{ name: "SQLite (better-sqlite3)", value: "sqlite" }
|
|
806
|
+
]
|
|
807
|
+
});
|
|
808
|
+
const packageManager = await prompts.select({
|
|
809
|
+
message: "Package manager:",
|
|
810
|
+
default: "pnpm",
|
|
811
|
+
choices: [
|
|
812
|
+
{ name: "pnpm", value: "pnpm" },
|
|
813
|
+
{ name: "npm", value: "npm" },
|
|
814
|
+
{ name: "yarn", value: "yarn" },
|
|
815
|
+
{ name: "bun", value: "bun" }
|
|
816
|
+
]
|
|
817
|
+
});
|
|
818
|
+
process.stdout.write("\n" + kleur__default.default.dim(" Summary:\n"));
|
|
819
|
+
process.stdout.write(kleur__default.default.dim(` dir ${targetDir}
|
|
820
|
+
`));
|
|
821
|
+
process.stdout.write(kleur__default.default.dim(` template ${template}
|
|
822
|
+
`));
|
|
823
|
+
process.stdout.write(kleur__default.default.dim(` provider ${provider}
|
|
824
|
+
`));
|
|
825
|
+
if (tools.length) process.stdout.write(kleur__default.default.dim(` tools ${tools.join(", ")}
|
|
826
|
+
`));
|
|
827
|
+
process.stdout.write(kleur__default.default.dim(` memory ${memory}
|
|
828
|
+
`));
|
|
829
|
+
process.stdout.write(kleur__default.default.dim(` pm ${packageManager}
|
|
830
|
+
|
|
831
|
+
`));
|
|
832
|
+
const proceed = await prompts.confirm({ message: "Generate?", default: true });
|
|
833
|
+
if (!proceed) {
|
|
834
|
+
process.stdout.write(kleur__default.default.yellow("Cancelled.\n"));
|
|
835
|
+
return { cancelled: true, options: { targetDir, template, provider, tools, memory, packageManager } };
|
|
836
|
+
}
|
|
837
|
+
return {
|
|
838
|
+
cancelled: false,
|
|
839
|
+
options: {
|
|
840
|
+
targetDir: path__default.default.resolve(process.cwd(), targetDir),
|
|
841
|
+
template,
|
|
842
|
+
provider,
|
|
843
|
+
tools,
|
|
844
|
+
memory,
|
|
845
|
+
packageManager
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
} catch (err) {
|
|
849
|
+
if (err.name === "ExitPromptError") {
|
|
850
|
+
process.stdout.write(kleur__default.default.yellow("\nCancelled.\n"));
|
|
851
|
+
return { cancelled: true, options: { targetDir: "", template: "react" } };
|
|
852
|
+
}
|
|
853
|
+
throw err;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function printNextSteps(options) {
|
|
857
|
+
const dir = path__default.default.relative(process.cwd(), options.targetDir) || ".";
|
|
858
|
+
const pm = options.packageManager ?? "pnpm";
|
|
859
|
+
const installCmd = pm === "npm" ? "npm install" : `${pm} install`;
|
|
860
|
+
const runCmd = pm === "npm" ? "npm run dev" : `${pm} dev`;
|
|
861
|
+
process.stdout.write("\n" + kleur__default.default.green("\u2713 Created starter at ") + kleur__default.default.bold(dir) + "\n\n");
|
|
862
|
+
process.stdout.write(kleur__default.default.bold("Next steps:\n\n"));
|
|
863
|
+
process.stdout.write(` ${kleur__default.default.cyan("cd")} ${dir}
|
|
864
|
+
`);
|
|
865
|
+
process.stdout.write(` ${kleur__default.default.cyan(installCmd)}
|
|
866
|
+
`);
|
|
867
|
+
if (options.provider && options.provider !== "demo" && options.provider !== "ollama") {
|
|
868
|
+
process.stdout.write(
|
|
869
|
+
` ${kleur__default.default.cyan("cp")} .env.example .env ${kleur__default.default.dim("# add your API key")}
|
|
870
|
+
`
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
process.stdout.write(` ${kleur__default.default.cyan(runCmd)}
|
|
874
|
+
|
|
875
|
+
`);
|
|
876
|
+
process.stdout.write(kleur__default.default.dim(" Docs: https://agentskit.io/docs\n\n"));
|
|
877
|
+
}
|
|
878
|
+
function formatEvent(event) {
|
|
879
|
+
switch (event.type) {
|
|
880
|
+
case "agent:step":
|
|
881
|
+
return `[step ${event.step}] ${event.action}`;
|
|
882
|
+
case "llm:start":
|
|
883
|
+
return `[llm] start (${event.messageCount} messages)`;
|
|
884
|
+
case "llm:end": {
|
|
885
|
+
const preview = event.content.length > 100 ? event.content.slice(0, 100) + "..." : event.content;
|
|
886
|
+
return `[llm] done (${event.durationMs}ms) "${preview}"`;
|
|
887
|
+
}
|
|
888
|
+
case "tool:start":
|
|
889
|
+
return `[tool] ${event.name} ${JSON.stringify(event.args)}`;
|
|
890
|
+
case "tool:end":
|
|
891
|
+
return `[tool] ${event.name} done (${event.durationMs}ms)`;
|
|
892
|
+
case "error":
|
|
893
|
+
return `[error] ${event.error.message}`;
|
|
894
|
+
default:
|
|
895
|
+
return `[${event.type}]`;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
async function runAgent(task, options) {
|
|
899
|
+
if (options.skill && options.skills) {
|
|
900
|
+
process.stderr.write("Error: --skill and --skills are mutually exclusive. Use one or the other.\n");
|
|
901
|
+
process.exit(1);
|
|
902
|
+
}
|
|
903
|
+
const { adapter } = resolveChatProvider({
|
|
904
|
+
provider: options.provider,
|
|
905
|
+
model: options.model,
|
|
906
|
+
apiKey: options.apiKey,
|
|
907
|
+
baseUrl: options.baseUrl
|
|
908
|
+
});
|
|
909
|
+
const tools = resolveTools(options.tools);
|
|
910
|
+
const skill = options.skills ? resolveSkills(options.skills) : resolveSkill(options.skill);
|
|
911
|
+
const memory = options.memory ? resolveMemory(options.memoryBackend, options.memory) : void 0;
|
|
912
|
+
const observers = [];
|
|
913
|
+
if (options.verbose) {
|
|
914
|
+
observers.push({
|
|
915
|
+
name: "cli-verbose",
|
|
916
|
+
on(event) {
|
|
917
|
+
process.stderr.write(formatEvent(event) + "\n");
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
const runtime$1 = runtime.createRuntime({
|
|
922
|
+
adapter,
|
|
923
|
+
tools,
|
|
924
|
+
memory,
|
|
925
|
+
systemPrompt: options.systemPrompt,
|
|
926
|
+
maxSteps: options.maxSteps ? parseInt(options.maxSteps, 10) : void 0,
|
|
927
|
+
observers
|
|
928
|
+
});
|
|
929
|
+
const result = await runtime$1.run(task, {
|
|
930
|
+
skill: skill ?? void 0
|
|
931
|
+
});
|
|
932
|
+
process.stdout.write(result.content + "\n");
|
|
933
|
+
}
|
|
934
|
+
function RunApp({ task, options }) {
|
|
935
|
+
const [status, setStatus] = React3.useState("running");
|
|
936
|
+
const [currentStep, setCurrentStep] = React3.useState(0);
|
|
937
|
+
const [toolCalls, setToolCalls] = React3.useState([]);
|
|
938
|
+
const [result, setResult] = React3.useState("");
|
|
939
|
+
const [error, setError] = React3.useState("");
|
|
940
|
+
const [durationMs, setDurationMs] = React3.useState(0);
|
|
941
|
+
React3.useEffect(() => {
|
|
942
|
+
async function execute() {
|
|
943
|
+
if (options.skill && options.skills) {
|
|
944
|
+
setError("--skill and --skills are mutually exclusive.");
|
|
945
|
+
setStatus("error");
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
const { adapter } = resolveChatProvider({
|
|
949
|
+
provider: options.provider,
|
|
950
|
+
model: options.model,
|
|
951
|
+
apiKey: options.apiKey,
|
|
952
|
+
baseUrl: options.baseUrl
|
|
953
|
+
});
|
|
954
|
+
const tools = resolveTools(options.tools);
|
|
955
|
+
const skill = options.skills ? resolveSkills(options.skills) : resolveSkill(options.skill);
|
|
956
|
+
const memory = options.memory ? resolveMemory(options.memoryBackend, options.memory) : void 0;
|
|
957
|
+
const observers = [{
|
|
958
|
+
name: "run-ui",
|
|
959
|
+
on(event) {
|
|
960
|
+
switch (event.type) {
|
|
961
|
+
case "agent:step":
|
|
962
|
+
setCurrentStep(event.step);
|
|
963
|
+
break;
|
|
964
|
+
case "tool:start":
|
|
965
|
+
setToolCalls((prev) => [...prev, { name: event.name, status: "running" }]);
|
|
966
|
+
break;
|
|
967
|
+
case "tool:end":
|
|
968
|
+
setToolCalls((prev) => prev.map(
|
|
969
|
+
(tc) => tc.name === event.name && tc.status === "running" ? { ...tc, status: "done", durationMs: event.durationMs } : tc
|
|
970
|
+
));
|
|
971
|
+
break;
|
|
972
|
+
case "error":
|
|
973
|
+
setToolCalls((prev) => prev.map(
|
|
974
|
+
(tc) => tc.status === "running" ? { ...tc, status: "error" } : tc
|
|
975
|
+
));
|
|
976
|
+
break;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}];
|
|
980
|
+
const runtime$1 = runtime.createRuntime({
|
|
981
|
+
adapter,
|
|
982
|
+
tools,
|
|
983
|
+
memory,
|
|
984
|
+
systemPrompt: options.systemPrompt,
|
|
985
|
+
maxSteps: options.maxSteps ? parseInt(options.maxSteps, 10) : void 0,
|
|
986
|
+
observers
|
|
987
|
+
});
|
|
988
|
+
try {
|
|
989
|
+
const runResult = await runtime$1.run(task, { skill: skill ?? void 0 });
|
|
990
|
+
setResult(runResult.content);
|
|
991
|
+
setDurationMs(runResult.durationMs);
|
|
992
|
+
setStatus("done");
|
|
993
|
+
} catch (err) {
|
|
994
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
995
|
+
setStatus("error");
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
void execute();
|
|
999
|
+
}, []);
|
|
1000
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", gap: 1, children: [
|
|
1001
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, color: "cyan", children: "agentskit run" }),
|
|
1002
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { dimColor: true, children: [
|
|
1003
|
+
"Task: ",
|
|
1004
|
+
task
|
|
1005
|
+
] }),
|
|
1006
|
+
status === "running" && currentStep > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: "yellow", children: [
|
|
1007
|
+
"\u27F3",
|
|
1008
|
+
" Step ",
|
|
1009
|
+
currentStep
|
|
1010
|
+
] }),
|
|
1011
|
+
toolCalls.map((tc, i) => /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginLeft: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: tc.status === "running" ? "yellow" : tc.status === "done" ? "green" : "red", children: [
|
|
1012
|
+
tc.status === "running" ? "\u27F3" : tc.status === "done" ? "\u2713" : "\u2717",
|
|
1013
|
+
" ",
|
|
1014
|
+
tc.name,
|
|
1015
|
+
tc.durationMs !== void 0 ? ` (${tc.durationMs}ms)` : ""
|
|
1016
|
+
] }) }, i)),
|
|
1017
|
+
status === "running" && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "yellow", children: "Running..." }),
|
|
1018
|
+
status === "done" && /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
1019
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: "green", bold: true, children: [
|
|
1020
|
+
"Done (",
|
|
1021
|
+
durationMs,
|
|
1022
|
+
"ms)"
|
|
1023
|
+
] }),
|
|
1024
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: result })
|
|
1025
|
+
] }),
|
|
1026
|
+
status === "error" && /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: "red", bold: true, children: [
|
|
1027
|
+
"Error: ",
|
|
1028
|
+
error
|
|
1029
|
+
] })
|
|
1030
|
+
] });
|
|
1031
|
+
}
|
|
1032
|
+
var PROVIDER_ENV_KEYS = {
|
|
1033
|
+
openai: "OPENAI_API_KEY",
|
|
1034
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
1035
|
+
gemini: "GEMINI_API_KEY",
|
|
1036
|
+
deepseek: "DEEPSEEK_API_KEY",
|
|
1037
|
+
grok: "XAI_API_KEY",
|
|
1038
|
+
kimi: "KIMI_API_KEY"
|
|
1039
|
+
};
|
|
1040
|
+
var PROVIDER_REACH_URLS = {
|
|
1041
|
+
openai: "https://api.openai.com/v1/models",
|
|
1042
|
+
anthropic: "https://api.anthropic.com",
|
|
1043
|
+
gemini: "https://generativelanguage.googleapis.com",
|
|
1044
|
+
ollama: "http://localhost:11434/api/tags"
|
|
1045
|
+
};
|
|
1046
|
+
async function checkNodeVersion() {
|
|
1047
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
1048
|
+
if (Number.isNaN(major)) {
|
|
1049
|
+
return { status: "fail", name: "Node version", detail: "Could not parse process.versions.node" };
|
|
1050
|
+
}
|
|
1051
|
+
if (major < 22) {
|
|
1052
|
+
return {
|
|
1053
|
+
status: "fail",
|
|
1054
|
+
name: "Node version",
|
|
1055
|
+
detail: `Node ${process.versions.node} (need 22+)`,
|
|
1056
|
+
fix: "Install Node 22 LTS or newer (https://nodejs.org)"
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
if (major === 25) {
|
|
1060
|
+
return {
|
|
1061
|
+
status: "warn",
|
|
1062
|
+
name: "Node version",
|
|
1063
|
+
detail: `Node ${process.versions.node} \u2014 Docusaurus apps may break here`,
|
|
1064
|
+
fix: "Use Node 22 LTS for the legacy docs app, or stay on 25 for everything else"
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
return { status: "pass", name: "Node version", detail: `Node ${process.versions.node}` };
|
|
1068
|
+
}
|
|
1069
|
+
async function checkPnpm() {
|
|
1070
|
+
const cwd = process.cwd();
|
|
1071
|
+
const hasPnpm = fs.existsSync(path.join(cwd, "pnpm-lock.yaml")) || fs.existsSync(path.join(cwd, "pnpm-workspace.yaml"));
|
|
1072
|
+
if (hasPnpm) {
|
|
1073
|
+
return { status: "pass", name: "Package manager", detail: "pnpm detected (lockfile)" };
|
|
1074
|
+
}
|
|
1075
|
+
if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
|
|
1076
|
+
return { status: "warn", name: "Package manager", detail: "npm detected \u2014 pnpm recommended for monorepo workflows" };
|
|
1077
|
+
}
|
|
1078
|
+
if (fs.existsSync(path.join(cwd, "yarn.lock"))) {
|
|
1079
|
+
return { status: "pass", name: "Package manager", detail: "yarn detected" };
|
|
1080
|
+
}
|
|
1081
|
+
if (fs.existsSync(path.join(cwd, "bun.lock")) || fs.existsSync(path.join(cwd, "bun.lockb"))) {
|
|
1082
|
+
return { status: "pass", name: "Package manager", detail: "bun detected" };
|
|
1083
|
+
}
|
|
1084
|
+
return {
|
|
1085
|
+
status: "skip",
|
|
1086
|
+
name: "Package manager",
|
|
1087
|
+
detail: "No lockfile found in cwd"
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
async function checkPackageJson() {
|
|
1091
|
+
const path4 = path.join(process.cwd(), "package.json");
|
|
1092
|
+
if (!fs.existsSync(path4)) {
|
|
1093
|
+
return {
|
|
1094
|
+
status: "warn",
|
|
1095
|
+
name: "package.json",
|
|
1096
|
+
detail: "No package.json in cwd",
|
|
1097
|
+
fix: "Run from a project directory (or use `agentskit init` to create one)"
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
try {
|
|
1101
|
+
const pkg = JSON.parse(await promises.readFile(path4, "utf8"));
|
|
1102
|
+
const deps = {
|
|
1103
|
+
...pkg.dependencies ?? {},
|
|
1104
|
+
...pkg.devDependencies ?? {}
|
|
1105
|
+
};
|
|
1106
|
+
const akDeps = Object.entries(deps).filter(([name]) => name.startsWith("@agentskit/"));
|
|
1107
|
+
if (akDeps.length === 0) {
|
|
1108
|
+
return { status: "skip", name: "AgentsKit packages", detail: "No @agentskit/* deps found in package.json" };
|
|
1109
|
+
}
|
|
1110
|
+
return {
|
|
1111
|
+
status: "pass",
|
|
1112
|
+
name: "AgentsKit packages",
|
|
1113
|
+
detail: `${akDeps.length} installed: ${akDeps.map(([n]) => n.replace("@agentskit/", "")).join(", ")}`
|
|
1114
|
+
};
|
|
1115
|
+
} catch (err) {
|
|
1116
|
+
return {
|
|
1117
|
+
status: "fail",
|
|
1118
|
+
name: "package.json",
|
|
1119
|
+
detail: `Could not parse: ${err.message}`
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
async function checkProviderEnv(provider) {
|
|
1124
|
+
const envKey = PROVIDER_ENV_KEYS[provider];
|
|
1125
|
+
if (!envKey) {
|
|
1126
|
+
return { status: "skip", name: `${provider} API key`, detail: "No env-key requirement for this provider" };
|
|
1127
|
+
}
|
|
1128
|
+
const value = process.env[envKey];
|
|
1129
|
+
if (!value) {
|
|
1130
|
+
return {
|
|
1131
|
+
status: "fail",
|
|
1132
|
+
name: `${provider} API key`,
|
|
1133
|
+
detail: `${envKey} is not set`,
|
|
1134
|
+
fix: `export ${envKey}=...`
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
if (value.length < 16) {
|
|
1138
|
+
return {
|
|
1139
|
+
status: "warn",
|
|
1140
|
+
name: `${provider} API key`,
|
|
1141
|
+
detail: `${envKey} looks too short (${value.length} chars)`,
|
|
1142
|
+
fix: "Verify the key is complete and not truncated"
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
return { status: "pass", name: `${provider} API key`, detail: `${envKey} set (${value.length} chars)` };
|
|
1146
|
+
}
|
|
1147
|
+
async function checkProviderReachable(provider, fetchImpl = fetch, timeoutMs = 4e3) {
|
|
1148
|
+
const url = PROVIDER_REACH_URLS[provider];
|
|
1149
|
+
if (!url) {
|
|
1150
|
+
return { status: "skip", name: `${provider} reachable`, detail: "No reachability check for this provider" };
|
|
1151
|
+
}
|
|
1152
|
+
const controller = new AbortController();
|
|
1153
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1154
|
+
try {
|
|
1155
|
+
const res = await fetchImpl(url, {
|
|
1156
|
+
method: "GET",
|
|
1157
|
+
signal: controller.signal
|
|
1158
|
+
// No auth — we just want to confirm DNS + network reach.
|
|
1159
|
+
});
|
|
1160
|
+
return {
|
|
1161
|
+
status: "pass",
|
|
1162
|
+
name: `${provider} reachable`,
|
|
1163
|
+
detail: `${url} \u2192 HTTP ${res.status}`
|
|
1164
|
+
};
|
|
1165
|
+
} catch (err) {
|
|
1166
|
+
const reason = err.name === "AbortError" ? `timeout after ${timeoutMs}ms` : err.message;
|
|
1167
|
+
return {
|
|
1168
|
+
status: "fail",
|
|
1169
|
+
name: `${provider} reachable`,
|
|
1170
|
+
detail: `${url} \u2192 ${reason}`,
|
|
1171
|
+
fix: provider === "ollama" ? "Start Ollama: `ollama serve` (or install from https://ollama.com)" : "Check network / firewall / VPN settings"
|
|
1172
|
+
};
|
|
1173
|
+
} finally {
|
|
1174
|
+
clearTimeout(timer);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
async function checkConfig() {
|
|
1178
|
+
try {
|
|
1179
|
+
const config = await loadConfig();
|
|
1180
|
+
if (!config) {
|
|
1181
|
+
return { status: "skip", name: "AgentsKit config", detail: "No .agentskit.config or package.json#agentskit found" };
|
|
1182
|
+
}
|
|
1183
|
+
return {
|
|
1184
|
+
status: "pass",
|
|
1185
|
+
name: "AgentsKit config",
|
|
1186
|
+
detail: `loaded \u2014 defaults: ${JSON.stringify(config.defaults ?? {})}`
|
|
1187
|
+
};
|
|
1188
|
+
} catch (err) {
|
|
1189
|
+
return {
|
|
1190
|
+
status: "warn",
|
|
1191
|
+
name: "AgentsKit config",
|
|
1192
|
+
detail: `Could not load: ${err.message}`
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
async function runDoctor(options = {}) {
|
|
1197
|
+
const providers2 = options.providers ?? ["openai", "anthropic", "gemini", "ollama"];
|
|
1198
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1199
|
+
const checks = [
|
|
1200
|
+
checkNodeVersion(),
|
|
1201
|
+
checkPnpm(),
|
|
1202
|
+
checkPackageJson(),
|
|
1203
|
+
checkConfig()
|
|
1204
|
+
];
|
|
1205
|
+
for (const provider of providers2) {
|
|
1206
|
+
checks.push(checkProviderEnv(provider));
|
|
1207
|
+
if (!options.noNetwork) {
|
|
1208
|
+
checks.push(checkProviderReachable(provider, fetchImpl));
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
const results = await Promise.all(checks);
|
|
1212
|
+
return {
|
|
1213
|
+
results,
|
|
1214
|
+
pass: results.filter((r) => r.status === "pass").length,
|
|
1215
|
+
warn: results.filter((r) => r.status === "warn").length,
|
|
1216
|
+
fail: results.filter((r) => r.status === "fail").length,
|
|
1217
|
+
skip: results.filter((r) => r.status === "skip").length
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
var ICON = {
|
|
1221
|
+
pass: "\u2713",
|
|
1222
|
+
warn: "!",
|
|
1223
|
+
fail: "\u2717",
|
|
1224
|
+
skip: "\xB7"
|
|
1225
|
+
};
|
|
1226
|
+
function renderReport(report, opts = {}) {
|
|
1227
|
+
const color = opts.color ?? true;
|
|
1228
|
+
const c = (code, text) => color ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
1229
|
+
const colorFor = {
|
|
1230
|
+
pass: (t) => c("32", t),
|
|
1231
|
+
warn: (t) => c("33", t),
|
|
1232
|
+
fail: (t) => c("31", t),
|
|
1233
|
+
skip: (t) => c("90", t)
|
|
1234
|
+
};
|
|
1235
|
+
const lines = [];
|
|
1236
|
+
lines.push("");
|
|
1237
|
+
lines.push(c("1", "agentskit doctor"));
|
|
1238
|
+
lines.push("");
|
|
1239
|
+
for (const r of report.results) {
|
|
1240
|
+
const icon = colorFor[r.status](ICON[r.status]);
|
|
1241
|
+
const name = r.name.padEnd(28);
|
|
1242
|
+
const detail = r.detail ? c("90", r.detail) : "";
|
|
1243
|
+
lines.push(` ${icon} ${name} ${detail}`);
|
|
1244
|
+
if (r.fix && (r.status === "fail" || r.status === "warn")) {
|
|
1245
|
+
lines.push(` ${c("90", "\u21B3 " + r.fix)}`);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
lines.push("");
|
|
1249
|
+
const summary = [
|
|
1250
|
+
`${report.pass} pass`,
|
|
1251
|
+
report.warn > 0 ? colorFor.warn(`${report.warn} warn`) : `${report.warn} warn`,
|
|
1252
|
+
report.fail > 0 ? colorFor.fail(`${report.fail} fail`) : `${report.fail} fail`,
|
|
1253
|
+
`${report.skip} skip`
|
|
1254
|
+
].join(" \xB7 ");
|
|
1255
|
+
lines.push(` ${c("1", "Summary:")} ${summary}`);
|
|
1256
|
+
lines.push("");
|
|
1257
|
+
return lines.join("\n");
|
|
1258
|
+
}
|
|
1259
|
+
var DEFAULT_WATCH = [
|
|
1260
|
+
"**/*.ts",
|
|
1261
|
+
"**/*.tsx",
|
|
1262
|
+
"**/*.mjs",
|
|
1263
|
+
"**/*.json",
|
|
1264
|
+
".agentskit.config.*"
|
|
1265
|
+
];
|
|
1266
|
+
var DEFAULT_IGNORE = [
|
|
1267
|
+
"**/node_modules/**",
|
|
1268
|
+
"**/dist/**",
|
|
1269
|
+
"**/build/**",
|
|
1270
|
+
"**/.next/**",
|
|
1271
|
+
"**/.turbo/**",
|
|
1272
|
+
"**/.git/**",
|
|
1273
|
+
"**/coverage/**",
|
|
1274
|
+
"**/*.test.ts",
|
|
1275
|
+
"**/*.spec.ts"
|
|
1276
|
+
];
|
|
1277
|
+
function startDev(options) {
|
|
1278
|
+
const entry = path.resolve(process.cwd(), options.entry);
|
|
1279
|
+
if (!fs.existsSync(entry)) {
|
|
1280
|
+
throw new Error(`Entry file not found: ${entry}`);
|
|
1281
|
+
}
|
|
1282
|
+
const stdout = options.stdout ?? process.stdout;
|
|
1283
|
+
const stderr = options.stderr ?? process.stderr;
|
|
1284
|
+
const debounceMs = options.debounceMs ?? 200;
|
|
1285
|
+
const isTs = entry.endsWith(".ts") || entry.endsWith(".tsx");
|
|
1286
|
+
const cmd = isTs ? "tsx" : "node";
|
|
1287
|
+
const baseArgs = [entry, ...options.scriptArgs ?? []];
|
|
1288
|
+
const spawnFn = options.spawn ?? ((c, a) => child_process.spawn(c, a, {
|
|
1289
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
1290
|
+
env: { ...process.env, FORCE_COLOR: "1" }
|
|
1291
|
+
}));
|
|
1292
|
+
const watchPaths = options.watch ?? DEFAULT_WATCH;
|
|
1293
|
+
const ignorePaths = [...DEFAULT_IGNORE, ...options.ignore ?? []];
|
|
1294
|
+
const watcherFactory = options.watcher ?? ((paths, opts) => chokidar__default.default.watch(paths, { ignored: opts.ignored, ignoreInitial: true }));
|
|
1295
|
+
const watcher = watcherFactory(watchPaths, { ignored: ignorePaths });
|
|
1296
|
+
let child;
|
|
1297
|
+
let restartCount = 0;
|
|
1298
|
+
let restartTimer;
|
|
1299
|
+
let stopped = false;
|
|
1300
|
+
let resolveDone;
|
|
1301
|
+
const done = new Promise((r) => {
|
|
1302
|
+
resolveDone = r;
|
|
1303
|
+
});
|
|
1304
|
+
const banner = (msg, color = "green") => {
|
|
1305
|
+
const time = (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8);
|
|
1306
|
+
stdout.write(kleur__default.default[color](`[agentskit dev ${time}] `) + msg + "\n");
|
|
1307
|
+
};
|
|
1308
|
+
const startChild = () => {
|
|
1309
|
+
restartCount++;
|
|
1310
|
+
banner(`\u25B8 starting ${kleur__default.default.bold(path.basename(entry))} (restart #${restartCount - 1})`, "cyan");
|
|
1311
|
+
const c = spawnFn(cmd, baseArgs);
|
|
1312
|
+
child = c;
|
|
1313
|
+
c.stdout?.on("data", (d) => stdout.write(d));
|
|
1314
|
+
c.stderr?.on("data", (d) => stderr.write(d));
|
|
1315
|
+
c.on("exit", (code, signal) => {
|
|
1316
|
+
if (stopped) return;
|
|
1317
|
+
if (signal === "SIGTERM" || signal === "SIGINT") return;
|
|
1318
|
+
if (code === 0) {
|
|
1319
|
+
banner(`\u2713 exited cleanly \u2014 waiting for changes`, "green");
|
|
1320
|
+
} else {
|
|
1321
|
+
banner(`\u2717 exited with code ${code} \u2014 waiting for changes`, "red");
|
|
1322
|
+
}
|
|
1323
|
+
});
|
|
1324
|
+
};
|
|
1325
|
+
const restart = (path4) => {
|
|
1326
|
+
if (restartTimer) clearTimeout(restartTimer);
|
|
1327
|
+
restartTimer = setTimeout(() => {
|
|
1328
|
+
restartTimer = void 0;
|
|
1329
|
+
banner(`\u21BB change detected \u2014 ${path4}`, "yellow");
|
|
1330
|
+
if (child && !child.killed && child.exitCode === null) {
|
|
1331
|
+
child.kill("SIGTERM");
|
|
1332
|
+
}
|
|
1333
|
+
setTimeout(startChild, 80);
|
|
1334
|
+
}, debounceMs);
|
|
1335
|
+
};
|
|
1336
|
+
watcher.on("change", restart);
|
|
1337
|
+
watcher.on("add", restart);
|
|
1338
|
+
watcher.on("unlink", restart);
|
|
1339
|
+
startChild();
|
|
1340
|
+
const stop = async () => {
|
|
1341
|
+
if (stopped) return;
|
|
1342
|
+
stopped = true;
|
|
1343
|
+
if (restartTimer) clearTimeout(restartTimer);
|
|
1344
|
+
if (child && !child.killed && child.exitCode === null) {
|
|
1345
|
+
child.kill("SIGTERM");
|
|
1346
|
+
}
|
|
1347
|
+
await watcher.close();
|
|
1348
|
+
banner(`stopped`, "cyan");
|
|
1349
|
+
resolveDone();
|
|
1350
|
+
};
|
|
1351
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
1352
|
+
process.stdin.setRawMode(true);
|
|
1353
|
+
process.stdin.resume();
|
|
1354
|
+
process.stdin.on("data", (data) => {
|
|
1355
|
+
const key = data.toString();
|
|
1356
|
+
if (key === "r") restart("manual");
|
|
1357
|
+
if (key === "q" || key === "") void stop();
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
return {
|
|
1361
|
+
done,
|
|
1362
|
+
stop,
|
|
1363
|
+
restarts: () => restartCount
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
async function startTunnel(options) {
|
|
1367
|
+
const stdout = options.stdout ?? process.stdout;
|
|
1368
|
+
const open = options.open ?? (async (opts) => {
|
|
1369
|
+
const lt = (await import('localtunnel')).default;
|
|
1370
|
+
return await lt(opts);
|
|
1371
|
+
});
|
|
1372
|
+
const banner = (msg, color = "green") => {
|
|
1373
|
+
const time = (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8);
|
|
1374
|
+
stdout.write(kleur__default.default[color](`[agentskit tunnel ${time}] `) + msg + "\n");
|
|
1375
|
+
};
|
|
1376
|
+
banner(`opening tunnel to ${options.host ?? "localhost"}:${options.port}...`, "cyan");
|
|
1377
|
+
const tunnel = await open({
|
|
1378
|
+
port: options.port,
|
|
1379
|
+
subdomain: options.subdomain,
|
|
1380
|
+
local_host: options.host
|
|
1381
|
+
});
|
|
1382
|
+
let requests = 0;
|
|
1383
|
+
let stopped = false;
|
|
1384
|
+
let resolveDone;
|
|
1385
|
+
const done = new Promise((r) => {
|
|
1386
|
+
resolveDone = r;
|
|
1387
|
+
});
|
|
1388
|
+
tunnel.on("request", () => {
|
|
1389
|
+
requests++;
|
|
1390
|
+
});
|
|
1391
|
+
tunnel.on("close", () => {
|
|
1392
|
+
if (stopped) return;
|
|
1393
|
+
banner(`tunnel closed by remote`, "yellow");
|
|
1394
|
+
resolveDone();
|
|
1395
|
+
});
|
|
1396
|
+
tunnel.on("error", (...args) => {
|
|
1397
|
+
const err = args[0];
|
|
1398
|
+
banner(`error: ${err?.message ?? "unknown"}`, "red");
|
|
1399
|
+
});
|
|
1400
|
+
banner(`\u2713 ready`, "green");
|
|
1401
|
+
stdout.write("\n");
|
|
1402
|
+
stdout.write(` ${kleur__default.default.bold("Public URL:")} ${kleur__default.default.cyan(tunnel.url)}
|
|
1403
|
+
`);
|
|
1404
|
+
stdout.write(` ${kleur__default.default.bold("Local:")} http://${options.host ?? "localhost"}:${options.port}
|
|
1405
|
+
`);
|
|
1406
|
+
stdout.write("\n");
|
|
1407
|
+
stdout.write(kleur__default.default.dim(` Forward webhooks here, then ${kleur__default.default.bold("Ctrl+C")} to stop.
|
|
1408
|
+
|
|
1409
|
+
`));
|
|
1410
|
+
options.onReady?.(tunnel.url);
|
|
1411
|
+
const stop = async () => {
|
|
1412
|
+
if (stopped) return;
|
|
1413
|
+
stopped = true;
|
|
1414
|
+
tunnel.close();
|
|
1415
|
+
banner(`stopped \u2014 proxied ${requests} request${requests === 1 ? "" : "s"}`, "cyan");
|
|
1416
|
+
resolveDone();
|
|
1417
|
+
};
|
|
1418
|
+
if (process.stdin.isTTY) {
|
|
1419
|
+
process.on("SIGINT", () => {
|
|
1420
|
+
void stop();
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
return {
|
|
1424
|
+
url: tunnel.url,
|
|
1425
|
+
done,
|
|
1426
|
+
stop,
|
|
1427
|
+
requests: () => requests
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
249
1430
|
|
|
250
1431
|
// src/commands.ts
|
|
1432
|
+
function mergeWithConfig(options, config) {
|
|
1433
|
+
if (!config) return options;
|
|
1434
|
+
return {
|
|
1435
|
+
...options,
|
|
1436
|
+
// Config defaults — only apply if CLI flag wasn't set
|
|
1437
|
+
provider: options.provider !== "demo" ? options.provider : config.defaults?.provider ?? options.provider,
|
|
1438
|
+
model: options.model ?? config.defaults?.model
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
251
1441
|
function createCli() {
|
|
252
1442
|
const program = new commander.Command();
|
|
253
1443
|
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
|
|
254
|
-
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>", "Path for file-based memory", ".agentskit-history.json").action((options) => {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
apiKey: options.apiKey,
|
|
259
|
-
baseUrl: options.baseUrl,
|
|
260
|
-
provider:
|
|
261
|
-
model:
|
|
1444
|
+
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>", "Path for file-based memory", ".agentskit-history.json").option("--tools <tools>", "Comma-separated tools: web_search,filesystem,shell").option("--skill <skills>", "Comma-separated skills: researcher,coder,planner,critic,summarizer").option("--memory-backend <backend>", "Memory backend: file (default), sqlite").option("--no-config", "Skip loading .agentskit.config.json").action(async (options) => {
|
|
1445
|
+
const config = options.config !== false ? await loadConfig() : void 0;
|
|
1446
|
+
const merged = mergeWithConfig(options, config);
|
|
1447
|
+
const chatOptions = {
|
|
1448
|
+
apiKey: merged.apiKey ?? options.apiKey,
|
|
1449
|
+
baseUrl: merged.baseUrl ?? options.baseUrl,
|
|
1450
|
+
provider: merged.provider,
|
|
1451
|
+
model: merged.model,
|
|
262
1452
|
system: options.system,
|
|
263
|
-
memoryPath: options.memory
|
|
264
|
-
|
|
1453
|
+
memoryPath: options.memory,
|
|
1454
|
+
tools: options.tools,
|
|
1455
|
+
skill: options.skill,
|
|
1456
|
+
memoryBackend: options.memoryBackend,
|
|
1457
|
+
agentsKitConfig: config
|
|
1458
|
+
};
|
|
1459
|
+
process.stdout.write(`${renderChatHeader(chatOptions)}
|
|
1460
|
+
`);
|
|
1461
|
+
ink.render(React3__default.default.createElement(ChatApp, chatOptions));
|
|
1462
|
+
});
|
|
1463
|
+
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) => {
|
|
1464
|
+
const task = options.task ?? positionalTask;
|
|
1465
|
+
if (!task) {
|
|
1466
|
+
process.stderr.write("Error: task is required. Pass as argument or use --task.\n");
|
|
1467
|
+
process.exit(1);
|
|
1468
|
+
}
|
|
1469
|
+
const config = options.config !== false ? await loadConfig() : void 0;
|
|
1470
|
+
const merged = mergeWithConfig(options, config);
|
|
1471
|
+
if (options.pretty) {
|
|
1472
|
+
ink.render(React3__default.default.createElement(RunApp, { task, options }));
|
|
1473
|
+
} else {
|
|
1474
|
+
try {
|
|
1475
|
+
await runAgent(task, { ...options, provider: merged.provider, model: merged.model });
|
|
1476
|
+
} catch (err) {
|
|
1477
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
1478
|
+
`);
|
|
1479
|
+
process.exit(1);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1483
|
+
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) => {
|
|
1484
|
+
const isCi = !process.stdout.isTTY || rawOptions.yes || rawOptions.template;
|
|
1485
|
+
let resolved;
|
|
1486
|
+
if (isCi) {
|
|
1487
|
+
const template = rawOptions.template ?? "react";
|
|
1488
|
+
resolved = {
|
|
1489
|
+
targetDir: path__default.default.resolve(process.cwd(), rawOptions.dir),
|
|
1490
|
+
template,
|
|
1491
|
+
provider: rawOptions.provider ?? "demo",
|
|
1492
|
+
tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
|
|
1493
|
+
memory: rawOptions.memory ?? "none",
|
|
1494
|
+
packageManager: rawOptions.pm ?? "pnpm"
|
|
1495
|
+
};
|
|
1496
|
+
} else {
|
|
1497
|
+
const result = await runInteractiveInit({
|
|
1498
|
+
dir: rawOptions.dir,
|
|
1499
|
+
template: rawOptions.template
|
|
1500
|
+
});
|
|
1501
|
+
if (result.cancelled) {
|
|
1502
|
+
process.exit(0);
|
|
1503
|
+
}
|
|
1504
|
+
resolved = result.options;
|
|
1505
|
+
}
|
|
1506
|
+
await writeStarterProject(resolved);
|
|
1507
|
+
if (isCi) {
|
|
1508
|
+
process.stdout.write(
|
|
1509
|
+
`Created ${resolved.template} starter in ${path__default.default.relative(process.cwd(), resolved.targetDir) || "."}
|
|
1510
|
+
`
|
|
1511
|
+
);
|
|
1512
|
+
} else {
|
|
1513
|
+
printNextSteps(resolved);
|
|
1514
|
+
}
|
|
265
1515
|
});
|
|
266
|
-
program.command("
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
1516
|
+
program.command("doctor").description("Diagnose your AgentsKit environment.").option("--no-network", "Skip provider reachability checks").option(
|
|
1517
|
+
"--providers <providers>",
|
|
1518
|
+
"Comma-separated providers to check (default: openai,anthropic,gemini,ollama)"
|
|
1519
|
+
).option("--json", "Emit JSON instead of formatted output").action(async (options) => {
|
|
1520
|
+
const providers2 = options.providers ? options.providers.split(",").map((p) => p.trim()).filter(Boolean) : void 0;
|
|
1521
|
+
const report = await runDoctor({
|
|
1522
|
+
providers: providers2,
|
|
1523
|
+
noNetwork: options.network === false
|
|
270
1524
|
});
|
|
271
|
-
|
|
1525
|
+
if (options.json) {
|
|
1526
|
+
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
1527
|
+
} else {
|
|
1528
|
+
process.stdout.write(renderReport(report, { color: process.stdout.isTTY }));
|
|
1529
|
+
}
|
|
1530
|
+
if (report.fail > 0) process.exit(1);
|
|
1531
|
+
});
|
|
1532
|
+
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) => {
|
|
1533
|
+
const entry = positional ?? "src/index.ts";
|
|
1534
|
+
const watch = options.watch ? options.watch.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
1535
|
+
const ignore = options.ignore ? options.ignore.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
1536
|
+
try {
|
|
1537
|
+
const controller = startDev({
|
|
1538
|
+
entry,
|
|
1539
|
+
watch,
|
|
1540
|
+
ignore,
|
|
1541
|
+
debounceMs: Number(options.debounce) || 200
|
|
1542
|
+
});
|
|
1543
|
+
await controller.done;
|
|
1544
|
+
} catch (err) {
|
|
1545
|
+
process.stderr.write(`Error: ${err.message}
|
|
1546
|
+
`);
|
|
1547
|
+
process.exit(1);
|
|
1548
|
+
}
|
|
1549
|
+
});
|
|
1550
|
+
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) => {
|
|
1551
|
+
const portNum = Number(port);
|
|
1552
|
+
if (Number.isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
1553
|
+
process.stderr.write(`Error: invalid port: ${port}
|
|
1554
|
+
`);
|
|
1555
|
+
process.exit(2);
|
|
1556
|
+
}
|
|
1557
|
+
try {
|
|
1558
|
+
const controller = await startTunnel({
|
|
1559
|
+
port: portNum,
|
|
1560
|
+
subdomain: options.subdomain,
|
|
1561
|
+
host: options.host
|
|
1562
|
+
});
|
|
1563
|
+
await controller.done;
|
|
1564
|
+
} catch (err) {
|
|
1565
|
+
process.stderr.write(`Error: ${err.message}
|
|
272
1566
|
`);
|
|
1567
|
+
process.exit(1);
|
|
1568
|
+
}
|
|
273
1569
|
});
|
|
274
1570
|
return program;
|
|
275
1571
|
}
|