@kitnai/cli 0.1.31 → 0.1.33
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 +59 -2
- package/dist/index.js +546 -201
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -163,8 +163,8 @@ import { z } from "zod";
|
|
|
163
163
|
function getRegistryUrl(entry) {
|
|
164
164
|
return typeof entry === "string" ? entry : entry.url;
|
|
165
165
|
}
|
|
166
|
-
function resolveRoutesAlias(
|
|
167
|
-
const fw =
|
|
166
|
+
function resolveRoutesAlias(config2) {
|
|
167
|
+
const fw = config2.framework ?? "hono";
|
|
168
168
|
return FRAMEWORK_TO_ADAPTER[fw] ?? fw;
|
|
169
169
|
}
|
|
170
170
|
async function readConfig(projectDir) {
|
|
@@ -175,8 +175,8 @@ async function readConfig(projectDir) {
|
|
|
175
175
|
return null;
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
async function writeConfig(projectDir,
|
|
179
|
-
const data = { $schema: "https://kitn.dev/schema/config.json", ...
|
|
178
|
+
async function writeConfig(projectDir, config2) {
|
|
179
|
+
const data = { $schema: "https://kitn.dev/schema/config.json", ...config2 };
|
|
180
180
|
await writeFile2(join3(projectDir, CONFIG_FILE), JSON.stringify(data, null, 2) + "\n");
|
|
181
181
|
}
|
|
182
182
|
async function readLock(projectDir) {
|
|
@@ -197,17 +197,17 @@ async function writeLock(projectDir, lock) {
|
|
|
197
197
|
}
|
|
198
198
|
await writeFile2(join3(projectDir, LOCK_FILE), JSON.stringify(lock, null, 2) + "\n");
|
|
199
199
|
}
|
|
200
|
-
function getInstallPath(
|
|
200
|
+
function getInstallPath(config2, type, fileName, namespace) {
|
|
201
201
|
const aliasKey = typeToAliasKey[type];
|
|
202
|
-
const baseAlias =
|
|
203
|
-
const base =
|
|
202
|
+
const baseAlias = config2.aliases.base ?? "src/ai";
|
|
203
|
+
const base = config2.aliases[aliasKey] ?? join3(baseAlias, aliasKey);
|
|
204
204
|
if (namespace && namespace !== "@kitn") {
|
|
205
205
|
const nsDir = namespace.replace("@", "");
|
|
206
206
|
return join3(base, nsDir, fileName);
|
|
207
207
|
}
|
|
208
208
|
return join3(base, fileName);
|
|
209
209
|
}
|
|
210
|
-
var componentType, installedComponentSchema, registryEntrySchema, registryValueSchema, configSchema, FRAMEWORK_TO_ADAPTER, CONFIG_FILE, LOCK_FILE, lockSchema, typeToAliasKey;
|
|
210
|
+
var componentType, installedComponentSchema, registryEntrySchema, registryValueSchema, DEFAULT_REGISTRY_URL, DEFAULT_REGISTRIES, DEFAULT_ALIASES, configSchema, FRAMEWORK_TO_ADAPTER, CONFIG_FILE, LOCK_FILE, lockSchema, typeToAliasKey;
|
|
211
211
|
var init_config = __esm({
|
|
212
212
|
"src/utils/config.ts"() {
|
|
213
213
|
"use strict";
|
|
@@ -228,6 +228,22 @@ var init_config = __esm({
|
|
|
228
228
|
description: z.string().optional()
|
|
229
229
|
});
|
|
230
230
|
registryValueSchema = z.union([z.string(), registryEntrySchema]);
|
|
231
|
+
DEFAULT_REGISTRY_URL = "https://kitn-ai.github.io/kitn/r/{type}/{name}.json";
|
|
232
|
+
DEFAULT_REGISTRIES = {
|
|
233
|
+
"@kitn": {
|
|
234
|
+
url: DEFAULT_REGISTRY_URL,
|
|
235
|
+
homepage: "https://kitn.ai",
|
|
236
|
+
description: "Official kitn AI agent components"
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
DEFAULT_ALIASES = {
|
|
240
|
+
base: "src/ai",
|
|
241
|
+
agents: "src/ai/agents",
|
|
242
|
+
tools: "src/ai/tools",
|
|
243
|
+
skills: "src/ai/skills",
|
|
244
|
+
storage: "src/ai/storage",
|
|
245
|
+
crons: "src/ai/crons"
|
|
246
|
+
};
|
|
231
247
|
configSchema = z.object({
|
|
232
248
|
$schema: z.string().optional(),
|
|
233
249
|
runtime: z.enum(["bun", "node", "deno"]),
|
|
@@ -241,7 +257,9 @@ var init_config = __esm({
|
|
|
241
257
|
crons: z.string().optional()
|
|
242
258
|
}),
|
|
243
259
|
registries: z.record(z.string(), registryValueSchema),
|
|
244
|
-
|
|
260
|
+
chatService: z.object({
|
|
261
|
+
url: z.string().optional()
|
|
262
|
+
}).optional()
|
|
245
263
|
});
|
|
246
264
|
FRAMEWORK_TO_ADAPTER = {
|
|
247
265
|
hono: "hono",
|
|
@@ -268,38 +286,38 @@ function stripJsonc(text3) {
|
|
|
268
286
|
return text3.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([}\]])/g, "$1");
|
|
269
287
|
}
|
|
270
288
|
function patchTsconfig(tsconfigContent, paths, removePrefixes) {
|
|
271
|
-
const
|
|
272
|
-
if (!
|
|
273
|
-
|
|
289
|
+
const config2 = JSON.parse(stripJsonc(tsconfigContent));
|
|
290
|
+
if (!config2.compilerOptions) {
|
|
291
|
+
config2.compilerOptions = {};
|
|
274
292
|
}
|
|
275
|
-
if (!
|
|
276
|
-
|
|
293
|
+
if (!config2.compilerOptions.paths) {
|
|
294
|
+
config2.compilerOptions.paths = {};
|
|
277
295
|
}
|
|
278
296
|
if (removePrefixes) {
|
|
279
|
-
for (const key of Object.keys(
|
|
297
|
+
for (const key of Object.keys(config2.compilerOptions.paths)) {
|
|
280
298
|
if (removePrefixes.some((prefix) => key.startsWith(prefix))) {
|
|
281
|
-
delete
|
|
299
|
+
delete config2.compilerOptions.paths[key];
|
|
282
300
|
}
|
|
283
301
|
}
|
|
284
302
|
}
|
|
285
303
|
for (const [key, value] of Object.entries(paths)) {
|
|
286
|
-
|
|
304
|
+
config2.compilerOptions.paths[key] = value;
|
|
287
305
|
}
|
|
288
306
|
const ES_TARGETS = ["es3", "es5", "es6", "es2015", "es2016", "es2017", "es2018", "es2019", "es2020", "es2021"];
|
|
289
|
-
const currentTarget = (
|
|
307
|
+
const currentTarget = (config2.compilerOptions.target ?? "").toLowerCase();
|
|
290
308
|
if (!currentTarget || ES_TARGETS.includes(currentTarget)) {
|
|
291
|
-
|
|
309
|
+
config2.compilerOptions.target = "ES2022";
|
|
292
310
|
}
|
|
293
|
-
if (!
|
|
294
|
-
|
|
311
|
+
if (!config2.compilerOptions.moduleResolution) {
|
|
312
|
+
config2.compilerOptions.moduleResolution = "bundler";
|
|
295
313
|
}
|
|
296
|
-
if (!
|
|
297
|
-
|
|
314
|
+
if (!config2.compilerOptions.module) {
|
|
315
|
+
config2.compilerOptions.module = "ESNext";
|
|
298
316
|
}
|
|
299
|
-
if (
|
|
300
|
-
|
|
317
|
+
if (config2.compilerOptions.skipLibCheck === void 0) {
|
|
318
|
+
config2.compilerOptions.skipLibCheck = true;
|
|
301
319
|
}
|
|
302
|
-
return JSON.stringify(
|
|
320
|
+
return JSON.stringify(config2, null, 2) + "\n";
|
|
303
321
|
}
|
|
304
322
|
async function patchProjectTsconfig(projectDir, paths, removePrefixes) {
|
|
305
323
|
const tsconfigPath = join4(projectDir, "tsconfig.json");
|
|
@@ -581,8 +599,8 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
581
599
|
const lines = [];
|
|
582
600
|
if (exampleContent && !exampleContent.endsWith("\n")) lines.push("");
|
|
583
601
|
for (const key of missingFromExample) {
|
|
584
|
-
const
|
|
585
|
-
lines.push(`# ${
|
|
602
|
+
const config2 = envVars[key];
|
|
603
|
+
lines.push(`# ${config2.description}${config2.url ? ` (${config2.url})` : ""}`);
|
|
586
604
|
lines.push(`${key}=`);
|
|
587
605
|
}
|
|
588
606
|
await writeFile5(examplePath, exampleContent + lines.join("\n") + "\n");
|
|
@@ -594,9 +612,9 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
594
612
|
`${missingFromEnv.length} environment variable(s) needed:`
|
|
595
613
|
);
|
|
596
614
|
for (const key of missingFromEnv) {
|
|
597
|
-
const
|
|
598
|
-
const req =
|
|
599
|
-
p.log.message(` ${pc2.yellow(key)}${req}: ${
|
|
615
|
+
const config2 = envVars[key];
|
|
616
|
+
const req = config2.required !== false ? pc2.red("*") : "";
|
|
617
|
+
p.log.message(` ${pc2.yellow(key)}${req}: ${config2.description}${config2.url ? pc2.dim(` -> ${config2.url}`) : ""}`);
|
|
600
618
|
}
|
|
601
619
|
const shouldPrompt = await p.confirm({
|
|
602
620
|
message: "Would you like to enter values now?",
|
|
@@ -608,8 +626,8 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
608
626
|
}
|
|
609
627
|
const newEntries = [];
|
|
610
628
|
for (const key of missingFromEnv) {
|
|
611
|
-
const
|
|
612
|
-
const isSecret =
|
|
629
|
+
const config2 = envVars[key];
|
|
630
|
+
const isSecret = config2.secret !== false;
|
|
613
631
|
let value;
|
|
614
632
|
if (isSecret) {
|
|
615
633
|
value = await p.password({
|
|
@@ -618,7 +636,7 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
618
636
|
} else {
|
|
619
637
|
value = await p.text({
|
|
620
638
|
message: `${key}:`,
|
|
621
|
-
placeholder:
|
|
639
|
+
placeholder: config2.description
|
|
622
640
|
});
|
|
623
641
|
}
|
|
624
642
|
if (p.isCancel(value)) {
|
|
@@ -867,18 +885,18 @@ import { relative as relative2 } from "path";
|
|
|
867
885
|
async function addCommand(components, opts) {
|
|
868
886
|
p2.intro(pc3.bgCyan(pc3.black(" kitn add ")));
|
|
869
887
|
const cwd = process.cwd();
|
|
870
|
-
const
|
|
871
|
-
if (!
|
|
888
|
+
const config2 = await readConfig(cwd);
|
|
889
|
+
if (!config2) {
|
|
872
890
|
p2.log.error("No kitn.json found. Run `kitn init` first.");
|
|
873
891
|
process.exit(1);
|
|
874
892
|
}
|
|
875
893
|
const lock = await readLock(cwd);
|
|
876
894
|
if (components.length === 0) {
|
|
877
|
-
const fetcher2 = new RegistryFetcher(
|
|
895
|
+
const fetcher2 = new RegistryFetcher(config2.registries);
|
|
878
896
|
const s2 = p2.spinner();
|
|
879
897
|
s2.start("Fetching registry...");
|
|
880
898
|
const allItems = [];
|
|
881
|
-
for (const namespace of Object.keys(
|
|
899
|
+
for (const namespace of Object.keys(config2.registries)) {
|
|
882
900
|
try {
|
|
883
901
|
const index = await fetcher2.fetchIndex(namespace);
|
|
884
902
|
for (const item of index.items) {
|
|
@@ -957,18 +975,18 @@ async function addCommand(components, opts) {
|
|
|
957
975
|
}
|
|
958
976
|
const resolvedComponents = components.map((c) => {
|
|
959
977
|
if (c === "routes") {
|
|
960
|
-
return resolveRoutesAlias(
|
|
978
|
+
return resolveRoutesAlias(config2);
|
|
961
979
|
}
|
|
962
980
|
return c;
|
|
963
981
|
});
|
|
964
982
|
const refs = resolvedComponents.map(parseComponentRef);
|
|
965
|
-
const fetcher = new RegistryFetcher(
|
|
983
|
+
const fetcher = new RegistryFetcher(config2.registries);
|
|
966
984
|
const s = p2.spinner();
|
|
967
985
|
s.start("Resolving dependencies...");
|
|
968
986
|
const preResolvedTypes = /* @__PURE__ */ new Map();
|
|
969
987
|
let expandedNames = [...resolvedComponents];
|
|
970
988
|
try {
|
|
971
|
-
const namespacesToFetch = Object.keys(
|
|
989
|
+
const namespacesToFetch = Object.keys(config2.registries);
|
|
972
990
|
const allIndexItems = [];
|
|
973
991
|
for (const namespace of namespacesToFetch) {
|
|
974
992
|
try {
|
|
@@ -1110,7 +1128,7 @@ async function addCommand(components, opts) {
|
|
|
1110
1128
|
}
|
|
1111
1129
|
}
|
|
1112
1130
|
if (slotReplacements.size > 0) {
|
|
1113
|
-
const baseDir2 =
|
|
1131
|
+
const baseDir2 = config2.aliases.base ?? "src/ai";
|
|
1114
1132
|
for (const [oldKey] of slotReplacements) {
|
|
1115
1133
|
const oldEntry = lock[oldKey];
|
|
1116
1134
|
if (!oldEntry) continue;
|
|
@@ -1122,9 +1140,9 @@ async function addCommand(components, opts) {
|
|
|
1122
1140
|
}
|
|
1123
1141
|
const barrelPath2 = join7(cwd, baseDir2, "index.ts");
|
|
1124
1142
|
const barrelEligibleDirs = /* @__PURE__ */ new Set([
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1143
|
+
config2.aliases.agents,
|
|
1144
|
+
config2.aliases.tools,
|
|
1145
|
+
config2.aliases.skills
|
|
1128
1146
|
]);
|
|
1129
1147
|
if (existsSync(barrelPath2)) {
|
|
1130
1148
|
let barrelContent = await readFile6(barrelPath2, "utf-8");
|
|
@@ -1188,7 +1206,7 @@ async function addCommand(components, opts) {
|
|
|
1188
1206
|
}
|
|
1189
1207
|
}
|
|
1190
1208
|
if (item.type === "kitn:package") {
|
|
1191
|
-
const baseDir2 =
|
|
1209
|
+
const baseDir2 = config2.aliases.base ?? "src/ai";
|
|
1192
1210
|
for (const file of item.files) {
|
|
1193
1211
|
const targetPath = join7(cwd, baseDir2, file.path);
|
|
1194
1212
|
const relativePath = join7(baseDir2, file.path);
|
|
@@ -1258,10 +1276,10 @@ async function addCommand(components, opts) {
|
|
|
1258
1276
|
}
|
|
1259
1277
|
})();
|
|
1260
1278
|
const fileName = file.path.split("/").pop();
|
|
1261
|
-
const installPath = getInstallPath(
|
|
1279
|
+
const installPath = getInstallPath(config2, item.type, fileName, ns);
|
|
1262
1280
|
const targetPath = join7(cwd, installPath);
|
|
1263
1281
|
const relativePath = installPath;
|
|
1264
|
-
const content = rewriteKitnImports(file.content, item.type, fileName,
|
|
1282
|
+
const content = rewriteKitnImports(file.content, item.type, fileName, config2.aliases);
|
|
1265
1283
|
const status = await checkFileStatus(targetPath, content);
|
|
1266
1284
|
switch (status) {
|
|
1267
1285
|
case "new" /* New */:
|
|
@@ -1298,7 +1316,7 @@ async function addCommand(components, opts) {
|
|
|
1298
1316
|
}
|
|
1299
1317
|
const allContent = item.files.map((f) => {
|
|
1300
1318
|
const fn = f.path.split("/").pop();
|
|
1301
|
-
return rewriteKitnImports(f.content, item.type, fn,
|
|
1319
|
+
return rewriteKitnImports(f.content, item.type, fn, config2.aliases);
|
|
1302
1320
|
}).join("\n");
|
|
1303
1321
|
const installedKey = ns === "@kitn" ? item.name : `${ns}/${item.name}`;
|
|
1304
1322
|
lock[installedKey] = {
|
|
@@ -1309,7 +1327,7 @@ async function addCommand(components, opts) {
|
|
|
1309
1327
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1310
1328
|
files: item.files.map((f) => {
|
|
1311
1329
|
const fileName = f.path.split("/").pop();
|
|
1312
|
-
return getInstallPath(
|
|
1330
|
+
return getInstallPath(config2, item.type, fileName, ns);
|
|
1313
1331
|
}),
|
|
1314
1332
|
hash: contentHash(allContent),
|
|
1315
1333
|
registryDependencies: item.registryDependencies
|
|
@@ -1317,7 +1335,7 @@ async function addCommand(components, opts) {
|
|
|
1317
1335
|
}
|
|
1318
1336
|
}
|
|
1319
1337
|
const BARREL_ELIGIBLE = /* @__PURE__ */ new Set(["kitn:agent", "kitn:tool", "kitn:skill", "kitn:cron"]);
|
|
1320
|
-
const baseDir =
|
|
1338
|
+
const baseDir = config2.aliases.base ?? "src/ai";
|
|
1321
1339
|
const barrelPath = join7(cwd, baseDir, "index.ts");
|
|
1322
1340
|
const barrelDir = join7(cwd, baseDir);
|
|
1323
1341
|
const barrelImports = [];
|
|
@@ -1326,7 +1344,7 @@ async function addCommand(components, opts) {
|
|
|
1326
1344
|
const ref = refs.find((r) => r.name === item.name) ?? { namespace: "@kitn", name: item.name, version: void 0 };
|
|
1327
1345
|
for (const file of item.files) {
|
|
1328
1346
|
const fileName = file.path.split("/").pop();
|
|
1329
|
-
const installPath = getInstallPath(
|
|
1347
|
+
const installPath = getInstallPath(config2, item.type, fileName, ref.namespace);
|
|
1330
1348
|
const filePath = join7(cwd, installPath);
|
|
1331
1349
|
const importPath = "./" + relative2(barrelDir, filePath).replace(/\\/g, "/");
|
|
1332
1350
|
barrelImports.push(importPath);
|
|
@@ -1357,7 +1375,7 @@ async function addCommand(components, opts) {
|
|
|
1357
1375
|
);
|
|
1358
1376
|
}
|
|
1359
1377
|
}
|
|
1360
|
-
await writeConfig(cwd,
|
|
1378
|
+
await writeConfig(cwd, config2);
|
|
1361
1379
|
await writeLock(cwd, lock);
|
|
1362
1380
|
if (totalDeps > 0) {
|
|
1363
1381
|
const pm = await detectPackageManager(cwd);
|
|
@@ -1394,7 +1412,7 @@ async function addCommand(components, opts) {
|
|
|
1394
1412
|
const resolvedNames = new Set(resolved.map((r) => r.name));
|
|
1395
1413
|
const projectInstalled = new Set(Object.keys(lock));
|
|
1396
1414
|
const hints = [];
|
|
1397
|
-
const adapterName = resolveRoutesAlias(
|
|
1415
|
+
const adapterName = resolveRoutesAlias(config2);
|
|
1398
1416
|
if (resolvedNames.has("core") && !resolvedNames.has(adapterName) && !projectInstalled.has(adapterName)) {
|
|
1399
1417
|
hints.push(`Run ${pc3.cyan(`kitn add routes`)} to install the HTTP adapter.`);
|
|
1400
1418
|
}
|
|
@@ -1470,10 +1488,10 @@ ${content}`;
|
|
|
1470
1488
|
}
|
|
1471
1489
|
return content;
|
|
1472
1490
|
}
|
|
1473
|
-
async function generateRulesFiles(cwd,
|
|
1474
|
-
const rulesConfig = await fetchRulesConfig(
|
|
1475
|
-
const template = await fetchRulesTemplate(
|
|
1476
|
-
const rendered = renderTemplate(template,
|
|
1491
|
+
async function generateRulesFiles(cwd, config2, selectedToolIds) {
|
|
1492
|
+
const rulesConfig = await fetchRulesConfig(config2.registries);
|
|
1493
|
+
const template = await fetchRulesTemplate(config2.registries);
|
|
1494
|
+
const rendered = renderTemplate(template, config2.aliases);
|
|
1477
1495
|
const toolsToWrite = selectedToolIds ? rulesConfig.tools.filter((t) => selectedToolIds.includes(t.id)) : rulesConfig.tools;
|
|
1478
1496
|
const written = [];
|
|
1479
1497
|
for (const tool of toolsToWrite) {
|
|
@@ -1568,8 +1586,20 @@ __export(init_exports, {
|
|
|
1568
1586
|
});
|
|
1569
1587
|
import * as p3 from "@clack/prompts";
|
|
1570
1588
|
import pc4 from "picocolors";
|
|
1571
|
-
import { mkdir as mkdir5, writeFile as writeFile8 } from "fs/promises";
|
|
1589
|
+
import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
1572
1590
|
import { join as join9 } from "path";
|
|
1591
|
+
async function detectFramework(cwd) {
|
|
1592
|
+
try {
|
|
1593
|
+
const pkg = JSON.parse(await readFile7(join9(cwd, "package.json"), "utf-8"));
|
|
1594
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1595
|
+
if (deps["elysia"]) return "elysia";
|
|
1596
|
+
if (deps["@hono/zod-openapi"]) return "hono-openapi";
|
|
1597
|
+
if (deps["hono"]) return "hono";
|
|
1598
|
+
return null;
|
|
1599
|
+
} catch {
|
|
1600
|
+
return null;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1573
1603
|
function getPluginTemplate(framework) {
|
|
1574
1604
|
const adapterName = framework === "hono-openapi" ? "hono-openapi" : framework;
|
|
1575
1605
|
return `import { createAIPlugin } from "@kitn/adapters/${adapterName}";
|
|
@@ -1637,12 +1667,41 @@ async function initCommand(opts = {}) {
|
|
|
1637
1667
|
}
|
|
1638
1668
|
const validFrameworks = ["hono", "hono-openapi", "elysia"];
|
|
1639
1669
|
let framework;
|
|
1670
|
+
const detected = await detectFramework(cwd);
|
|
1640
1671
|
if (opts.framework) {
|
|
1641
1672
|
if (!validFrameworks.includes(opts.framework)) {
|
|
1642
1673
|
p3.log.error(`Invalid framework: ${opts.framework}. Must be one of: ${validFrameworks.join(", ")}`);
|
|
1643
1674
|
process.exit(1);
|
|
1644
1675
|
}
|
|
1645
1676
|
framework = opts.framework;
|
|
1677
|
+
} else if (detected) {
|
|
1678
|
+
framework = detected;
|
|
1679
|
+
p3.log.info(`Detected ${pc4.bold(detected)} from package.json`);
|
|
1680
|
+
if (!opts.yes) {
|
|
1681
|
+
const confirm6 = await p3.confirm({
|
|
1682
|
+
message: `Use ${detected}?`,
|
|
1683
|
+
initialValue: true
|
|
1684
|
+
});
|
|
1685
|
+
if (p3.isCancel(confirm6)) {
|
|
1686
|
+
p3.cancel("Init cancelled.");
|
|
1687
|
+
process.exit(0);
|
|
1688
|
+
}
|
|
1689
|
+
if (!confirm6) {
|
|
1690
|
+
const selected = await p3.select({
|
|
1691
|
+
message: "Which HTTP framework do you use?",
|
|
1692
|
+
options: [
|
|
1693
|
+
{ value: "hono", label: "Hono" },
|
|
1694
|
+
{ value: "hono-openapi", label: "Hono + OpenAPI", hint: "zod-openapi routes with /doc endpoint" },
|
|
1695
|
+
{ value: "elysia", label: "Elysia", hint: "Bun-native framework" }
|
|
1696
|
+
]
|
|
1697
|
+
});
|
|
1698
|
+
if (p3.isCancel(selected)) {
|
|
1699
|
+
p3.cancel("Init cancelled.");
|
|
1700
|
+
process.exit(0);
|
|
1701
|
+
}
|
|
1702
|
+
framework = selected;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1646
1705
|
} else if (opts.yes) {
|
|
1647
1706
|
framework = "hono";
|
|
1648
1707
|
} else {
|
|
@@ -1677,7 +1736,7 @@ async function initCommand(opts = {}) {
|
|
|
1677
1736
|
}
|
|
1678
1737
|
baseDir = base;
|
|
1679
1738
|
}
|
|
1680
|
-
const
|
|
1739
|
+
const config2 = {
|
|
1681
1740
|
runtime,
|
|
1682
1741
|
framework,
|
|
1683
1742
|
aliases: {
|
|
@@ -1689,7 +1748,7 @@ async function initCommand(opts = {}) {
|
|
|
1689
1748
|
},
|
|
1690
1749
|
registries: {
|
|
1691
1750
|
"@kitn": {
|
|
1692
|
-
url:
|
|
1751
|
+
url: DEFAULT_REGISTRY_URL,
|
|
1693
1752
|
homepage: "https://kitn.ai",
|
|
1694
1753
|
description: "Official kitn AI agent components"
|
|
1695
1754
|
}
|
|
@@ -1697,7 +1756,7 @@ async function initCommand(opts = {}) {
|
|
|
1697
1756
|
};
|
|
1698
1757
|
const s = p3.spinner();
|
|
1699
1758
|
s.start("Writing kitn.json");
|
|
1700
|
-
await writeConfig(cwd,
|
|
1759
|
+
await writeConfig(cwd, config2);
|
|
1701
1760
|
s.stop("Created kitn.json");
|
|
1702
1761
|
await patchProjectTsconfig(
|
|
1703
1762
|
cwd,
|
|
@@ -1715,7 +1774,7 @@ async function initCommand(opts = {}) {
|
|
|
1715
1774
|
await writeFile8(pluginPath, getPluginTemplate(framework));
|
|
1716
1775
|
p3.log.success(`Created ${pc4.bold(baseDir + "/plugin.ts")} \u2014 configure your AI provider there`);
|
|
1717
1776
|
try {
|
|
1718
|
-
const rulesConfig = await fetchRulesConfig(
|
|
1777
|
+
const rulesConfig = await fetchRulesConfig(config2.registries);
|
|
1719
1778
|
let selectedToolIds;
|
|
1720
1779
|
if (opts.yes) {
|
|
1721
1780
|
selectedToolIds = rulesConfig.tools.map((t) => t.id);
|
|
@@ -1736,9 +1795,7 @@ async function initCommand(opts = {}) {
|
|
|
1736
1795
|
}
|
|
1737
1796
|
}
|
|
1738
1797
|
if (selectedToolIds.length > 0) {
|
|
1739
|
-
const
|
|
1740
|
-
await writeConfig(cwd, updatedConfig);
|
|
1741
|
-
const written = await generateRulesFiles(cwd, updatedConfig, selectedToolIds);
|
|
1798
|
+
const written = await generateRulesFiles(cwd, config2, selectedToolIds);
|
|
1742
1799
|
for (const filePath of written) {
|
|
1743
1800
|
p3.log.success(`Created ${pc4.bold(filePath)}`);
|
|
1744
1801
|
}
|
|
@@ -1786,8 +1843,8 @@ import * as p4 from "@clack/prompts";
|
|
|
1786
1843
|
import pc5 from "picocolors";
|
|
1787
1844
|
async function listCommand(typeFilter, opts) {
|
|
1788
1845
|
const cwd = process.cwd();
|
|
1789
|
-
const
|
|
1790
|
-
if (!
|
|
1846
|
+
const config2 = await readConfig(cwd);
|
|
1847
|
+
if (!config2) {
|
|
1791
1848
|
p4.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1792
1849
|
process.exit(1);
|
|
1793
1850
|
}
|
|
@@ -1797,9 +1854,9 @@ async function listCommand(typeFilter, opts) {
|
|
|
1797
1854
|
p4.log.error(`Unknown type ${pc5.bold(rawType)}. Valid types: agent, tool, skill, storage, package`);
|
|
1798
1855
|
process.exit(1);
|
|
1799
1856
|
}
|
|
1800
|
-
const fetcher = new RegistryFetcher(
|
|
1801
|
-
const namespacesToFetch = opts.registry ? [opts.registry] : Object.keys(
|
|
1802
|
-
if (opts.registry && !
|
|
1857
|
+
const fetcher = new RegistryFetcher(config2.registries);
|
|
1858
|
+
const namespacesToFetch = opts.registry ? [opts.registry] : Object.keys(config2.registries);
|
|
1859
|
+
if (opts.registry && !config2.registries[opts.registry]) {
|
|
1803
1860
|
p4.log.error(`Registry ${pc5.bold(opts.registry)} is not configured. Run ${pc5.bold("kitn registry list")} to see configured registries.`);
|
|
1804
1861
|
process.exit(1);
|
|
1805
1862
|
}
|
|
@@ -1912,12 +1969,12 @@ import * as p5 from "@clack/prompts";
|
|
|
1912
1969
|
import { join as join10 } from "path";
|
|
1913
1970
|
async function diffCommand(componentName) {
|
|
1914
1971
|
const cwd = process.cwd();
|
|
1915
|
-
const
|
|
1916
|
-
if (!
|
|
1972
|
+
const config2 = await readConfig(cwd);
|
|
1973
|
+
if (!config2) {
|
|
1917
1974
|
p5.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1918
1975
|
process.exit(1);
|
|
1919
1976
|
}
|
|
1920
|
-
const input = componentName === "routes" ? resolveRoutesAlias(
|
|
1977
|
+
const input = componentName === "routes" ? resolveRoutesAlias(config2) : componentName;
|
|
1921
1978
|
const ref = parseComponentRef(input);
|
|
1922
1979
|
const lock = await readLock(cwd);
|
|
1923
1980
|
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
@@ -1927,7 +1984,7 @@ async function diffCommand(componentName) {
|
|
|
1927
1984
|
process.exit(1);
|
|
1928
1985
|
}
|
|
1929
1986
|
const namespace = installed.registry ?? ref.namespace;
|
|
1930
|
-
const fetcher = new RegistryFetcher(
|
|
1987
|
+
const fetcher = new RegistryFetcher(config2.registries);
|
|
1931
1988
|
const index = await fetcher.fetchIndex(namespace);
|
|
1932
1989
|
const indexItem = index.items.find((i) => i.name === ref.name);
|
|
1933
1990
|
if (!indexItem) {
|
|
@@ -1939,7 +1996,7 @@ async function diffCommand(componentName) {
|
|
|
1939
1996
|
let hasDiff = false;
|
|
1940
1997
|
for (const file of registryItem.files) {
|
|
1941
1998
|
if (indexItem.type === "kitn:package") {
|
|
1942
|
-
const baseDir =
|
|
1999
|
+
const baseDir = config2.aliases.base ?? "src/ai";
|
|
1943
2000
|
const localPath = join10(cwd, baseDir, file.path);
|
|
1944
2001
|
const relativePath = join10(baseDir, file.path);
|
|
1945
2002
|
const localContent = await readExistingFile(localPath);
|
|
@@ -1965,7 +2022,7 @@ async function diffCommand(componentName) {
|
|
|
1965
2022
|
return "storage";
|
|
1966
2023
|
}
|
|
1967
2024
|
})();
|
|
1968
|
-
const localPath = join10(cwd,
|
|
2025
|
+
const localPath = join10(cwd, config2.aliases[aliasKey], fileName);
|
|
1969
2026
|
const localContent = await readExistingFile(localPath);
|
|
1970
2027
|
if (localContent === null) {
|
|
1971
2028
|
p5.log.warn(`${fileName}: file missing locally`);
|
|
@@ -2000,9 +2057,9 @@ __export(remove_exports, {
|
|
|
2000
2057
|
import * as p6 from "@clack/prompts";
|
|
2001
2058
|
import pc6 from "picocolors";
|
|
2002
2059
|
import { join as join11, relative as relative3, dirname as dirname5 } from "path";
|
|
2003
|
-
import { unlink as unlink3, readFile as
|
|
2060
|
+
import { unlink as unlink3, readFile as readFile8, writeFile as writeFile9 } from "fs/promises";
|
|
2004
2061
|
import { existsSync as existsSync2 } from "fs";
|
|
2005
|
-
async function removeSingleComponent(installedKey, lock,
|
|
2062
|
+
async function removeSingleComponent(installedKey, lock, config2, cwd) {
|
|
2006
2063
|
const entry = lock[installedKey];
|
|
2007
2064
|
if (!entry) return;
|
|
2008
2065
|
const deleted = [];
|
|
@@ -2014,16 +2071,16 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
2014
2071
|
p6.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
2015
2072
|
}
|
|
2016
2073
|
}
|
|
2017
|
-
const baseDir =
|
|
2074
|
+
const baseDir = config2.aliases.base ?? "src/ai";
|
|
2018
2075
|
const barrelPath = join11(cwd, baseDir, "index.ts");
|
|
2019
2076
|
const barrelDir = join11(cwd, baseDir);
|
|
2020
2077
|
const barrelEligibleDirs = /* @__PURE__ */ new Set([
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2078
|
+
config2.aliases.agents,
|
|
2079
|
+
config2.aliases.tools,
|
|
2080
|
+
config2.aliases.skills
|
|
2024
2081
|
]);
|
|
2025
2082
|
if (existsSync2(barrelPath) && deleted.length > 0) {
|
|
2026
|
-
let barrelContent = await
|
|
2083
|
+
let barrelContent = await readFile8(barrelPath, "utf-8");
|
|
2027
2084
|
let barrelChanged = false;
|
|
2028
2085
|
for (const filePath of deleted) {
|
|
2029
2086
|
const fileDir = dirname5(filePath);
|
|
@@ -2046,7 +2103,7 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
2046
2103
|
` + deleted.map((f) => ` ${pc6.red("-")} ${f}`).join("\n"));
|
|
2047
2104
|
}
|
|
2048
2105
|
}
|
|
2049
|
-
async function offerOrphanRemoval(removedDeps, lock,
|
|
2106
|
+
async function offerOrphanRemoval(removedDeps, lock, config2, cwd) {
|
|
2050
2107
|
if (removedDeps.size === 0) return;
|
|
2051
2108
|
const remaining = Object.entries(lock);
|
|
2052
2109
|
const neededDeps = /* @__PURE__ */ new Set();
|
|
@@ -2073,13 +2130,13 @@ async function offerOrphanRemoval(removedDeps, lock, config, cwd) {
|
|
|
2073
2130
|
});
|
|
2074
2131
|
if (p6.isCancel(selected)) return;
|
|
2075
2132
|
for (const key of selected) {
|
|
2076
|
-
await removeSingleComponent(key, lock,
|
|
2133
|
+
await removeSingleComponent(key, lock, config2, cwd);
|
|
2077
2134
|
}
|
|
2078
2135
|
}
|
|
2079
2136
|
async function removeCommand(componentName) {
|
|
2080
2137
|
const cwd = process.cwd();
|
|
2081
|
-
const
|
|
2082
|
-
if (!
|
|
2138
|
+
const config2 = await readConfig(cwd);
|
|
2139
|
+
if (!config2) {
|
|
2083
2140
|
p6.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2084
2141
|
process.exit(1);
|
|
2085
2142
|
}
|
|
@@ -2117,14 +2174,14 @@ async function removeCommand(componentName) {
|
|
|
2117
2174
|
}
|
|
2118
2175
|
}
|
|
2119
2176
|
for (const key of selectedKeys) {
|
|
2120
|
-
await removeSingleComponent(key, lock,
|
|
2177
|
+
await removeSingleComponent(key, lock, config2, cwd);
|
|
2121
2178
|
}
|
|
2122
|
-
await offerOrphanRemoval(allRemovedDeps, lock,
|
|
2179
|
+
await offerOrphanRemoval(allRemovedDeps, lock, config2, cwd);
|
|
2123
2180
|
await writeLock(cwd, lock);
|
|
2124
2181
|
p6.outro(pc6.green("Done!"));
|
|
2125
2182
|
return;
|
|
2126
2183
|
}
|
|
2127
|
-
const input = componentName === "routes" ? resolveRoutesAlias(
|
|
2184
|
+
const input = componentName === "routes" ? resolveRoutesAlias(config2) : componentName;
|
|
2128
2185
|
const ref = parseComponentRef(input);
|
|
2129
2186
|
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
2130
2187
|
const entry = lock[installedKey];
|
|
@@ -2141,8 +2198,8 @@ async function removeCommand(componentName) {
|
|
|
2141
2198
|
process.exit(0);
|
|
2142
2199
|
}
|
|
2143
2200
|
const removedDeps = new Set(entry.registryDependencies ?? []);
|
|
2144
|
-
await removeSingleComponent(installedKey, lock,
|
|
2145
|
-
await offerOrphanRemoval(removedDeps, lock,
|
|
2201
|
+
await removeSingleComponent(installedKey, lock, config2, cwd);
|
|
2202
|
+
await offerOrphanRemoval(removedDeps, lock, config2, cwd);
|
|
2146
2203
|
await writeLock(cwd, lock);
|
|
2147
2204
|
}
|
|
2148
2205
|
var init_remove = __esm({
|
|
@@ -2163,8 +2220,8 @@ import * as p7 from "@clack/prompts";
|
|
|
2163
2220
|
async function updateCommand(components) {
|
|
2164
2221
|
if (components.length === 0) {
|
|
2165
2222
|
const cwd = process.cwd();
|
|
2166
|
-
const
|
|
2167
|
-
if (!
|
|
2223
|
+
const config2 = await readConfig(cwd);
|
|
2224
|
+
if (!config2) {
|
|
2168
2225
|
p7.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2169
2226
|
process.exit(1);
|
|
2170
2227
|
}
|
|
@@ -2208,7 +2265,7 @@ import * as p8 from "@clack/prompts";
|
|
|
2208
2265
|
import pc7 from "picocolors";
|
|
2209
2266
|
import { join as join12, relative as relative4 } from "path";
|
|
2210
2267
|
import { existsSync as existsSync3 } from "fs";
|
|
2211
|
-
import { readFile as
|
|
2268
|
+
import { readFile as readFile9, writeFile as writeFile10, mkdir as mkdir6 } from "fs/promises";
|
|
2212
2269
|
function generateAgentSource(name) {
|
|
2213
2270
|
const camel = toCamelCase(name);
|
|
2214
2271
|
return `import { registerAgent } from "@kitn/core";
|
|
@@ -2306,8 +2363,8 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2306
2363
|
);
|
|
2307
2364
|
}
|
|
2308
2365
|
const cwd = opts?.cwd ?? process.cwd();
|
|
2309
|
-
const
|
|
2310
|
-
if (!
|
|
2366
|
+
const config2 = await readConfig(cwd);
|
|
2367
|
+
if (!config2) {
|
|
2311
2368
|
throw new Error(
|
|
2312
2369
|
`No kitn.json found in ${cwd}. Run ${pc7.bold("kitn init")} first.`
|
|
2313
2370
|
);
|
|
@@ -2315,7 +2372,7 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2315
2372
|
const validType = type;
|
|
2316
2373
|
const kitnType = typeToKitnType[validType];
|
|
2317
2374
|
const fileName = validType === "skill" ? `${name}.md` : `${name}.ts`;
|
|
2318
|
-
const filePath = join12(cwd, getInstallPath(
|
|
2375
|
+
const filePath = join12(cwd, getInstallPath(config2, kitnType, fileName));
|
|
2319
2376
|
const dummyContent = "";
|
|
2320
2377
|
const status = await checkFileStatus(filePath, dummyContent);
|
|
2321
2378
|
if (status !== "new" /* New */) {
|
|
@@ -2342,11 +2399,11 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2342
2399
|
await writeComponentFile(filePath, source);
|
|
2343
2400
|
let barrelUpdated = false;
|
|
2344
2401
|
if (BARREL_TYPES.includes(validType)) {
|
|
2345
|
-
const baseDir =
|
|
2402
|
+
const baseDir = config2.aliases.base ?? "src/ai";
|
|
2346
2403
|
const barrelPath = join12(cwd, baseDir, "index.ts");
|
|
2347
2404
|
let barrelContent;
|
|
2348
2405
|
if (existsSync3(barrelPath)) {
|
|
2349
|
-
barrelContent = await
|
|
2406
|
+
barrelContent = await readFile9(barrelPath, "utf-8");
|
|
2350
2407
|
} else {
|
|
2351
2408
|
barrelContent = createBarrelFile();
|
|
2352
2409
|
await mkdir6(join12(cwd, baseDir), { recursive: true });
|
|
@@ -2398,7 +2455,7 @@ var init_create = __esm({
|
|
|
2398
2455
|
});
|
|
2399
2456
|
|
|
2400
2457
|
// src/utils/component-resolver.ts
|
|
2401
|
-
import { readdir, readFile as
|
|
2458
|
+
import { readdir, readFile as readFile10 } from "fs/promises";
|
|
2402
2459
|
import { join as join13, relative as relative5 } from "path";
|
|
2403
2460
|
function stripSuffix(name, suffix) {
|
|
2404
2461
|
if (name.endsWith(`-${suffix}`)) {
|
|
@@ -2406,21 +2463,21 @@ function stripSuffix(name, suffix) {
|
|
|
2406
2463
|
}
|
|
2407
2464
|
return name;
|
|
2408
2465
|
}
|
|
2409
|
-
function toolsDir(
|
|
2410
|
-
const baseAlias =
|
|
2411
|
-
const tools =
|
|
2466
|
+
function toolsDir(config2, cwd) {
|
|
2467
|
+
const baseAlias = config2.aliases.base ?? "src/ai";
|
|
2468
|
+
const tools = config2.aliases.tools ?? join13(baseAlias, "tools");
|
|
2412
2469
|
return join13(cwd, tools);
|
|
2413
2470
|
}
|
|
2414
|
-
function agentsDir(
|
|
2415
|
-
const baseAlias =
|
|
2416
|
-
const agents =
|
|
2471
|
+
function agentsDir(config2, cwd) {
|
|
2472
|
+
const baseAlias = config2.aliases.base ?? "src/ai";
|
|
2473
|
+
const agents = config2.aliases.agents ?? join13(baseAlias, "agents");
|
|
2417
2474
|
return join13(cwd, agents);
|
|
2418
2475
|
}
|
|
2419
2476
|
async function findFile(dir, candidates) {
|
|
2420
2477
|
for (const name of candidates) {
|
|
2421
2478
|
const filePath = join13(dir, `${name}.ts`);
|
|
2422
2479
|
try {
|
|
2423
|
-
await
|
|
2480
|
+
await readFile10(filePath);
|
|
2424
2481
|
return filePath;
|
|
2425
2482
|
} catch {
|
|
2426
2483
|
}
|
|
@@ -2442,20 +2499,20 @@ function computeImportPath(fromDir, toFile) {
|
|
|
2442
2499
|
}
|
|
2443
2500
|
return rel.replace(/\.ts$/, ".js");
|
|
2444
2501
|
}
|
|
2445
|
-
async function resolveToolByName(name,
|
|
2446
|
-
const tDir = toolsDir(
|
|
2447
|
-
const aDir = agentsDir(
|
|
2502
|
+
async function resolveToolByName(name, config2, cwd) {
|
|
2503
|
+
const tDir = toolsDir(config2, cwd);
|
|
2504
|
+
const aDir = agentsDir(config2, cwd);
|
|
2448
2505
|
const lock = await readLock(cwd);
|
|
2449
2506
|
for (const [componentName, entry] of Object.entries(lock)) {
|
|
2450
2507
|
if (componentName === name || componentName === `${name}-tool`) {
|
|
2451
2508
|
const toolFile = entry.files.find((f) => {
|
|
2452
|
-
const toolsAlias =
|
|
2509
|
+
const toolsAlias = config2.aliases.tools ?? join13(config2.aliases.base ?? "src/ai", "tools");
|
|
2453
2510
|
return f.startsWith(toolsAlias);
|
|
2454
2511
|
});
|
|
2455
2512
|
if (toolFile) {
|
|
2456
2513
|
const filePath2 = join13(cwd, toolFile);
|
|
2457
2514
|
try {
|
|
2458
|
-
const source2 = await
|
|
2515
|
+
const source2 = await readFile10(filePath2, "utf-8");
|
|
2459
2516
|
const exportName2 = parseExportName(source2);
|
|
2460
2517
|
if (exportName2) {
|
|
2461
2518
|
return {
|
|
@@ -2473,7 +2530,7 @@ async function resolveToolByName(name, config, cwd) {
|
|
|
2473
2530
|
const uniqueCandidates = [...new Set(candidates)];
|
|
2474
2531
|
const filePath = await findFile(tDir, uniqueCandidates);
|
|
2475
2532
|
if (!filePath) return null;
|
|
2476
|
-
const source = await
|
|
2533
|
+
const source = await readFile10(filePath, "utf-8");
|
|
2477
2534
|
const exportName = parseExportName(source);
|
|
2478
2535
|
if (!exportName) return null;
|
|
2479
2536
|
return {
|
|
@@ -2482,19 +2539,19 @@ async function resolveToolByName(name, config, cwd) {
|
|
|
2482
2539
|
importPath: computeImportPath(aDir, filePath)
|
|
2483
2540
|
};
|
|
2484
2541
|
}
|
|
2485
|
-
async function resolveAgentByName(name,
|
|
2486
|
-
const aDir = agentsDir(
|
|
2542
|
+
async function resolveAgentByName(name, config2, cwd) {
|
|
2543
|
+
const aDir = agentsDir(config2, cwd);
|
|
2487
2544
|
const lock = await readLock(cwd);
|
|
2488
2545
|
for (const [componentName, entry] of Object.entries(lock)) {
|
|
2489
2546
|
if (componentName === name || componentName === `${name}-agent`) {
|
|
2490
2547
|
const agentFile = entry.files.find((f) => {
|
|
2491
|
-
const agentsAlias =
|
|
2548
|
+
const agentsAlias = config2.aliases.agents ?? join13(config2.aliases.base ?? "src/ai", "agents");
|
|
2492
2549
|
return f.startsWith(agentsAlias);
|
|
2493
2550
|
});
|
|
2494
2551
|
if (agentFile) {
|
|
2495
2552
|
const filePath2 = join13(cwd, agentFile);
|
|
2496
2553
|
try {
|
|
2497
|
-
const source2 = await
|
|
2554
|
+
const source2 = await readFile10(filePath2, "utf-8");
|
|
2498
2555
|
const agentName2 = parseAgentName(source2);
|
|
2499
2556
|
return {
|
|
2500
2557
|
filePath: filePath2,
|
|
@@ -2509,7 +2566,7 @@ async function resolveAgentByName(name, config, cwd) {
|
|
|
2509
2566
|
const uniqueCandidates = [...new Set(candidates)];
|
|
2510
2567
|
const filePath = await findFile(aDir, uniqueCandidates);
|
|
2511
2568
|
if (!filePath) return null;
|
|
2512
|
-
const source = await
|
|
2569
|
+
const source = await readFile10(filePath, "utf-8");
|
|
2513
2570
|
const agentName = parseAgentName(source);
|
|
2514
2571
|
const fallbackName = filePath.split("/").pop().replace(/\.ts$/, "");
|
|
2515
2572
|
return {
|
|
@@ -2517,12 +2574,12 @@ async function resolveAgentByName(name, config, cwd) {
|
|
|
2517
2574
|
name: agentName ?? fallbackName
|
|
2518
2575
|
};
|
|
2519
2576
|
}
|
|
2520
|
-
async function listTools(
|
|
2521
|
-
const dir = toolsDir(
|
|
2577
|
+
async function listTools(config2, cwd) {
|
|
2578
|
+
const dir = toolsDir(config2, cwd);
|
|
2522
2579
|
return listComponentsInDir(dir);
|
|
2523
2580
|
}
|
|
2524
|
-
async function listAgents(
|
|
2525
|
-
const dir = agentsDir(
|
|
2581
|
+
async function listAgents(config2, cwd) {
|
|
2582
|
+
const dir = agentsDir(config2, cwd);
|
|
2526
2583
|
return listComponentsInDir(dir);
|
|
2527
2584
|
}
|
|
2528
2585
|
async function listComponentsInDir(dir) {
|
|
@@ -2797,13 +2854,13 @@ __export(link_exports, {
|
|
|
2797
2854
|
});
|
|
2798
2855
|
import * as p9 from "@clack/prompts";
|
|
2799
2856
|
import pc8 from "picocolors";
|
|
2800
|
-
import { readFile as
|
|
2857
|
+
import { readFile as readFile11, writeFile as writeFile11 } from "fs/promises";
|
|
2801
2858
|
import { basename } from "path";
|
|
2802
2859
|
async function linkCommand(type, name, opts) {
|
|
2803
2860
|
p9.intro(pc8.bgCyan(pc8.black(" kitn link ")));
|
|
2804
2861
|
const cwd = process.cwd();
|
|
2805
|
-
const
|
|
2806
|
-
if (!
|
|
2862
|
+
const config2 = await readConfig(cwd);
|
|
2863
|
+
if (!config2) {
|
|
2807
2864
|
p9.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2808
2865
|
process.exit(1);
|
|
2809
2866
|
}
|
|
@@ -2815,7 +2872,7 @@ async function linkCommand(type, name, opts) {
|
|
|
2815
2872
|
}
|
|
2816
2873
|
let toolName = name;
|
|
2817
2874
|
if (!toolName) {
|
|
2818
|
-
const tools = await listTools(
|
|
2875
|
+
const tools = await listTools(config2, cwd);
|
|
2819
2876
|
if (tools.length === 0) {
|
|
2820
2877
|
p9.log.error("No tools found in your project.");
|
|
2821
2878
|
process.exit(1);
|
|
@@ -2833,7 +2890,7 @@ async function linkCommand(type, name, opts) {
|
|
|
2833
2890
|
}
|
|
2834
2891
|
toolName = selected;
|
|
2835
2892
|
}
|
|
2836
|
-
const tool = await resolveToolByName(toolName,
|
|
2893
|
+
const tool = await resolveToolByName(toolName, config2, cwd);
|
|
2837
2894
|
if (!tool) {
|
|
2838
2895
|
p9.log.error(
|
|
2839
2896
|
`Tool "${toolName}" not found. Check that the file exists in your tools directory.`
|
|
@@ -2842,7 +2899,7 @@ async function linkCommand(type, name, opts) {
|
|
|
2842
2899
|
}
|
|
2843
2900
|
let agentName = opts?.to;
|
|
2844
2901
|
if (!agentName) {
|
|
2845
|
-
const agents = await listAgents(
|
|
2902
|
+
const agents = await listAgents(config2, cwd);
|
|
2846
2903
|
if (agents.length === 0) {
|
|
2847
2904
|
p9.log.error("No agents found in your project.");
|
|
2848
2905
|
process.exit(1);
|
|
@@ -2860,14 +2917,14 @@ async function linkCommand(type, name, opts) {
|
|
|
2860
2917
|
}
|
|
2861
2918
|
agentName = selected;
|
|
2862
2919
|
}
|
|
2863
|
-
const agent = await resolveAgentByName(agentName,
|
|
2920
|
+
const agent = await resolveAgentByName(agentName, config2, cwd);
|
|
2864
2921
|
if (!agent) {
|
|
2865
2922
|
p9.log.error(
|
|
2866
2923
|
`Agent "${agentName}" not found. Check that the file exists in your agents directory.`
|
|
2867
2924
|
);
|
|
2868
2925
|
process.exit(1);
|
|
2869
2926
|
}
|
|
2870
|
-
const agentContent = await
|
|
2927
|
+
const agentContent = await readFile11(agent.filePath, "utf-8");
|
|
2871
2928
|
const toolRef = {
|
|
2872
2929
|
exportName: tool.exportName,
|
|
2873
2930
|
importPath: tool.importPath
|
|
@@ -2913,13 +2970,13 @@ __export(unlink_exports, {
|
|
|
2913
2970
|
});
|
|
2914
2971
|
import * as p10 from "@clack/prompts";
|
|
2915
2972
|
import pc9 from "picocolors";
|
|
2916
|
-
import { readFile as
|
|
2973
|
+
import { readFile as readFile12, writeFile as writeFile12 } from "fs/promises";
|
|
2917
2974
|
import { basename as basename2 } from "path";
|
|
2918
2975
|
async function unlinkCommand(type, name, opts) {
|
|
2919
2976
|
p10.intro(pc9.bgCyan(pc9.black(" kitn unlink ")));
|
|
2920
2977
|
const cwd = process.cwd();
|
|
2921
|
-
const
|
|
2922
|
-
if (!
|
|
2978
|
+
const config2 = await readConfig(cwd);
|
|
2979
|
+
if (!config2) {
|
|
2923
2980
|
p10.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2924
2981
|
process.exit(1);
|
|
2925
2982
|
}
|
|
@@ -2931,7 +2988,7 @@ async function unlinkCommand(type, name, opts) {
|
|
|
2931
2988
|
}
|
|
2932
2989
|
let toolName = name;
|
|
2933
2990
|
if (!toolName) {
|
|
2934
|
-
const tools = await listTools(
|
|
2991
|
+
const tools = await listTools(config2, cwd);
|
|
2935
2992
|
if (tools.length === 0) {
|
|
2936
2993
|
p10.log.error("No tools found in your project.");
|
|
2937
2994
|
process.exit(1);
|
|
@@ -2949,7 +3006,7 @@ async function unlinkCommand(type, name, opts) {
|
|
|
2949
3006
|
}
|
|
2950
3007
|
toolName = selected;
|
|
2951
3008
|
}
|
|
2952
|
-
const tool = await resolveToolByName(toolName,
|
|
3009
|
+
const tool = await resolveToolByName(toolName, config2, cwd);
|
|
2953
3010
|
if (!tool) {
|
|
2954
3011
|
p10.log.error(
|
|
2955
3012
|
`Tool "${toolName}" not found. Check that the file exists in your tools directory.`
|
|
@@ -2958,7 +3015,7 @@ async function unlinkCommand(type, name, opts) {
|
|
|
2958
3015
|
}
|
|
2959
3016
|
let agentName = opts?.from;
|
|
2960
3017
|
if (!agentName) {
|
|
2961
|
-
const agents = await listAgents(
|
|
3018
|
+
const agents = await listAgents(config2, cwd);
|
|
2962
3019
|
if (agents.length === 0) {
|
|
2963
3020
|
p10.log.error("No agents found in your project.");
|
|
2964
3021
|
process.exit(1);
|
|
@@ -2976,14 +3033,14 @@ async function unlinkCommand(type, name, opts) {
|
|
|
2976
3033
|
}
|
|
2977
3034
|
agentName = selected;
|
|
2978
3035
|
}
|
|
2979
|
-
const agent = await resolveAgentByName(agentName,
|
|
3036
|
+
const agent = await resolveAgentByName(agentName, config2, cwd);
|
|
2980
3037
|
if (!agent) {
|
|
2981
3038
|
p10.log.error(
|
|
2982
3039
|
`Agent "${agentName}" not found. Check that the file exists in your agents directory.`
|
|
2983
3040
|
);
|
|
2984
3041
|
process.exit(1);
|
|
2985
3042
|
}
|
|
2986
|
-
const agentContent = await
|
|
3043
|
+
const agentContent = await readFile12(agent.filePath, "utf-8");
|
|
2987
3044
|
const toolRef = {
|
|
2988
3045
|
exportName: tool.exportName,
|
|
2989
3046
|
importPath: tool.importPath
|
|
@@ -3031,13 +3088,13 @@ import * as p11 from "@clack/prompts";
|
|
|
3031
3088
|
import pc10 from "picocolors";
|
|
3032
3089
|
async function infoCommand(component) {
|
|
3033
3090
|
const cwd = process.cwd();
|
|
3034
|
-
const
|
|
3035
|
-
if (!
|
|
3091
|
+
const config2 = await readConfig(cwd);
|
|
3092
|
+
if (!config2) {
|
|
3036
3093
|
p11.log.error("No kitn.json found. Run `kitn init` first.");
|
|
3037
3094
|
process.exit(1);
|
|
3038
3095
|
}
|
|
3039
3096
|
const ref = parseComponentRef(component);
|
|
3040
|
-
const fetcher = new RegistryFetcher(
|
|
3097
|
+
const fetcher = new RegistryFetcher(config2.registries);
|
|
3041
3098
|
const s = p11.spinner();
|
|
3042
3099
|
s.start("Fetching component info...");
|
|
3043
3100
|
let index;
|
|
@@ -3200,40 +3257,33 @@ import pc12 from "picocolors";
|
|
|
3200
3257
|
async function rulesCommand() {
|
|
3201
3258
|
p13.intro(pc12.bgCyan(pc12.black(" kitn rules ")));
|
|
3202
3259
|
const cwd = process.cwd();
|
|
3203
|
-
const
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3260
|
+
const config2 = await readConfig(cwd);
|
|
3261
|
+
const registries = config2?.registries ?? DEFAULT_REGISTRIES;
|
|
3262
|
+
const aliases = config2?.aliases ?? DEFAULT_ALIASES;
|
|
3263
|
+
const rulesConfig = await fetchRulesConfig(registries);
|
|
3264
|
+
const selected = await p13.multiselect({
|
|
3265
|
+
message: "Which AI coding tools do you use?",
|
|
3266
|
+
options: rulesConfig.tools.map((t) => ({
|
|
3267
|
+
value: t.id,
|
|
3268
|
+
label: t.name,
|
|
3269
|
+
hint: t.description
|
|
3270
|
+
})),
|
|
3271
|
+
required: false
|
|
3272
|
+
});
|
|
3273
|
+
if (p13.isCancel(selected)) {
|
|
3274
|
+
p13.cancel("Cancelled.");
|
|
3275
|
+
process.exit(0);
|
|
3207
3276
|
}
|
|
3208
|
-
|
|
3209
|
-
if (
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
options: rulesConfig.tools.map((t) => ({
|
|
3214
|
-
value: t.id,
|
|
3215
|
-
label: t.name,
|
|
3216
|
-
hint: t.description
|
|
3217
|
-
})),
|
|
3218
|
-
required: false
|
|
3219
|
-
});
|
|
3220
|
-
if (p13.isCancel(selected)) {
|
|
3221
|
-
p13.cancel("Cancelled.");
|
|
3222
|
-
process.exit(0);
|
|
3223
|
-
}
|
|
3224
|
-
selectedIds = selected;
|
|
3225
|
-
if (selectedIds.length === 0) {
|
|
3226
|
-
p13.log.warn("No tools selected. Nothing to generate.");
|
|
3227
|
-
p13.outro("Done.");
|
|
3228
|
-
return;
|
|
3229
|
-
}
|
|
3230
|
-
const updatedConfig = { ...config, aiTools: selectedIds };
|
|
3231
|
-
await writeConfig(cwd, updatedConfig);
|
|
3232
|
-
p13.log.info(`Saved tool selections to ${pc12.bold("kitn.json")}`);
|
|
3277
|
+
const selectedIds = selected;
|
|
3278
|
+
if (selectedIds.length === 0) {
|
|
3279
|
+
p13.log.warn("No tools selected. Nothing to generate.");
|
|
3280
|
+
p13.outro("Done.");
|
|
3281
|
+
return;
|
|
3233
3282
|
}
|
|
3234
3283
|
const s = p13.spinner();
|
|
3235
3284
|
s.start("Generating rules files");
|
|
3236
|
-
const
|
|
3285
|
+
const effectiveConfig = { registries, aliases };
|
|
3286
|
+
const written = await generateRulesFiles(cwd, effectiveConfig, selectedIds);
|
|
3237
3287
|
s.stop("Rules files generated");
|
|
3238
3288
|
for (const filePath of written) {
|
|
3239
3289
|
p13.log.success(`${pc12.green("+")} ${filePath}`);
|
|
@@ -3248,6 +3298,284 @@ var init_rules = __esm({
|
|
|
3248
3298
|
}
|
|
3249
3299
|
});
|
|
3250
3300
|
|
|
3301
|
+
// src/commands/config.ts
|
|
3302
|
+
var config_exports = {};
|
|
3303
|
+
__export(config_exports, {
|
|
3304
|
+
configGetCommand: () => configGetCommand,
|
|
3305
|
+
configListCommand: () => configListCommand,
|
|
3306
|
+
configSetCommand: () => configSetCommand,
|
|
3307
|
+
readUserConfig: () => readUserConfig
|
|
3308
|
+
});
|
|
3309
|
+
import * as p14 from "@clack/prompts";
|
|
3310
|
+
import pc13 from "picocolors";
|
|
3311
|
+
import { readFile as readFile13, writeFile as writeFile13, mkdir as mkdir7 } from "fs/promises";
|
|
3312
|
+
import { join as join14 } from "path";
|
|
3313
|
+
import { homedir as homedir2 } from "os";
|
|
3314
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3315
|
+
async function readUserConfig() {
|
|
3316
|
+
try {
|
|
3317
|
+
const raw = await readFile13(CONFIG_FILE2, "utf-8");
|
|
3318
|
+
return JSON.parse(raw);
|
|
3319
|
+
} catch {
|
|
3320
|
+
return {};
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
async function writeUserConfig(config2) {
|
|
3324
|
+
if (!existsSync4(CONFIG_DIR)) {
|
|
3325
|
+
await mkdir7(CONFIG_DIR, { recursive: true });
|
|
3326
|
+
}
|
|
3327
|
+
await writeFile13(CONFIG_FILE2, JSON.stringify(config2, null, 2) + "\n");
|
|
3328
|
+
}
|
|
3329
|
+
async function configSetCommand(key, value) {
|
|
3330
|
+
if (!VALID_KEYS.includes(key)) {
|
|
3331
|
+
p14.log.error(`Unknown config key: ${pc13.red(key)}`);
|
|
3332
|
+
p14.log.info(`Valid keys: ${VALID_KEYS.join(", ")}`);
|
|
3333
|
+
process.exit(1);
|
|
3334
|
+
}
|
|
3335
|
+
const config2 = await readUserConfig();
|
|
3336
|
+
config2[key] = value;
|
|
3337
|
+
await writeUserConfig(config2);
|
|
3338
|
+
const displayValue = key.includes("key") ? value.slice(0, 8) + "..." : value;
|
|
3339
|
+
p14.log.success(`Set ${pc13.cyan(key)} = ${pc13.green(displayValue)}`);
|
|
3340
|
+
}
|
|
3341
|
+
async function configGetCommand(key) {
|
|
3342
|
+
const config2 = await readUserConfig();
|
|
3343
|
+
const value = config2[key];
|
|
3344
|
+
if (value === void 0) {
|
|
3345
|
+
p14.log.warn(`${pc13.cyan(key)} is not set`);
|
|
3346
|
+
} else {
|
|
3347
|
+
const displayValue = key.includes("key") ? value.slice(0, 8) + "..." : value;
|
|
3348
|
+
p14.log.info(`${pc13.cyan(key)} = ${pc13.green(displayValue)}`);
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
async function configListCommand() {
|
|
3352
|
+
const config2 = await readUserConfig();
|
|
3353
|
+
const entries = Object.entries(config2);
|
|
3354
|
+
if (entries.length === 0) {
|
|
3355
|
+
p14.log.info("No user configuration set.");
|
|
3356
|
+
p14.log.info(`Run ${pc13.cyan("kitn config set <key> <value>")} to configure.`);
|
|
3357
|
+
return;
|
|
3358
|
+
}
|
|
3359
|
+
for (const [key, value] of entries) {
|
|
3360
|
+
const displayValue = key.includes("key") ? value.slice(0, 8) + "..." : value;
|
|
3361
|
+
p14.log.info(`${pc13.cyan(key)} = ${pc13.green(displayValue)}`);
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
var CONFIG_DIR, CONFIG_FILE2, VALID_KEYS;
|
|
3365
|
+
var init_config2 = __esm({
|
|
3366
|
+
"src/commands/config.ts"() {
|
|
3367
|
+
"use strict";
|
|
3368
|
+
CONFIG_DIR = join14(homedir2(), ".kitn");
|
|
3369
|
+
CONFIG_FILE2 = join14(CONFIG_DIR, "config.json");
|
|
3370
|
+
VALID_KEYS = ["chat-url", "api-key"];
|
|
3371
|
+
}
|
|
3372
|
+
});
|
|
3373
|
+
|
|
3374
|
+
// src/commands/chat.ts
|
|
3375
|
+
var chat_exports = {};
|
|
3376
|
+
__export(chat_exports, {
|
|
3377
|
+
buildRequestPayload: () => buildRequestPayload,
|
|
3378
|
+
chatCommand: () => chatCommand,
|
|
3379
|
+
formatPlan: () => formatPlan,
|
|
3380
|
+
resolveServiceUrl: () => resolveServiceUrl
|
|
3381
|
+
});
|
|
3382
|
+
import * as p15 from "@clack/prompts";
|
|
3383
|
+
import pc14 from "picocolors";
|
|
3384
|
+
async function resolveServiceUrl(urlOverride, chatServiceConfig) {
|
|
3385
|
+
if (urlOverride) return urlOverride;
|
|
3386
|
+
if (process.env.KITN_CHAT_URL) return process.env.KITN_CHAT_URL;
|
|
3387
|
+
const userConfig = await readUserConfig();
|
|
3388
|
+
if (userConfig["chat-url"]) return userConfig["chat-url"];
|
|
3389
|
+
if (chatServiceConfig?.url) return chatServiceConfig.url;
|
|
3390
|
+
return DEFAULT_SERVICE_URL;
|
|
3391
|
+
}
|
|
3392
|
+
function buildRequestPayload(message, metadata) {
|
|
3393
|
+
return { message, metadata };
|
|
3394
|
+
}
|
|
3395
|
+
function formatPlan(plan) {
|
|
3396
|
+
const lines = [plan.summary, ""];
|
|
3397
|
+
for (let i = 0; i < plan.steps.length; i++) {
|
|
3398
|
+
const step = plan.steps[i];
|
|
3399
|
+
const num = `${i + 1}.`;
|
|
3400
|
+
const label = formatStepLabel(step);
|
|
3401
|
+
lines.push(`${num} ${label} - ${step.reason}`);
|
|
3402
|
+
}
|
|
3403
|
+
return lines.join("\n");
|
|
3404
|
+
}
|
|
3405
|
+
function formatStepLabel(step) {
|
|
3406
|
+
switch (step.action) {
|
|
3407
|
+
case "add":
|
|
3408
|
+
return `Add ${pc14.cyan(step.component)}`;
|
|
3409
|
+
case "remove":
|
|
3410
|
+
return `Remove ${pc14.red(step.component)}`;
|
|
3411
|
+
case "create":
|
|
3412
|
+
return `Create ${pc14.green(`${step.type}/${step.name}`)}`;
|
|
3413
|
+
case "link":
|
|
3414
|
+
return `Link ${pc14.cyan(step.toolName)} \u2192 ${pc14.cyan(step.agentName)}`;
|
|
3415
|
+
case "unlink":
|
|
3416
|
+
return `Unlink ${pc14.red(step.toolName)} from ${pc14.cyan(step.agentName)}`;
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
async function chatCommand(message, opts) {
|
|
3420
|
+
const cwd = process.cwd();
|
|
3421
|
+
const config2 = await readConfig(cwd);
|
|
3422
|
+
if (!config2) {
|
|
3423
|
+
p15.log.error("No kitn.json found. Run `kitn init` first.");
|
|
3424
|
+
process.exit(1);
|
|
3425
|
+
}
|
|
3426
|
+
if (!message) {
|
|
3427
|
+
p15.log.error('Please provide a message. Usage: kitn chat "add a weather tool"');
|
|
3428
|
+
process.exit(1);
|
|
3429
|
+
}
|
|
3430
|
+
p15.intro(pc14.bold("kitn assistant"));
|
|
3431
|
+
const s = p15.spinner();
|
|
3432
|
+
s.start("Gathering project context...");
|
|
3433
|
+
let registryIndex;
|
|
3434
|
+
let installed;
|
|
3435
|
+
try {
|
|
3436
|
+
const fetcher = new RegistryFetcher(config2.registries);
|
|
3437
|
+
const indices = [];
|
|
3438
|
+
for (const namespace of Object.keys(config2.registries)) {
|
|
3439
|
+
try {
|
|
3440
|
+
const index = await fetcher.fetchIndex(namespace);
|
|
3441
|
+
indices.push(index);
|
|
3442
|
+
} catch {
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
registryIndex = indices;
|
|
3446
|
+
const lock = await readLock(cwd);
|
|
3447
|
+
installed = Object.keys(lock);
|
|
3448
|
+
} catch {
|
|
3449
|
+
s.stop(pc14.red("Failed to gather context"));
|
|
3450
|
+
p15.log.error("Could not read project context. Check your kitn.json and network connection.");
|
|
3451
|
+
process.exit(1);
|
|
3452
|
+
}
|
|
3453
|
+
s.stop("Context gathered");
|
|
3454
|
+
s.start("Thinking...");
|
|
3455
|
+
const serviceUrl = await resolveServiceUrl(opts?.url, config2.chatService);
|
|
3456
|
+
const payload = buildRequestPayload(message, { registryIndex, installed });
|
|
3457
|
+
let response;
|
|
3458
|
+
try {
|
|
3459
|
+
const headers = {
|
|
3460
|
+
"Content-Type": "application/json"
|
|
3461
|
+
};
|
|
3462
|
+
if (process.env.KITN_API_KEY) {
|
|
3463
|
+
headers["Authorization"] = `Bearer ${process.env.KITN_API_KEY}`;
|
|
3464
|
+
}
|
|
3465
|
+
response = await fetch(`${serviceUrl}/api/chat`, {
|
|
3466
|
+
method: "POST",
|
|
3467
|
+
headers,
|
|
3468
|
+
body: JSON.stringify(payload)
|
|
3469
|
+
});
|
|
3470
|
+
} catch (err) {
|
|
3471
|
+
s.stop(pc14.red("Connection failed"));
|
|
3472
|
+
p15.log.error(`Could not reach chat service at ${serviceUrl}. ${err.message ?? ""}`);
|
|
3473
|
+
process.exit(1);
|
|
3474
|
+
}
|
|
3475
|
+
if (!response.ok) {
|
|
3476
|
+
s.stop(pc14.red("Request failed"));
|
|
3477
|
+
p15.log.error(`Chat service returned ${response.status}: ${response.statusText}`);
|
|
3478
|
+
process.exit(1);
|
|
3479
|
+
}
|
|
3480
|
+
let data;
|
|
3481
|
+
try {
|
|
3482
|
+
data = await response.json();
|
|
3483
|
+
} catch {
|
|
3484
|
+
s.stop(pc14.red("Invalid response"));
|
|
3485
|
+
p15.log.error("Chat service returned an invalid response.");
|
|
3486
|
+
process.exit(1);
|
|
3487
|
+
}
|
|
3488
|
+
s.stop("Done");
|
|
3489
|
+
if (data.rejected) {
|
|
3490
|
+
p15.log.warn(data.text ?? "Request was rejected by the assistant.");
|
|
3491
|
+
p15.outro("Try rephrasing your request.");
|
|
3492
|
+
return;
|
|
3493
|
+
}
|
|
3494
|
+
if (!data.plan) {
|
|
3495
|
+
p15.log.info(data.text ?? "No actionable plan returned.");
|
|
3496
|
+
p15.outro("Nothing to do.");
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
3499
|
+
p15.log.message(formatPlan(data.plan));
|
|
3500
|
+
const steps = data.plan.steps;
|
|
3501
|
+
const action = await p15.select({
|
|
3502
|
+
message: "How would you like to proceed?",
|
|
3503
|
+
options: [
|
|
3504
|
+
{ value: "all", label: "Yes, run all steps" },
|
|
3505
|
+
{ value: "select", label: "Select which steps to run" },
|
|
3506
|
+
{ value: "cancel", label: "Cancel" }
|
|
3507
|
+
]
|
|
3508
|
+
});
|
|
3509
|
+
if (p15.isCancel(action) || action === "cancel") {
|
|
3510
|
+
p15.cancel("Cancelled.");
|
|
3511
|
+
return;
|
|
3512
|
+
}
|
|
3513
|
+
let selectedSteps;
|
|
3514
|
+
if (action === "select") {
|
|
3515
|
+
const choices = await p15.multiselect({
|
|
3516
|
+
message: "Select steps to run:",
|
|
3517
|
+
options: steps.map((step, i) => ({
|
|
3518
|
+
value: i,
|
|
3519
|
+
label: `${formatStepLabel(step)} - ${step.reason}`
|
|
3520
|
+
}))
|
|
3521
|
+
});
|
|
3522
|
+
if (p15.isCancel(choices)) {
|
|
3523
|
+
p15.cancel("Cancelled.");
|
|
3524
|
+
return;
|
|
3525
|
+
}
|
|
3526
|
+
selectedSteps = choices.map((i) => steps[i]);
|
|
3527
|
+
} else {
|
|
3528
|
+
selectedSteps = steps;
|
|
3529
|
+
}
|
|
3530
|
+
for (const step of selectedSteps) {
|
|
3531
|
+
s.start(`Running: ${formatStepLabel(step)}...`);
|
|
3532
|
+
try {
|
|
3533
|
+
switch (step.action) {
|
|
3534
|
+
case "add": {
|
|
3535
|
+
const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
3536
|
+
await addCommand2([step.component], { yes: true });
|
|
3537
|
+
break;
|
|
3538
|
+
}
|
|
3539
|
+
case "create": {
|
|
3540
|
+
const { createCommand: createCommand2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
3541
|
+
await createCommand2(step.type, step.name);
|
|
3542
|
+
break;
|
|
3543
|
+
}
|
|
3544
|
+
case "link": {
|
|
3545
|
+
const { linkCommand: linkCommand2 } = await Promise.resolve().then(() => (init_link(), link_exports));
|
|
3546
|
+
await linkCommand2("tool", step.toolName, { to: step.agentName });
|
|
3547
|
+
break;
|
|
3548
|
+
}
|
|
3549
|
+
case "remove": {
|
|
3550
|
+
const { removeCommand: removeCommand2 } = await Promise.resolve().then(() => (init_remove(), remove_exports));
|
|
3551
|
+
await removeCommand2(step.component);
|
|
3552
|
+
break;
|
|
3553
|
+
}
|
|
3554
|
+
case "unlink": {
|
|
3555
|
+
const { unlinkCommand: unlinkCommand2 } = await Promise.resolve().then(() => (init_unlink(), unlink_exports));
|
|
3556
|
+
await unlinkCommand2("tool", step.toolName, { from: step.agentName });
|
|
3557
|
+
break;
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
s.stop(pc14.green(`Done: ${formatStepLabel(step)}`));
|
|
3561
|
+
} catch (err) {
|
|
3562
|
+
s.stop(pc14.red(`Failed: ${formatStepLabel(step)}`));
|
|
3563
|
+
p15.log.error(err.message ?? "Unknown error");
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
p15.outro(pc14.green("All done! Run your dev server to test the new components."));
|
|
3567
|
+
}
|
|
3568
|
+
var DEFAULT_SERVICE_URL;
|
|
3569
|
+
var init_chat = __esm({
|
|
3570
|
+
"src/commands/chat.ts"() {
|
|
3571
|
+
"use strict";
|
|
3572
|
+
init_config();
|
|
3573
|
+
init_fetcher();
|
|
3574
|
+
init_config2();
|
|
3575
|
+
DEFAULT_SERVICE_URL = "https://chat.kitn.dev";
|
|
3576
|
+
}
|
|
3577
|
+
});
|
|
3578
|
+
|
|
3251
3579
|
// src/commands/registry.ts
|
|
3252
3580
|
var registry_exports = {};
|
|
3253
3581
|
__export(registry_exports, {
|
|
@@ -3255,12 +3583,12 @@ __export(registry_exports, {
|
|
|
3255
3583
|
registryListCommand: () => registryListCommand,
|
|
3256
3584
|
registryRemoveCommand: () => registryRemoveCommand
|
|
3257
3585
|
});
|
|
3258
|
-
import * as
|
|
3259
|
-
import
|
|
3586
|
+
import * as p16 from "@clack/prompts";
|
|
3587
|
+
import pc15 from "picocolors";
|
|
3260
3588
|
async function registryAddCommand(namespace, url, opts = {}) {
|
|
3261
3589
|
const cwd = opts.cwd ?? process.cwd();
|
|
3262
|
-
const
|
|
3263
|
-
if (!
|
|
3590
|
+
const config2 = await readConfig(cwd);
|
|
3591
|
+
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3264
3592
|
if (!namespace.startsWith("@")) {
|
|
3265
3593
|
throw new Error("Namespace must start with @ (e.g. @myteam)");
|
|
3266
3594
|
}
|
|
@@ -3270,28 +3598,28 @@ async function registryAddCommand(namespace, url, opts = {}) {
|
|
|
3270
3598
|
if (!url.includes("{name}")) {
|
|
3271
3599
|
throw new Error("URL template must include {name} placeholder");
|
|
3272
3600
|
}
|
|
3273
|
-
if (
|
|
3601
|
+
if (config2.registries[namespace] && !opts.overwrite) {
|
|
3274
3602
|
throw new Error(`Registry '${namespace}' is already configured. Use --overwrite to replace.`);
|
|
3275
3603
|
}
|
|
3276
3604
|
if (opts.homepage || opts.description) {
|
|
3277
3605
|
const entry = { url };
|
|
3278
3606
|
if (opts.homepage) entry.homepage = opts.homepage;
|
|
3279
3607
|
if (opts.description) entry.description = opts.description;
|
|
3280
|
-
|
|
3608
|
+
config2.registries[namespace] = entry;
|
|
3281
3609
|
} else {
|
|
3282
|
-
|
|
3610
|
+
config2.registries[namespace] = url;
|
|
3283
3611
|
}
|
|
3284
|
-
await writeConfig(cwd,
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
if (opts.homepage)
|
|
3288
|
-
if (opts.description)
|
|
3612
|
+
await writeConfig(cwd, config2);
|
|
3613
|
+
p16.log.success(`Added registry ${pc15.bold(namespace)}`);
|
|
3614
|
+
p16.log.message(pc15.dim(` ${url}`));
|
|
3615
|
+
if (opts.homepage) p16.log.message(pc15.dim(` Homepage: ${opts.homepage}`));
|
|
3616
|
+
if (opts.description) p16.log.message(pc15.dim(` ${opts.description}`));
|
|
3289
3617
|
}
|
|
3290
3618
|
async function registryRemoveCommand(namespace, opts = {}) {
|
|
3291
3619
|
const cwd = opts.cwd ?? process.cwd();
|
|
3292
|
-
const
|
|
3293
|
-
if (!
|
|
3294
|
-
if (!
|
|
3620
|
+
const config2 = await readConfig(cwd);
|
|
3621
|
+
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3622
|
+
if (!config2.registries[namespace]) {
|
|
3295
3623
|
throw new Error(`Registry '${namespace}' is not configured.`);
|
|
3296
3624
|
}
|
|
3297
3625
|
if (namespace === "@kitn" && !opts.force) {
|
|
@@ -3304,35 +3632,35 @@ async function registryRemoveCommand(namespace, opts = {}) {
|
|
|
3304
3632
|
affectedComponents.push(name);
|
|
3305
3633
|
}
|
|
3306
3634
|
}
|
|
3307
|
-
delete
|
|
3308
|
-
await writeConfig(cwd,
|
|
3309
|
-
|
|
3635
|
+
delete config2.registries[namespace];
|
|
3636
|
+
await writeConfig(cwd, config2);
|
|
3637
|
+
p16.log.success(`Removed registry ${pc15.bold(namespace)}`);
|
|
3310
3638
|
if (affectedComponents.length > 0) {
|
|
3311
|
-
|
|
3312
|
-
` + affectedComponents.map((name) => ` ${
|
|
3639
|
+
p16.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:
|
|
3640
|
+
` + affectedComponents.map((name) => ` ${pc15.yellow("!")} ${name}`).join("\n"));
|
|
3313
3641
|
}
|
|
3314
3642
|
return { affectedComponents };
|
|
3315
3643
|
}
|
|
3316
3644
|
async function registryListCommand(opts = {}) {
|
|
3317
3645
|
const cwd = opts.cwd ?? process.cwd();
|
|
3318
|
-
const
|
|
3319
|
-
if (!
|
|
3320
|
-
const entries = Object.entries(
|
|
3646
|
+
const config2 = await readConfig(cwd);
|
|
3647
|
+
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3648
|
+
const entries = Object.entries(config2.registries).map(([namespace, value]) => {
|
|
3321
3649
|
const url = getRegistryUrl(value);
|
|
3322
3650
|
const homepage = typeof value === "object" ? value.homepage : void 0;
|
|
3323
3651
|
const description = typeof value === "object" ? value.description : void 0;
|
|
3324
3652
|
return { namespace, url, homepage, description };
|
|
3325
3653
|
});
|
|
3326
3654
|
if (entries.length === 0) {
|
|
3327
|
-
|
|
3655
|
+
p16.log.message(pc15.dim(" No registries configured."));
|
|
3328
3656
|
} else {
|
|
3329
3657
|
const lines = [];
|
|
3330
3658
|
for (const { namespace, url, homepage, description } of entries) {
|
|
3331
|
-
lines.push(` ${
|
|
3659
|
+
lines.push(` ${pc15.bold(namespace.padEnd(16))} ${pc15.dim(url)}`);
|
|
3332
3660
|
if (description) lines.push(` ${" ".repeat(16)} ${description}`);
|
|
3333
|
-
if (homepage) lines.push(` ${" ".repeat(16)} ${
|
|
3661
|
+
if (homepage) lines.push(` ${" ".repeat(16)} ${pc15.dim(homepage)}`);
|
|
3334
3662
|
}
|
|
3335
|
-
|
|
3663
|
+
p16.log.message(lines.join("\n"));
|
|
3336
3664
|
}
|
|
3337
3665
|
return entries;
|
|
3338
3666
|
}
|
|
@@ -3346,7 +3674,7 @@ var init_registry = __esm({
|
|
|
3346
3674
|
// src/index.ts
|
|
3347
3675
|
init_update_check();
|
|
3348
3676
|
import { Command } from "commander";
|
|
3349
|
-
var VERSION = true ? "0.1.
|
|
3677
|
+
var VERSION = true ? "0.1.33" : "0.0.0-dev";
|
|
3350
3678
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
3351
3679
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
3352
3680
|
program.command("init").description("Initialize kitn in your project").option("-r, --runtime <runtime>", "runtime to use (bun, node, deno)").option("-f, --framework <framework>", "HTTP framework (hono, hono-openapi, elysia)").option("-b, --base <path>", "base directory for components (default: src/ai)").option("-y, --yes", "accept all defaults without prompting").action(async (opts) => {
|
|
@@ -3397,6 +3725,10 @@ program.command("rules").description("Regenerate AI coding tool rules files").ac
|
|
|
3397
3725
|
const { rulesCommand: rulesCommand2 } = await Promise.resolve().then(() => (init_rules(), rules_exports));
|
|
3398
3726
|
await rulesCommand2();
|
|
3399
3727
|
});
|
|
3728
|
+
program.command("chat").description("AI-powered scaffolding assistant \u2014 describe what you need in plain English").argument("<message>", 'what you want to build (e.g. "I want a weather agent")').option("-u, --url <url>", "chat service URL (overrides config and default)").action(async (message, opts) => {
|
|
3729
|
+
const { chatCommand: chatCommand2 } = await Promise.resolve().then(() => (init_chat(), chat_exports));
|
|
3730
|
+
await chatCommand2(message, opts);
|
|
3731
|
+
});
|
|
3400
3732
|
var registry = program.command("registry").description("Manage component registries");
|
|
3401
3733
|
registry.command("add").description("Add a component registry").argument("<namespace>", "registry namespace (e.g. @myteam)").argument("<url>", "URL template with {type} and {name} placeholders").option("-o, --overwrite", "overwrite if namespace already exists").option("--homepage <url>", "registry homepage URL").option("--description <text>", "short description of the registry").action(async (namespace, url, opts) => {
|
|
3402
3734
|
const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
@@ -3410,6 +3742,19 @@ registry.command("list").description("List all configured registries").action(as
|
|
|
3410
3742
|
const { registryListCommand: registryListCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
3411
3743
|
await registryListCommand2();
|
|
3412
3744
|
});
|
|
3745
|
+
var config = program.command("config").description("Manage user-level configuration");
|
|
3746
|
+
config.command("set").description("Set a config value").argument("<key>", "config key (chat-url, api-key)").argument("<value>", "config value").action(async (key, value) => {
|
|
3747
|
+
const { configSetCommand: configSetCommand2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
3748
|
+
await configSetCommand2(key, value);
|
|
3749
|
+
});
|
|
3750
|
+
config.command("get").description("Get a config value").argument("<key>", "config key").action(async (key) => {
|
|
3751
|
+
const { configGetCommand: configGetCommand2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
3752
|
+
await configGetCommand2(key);
|
|
3753
|
+
});
|
|
3754
|
+
config.command("list").description("List all config values").action(async () => {
|
|
3755
|
+
const { configListCommand: configListCommand2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
3756
|
+
await configListCommand2();
|
|
3757
|
+
});
|
|
3413
3758
|
await program.parseAsync();
|
|
3414
3759
|
var ranCommand = program.args[0];
|
|
3415
3760
|
if (ranCommand !== "check") {
|