@kitnai/cli 0.1.32 → 0.1.34
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 +588 -239
- 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");
|
|
@@ -1170,8 +1188,8 @@ async function addCommand(components, opts) {
|
|
|
1170
1188
|
if (!opts.yes && process.stdin.isTTY) {
|
|
1171
1189
|
const totalComponents = resolved.length;
|
|
1172
1190
|
const summary = totalDeps > 0 ? `Install ${totalComponents} component(s) and ${totalDeps} npm package(s)?` : `Install ${totalComponents} component(s)?`;
|
|
1173
|
-
const
|
|
1174
|
-
if (p2.isCancel(
|
|
1191
|
+
const confirm7 = await p2.confirm({ message: summary });
|
|
1192
|
+
if (p2.isCancel(confirm7) || !confirm7) {
|
|
1175
1193
|
p2.cancel("Cancelled.");
|
|
1176
1194
|
process.exit(0);
|
|
1177
1195
|
}
|
|
@@ -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
|
}
|
|
@@ -1423,8 +1441,14 @@ var init_add = __esm({
|
|
|
1423
1441
|
});
|
|
1424
1442
|
|
|
1425
1443
|
// src/installers/rules-generator.ts
|
|
1426
|
-
import { writeFile as writeFile7, mkdir as mkdir4 } from "fs/promises";
|
|
1444
|
+
import { writeFile as writeFile7, mkdir as mkdir4, readFile as readFile7 } from "fs/promises";
|
|
1427
1445
|
import { join as join8, dirname as dirname4 } from "path";
|
|
1446
|
+
async function loadFallbackTemplate() {
|
|
1447
|
+
if (!_fallbackTemplate) {
|
|
1448
|
+
_fallbackTemplate = await readFile7(TEMPLATE_PATH, "utf-8");
|
|
1449
|
+
}
|
|
1450
|
+
return _fallbackTemplate;
|
|
1451
|
+
}
|
|
1428
1452
|
function deriveRulesBaseUrl(registries) {
|
|
1429
1453
|
const kitnEntry = registries["@kitn"];
|
|
1430
1454
|
if (!kitnEntry) {
|
|
@@ -1450,7 +1474,7 @@ async function fetchRulesTemplate(registries) {
|
|
|
1450
1474
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1451
1475
|
return await res.text();
|
|
1452
1476
|
} catch {
|
|
1453
|
-
return
|
|
1477
|
+
return loadFallbackTemplate();
|
|
1454
1478
|
}
|
|
1455
1479
|
}
|
|
1456
1480
|
function renderTemplate(template, aliases) {
|
|
@@ -1470,10 +1494,10 @@ ${content}`;
|
|
|
1470
1494
|
}
|
|
1471
1495
|
return content;
|
|
1472
1496
|
}
|
|
1473
|
-
async function generateRulesFiles(cwd,
|
|
1474
|
-
const rulesConfig = await fetchRulesConfig(
|
|
1475
|
-
const template = await fetchRulesTemplate(
|
|
1476
|
-
const rendered = renderTemplate(template,
|
|
1497
|
+
async function generateRulesFiles(cwd, config2, selectedToolIds) {
|
|
1498
|
+
const rulesConfig = await fetchRulesConfig(config2.registries);
|
|
1499
|
+
const template = await fetchRulesTemplate(config2.registries);
|
|
1500
|
+
const rendered = renderTemplate(template, config2.aliases);
|
|
1477
1501
|
const toolsToWrite = selectedToolIds ? rulesConfig.tools.filter((t) => selectedToolIds.includes(t.id)) : rulesConfig.tools;
|
|
1478
1502
|
const written = [];
|
|
1479
1503
|
for (const tool of toolsToWrite) {
|
|
@@ -1485,11 +1509,12 @@ async function generateRulesFiles(cwd, config, selectedToolIds) {
|
|
|
1485
1509
|
}
|
|
1486
1510
|
return written;
|
|
1487
1511
|
}
|
|
1488
|
-
var FALLBACK_CONFIG,
|
|
1512
|
+
var TEMPLATE_PATH, FALLBACK_CONFIG, _fallbackTemplate;
|
|
1489
1513
|
var init_rules_generator = __esm({
|
|
1490
1514
|
"src/installers/rules-generator.ts"() {
|
|
1491
1515
|
"use strict";
|
|
1492
1516
|
init_config();
|
|
1517
|
+
TEMPLATE_PATH = join8(import.meta.dirname, "rules-template.md");
|
|
1493
1518
|
FALLBACK_CONFIG = {
|
|
1494
1519
|
version: "1.0.0",
|
|
1495
1520
|
tools: [
|
|
@@ -1507,7 +1532,7 @@ var init_rules_generator = __esm({
|
|
|
1507
1532
|
format: "mdc",
|
|
1508
1533
|
frontmatter: {
|
|
1509
1534
|
description: "kitn AI agent framework conventions and patterns",
|
|
1510
|
-
globs: "src/ai/**/*.ts, kitn.json"
|
|
1535
|
+
globs: "src/ai/**/*.ts, src/ai/**/*.md, kitn.json, kitn.lock"
|
|
1511
1536
|
}
|
|
1512
1537
|
},
|
|
1513
1538
|
{
|
|
@@ -1530,34 +1555,6 @@ var init_rules_generator = __esm({
|
|
|
1530
1555
|
}
|
|
1531
1556
|
]
|
|
1532
1557
|
};
|
|
1533
|
-
FALLBACK_TEMPLATE = `# kitn AI Agent Framework
|
|
1534
|
-
|
|
1535
|
-
This project uses **kitn** to build multi-agent AI systems.
|
|
1536
|
-
|
|
1537
|
-
## Project Structure
|
|
1538
|
-
|
|
1539
|
-
AI components live under \`{base}\`:
|
|
1540
|
-
|
|
1541
|
-
- \`{agents}/\` \u2014 Agent definitions
|
|
1542
|
-
- \`{tools}/\` \u2014 Tool definitions
|
|
1543
|
-
- \`{skills}/\` \u2014 Skill files (markdown)
|
|
1544
|
-
- \`{storage}/\` \u2014 Storage providers
|
|
1545
|
-
- \`{crons}/\` \u2014 Cron job definitions
|
|
1546
|
-
|
|
1547
|
-
## Patterns
|
|
1548
|
-
|
|
1549
|
-
- Agents: \`registerAgent({ name, system, tools })\` from \`@kitn/core\`
|
|
1550
|
-
- Tools: \`tool()\` from \`ai\` + \`registerTool()\` from \`@kitn/core\`
|
|
1551
|
-
- Always use \`.js\` extension in relative imports
|
|
1552
|
-
- Use \`@kitn/core\` for core imports, \`ai\` for Vercel AI SDK
|
|
1553
|
-
|
|
1554
|
-
## CLI
|
|
1555
|
-
|
|
1556
|
-
- \`kitn add <name>\` \u2014 install from registry
|
|
1557
|
-
- \`kitn create <type> <name>\` \u2014 scaffold locally
|
|
1558
|
-
- \`kitn link tool <name> --to <agent>\` \u2014 wire a tool to an agent
|
|
1559
|
-
- \`kitn list\` \u2014 browse components
|
|
1560
|
-
`;
|
|
1561
1558
|
}
|
|
1562
1559
|
});
|
|
1563
1560
|
|
|
@@ -1568,11 +1565,11 @@ __export(init_exports, {
|
|
|
1568
1565
|
});
|
|
1569
1566
|
import * as p3 from "@clack/prompts";
|
|
1570
1567
|
import pc4 from "picocolors";
|
|
1571
|
-
import { mkdir as mkdir5, readFile as
|
|
1568
|
+
import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
|
|
1572
1569
|
import { join as join9 } from "path";
|
|
1573
1570
|
async function detectFramework(cwd) {
|
|
1574
1571
|
try {
|
|
1575
|
-
const pkg = JSON.parse(await
|
|
1572
|
+
const pkg = JSON.parse(await readFile8(join9(cwd, "package.json"), "utf-8"));
|
|
1576
1573
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1577
1574
|
if (deps["elysia"]) return "elysia";
|
|
1578
1575
|
if (deps["@hono/zod-openapi"]) return "hono-openapi";
|
|
@@ -1660,15 +1657,15 @@ async function initCommand(opts = {}) {
|
|
|
1660
1657
|
framework = detected;
|
|
1661
1658
|
p3.log.info(`Detected ${pc4.bold(detected)} from package.json`);
|
|
1662
1659
|
if (!opts.yes) {
|
|
1663
|
-
const
|
|
1660
|
+
const confirm7 = await p3.confirm({
|
|
1664
1661
|
message: `Use ${detected}?`,
|
|
1665
1662
|
initialValue: true
|
|
1666
1663
|
});
|
|
1667
|
-
if (p3.isCancel(
|
|
1664
|
+
if (p3.isCancel(confirm7)) {
|
|
1668
1665
|
p3.cancel("Init cancelled.");
|
|
1669
1666
|
process.exit(0);
|
|
1670
1667
|
}
|
|
1671
|
-
if (!
|
|
1668
|
+
if (!confirm7) {
|
|
1672
1669
|
const selected = await p3.select({
|
|
1673
1670
|
message: "Which HTTP framework do you use?",
|
|
1674
1671
|
options: [
|
|
@@ -1718,7 +1715,7 @@ async function initCommand(opts = {}) {
|
|
|
1718
1715
|
}
|
|
1719
1716
|
baseDir = base;
|
|
1720
1717
|
}
|
|
1721
|
-
const
|
|
1718
|
+
const config2 = {
|
|
1722
1719
|
runtime,
|
|
1723
1720
|
framework,
|
|
1724
1721
|
aliases: {
|
|
@@ -1730,7 +1727,7 @@ async function initCommand(opts = {}) {
|
|
|
1730
1727
|
},
|
|
1731
1728
|
registries: {
|
|
1732
1729
|
"@kitn": {
|
|
1733
|
-
url:
|
|
1730
|
+
url: DEFAULT_REGISTRY_URL,
|
|
1734
1731
|
homepage: "https://kitn.ai",
|
|
1735
1732
|
description: "Official kitn AI agent components"
|
|
1736
1733
|
}
|
|
@@ -1738,7 +1735,7 @@ async function initCommand(opts = {}) {
|
|
|
1738
1735
|
};
|
|
1739
1736
|
const s = p3.spinner();
|
|
1740
1737
|
s.start("Writing kitn.json");
|
|
1741
|
-
await writeConfig(cwd,
|
|
1738
|
+
await writeConfig(cwd, config2);
|
|
1742
1739
|
s.stop("Created kitn.json");
|
|
1743
1740
|
await patchProjectTsconfig(
|
|
1744
1741
|
cwd,
|
|
@@ -1756,7 +1753,7 @@ async function initCommand(opts = {}) {
|
|
|
1756
1753
|
await writeFile8(pluginPath, getPluginTemplate(framework));
|
|
1757
1754
|
p3.log.success(`Created ${pc4.bold(baseDir + "/plugin.ts")} \u2014 configure your AI provider there`);
|
|
1758
1755
|
try {
|
|
1759
|
-
const rulesConfig = await fetchRulesConfig(
|
|
1756
|
+
const rulesConfig = await fetchRulesConfig(config2.registries);
|
|
1760
1757
|
let selectedToolIds;
|
|
1761
1758
|
if (opts.yes) {
|
|
1762
1759
|
selectedToolIds = rulesConfig.tools.map((t) => t.id);
|
|
@@ -1777,9 +1774,7 @@ async function initCommand(opts = {}) {
|
|
|
1777
1774
|
}
|
|
1778
1775
|
}
|
|
1779
1776
|
if (selectedToolIds.length > 0) {
|
|
1780
|
-
const
|
|
1781
|
-
await writeConfig(cwd, updatedConfig);
|
|
1782
|
-
const written = await generateRulesFiles(cwd, updatedConfig, selectedToolIds);
|
|
1777
|
+
const written = await generateRulesFiles(cwd, config2, selectedToolIds);
|
|
1783
1778
|
for (const filePath of written) {
|
|
1784
1779
|
p3.log.success(`Created ${pc4.bold(filePath)}`);
|
|
1785
1780
|
}
|
|
@@ -1827,8 +1822,8 @@ import * as p4 from "@clack/prompts";
|
|
|
1827
1822
|
import pc5 from "picocolors";
|
|
1828
1823
|
async function listCommand(typeFilter, opts) {
|
|
1829
1824
|
const cwd = process.cwd();
|
|
1830
|
-
const
|
|
1831
|
-
if (!
|
|
1825
|
+
const config2 = await readConfig(cwd);
|
|
1826
|
+
if (!config2) {
|
|
1832
1827
|
p4.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1833
1828
|
process.exit(1);
|
|
1834
1829
|
}
|
|
@@ -1838,9 +1833,9 @@ async function listCommand(typeFilter, opts) {
|
|
|
1838
1833
|
p4.log.error(`Unknown type ${pc5.bold(rawType)}. Valid types: agent, tool, skill, storage, package`);
|
|
1839
1834
|
process.exit(1);
|
|
1840
1835
|
}
|
|
1841
|
-
const fetcher = new RegistryFetcher(
|
|
1842
|
-
const namespacesToFetch = opts.registry ? [opts.registry] : Object.keys(
|
|
1843
|
-
if (opts.registry && !
|
|
1836
|
+
const fetcher = new RegistryFetcher(config2.registries);
|
|
1837
|
+
const namespacesToFetch = opts.registry ? [opts.registry] : Object.keys(config2.registries);
|
|
1838
|
+
if (opts.registry && !config2.registries[opts.registry]) {
|
|
1844
1839
|
p4.log.error(`Registry ${pc5.bold(opts.registry)} is not configured. Run ${pc5.bold("kitn registry list")} to see configured registries.`);
|
|
1845
1840
|
process.exit(1);
|
|
1846
1841
|
}
|
|
@@ -1953,12 +1948,12 @@ import * as p5 from "@clack/prompts";
|
|
|
1953
1948
|
import { join as join10 } from "path";
|
|
1954
1949
|
async function diffCommand(componentName) {
|
|
1955
1950
|
const cwd = process.cwd();
|
|
1956
|
-
const
|
|
1957
|
-
if (!
|
|
1951
|
+
const config2 = await readConfig(cwd);
|
|
1952
|
+
if (!config2) {
|
|
1958
1953
|
p5.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1959
1954
|
process.exit(1);
|
|
1960
1955
|
}
|
|
1961
|
-
const input = componentName === "routes" ? resolveRoutesAlias(
|
|
1956
|
+
const input = componentName === "routes" ? resolveRoutesAlias(config2) : componentName;
|
|
1962
1957
|
const ref = parseComponentRef(input);
|
|
1963
1958
|
const lock = await readLock(cwd);
|
|
1964
1959
|
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
@@ -1968,7 +1963,7 @@ async function diffCommand(componentName) {
|
|
|
1968
1963
|
process.exit(1);
|
|
1969
1964
|
}
|
|
1970
1965
|
const namespace = installed.registry ?? ref.namespace;
|
|
1971
|
-
const fetcher = new RegistryFetcher(
|
|
1966
|
+
const fetcher = new RegistryFetcher(config2.registries);
|
|
1972
1967
|
const index = await fetcher.fetchIndex(namespace);
|
|
1973
1968
|
const indexItem = index.items.find((i) => i.name === ref.name);
|
|
1974
1969
|
if (!indexItem) {
|
|
@@ -1980,7 +1975,7 @@ async function diffCommand(componentName) {
|
|
|
1980
1975
|
let hasDiff = false;
|
|
1981
1976
|
for (const file of registryItem.files) {
|
|
1982
1977
|
if (indexItem.type === "kitn:package") {
|
|
1983
|
-
const baseDir =
|
|
1978
|
+
const baseDir = config2.aliases.base ?? "src/ai";
|
|
1984
1979
|
const localPath = join10(cwd, baseDir, file.path);
|
|
1985
1980
|
const relativePath = join10(baseDir, file.path);
|
|
1986
1981
|
const localContent = await readExistingFile(localPath);
|
|
@@ -2006,7 +2001,7 @@ async function diffCommand(componentName) {
|
|
|
2006
2001
|
return "storage";
|
|
2007
2002
|
}
|
|
2008
2003
|
})();
|
|
2009
|
-
const localPath = join10(cwd,
|
|
2004
|
+
const localPath = join10(cwd, config2.aliases[aliasKey], fileName);
|
|
2010
2005
|
const localContent = await readExistingFile(localPath);
|
|
2011
2006
|
if (localContent === null) {
|
|
2012
2007
|
p5.log.warn(`${fileName}: file missing locally`);
|
|
@@ -2041,9 +2036,9 @@ __export(remove_exports, {
|
|
|
2041
2036
|
import * as p6 from "@clack/prompts";
|
|
2042
2037
|
import pc6 from "picocolors";
|
|
2043
2038
|
import { join as join11, relative as relative3, dirname as dirname5 } from "path";
|
|
2044
|
-
import { unlink as unlink3, readFile as
|
|
2039
|
+
import { unlink as unlink3, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
|
|
2045
2040
|
import { existsSync as existsSync2 } from "fs";
|
|
2046
|
-
async function removeSingleComponent(installedKey, lock,
|
|
2041
|
+
async function removeSingleComponent(installedKey, lock, config2, cwd) {
|
|
2047
2042
|
const entry = lock[installedKey];
|
|
2048
2043
|
if (!entry) return;
|
|
2049
2044
|
const deleted = [];
|
|
@@ -2055,16 +2050,16 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
2055
2050
|
p6.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
2056
2051
|
}
|
|
2057
2052
|
}
|
|
2058
|
-
const baseDir =
|
|
2053
|
+
const baseDir = config2.aliases.base ?? "src/ai";
|
|
2059
2054
|
const barrelPath = join11(cwd, baseDir, "index.ts");
|
|
2060
2055
|
const barrelDir = join11(cwd, baseDir);
|
|
2061
2056
|
const barrelEligibleDirs = /* @__PURE__ */ new Set([
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2057
|
+
config2.aliases.agents,
|
|
2058
|
+
config2.aliases.tools,
|
|
2059
|
+
config2.aliases.skills
|
|
2065
2060
|
]);
|
|
2066
2061
|
if (existsSync2(barrelPath) && deleted.length > 0) {
|
|
2067
|
-
let barrelContent = await
|
|
2062
|
+
let barrelContent = await readFile9(barrelPath, "utf-8");
|
|
2068
2063
|
let barrelChanged = false;
|
|
2069
2064
|
for (const filePath of deleted) {
|
|
2070
2065
|
const fileDir = dirname5(filePath);
|
|
@@ -2087,7 +2082,7 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
2087
2082
|
` + deleted.map((f) => ` ${pc6.red("-")} ${f}`).join("\n"));
|
|
2088
2083
|
}
|
|
2089
2084
|
}
|
|
2090
|
-
async function offerOrphanRemoval(removedDeps, lock,
|
|
2085
|
+
async function offerOrphanRemoval(removedDeps, lock, config2, cwd) {
|
|
2091
2086
|
if (removedDeps.size === 0) return;
|
|
2092
2087
|
const remaining = Object.entries(lock);
|
|
2093
2088
|
const neededDeps = /* @__PURE__ */ new Set();
|
|
@@ -2114,13 +2109,13 @@ async function offerOrphanRemoval(removedDeps, lock, config, cwd) {
|
|
|
2114
2109
|
});
|
|
2115
2110
|
if (p6.isCancel(selected)) return;
|
|
2116
2111
|
for (const key of selected) {
|
|
2117
|
-
await removeSingleComponent(key, lock,
|
|
2112
|
+
await removeSingleComponent(key, lock, config2, cwd);
|
|
2118
2113
|
}
|
|
2119
2114
|
}
|
|
2120
2115
|
async function removeCommand(componentName) {
|
|
2121
2116
|
const cwd = process.cwd();
|
|
2122
|
-
const
|
|
2123
|
-
if (!
|
|
2117
|
+
const config2 = await readConfig(cwd);
|
|
2118
|
+
if (!config2) {
|
|
2124
2119
|
p6.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2125
2120
|
process.exit(1);
|
|
2126
2121
|
}
|
|
@@ -2158,14 +2153,14 @@ async function removeCommand(componentName) {
|
|
|
2158
2153
|
}
|
|
2159
2154
|
}
|
|
2160
2155
|
for (const key of selectedKeys) {
|
|
2161
|
-
await removeSingleComponent(key, lock,
|
|
2156
|
+
await removeSingleComponent(key, lock, config2, cwd);
|
|
2162
2157
|
}
|
|
2163
|
-
await offerOrphanRemoval(allRemovedDeps, lock,
|
|
2158
|
+
await offerOrphanRemoval(allRemovedDeps, lock, config2, cwd);
|
|
2164
2159
|
await writeLock(cwd, lock);
|
|
2165
2160
|
p6.outro(pc6.green("Done!"));
|
|
2166
2161
|
return;
|
|
2167
2162
|
}
|
|
2168
|
-
const input = componentName === "routes" ? resolveRoutesAlias(
|
|
2163
|
+
const input = componentName === "routes" ? resolveRoutesAlias(config2) : componentName;
|
|
2169
2164
|
const ref = parseComponentRef(input);
|
|
2170
2165
|
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
2171
2166
|
const entry = lock[installedKey];
|
|
@@ -2182,8 +2177,8 @@ async function removeCommand(componentName) {
|
|
|
2182
2177
|
process.exit(0);
|
|
2183
2178
|
}
|
|
2184
2179
|
const removedDeps = new Set(entry.registryDependencies ?? []);
|
|
2185
|
-
await removeSingleComponent(installedKey, lock,
|
|
2186
|
-
await offerOrphanRemoval(removedDeps, lock,
|
|
2180
|
+
await removeSingleComponent(installedKey, lock, config2, cwd);
|
|
2181
|
+
await offerOrphanRemoval(removedDeps, lock, config2, cwd);
|
|
2187
2182
|
await writeLock(cwd, lock);
|
|
2188
2183
|
}
|
|
2189
2184
|
var init_remove = __esm({
|
|
@@ -2204,8 +2199,8 @@ import * as p7 from "@clack/prompts";
|
|
|
2204
2199
|
async function updateCommand(components) {
|
|
2205
2200
|
if (components.length === 0) {
|
|
2206
2201
|
const cwd = process.cwd();
|
|
2207
|
-
const
|
|
2208
|
-
if (!
|
|
2202
|
+
const config2 = await readConfig(cwd);
|
|
2203
|
+
if (!config2) {
|
|
2209
2204
|
p7.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2210
2205
|
process.exit(1);
|
|
2211
2206
|
}
|
|
@@ -2249,7 +2244,7 @@ import * as p8 from "@clack/prompts";
|
|
|
2249
2244
|
import pc7 from "picocolors";
|
|
2250
2245
|
import { join as join12, relative as relative4 } from "path";
|
|
2251
2246
|
import { existsSync as existsSync3 } from "fs";
|
|
2252
|
-
import { readFile as
|
|
2247
|
+
import { readFile as readFile10, writeFile as writeFile10, mkdir as mkdir6 } from "fs/promises";
|
|
2253
2248
|
function generateAgentSource(name) {
|
|
2254
2249
|
const camel = toCamelCase(name);
|
|
2255
2250
|
return `import { registerAgent } from "@kitn/core";
|
|
@@ -2347,8 +2342,8 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2347
2342
|
);
|
|
2348
2343
|
}
|
|
2349
2344
|
const cwd = opts?.cwd ?? process.cwd();
|
|
2350
|
-
const
|
|
2351
|
-
if (!
|
|
2345
|
+
const config2 = await readConfig(cwd);
|
|
2346
|
+
if (!config2) {
|
|
2352
2347
|
throw new Error(
|
|
2353
2348
|
`No kitn.json found in ${cwd}. Run ${pc7.bold("kitn init")} first.`
|
|
2354
2349
|
);
|
|
@@ -2356,7 +2351,7 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2356
2351
|
const validType = type;
|
|
2357
2352
|
const kitnType = typeToKitnType[validType];
|
|
2358
2353
|
const fileName = validType === "skill" ? `${name}.md` : `${name}.ts`;
|
|
2359
|
-
const filePath = join12(cwd, getInstallPath(
|
|
2354
|
+
const filePath = join12(cwd, getInstallPath(config2, kitnType, fileName));
|
|
2360
2355
|
const dummyContent = "";
|
|
2361
2356
|
const status = await checkFileStatus(filePath, dummyContent);
|
|
2362
2357
|
if (status !== "new" /* New */) {
|
|
@@ -2383,11 +2378,11 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2383
2378
|
await writeComponentFile(filePath, source);
|
|
2384
2379
|
let barrelUpdated = false;
|
|
2385
2380
|
if (BARREL_TYPES.includes(validType)) {
|
|
2386
|
-
const baseDir =
|
|
2381
|
+
const baseDir = config2.aliases.base ?? "src/ai";
|
|
2387
2382
|
const barrelPath = join12(cwd, baseDir, "index.ts");
|
|
2388
2383
|
let barrelContent;
|
|
2389
2384
|
if (existsSync3(barrelPath)) {
|
|
2390
|
-
barrelContent = await
|
|
2385
|
+
barrelContent = await readFile10(barrelPath, "utf-8");
|
|
2391
2386
|
} else {
|
|
2392
2387
|
barrelContent = createBarrelFile();
|
|
2393
2388
|
await mkdir6(join12(cwd, baseDir), { recursive: true });
|
|
@@ -2439,7 +2434,7 @@ var init_create = __esm({
|
|
|
2439
2434
|
});
|
|
2440
2435
|
|
|
2441
2436
|
// src/utils/component-resolver.ts
|
|
2442
|
-
import { readdir, readFile as
|
|
2437
|
+
import { readdir, readFile as readFile11 } from "fs/promises";
|
|
2443
2438
|
import { join as join13, relative as relative5 } from "path";
|
|
2444
2439
|
function stripSuffix(name, suffix) {
|
|
2445
2440
|
if (name.endsWith(`-${suffix}`)) {
|
|
@@ -2447,21 +2442,21 @@ function stripSuffix(name, suffix) {
|
|
|
2447
2442
|
}
|
|
2448
2443
|
return name;
|
|
2449
2444
|
}
|
|
2450
|
-
function toolsDir(
|
|
2451
|
-
const baseAlias =
|
|
2452
|
-
const tools =
|
|
2445
|
+
function toolsDir(config2, cwd) {
|
|
2446
|
+
const baseAlias = config2.aliases.base ?? "src/ai";
|
|
2447
|
+
const tools = config2.aliases.tools ?? join13(baseAlias, "tools");
|
|
2453
2448
|
return join13(cwd, tools);
|
|
2454
2449
|
}
|
|
2455
|
-
function agentsDir(
|
|
2456
|
-
const baseAlias =
|
|
2457
|
-
const agents =
|
|
2450
|
+
function agentsDir(config2, cwd) {
|
|
2451
|
+
const baseAlias = config2.aliases.base ?? "src/ai";
|
|
2452
|
+
const agents = config2.aliases.agents ?? join13(baseAlias, "agents");
|
|
2458
2453
|
return join13(cwd, agents);
|
|
2459
2454
|
}
|
|
2460
2455
|
async function findFile(dir, candidates) {
|
|
2461
2456
|
for (const name of candidates) {
|
|
2462
2457
|
const filePath = join13(dir, `${name}.ts`);
|
|
2463
2458
|
try {
|
|
2464
|
-
await
|
|
2459
|
+
await readFile11(filePath);
|
|
2465
2460
|
return filePath;
|
|
2466
2461
|
} catch {
|
|
2467
2462
|
}
|
|
@@ -2483,20 +2478,20 @@ function computeImportPath(fromDir, toFile) {
|
|
|
2483
2478
|
}
|
|
2484
2479
|
return rel.replace(/\.ts$/, ".js");
|
|
2485
2480
|
}
|
|
2486
|
-
async function resolveToolByName(name,
|
|
2487
|
-
const tDir = toolsDir(
|
|
2488
|
-
const aDir = agentsDir(
|
|
2481
|
+
async function resolveToolByName(name, config2, cwd) {
|
|
2482
|
+
const tDir = toolsDir(config2, cwd);
|
|
2483
|
+
const aDir = agentsDir(config2, cwd);
|
|
2489
2484
|
const lock = await readLock(cwd);
|
|
2490
2485
|
for (const [componentName, entry] of Object.entries(lock)) {
|
|
2491
2486
|
if (componentName === name || componentName === `${name}-tool`) {
|
|
2492
2487
|
const toolFile = entry.files.find((f) => {
|
|
2493
|
-
const toolsAlias =
|
|
2488
|
+
const toolsAlias = config2.aliases.tools ?? join13(config2.aliases.base ?? "src/ai", "tools");
|
|
2494
2489
|
return f.startsWith(toolsAlias);
|
|
2495
2490
|
});
|
|
2496
2491
|
if (toolFile) {
|
|
2497
2492
|
const filePath2 = join13(cwd, toolFile);
|
|
2498
2493
|
try {
|
|
2499
|
-
const source2 = await
|
|
2494
|
+
const source2 = await readFile11(filePath2, "utf-8");
|
|
2500
2495
|
const exportName2 = parseExportName(source2);
|
|
2501
2496
|
if (exportName2) {
|
|
2502
2497
|
return {
|
|
@@ -2514,7 +2509,7 @@ async function resolveToolByName(name, config, cwd) {
|
|
|
2514
2509
|
const uniqueCandidates = [...new Set(candidates)];
|
|
2515
2510
|
const filePath = await findFile(tDir, uniqueCandidates);
|
|
2516
2511
|
if (!filePath) return null;
|
|
2517
|
-
const source = await
|
|
2512
|
+
const source = await readFile11(filePath, "utf-8");
|
|
2518
2513
|
const exportName = parseExportName(source);
|
|
2519
2514
|
if (!exportName) return null;
|
|
2520
2515
|
return {
|
|
@@ -2523,19 +2518,19 @@ async function resolveToolByName(name, config, cwd) {
|
|
|
2523
2518
|
importPath: computeImportPath(aDir, filePath)
|
|
2524
2519
|
};
|
|
2525
2520
|
}
|
|
2526
|
-
async function resolveAgentByName(name,
|
|
2527
|
-
const aDir = agentsDir(
|
|
2521
|
+
async function resolveAgentByName(name, config2, cwd) {
|
|
2522
|
+
const aDir = agentsDir(config2, cwd);
|
|
2528
2523
|
const lock = await readLock(cwd);
|
|
2529
2524
|
for (const [componentName, entry] of Object.entries(lock)) {
|
|
2530
2525
|
if (componentName === name || componentName === `${name}-agent`) {
|
|
2531
2526
|
const agentFile = entry.files.find((f) => {
|
|
2532
|
-
const agentsAlias =
|
|
2527
|
+
const agentsAlias = config2.aliases.agents ?? join13(config2.aliases.base ?? "src/ai", "agents");
|
|
2533
2528
|
return f.startsWith(agentsAlias);
|
|
2534
2529
|
});
|
|
2535
2530
|
if (agentFile) {
|
|
2536
2531
|
const filePath2 = join13(cwd, agentFile);
|
|
2537
2532
|
try {
|
|
2538
|
-
const source2 = await
|
|
2533
|
+
const source2 = await readFile11(filePath2, "utf-8");
|
|
2539
2534
|
const agentName2 = parseAgentName(source2);
|
|
2540
2535
|
return {
|
|
2541
2536
|
filePath: filePath2,
|
|
@@ -2550,7 +2545,7 @@ async function resolveAgentByName(name, config, cwd) {
|
|
|
2550
2545
|
const uniqueCandidates = [...new Set(candidates)];
|
|
2551
2546
|
const filePath = await findFile(aDir, uniqueCandidates);
|
|
2552
2547
|
if (!filePath) return null;
|
|
2553
|
-
const source = await
|
|
2548
|
+
const source = await readFile11(filePath, "utf-8");
|
|
2554
2549
|
const agentName = parseAgentName(source);
|
|
2555
2550
|
const fallbackName = filePath.split("/").pop().replace(/\.ts$/, "");
|
|
2556
2551
|
return {
|
|
@@ -2558,12 +2553,12 @@ async function resolveAgentByName(name, config, cwd) {
|
|
|
2558
2553
|
name: agentName ?? fallbackName
|
|
2559
2554
|
};
|
|
2560
2555
|
}
|
|
2561
|
-
async function listTools(
|
|
2562
|
-
const dir = toolsDir(
|
|
2556
|
+
async function listTools(config2, cwd) {
|
|
2557
|
+
const dir = toolsDir(config2, cwd);
|
|
2563
2558
|
return listComponentsInDir(dir);
|
|
2564
2559
|
}
|
|
2565
|
-
async function listAgents(
|
|
2566
|
-
const dir = agentsDir(
|
|
2560
|
+
async function listAgents(config2, cwd) {
|
|
2561
|
+
const dir = agentsDir(config2, cwd);
|
|
2567
2562
|
return listComponentsInDir(dir);
|
|
2568
2563
|
}
|
|
2569
2564
|
async function listComponentsInDir(dir) {
|
|
@@ -2838,13 +2833,13 @@ __export(link_exports, {
|
|
|
2838
2833
|
});
|
|
2839
2834
|
import * as p9 from "@clack/prompts";
|
|
2840
2835
|
import pc8 from "picocolors";
|
|
2841
|
-
import { readFile as
|
|
2836
|
+
import { readFile as readFile12, writeFile as writeFile11 } from "fs/promises";
|
|
2842
2837
|
import { basename } from "path";
|
|
2843
2838
|
async function linkCommand(type, name, opts) {
|
|
2844
2839
|
p9.intro(pc8.bgCyan(pc8.black(" kitn link ")));
|
|
2845
2840
|
const cwd = process.cwd();
|
|
2846
|
-
const
|
|
2847
|
-
if (!
|
|
2841
|
+
const config2 = await readConfig(cwd);
|
|
2842
|
+
if (!config2) {
|
|
2848
2843
|
p9.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2849
2844
|
process.exit(1);
|
|
2850
2845
|
}
|
|
@@ -2856,7 +2851,7 @@ async function linkCommand(type, name, opts) {
|
|
|
2856
2851
|
}
|
|
2857
2852
|
let toolName = name;
|
|
2858
2853
|
if (!toolName) {
|
|
2859
|
-
const tools = await listTools(
|
|
2854
|
+
const tools = await listTools(config2, cwd);
|
|
2860
2855
|
if (tools.length === 0) {
|
|
2861
2856
|
p9.log.error("No tools found in your project.");
|
|
2862
2857
|
process.exit(1);
|
|
@@ -2874,7 +2869,7 @@ async function linkCommand(type, name, opts) {
|
|
|
2874
2869
|
}
|
|
2875
2870
|
toolName = selected;
|
|
2876
2871
|
}
|
|
2877
|
-
const tool = await resolveToolByName(toolName,
|
|
2872
|
+
const tool = await resolveToolByName(toolName, config2, cwd);
|
|
2878
2873
|
if (!tool) {
|
|
2879
2874
|
p9.log.error(
|
|
2880
2875
|
`Tool "${toolName}" not found. Check that the file exists in your tools directory.`
|
|
@@ -2883,7 +2878,7 @@ async function linkCommand(type, name, opts) {
|
|
|
2883
2878
|
}
|
|
2884
2879
|
let agentName = opts?.to;
|
|
2885
2880
|
if (!agentName) {
|
|
2886
|
-
const agents = await listAgents(
|
|
2881
|
+
const agents = await listAgents(config2, cwd);
|
|
2887
2882
|
if (agents.length === 0) {
|
|
2888
2883
|
p9.log.error("No agents found in your project.");
|
|
2889
2884
|
process.exit(1);
|
|
@@ -2901,14 +2896,14 @@ async function linkCommand(type, name, opts) {
|
|
|
2901
2896
|
}
|
|
2902
2897
|
agentName = selected;
|
|
2903
2898
|
}
|
|
2904
|
-
const agent = await resolveAgentByName(agentName,
|
|
2899
|
+
const agent = await resolveAgentByName(agentName, config2, cwd);
|
|
2905
2900
|
if (!agent) {
|
|
2906
2901
|
p9.log.error(
|
|
2907
2902
|
`Agent "${agentName}" not found. Check that the file exists in your agents directory.`
|
|
2908
2903
|
);
|
|
2909
2904
|
process.exit(1);
|
|
2910
2905
|
}
|
|
2911
|
-
const agentContent = await
|
|
2906
|
+
const agentContent = await readFile12(agent.filePath, "utf-8");
|
|
2912
2907
|
const toolRef = {
|
|
2913
2908
|
exportName: tool.exportName,
|
|
2914
2909
|
importPath: tool.importPath
|
|
@@ -2954,13 +2949,13 @@ __export(unlink_exports, {
|
|
|
2954
2949
|
});
|
|
2955
2950
|
import * as p10 from "@clack/prompts";
|
|
2956
2951
|
import pc9 from "picocolors";
|
|
2957
|
-
import { readFile as
|
|
2952
|
+
import { readFile as readFile13, writeFile as writeFile12 } from "fs/promises";
|
|
2958
2953
|
import { basename as basename2 } from "path";
|
|
2959
2954
|
async function unlinkCommand(type, name, opts) {
|
|
2960
2955
|
p10.intro(pc9.bgCyan(pc9.black(" kitn unlink ")));
|
|
2961
2956
|
const cwd = process.cwd();
|
|
2962
|
-
const
|
|
2963
|
-
if (!
|
|
2957
|
+
const config2 = await readConfig(cwd);
|
|
2958
|
+
if (!config2) {
|
|
2964
2959
|
p10.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2965
2960
|
process.exit(1);
|
|
2966
2961
|
}
|
|
@@ -2972,7 +2967,7 @@ async function unlinkCommand(type, name, opts) {
|
|
|
2972
2967
|
}
|
|
2973
2968
|
let toolName = name;
|
|
2974
2969
|
if (!toolName) {
|
|
2975
|
-
const tools = await listTools(
|
|
2970
|
+
const tools = await listTools(config2, cwd);
|
|
2976
2971
|
if (tools.length === 0) {
|
|
2977
2972
|
p10.log.error("No tools found in your project.");
|
|
2978
2973
|
process.exit(1);
|
|
@@ -2990,7 +2985,7 @@ async function unlinkCommand(type, name, opts) {
|
|
|
2990
2985
|
}
|
|
2991
2986
|
toolName = selected;
|
|
2992
2987
|
}
|
|
2993
|
-
const tool = await resolveToolByName(toolName,
|
|
2988
|
+
const tool = await resolveToolByName(toolName, config2, cwd);
|
|
2994
2989
|
if (!tool) {
|
|
2995
2990
|
p10.log.error(
|
|
2996
2991
|
`Tool "${toolName}" not found. Check that the file exists in your tools directory.`
|
|
@@ -2999,7 +2994,7 @@ async function unlinkCommand(type, name, opts) {
|
|
|
2999
2994
|
}
|
|
3000
2995
|
let agentName = opts?.from;
|
|
3001
2996
|
if (!agentName) {
|
|
3002
|
-
const agents = await listAgents(
|
|
2997
|
+
const agents = await listAgents(config2, cwd);
|
|
3003
2998
|
if (agents.length === 0) {
|
|
3004
2999
|
p10.log.error("No agents found in your project.");
|
|
3005
3000
|
process.exit(1);
|
|
@@ -3017,14 +3012,14 @@ async function unlinkCommand(type, name, opts) {
|
|
|
3017
3012
|
}
|
|
3018
3013
|
agentName = selected;
|
|
3019
3014
|
}
|
|
3020
|
-
const agent = await resolveAgentByName(agentName,
|
|
3015
|
+
const agent = await resolveAgentByName(agentName, config2, cwd);
|
|
3021
3016
|
if (!agent) {
|
|
3022
3017
|
p10.log.error(
|
|
3023
3018
|
`Agent "${agentName}" not found. Check that the file exists in your agents directory.`
|
|
3024
3019
|
);
|
|
3025
3020
|
process.exit(1);
|
|
3026
3021
|
}
|
|
3027
|
-
const agentContent = await
|
|
3022
|
+
const agentContent = await readFile13(agent.filePath, "utf-8");
|
|
3028
3023
|
const toolRef = {
|
|
3029
3024
|
exportName: tool.exportName,
|
|
3030
3025
|
importPath: tool.importPath
|
|
@@ -3072,13 +3067,13 @@ import * as p11 from "@clack/prompts";
|
|
|
3072
3067
|
import pc10 from "picocolors";
|
|
3073
3068
|
async function infoCommand(component) {
|
|
3074
3069
|
const cwd = process.cwd();
|
|
3075
|
-
const
|
|
3076
|
-
if (!
|
|
3070
|
+
const config2 = await readConfig(cwd);
|
|
3071
|
+
if (!config2) {
|
|
3077
3072
|
p11.log.error("No kitn.json found. Run `kitn init` first.");
|
|
3078
3073
|
process.exit(1);
|
|
3079
3074
|
}
|
|
3080
3075
|
const ref = parseComponentRef(component);
|
|
3081
|
-
const fetcher = new RegistryFetcher(
|
|
3076
|
+
const fetcher = new RegistryFetcher(config2.registries);
|
|
3082
3077
|
const s = p11.spinner();
|
|
3083
3078
|
s.start("Fetching component info...");
|
|
3084
3079
|
let index;
|
|
@@ -3241,40 +3236,33 @@ import pc12 from "picocolors";
|
|
|
3241
3236
|
async function rulesCommand() {
|
|
3242
3237
|
p13.intro(pc12.bgCyan(pc12.black(" kitn rules ")));
|
|
3243
3238
|
const cwd = process.cwd();
|
|
3244
|
-
const
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3239
|
+
const config2 = await readConfig(cwd);
|
|
3240
|
+
const registries = config2?.registries ?? DEFAULT_REGISTRIES;
|
|
3241
|
+
const aliases = config2?.aliases ?? DEFAULT_ALIASES;
|
|
3242
|
+
const rulesConfig = await fetchRulesConfig(registries);
|
|
3243
|
+
const selected = await p13.multiselect({
|
|
3244
|
+
message: "Which AI coding tools do you use?",
|
|
3245
|
+
options: rulesConfig.tools.map((t) => ({
|
|
3246
|
+
value: t.id,
|
|
3247
|
+
label: t.name,
|
|
3248
|
+
hint: t.description
|
|
3249
|
+
})),
|
|
3250
|
+
required: false
|
|
3251
|
+
});
|
|
3252
|
+
if (p13.isCancel(selected)) {
|
|
3253
|
+
p13.cancel("Cancelled.");
|
|
3254
|
+
process.exit(0);
|
|
3248
3255
|
}
|
|
3249
|
-
|
|
3250
|
-
if (
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
options: rulesConfig.tools.map((t) => ({
|
|
3255
|
-
value: t.id,
|
|
3256
|
-
label: t.name,
|
|
3257
|
-
hint: t.description
|
|
3258
|
-
})),
|
|
3259
|
-
required: false
|
|
3260
|
-
});
|
|
3261
|
-
if (p13.isCancel(selected)) {
|
|
3262
|
-
p13.cancel("Cancelled.");
|
|
3263
|
-
process.exit(0);
|
|
3264
|
-
}
|
|
3265
|
-
selectedIds = selected;
|
|
3266
|
-
if (selectedIds.length === 0) {
|
|
3267
|
-
p13.log.warn("No tools selected. Nothing to generate.");
|
|
3268
|
-
p13.outro("Done.");
|
|
3269
|
-
return;
|
|
3270
|
-
}
|
|
3271
|
-
const updatedConfig = { ...config, aiTools: selectedIds };
|
|
3272
|
-
await writeConfig(cwd, updatedConfig);
|
|
3273
|
-
p13.log.info(`Saved tool selections to ${pc12.bold("kitn.json")}`);
|
|
3256
|
+
const selectedIds = selected;
|
|
3257
|
+
if (selectedIds.length === 0) {
|
|
3258
|
+
p13.log.warn("No tools selected. Nothing to generate.");
|
|
3259
|
+
p13.outro("Done.");
|
|
3260
|
+
return;
|
|
3274
3261
|
}
|
|
3275
3262
|
const s = p13.spinner();
|
|
3276
3263
|
s.start("Generating rules files");
|
|
3277
|
-
const
|
|
3264
|
+
const effectiveConfig = { registries, aliases };
|
|
3265
|
+
const written = await generateRulesFiles(cwd, effectiveConfig, selectedIds);
|
|
3278
3266
|
s.stop("Rules files generated");
|
|
3279
3267
|
for (const filePath of written) {
|
|
3280
3268
|
p13.log.success(`${pc12.green("+")} ${filePath}`);
|
|
@@ -3289,6 +3277,79 @@ var init_rules = __esm({
|
|
|
3289
3277
|
}
|
|
3290
3278
|
});
|
|
3291
3279
|
|
|
3280
|
+
// src/commands/config.ts
|
|
3281
|
+
var config_exports = {};
|
|
3282
|
+
__export(config_exports, {
|
|
3283
|
+
configGetCommand: () => configGetCommand,
|
|
3284
|
+
configListCommand: () => configListCommand,
|
|
3285
|
+
configSetCommand: () => configSetCommand,
|
|
3286
|
+
readUserConfig: () => readUserConfig
|
|
3287
|
+
});
|
|
3288
|
+
import * as p14 from "@clack/prompts";
|
|
3289
|
+
import pc13 from "picocolors";
|
|
3290
|
+
import { readFile as readFile14, writeFile as writeFile13, mkdir as mkdir7 } from "fs/promises";
|
|
3291
|
+
import { join as join14 } from "path";
|
|
3292
|
+
import { homedir as homedir2 } from "os";
|
|
3293
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3294
|
+
async function readUserConfig() {
|
|
3295
|
+
try {
|
|
3296
|
+
const raw = await readFile14(CONFIG_FILE2, "utf-8");
|
|
3297
|
+
return JSON.parse(raw);
|
|
3298
|
+
} catch {
|
|
3299
|
+
return {};
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
async function writeUserConfig(config2) {
|
|
3303
|
+
if (!existsSync4(CONFIG_DIR)) {
|
|
3304
|
+
await mkdir7(CONFIG_DIR, { recursive: true });
|
|
3305
|
+
}
|
|
3306
|
+
await writeFile13(CONFIG_FILE2, JSON.stringify(config2, null, 2) + "\n");
|
|
3307
|
+
}
|
|
3308
|
+
async function configSetCommand(key, value) {
|
|
3309
|
+
if (!VALID_KEYS.includes(key)) {
|
|
3310
|
+
p14.log.error(`Unknown config key: ${pc13.red(key)}`);
|
|
3311
|
+
p14.log.info(`Valid keys: ${VALID_KEYS.join(", ")}`);
|
|
3312
|
+
process.exit(1);
|
|
3313
|
+
}
|
|
3314
|
+
const config2 = await readUserConfig();
|
|
3315
|
+
config2[key] = value;
|
|
3316
|
+
await writeUserConfig(config2);
|
|
3317
|
+
const displayValue = key.includes("key") ? value.slice(0, 8) + "..." : value;
|
|
3318
|
+
p14.log.success(`Set ${pc13.cyan(key)} = ${pc13.green(displayValue)}`);
|
|
3319
|
+
}
|
|
3320
|
+
async function configGetCommand(key) {
|
|
3321
|
+
const config2 = await readUserConfig();
|
|
3322
|
+
const value = config2[key];
|
|
3323
|
+
if (value === void 0) {
|
|
3324
|
+
p14.log.warn(`${pc13.cyan(key)} is not set`);
|
|
3325
|
+
} else {
|
|
3326
|
+
const displayValue = key.includes("key") ? value.slice(0, 8) + "..." : value;
|
|
3327
|
+
p14.log.info(`${pc13.cyan(key)} = ${pc13.green(displayValue)}`);
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
async function configListCommand() {
|
|
3331
|
+
const config2 = await readUserConfig();
|
|
3332
|
+
const entries = Object.entries(config2);
|
|
3333
|
+
if (entries.length === 0) {
|
|
3334
|
+
p14.log.info("No user configuration set.");
|
|
3335
|
+
p14.log.info(`Run ${pc13.cyan("kitn config set <key> <value>")} to configure.`);
|
|
3336
|
+
return;
|
|
3337
|
+
}
|
|
3338
|
+
for (const [key, value] of entries) {
|
|
3339
|
+
const displayValue = key.includes("key") ? value.slice(0, 8) + "..." : value;
|
|
3340
|
+
p14.log.info(`${pc13.cyan(key)} = ${pc13.green(displayValue)}`);
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
var CONFIG_DIR, CONFIG_FILE2, VALID_KEYS;
|
|
3344
|
+
var init_config2 = __esm({
|
|
3345
|
+
"src/commands/config.ts"() {
|
|
3346
|
+
"use strict";
|
|
3347
|
+
CONFIG_DIR = join14(homedir2(), ".kitn");
|
|
3348
|
+
CONFIG_FILE2 = join14(CONFIG_DIR, "config.json");
|
|
3349
|
+
VALID_KEYS = ["chat-url", "api-key"];
|
|
3350
|
+
}
|
|
3351
|
+
});
|
|
3352
|
+
|
|
3292
3353
|
// src/commands/registry.ts
|
|
3293
3354
|
var registry_exports = {};
|
|
3294
3355
|
__export(registry_exports, {
|
|
@@ -3296,12 +3357,12 @@ __export(registry_exports, {
|
|
|
3296
3357
|
registryListCommand: () => registryListCommand,
|
|
3297
3358
|
registryRemoveCommand: () => registryRemoveCommand
|
|
3298
3359
|
});
|
|
3299
|
-
import * as
|
|
3300
|
-
import
|
|
3360
|
+
import * as p15 from "@clack/prompts";
|
|
3361
|
+
import pc14 from "picocolors";
|
|
3301
3362
|
async function registryAddCommand(namespace, url, opts = {}) {
|
|
3302
3363
|
const cwd = opts.cwd ?? process.cwd();
|
|
3303
|
-
const
|
|
3304
|
-
if (!
|
|
3364
|
+
const config2 = await readConfig(cwd);
|
|
3365
|
+
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3305
3366
|
if (!namespace.startsWith("@")) {
|
|
3306
3367
|
throw new Error("Namespace must start with @ (e.g. @myteam)");
|
|
3307
3368
|
}
|
|
@@ -3311,28 +3372,28 @@ async function registryAddCommand(namespace, url, opts = {}) {
|
|
|
3311
3372
|
if (!url.includes("{name}")) {
|
|
3312
3373
|
throw new Error("URL template must include {name} placeholder");
|
|
3313
3374
|
}
|
|
3314
|
-
if (
|
|
3375
|
+
if (config2.registries[namespace] && !opts.overwrite) {
|
|
3315
3376
|
throw new Error(`Registry '${namespace}' is already configured. Use --overwrite to replace.`);
|
|
3316
3377
|
}
|
|
3317
3378
|
if (opts.homepage || opts.description) {
|
|
3318
3379
|
const entry = { url };
|
|
3319
3380
|
if (opts.homepage) entry.homepage = opts.homepage;
|
|
3320
3381
|
if (opts.description) entry.description = opts.description;
|
|
3321
|
-
|
|
3382
|
+
config2.registries[namespace] = entry;
|
|
3322
3383
|
} else {
|
|
3323
|
-
|
|
3384
|
+
config2.registries[namespace] = url;
|
|
3324
3385
|
}
|
|
3325
|
-
await writeConfig(cwd,
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
if (opts.homepage)
|
|
3329
|
-
if (opts.description)
|
|
3386
|
+
await writeConfig(cwd, config2);
|
|
3387
|
+
p15.log.success(`Added registry ${pc14.bold(namespace)}`);
|
|
3388
|
+
p15.log.message(pc14.dim(` ${url}`));
|
|
3389
|
+
if (opts.homepage) p15.log.message(pc14.dim(` Homepage: ${opts.homepage}`));
|
|
3390
|
+
if (opts.description) p15.log.message(pc14.dim(` ${opts.description}`));
|
|
3330
3391
|
}
|
|
3331
3392
|
async function registryRemoveCommand(namespace, opts = {}) {
|
|
3332
3393
|
const cwd = opts.cwd ?? process.cwd();
|
|
3333
|
-
const
|
|
3334
|
-
if (!
|
|
3335
|
-
if (!
|
|
3394
|
+
const config2 = await readConfig(cwd);
|
|
3395
|
+
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3396
|
+
if (!config2.registries[namespace]) {
|
|
3336
3397
|
throw new Error(`Registry '${namespace}' is not configured.`);
|
|
3337
3398
|
}
|
|
3338
3399
|
if (namespace === "@kitn" && !opts.force) {
|
|
@@ -3345,35 +3406,35 @@ async function registryRemoveCommand(namespace, opts = {}) {
|
|
|
3345
3406
|
affectedComponents.push(name);
|
|
3346
3407
|
}
|
|
3347
3408
|
}
|
|
3348
|
-
delete
|
|
3349
|
-
await writeConfig(cwd,
|
|
3350
|
-
|
|
3409
|
+
delete config2.registries[namespace];
|
|
3410
|
+
await writeConfig(cwd, config2);
|
|
3411
|
+
p15.log.success(`Removed registry ${pc14.bold(namespace)}`);
|
|
3351
3412
|
if (affectedComponents.length > 0) {
|
|
3352
|
-
|
|
3353
|
-
` + affectedComponents.map((name) => ` ${
|
|
3413
|
+
p15.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:
|
|
3414
|
+
` + affectedComponents.map((name) => ` ${pc14.yellow("!")} ${name}`).join("\n"));
|
|
3354
3415
|
}
|
|
3355
3416
|
return { affectedComponents };
|
|
3356
3417
|
}
|
|
3357
3418
|
async function registryListCommand(opts = {}) {
|
|
3358
3419
|
const cwd = opts.cwd ?? process.cwd();
|
|
3359
|
-
const
|
|
3360
|
-
if (!
|
|
3361
|
-
const entries = Object.entries(
|
|
3420
|
+
const config2 = await readConfig(cwd);
|
|
3421
|
+
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3422
|
+
const entries = Object.entries(config2.registries).map(([namespace, value]) => {
|
|
3362
3423
|
const url = getRegistryUrl(value);
|
|
3363
3424
|
const homepage = typeof value === "object" ? value.homepage : void 0;
|
|
3364
3425
|
const description = typeof value === "object" ? value.description : void 0;
|
|
3365
3426
|
return { namespace, url, homepage, description };
|
|
3366
3427
|
});
|
|
3367
3428
|
if (entries.length === 0) {
|
|
3368
|
-
|
|
3429
|
+
p15.log.message(pc14.dim(" No registries configured."));
|
|
3369
3430
|
} else {
|
|
3370
3431
|
const lines = [];
|
|
3371
3432
|
for (const { namespace, url, homepage, description } of entries) {
|
|
3372
|
-
lines.push(` ${
|
|
3433
|
+
lines.push(` ${pc14.bold(namespace.padEnd(16))} ${pc14.dim(url)}`);
|
|
3373
3434
|
if (description) lines.push(` ${" ".repeat(16)} ${description}`);
|
|
3374
|
-
if (homepage) lines.push(` ${" ".repeat(16)} ${
|
|
3435
|
+
if (homepage) lines.push(` ${" ".repeat(16)} ${pc14.dim(homepage)}`);
|
|
3375
3436
|
}
|
|
3376
|
-
|
|
3437
|
+
p15.log.message(lines.join("\n"));
|
|
3377
3438
|
}
|
|
3378
3439
|
return entries;
|
|
3379
3440
|
}
|
|
@@ -3384,10 +3445,281 @@ var init_registry = __esm({
|
|
|
3384
3445
|
}
|
|
3385
3446
|
});
|
|
3386
3447
|
|
|
3448
|
+
// src/commands/chat.ts
|
|
3449
|
+
var chat_exports = {};
|
|
3450
|
+
__export(chat_exports, {
|
|
3451
|
+
buildRequestPayload: () => buildRequestPayload,
|
|
3452
|
+
chatCommand: () => chatCommand,
|
|
3453
|
+
fetchGlobalRegistries: () => fetchGlobalRegistries,
|
|
3454
|
+
formatPlan: () => formatPlan,
|
|
3455
|
+
resolveServiceUrl: () => resolveServiceUrl
|
|
3456
|
+
});
|
|
3457
|
+
import * as p16 from "@clack/prompts";
|
|
3458
|
+
import pc15 from "picocolors";
|
|
3459
|
+
async function fetchGlobalRegistries(configuredNamespaces) {
|
|
3460
|
+
let directory;
|
|
3461
|
+
try {
|
|
3462
|
+
const res = await fetch(GLOBAL_REGISTRY_URL);
|
|
3463
|
+
if (!res.ok) return [];
|
|
3464
|
+
directory = await res.json();
|
|
3465
|
+
} catch {
|
|
3466
|
+
return [];
|
|
3467
|
+
}
|
|
3468
|
+
const unconfigured = directory.filter(
|
|
3469
|
+
(entry) => !configuredNamespaces.includes(entry.name)
|
|
3470
|
+
);
|
|
3471
|
+
if (unconfigured.length === 0) return [];
|
|
3472
|
+
const results = [];
|
|
3473
|
+
for (const entry of unconfigured) {
|
|
3474
|
+
try {
|
|
3475
|
+
const indexUrl = entry.url.replace("{type}/{name}.json", "registry.json");
|
|
3476
|
+
const res = await fetch(indexUrl);
|
|
3477
|
+
if (!res.ok) continue;
|
|
3478
|
+
const index = await res.json();
|
|
3479
|
+
const items = (index.items ?? []).map((item) => ({
|
|
3480
|
+
name: item.name,
|
|
3481
|
+
type: item.type,
|
|
3482
|
+
description: item.description,
|
|
3483
|
+
registryDependencies: item.registryDependencies
|
|
3484
|
+
}));
|
|
3485
|
+
results.push({ namespace: entry.name, url: entry.url, items });
|
|
3486
|
+
} catch {
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
return results;
|
|
3490
|
+
}
|
|
3491
|
+
async function resolveServiceUrl(urlOverride, chatServiceConfig) {
|
|
3492
|
+
if (urlOverride) return urlOverride;
|
|
3493
|
+
if (process.env.KITN_CHAT_URL) return process.env.KITN_CHAT_URL;
|
|
3494
|
+
const userConfig = await readUserConfig();
|
|
3495
|
+
if (userConfig["chat-url"]) return userConfig["chat-url"];
|
|
3496
|
+
if (chatServiceConfig?.url) return chatServiceConfig.url;
|
|
3497
|
+
return DEFAULT_SERVICE_URL;
|
|
3498
|
+
}
|
|
3499
|
+
function buildRequestPayload(message, metadata) {
|
|
3500
|
+
return { message, metadata };
|
|
3501
|
+
}
|
|
3502
|
+
function formatPlan(plan) {
|
|
3503
|
+
const lines = [plan.summary, ""];
|
|
3504
|
+
for (let i = 0; i < plan.steps.length; i++) {
|
|
3505
|
+
const step = plan.steps[i];
|
|
3506
|
+
const num = `${i + 1}.`;
|
|
3507
|
+
const label = formatStepLabel(step);
|
|
3508
|
+
lines.push(`${num} ${label} - ${step.reason}`);
|
|
3509
|
+
}
|
|
3510
|
+
return lines.join("\n");
|
|
3511
|
+
}
|
|
3512
|
+
function formatStepLabel(step) {
|
|
3513
|
+
switch (step.action) {
|
|
3514
|
+
case "add":
|
|
3515
|
+
return `Add ${pc15.cyan(step.component)}`;
|
|
3516
|
+
case "remove":
|
|
3517
|
+
return `Remove ${pc15.red(step.component)}`;
|
|
3518
|
+
case "create":
|
|
3519
|
+
return `Create ${pc15.green(`${step.type}/${step.name}`)}`;
|
|
3520
|
+
case "link":
|
|
3521
|
+
return `Link ${pc15.cyan(step.toolName)} \u2192 ${pc15.cyan(step.agentName)}`;
|
|
3522
|
+
case "unlink":
|
|
3523
|
+
return `Unlink ${pc15.red(step.toolName)} from ${pc15.cyan(step.agentName)}`;
|
|
3524
|
+
case "registry-add":
|
|
3525
|
+
return `Add registry ${pc15.magenta(step.namespace)}`;
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
async function chatCommand(message, opts) {
|
|
3529
|
+
const cwd = process.cwd();
|
|
3530
|
+
const config2 = await readConfig(cwd);
|
|
3531
|
+
if (!config2) {
|
|
3532
|
+
p16.log.error("No kitn.json found. Run `kitn init` first.");
|
|
3533
|
+
process.exit(1);
|
|
3534
|
+
}
|
|
3535
|
+
if (!message) {
|
|
3536
|
+
p16.log.error('Please provide a message. Usage: kitn chat "add a weather tool"');
|
|
3537
|
+
process.exit(1);
|
|
3538
|
+
}
|
|
3539
|
+
p16.intro(pc15.bold("kitn assistant"));
|
|
3540
|
+
const s = p16.spinner();
|
|
3541
|
+
s.start("Gathering project context...");
|
|
3542
|
+
let registryIndex;
|
|
3543
|
+
let installed;
|
|
3544
|
+
let globalRegistryIndex;
|
|
3545
|
+
try {
|
|
3546
|
+
const configuredNamespaces = Object.keys(config2.registries);
|
|
3547
|
+
const fetcher = new RegistryFetcher(config2.registries);
|
|
3548
|
+
const [indices, globalEntries, lock] = await Promise.all([
|
|
3549
|
+
Promise.all(
|
|
3550
|
+
configuredNamespaces.map(async (ns) => {
|
|
3551
|
+
try {
|
|
3552
|
+
return await fetcher.fetchIndex(ns);
|
|
3553
|
+
} catch {
|
|
3554
|
+
return null;
|
|
3555
|
+
}
|
|
3556
|
+
})
|
|
3557
|
+
),
|
|
3558
|
+
fetchGlobalRegistries(configuredNamespaces),
|
|
3559
|
+
readLock(cwd)
|
|
3560
|
+
]);
|
|
3561
|
+
registryIndex = indices.filter(Boolean).flatMap((index) => (index.items ?? []).map((item) => ({
|
|
3562
|
+
name: item.name,
|
|
3563
|
+
type: item.type,
|
|
3564
|
+
description: item.description,
|
|
3565
|
+
registryDependencies: item.registryDependencies
|
|
3566
|
+
})));
|
|
3567
|
+
installed = Object.keys(lock);
|
|
3568
|
+
globalRegistryIndex = globalEntries.length > 0 ? globalEntries : void 0;
|
|
3569
|
+
} catch {
|
|
3570
|
+
s.stop(pc15.red("Failed to gather context"));
|
|
3571
|
+
p16.log.error("Could not read project context. Check your kitn.json and network connection.");
|
|
3572
|
+
process.exit(1);
|
|
3573
|
+
}
|
|
3574
|
+
s.stop("Context gathered");
|
|
3575
|
+
s.start("Thinking...");
|
|
3576
|
+
const serviceUrl = await resolveServiceUrl(opts?.url, config2.chatService);
|
|
3577
|
+
const metadata = { registryIndex, installed };
|
|
3578
|
+
if (globalRegistryIndex) metadata.globalRegistryIndex = globalRegistryIndex;
|
|
3579
|
+
const payload = buildRequestPayload(message, metadata);
|
|
3580
|
+
let response;
|
|
3581
|
+
try {
|
|
3582
|
+
const headers = {
|
|
3583
|
+
"Content-Type": "application/json"
|
|
3584
|
+
};
|
|
3585
|
+
if (process.env.KITN_API_KEY) {
|
|
3586
|
+
headers["Authorization"] = `Bearer ${process.env.KITN_API_KEY}`;
|
|
3587
|
+
}
|
|
3588
|
+
response = await fetch(`${serviceUrl}/api/chat`, {
|
|
3589
|
+
method: "POST",
|
|
3590
|
+
headers,
|
|
3591
|
+
body: JSON.stringify(payload)
|
|
3592
|
+
});
|
|
3593
|
+
} catch (err) {
|
|
3594
|
+
s.stop(pc15.red("Connection failed"));
|
|
3595
|
+
p16.log.error(`Could not reach chat service at ${serviceUrl}. ${err.message ?? ""}`);
|
|
3596
|
+
process.exit(1);
|
|
3597
|
+
}
|
|
3598
|
+
if (!response.ok) {
|
|
3599
|
+
s.stop(pc15.red("Request failed"));
|
|
3600
|
+
p16.log.error(`Chat service returned ${response.status}: ${response.statusText}`);
|
|
3601
|
+
process.exit(1);
|
|
3602
|
+
}
|
|
3603
|
+
let data;
|
|
3604
|
+
try {
|
|
3605
|
+
data = await response.json();
|
|
3606
|
+
} catch {
|
|
3607
|
+
s.stop(pc15.red("Invalid response"));
|
|
3608
|
+
p16.log.error("Chat service returned an invalid response.");
|
|
3609
|
+
process.exit(1);
|
|
3610
|
+
}
|
|
3611
|
+
s.stop("Done");
|
|
3612
|
+
if (data.rejected) {
|
|
3613
|
+
p16.log.warn(data.text ?? "Request was rejected by the assistant.");
|
|
3614
|
+
p16.outro("Try rephrasing your request.");
|
|
3615
|
+
return;
|
|
3616
|
+
}
|
|
3617
|
+
if (!data.plan) {
|
|
3618
|
+
p16.log.info(data.text ?? "No actionable plan returned.");
|
|
3619
|
+
p16.outro("Nothing to do.");
|
|
3620
|
+
return;
|
|
3621
|
+
}
|
|
3622
|
+
p16.log.message(formatPlan(data.plan));
|
|
3623
|
+
const steps = data.plan.steps;
|
|
3624
|
+
let selectedSteps;
|
|
3625
|
+
if (steps.length === 1) {
|
|
3626
|
+
const confirm7 = await p16.confirm({
|
|
3627
|
+
message: `Run: ${formatStepLabel(steps[0])}?`
|
|
3628
|
+
});
|
|
3629
|
+
if (p16.isCancel(confirm7) || !confirm7) {
|
|
3630
|
+
p16.cancel("Cancelled.");
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3633
|
+
selectedSteps = steps;
|
|
3634
|
+
} else {
|
|
3635
|
+
const action = await p16.select({
|
|
3636
|
+
message: "How would you like to proceed?",
|
|
3637
|
+
options: [
|
|
3638
|
+
{ value: "all", label: "Yes, run all steps" },
|
|
3639
|
+
{ value: "select", label: "Select which steps to run" },
|
|
3640
|
+
{ value: "cancel", label: "Cancel" }
|
|
3641
|
+
]
|
|
3642
|
+
});
|
|
3643
|
+
if (p16.isCancel(action) || action === "cancel") {
|
|
3644
|
+
p16.cancel("Cancelled.");
|
|
3645
|
+
return;
|
|
3646
|
+
}
|
|
3647
|
+
if (action === "select") {
|
|
3648
|
+
const choices = await p16.multiselect({
|
|
3649
|
+
message: "Select steps to run:",
|
|
3650
|
+
options: steps.map((step, i) => ({
|
|
3651
|
+
value: i,
|
|
3652
|
+
label: `${formatStepLabel(step)} - ${step.reason}`
|
|
3653
|
+
}))
|
|
3654
|
+
});
|
|
3655
|
+
if (p16.isCancel(choices)) {
|
|
3656
|
+
p16.cancel("Cancelled.");
|
|
3657
|
+
return;
|
|
3658
|
+
}
|
|
3659
|
+
selectedSteps = choices.map((i) => steps[i]);
|
|
3660
|
+
} else {
|
|
3661
|
+
selectedSteps = steps;
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
for (const step of selectedSteps) {
|
|
3665
|
+
s.start(`Running: ${formatStepLabel(step)}...`);
|
|
3666
|
+
try {
|
|
3667
|
+
switch (step.action) {
|
|
3668
|
+
case "registry-add": {
|
|
3669
|
+
const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
3670
|
+
await registryAddCommand2(step.namespace, step.url, { overwrite: true });
|
|
3671
|
+
break;
|
|
3672
|
+
}
|
|
3673
|
+
case "add": {
|
|
3674
|
+
const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
3675
|
+
await addCommand2([step.component], { yes: true });
|
|
3676
|
+
break;
|
|
3677
|
+
}
|
|
3678
|
+
case "create": {
|
|
3679
|
+
const { createCommand: createCommand2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
3680
|
+
await createCommand2(step.type, step.name);
|
|
3681
|
+
break;
|
|
3682
|
+
}
|
|
3683
|
+
case "link": {
|
|
3684
|
+
const { linkCommand: linkCommand2 } = await Promise.resolve().then(() => (init_link(), link_exports));
|
|
3685
|
+
await linkCommand2("tool", step.toolName, { to: step.agentName });
|
|
3686
|
+
break;
|
|
3687
|
+
}
|
|
3688
|
+
case "remove": {
|
|
3689
|
+
const { removeCommand: removeCommand2 } = await Promise.resolve().then(() => (init_remove(), remove_exports));
|
|
3690
|
+
await removeCommand2(step.component);
|
|
3691
|
+
break;
|
|
3692
|
+
}
|
|
3693
|
+
case "unlink": {
|
|
3694
|
+
const { unlinkCommand: unlinkCommand2 } = await Promise.resolve().then(() => (init_unlink(), unlink_exports));
|
|
3695
|
+
await unlinkCommand2("tool", step.toolName, { from: step.agentName });
|
|
3696
|
+
break;
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
s.stop(pc15.green(`Done: ${formatStepLabel(step)}`));
|
|
3700
|
+
} catch (err) {
|
|
3701
|
+
s.stop(pc15.red(`Failed: ${formatStepLabel(step)}`));
|
|
3702
|
+
p16.log.error(err.message ?? "Unknown error");
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
p16.outro(pc15.green("All done! Run your dev server to test the new components."));
|
|
3706
|
+
}
|
|
3707
|
+
var DEFAULT_SERVICE_URL, GLOBAL_REGISTRY_URL;
|
|
3708
|
+
var init_chat = __esm({
|
|
3709
|
+
"src/commands/chat.ts"() {
|
|
3710
|
+
"use strict";
|
|
3711
|
+
init_config();
|
|
3712
|
+
init_fetcher();
|
|
3713
|
+
init_config2();
|
|
3714
|
+
DEFAULT_SERVICE_URL = "https://chat.kitn.dev";
|
|
3715
|
+
GLOBAL_REGISTRY_URL = "https://kitn-ai.github.io/registry/registries.json";
|
|
3716
|
+
}
|
|
3717
|
+
});
|
|
3718
|
+
|
|
3387
3719
|
// src/index.ts
|
|
3388
3720
|
init_update_check();
|
|
3389
3721
|
import { Command } from "commander";
|
|
3390
|
-
var VERSION = true ? "0.1.
|
|
3722
|
+
var VERSION = true ? "0.1.34" : "0.0.0-dev";
|
|
3391
3723
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
3392
3724
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
3393
3725
|
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) => {
|
|
@@ -3438,6 +3770,10 @@ program.command("rules").description("Regenerate AI coding tool rules files").ac
|
|
|
3438
3770
|
const { rulesCommand: rulesCommand2 } = await Promise.resolve().then(() => (init_rules(), rules_exports));
|
|
3439
3771
|
await rulesCommand2();
|
|
3440
3772
|
});
|
|
3773
|
+
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) => {
|
|
3774
|
+
const { chatCommand: chatCommand2 } = await Promise.resolve().then(() => (init_chat(), chat_exports));
|
|
3775
|
+
await chatCommand2(message, opts);
|
|
3776
|
+
});
|
|
3441
3777
|
var registry = program.command("registry").description("Manage component registries");
|
|
3442
3778
|
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) => {
|
|
3443
3779
|
const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
@@ -3451,6 +3787,19 @@ registry.command("list").description("List all configured registries").action(as
|
|
|
3451
3787
|
const { registryListCommand: registryListCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
3452
3788
|
await registryListCommand2();
|
|
3453
3789
|
});
|
|
3790
|
+
var config = program.command("config").description("Manage user-level configuration");
|
|
3791
|
+
config.command("set").description("Set a config value").argument("<key>", "config key (chat-url, api-key)").argument("<value>", "config value").action(async (key, value) => {
|
|
3792
|
+
const { configSetCommand: configSetCommand2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
3793
|
+
await configSetCommand2(key, value);
|
|
3794
|
+
});
|
|
3795
|
+
config.command("get").description("Get a config value").argument("<key>", "config key").action(async (key) => {
|
|
3796
|
+
const { configGetCommand: configGetCommand2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
3797
|
+
await configGetCommand2(key);
|
|
3798
|
+
});
|
|
3799
|
+
config.command("list").description("List all config values").action(async () => {
|
|
3800
|
+
const { configListCommand: configListCommand2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
3801
|
+
await configListCommand2();
|
|
3802
|
+
});
|
|
3454
3803
|
await program.parseAsync();
|
|
3455
3804
|
var ranCommand = program.args[0];
|
|
3456
3805
|
if (ranCommand !== "check") {
|