@kitnai/cli 0.1.9 → 0.1.11
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 +4 -5
- package/dist/index.js +372 -304
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -55,98 +55,90 @@ var init_config = __esm({
|
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
// src/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
process.exit(0);
|
|
58
|
+
// src/installers/tsconfig-patcher.ts
|
|
59
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
60
|
+
import { join as join3 } from "path";
|
|
61
|
+
function stripJsonc(text3) {
|
|
62
|
+
return text3.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([}\]])/g, "$1");
|
|
63
|
+
}
|
|
64
|
+
function patchTsconfig(tsconfigContent, paths, removePrefixes) {
|
|
65
|
+
const config = JSON.parse(stripJsonc(tsconfigContent));
|
|
66
|
+
if (!config.compilerOptions) {
|
|
67
|
+
config.compilerOptions = {};
|
|
68
|
+
}
|
|
69
|
+
if (!config.compilerOptions.paths) {
|
|
70
|
+
config.compilerOptions.paths = {};
|
|
71
|
+
}
|
|
72
|
+
if (removePrefixes) {
|
|
73
|
+
for (const key of Object.keys(config.compilerOptions.paths)) {
|
|
74
|
+
if (removePrefixes.some((prefix) => key.startsWith(prefix))) {
|
|
75
|
+
delete config.compilerOptions.paths[key];
|
|
76
|
+
}
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
options: [
|
|
83
|
-
{ value: "bun", label: "Bun", hint: "recommended" },
|
|
84
|
-
{ value: "node", label: "Node.js" },
|
|
85
|
-
{ value: "deno", label: "Deno" }
|
|
86
|
-
]
|
|
87
|
-
});
|
|
88
|
-
if (p.isCancel(runtime)) {
|
|
89
|
-
p.cancel("Init cancelled.");
|
|
90
|
-
process.exit(0);
|
|
79
|
+
for (const [key, value] of Object.entries(paths)) {
|
|
80
|
+
config.compilerOptions.paths[key] = value;
|
|
91
81
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
});
|
|
102
|
-
if (p.isCancel(framework)) {
|
|
103
|
-
p.cancel("Init cancelled.");
|
|
104
|
-
process.exit(0);
|
|
82
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
83
|
+
}
|
|
84
|
+
async function patchProjectTsconfig(projectDir, paths, removePrefixes) {
|
|
85
|
+
const tsconfigPath = join3(projectDir, "tsconfig.json");
|
|
86
|
+
let content;
|
|
87
|
+
try {
|
|
88
|
+
content = await readFile3(tsconfigPath, "utf-8");
|
|
89
|
+
} catch {
|
|
90
|
+
content = "{}";
|
|
105
91
|
}
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
p.cancel("Init cancelled.");
|
|
113
|
-
process.exit(0);
|
|
92
|
+
const patched = patchTsconfig(content, paths, removePrefixes);
|
|
93
|
+
await writeFile3(tsconfigPath, patched);
|
|
94
|
+
}
|
|
95
|
+
var init_tsconfig_patcher = __esm({
|
|
96
|
+
"src/installers/tsconfig-patcher.ts"() {
|
|
97
|
+
"use strict";
|
|
114
98
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
tools: `${baseDir}/tools`,
|
|
123
|
-
skills: `${baseDir}/skills`,
|
|
124
|
-
storage: `${baseDir}/storage`
|
|
125
|
-
},
|
|
126
|
-
registries: {
|
|
127
|
-
"@kitn": "https://kitn-ai.github.io/registry/r/{type}/{name}.json"
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
const s = p.spinner();
|
|
131
|
-
s.start("Writing kitn.json");
|
|
132
|
-
await writeConfig(cwd, config);
|
|
133
|
-
s.stop("Created kitn.json");
|
|
134
|
-
p.outro(pc2.green("Done! Run `kitn add core` to install the engine, then `kitn add routes` for HTTP routes."));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// src/installers/barrel-manager.ts
|
|
102
|
+
function createBarrelFile() {
|
|
103
|
+
return `${BARREL_COMMENT}
|
|
104
|
+
${EXPORT_LINE}
|
|
105
|
+
`;
|
|
135
106
|
}
|
|
136
|
-
|
|
137
|
-
"
|
|
107
|
+
function addImportToBarrel(content, importPath) {
|
|
108
|
+
const importLine = `import "${importPath}";`;
|
|
109
|
+
if (content.includes(importLine)) return content;
|
|
110
|
+
const exportIndex = content.indexOf(EXPORT_LINE);
|
|
111
|
+
if (exportIndex === -1) {
|
|
112
|
+
return `${content.trimEnd()}
|
|
113
|
+
${importLine}
|
|
114
|
+
${EXPORT_LINE}
|
|
115
|
+
`;
|
|
116
|
+
}
|
|
117
|
+
const before = content.slice(0, exportIndex);
|
|
118
|
+
const after = content.slice(exportIndex);
|
|
119
|
+
return `${before}${importLine}
|
|
120
|
+
${after}`;
|
|
121
|
+
}
|
|
122
|
+
function removeImportFromBarrel(content, importPath) {
|
|
123
|
+
const importLine = `import "${importPath}";`;
|
|
124
|
+
return content.split("\n").filter((line) => line.trim() !== importLine).join("\n");
|
|
125
|
+
}
|
|
126
|
+
var EXPORT_LINE, BARREL_COMMENT;
|
|
127
|
+
var init_barrel_manager = __esm({
|
|
128
|
+
"src/installers/barrel-manager.ts"() {
|
|
138
129
|
"use strict";
|
|
139
|
-
|
|
130
|
+
EXPORT_LINE = 'export { registerWithPlugin } from "@kitn/core";';
|
|
131
|
+
BARREL_COMMENT = "// Managed by kitn CLI \u2014 components auto-imported below";
|
|
140
132
|
}
|
|
141
133
|
});
|
|
142
134
|
|
|
143
135
|
// src/utils/detect.ts
|
|
144
136
|
import { access } from "fs/promises";
|
|
145
|
-
import { join as
|
|
137
|
+
import { join as join4 } from "path";
|
|
146
138
|
async function detectPackageManager(dir) {
|
|
147
139
|
for (const [lockfile, pm] of LOCKFILE_MAP) {
|
|
148
140
|
try {
|
|
149
|
-
await access(
|
|
141
|
+
await access(join4(dir, lockfile));
|
|
150
142
|
return pm;
|
|
151
143
|
} catch {
|
|
152
144
|
}
|
|
@@ -269,7 +261,7 @@ var init_resolver = __esm({
|
|
|
269
261
|
});
|
|
270
262
|
|
|
271
263
|
// src/installers/file-writer.ts
|
|
272
|
-
import { readFile as
|
|
264
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir2, access as access2 } from "fs/promises";
|
|
273
265
|
import { dirname } from "path";
|
|
274
266
|
import { createPatch } from "diff";
|
|
275
267
|
async function checkFileStatus(filePath, newContent) {
|
|
@@ -278,7 +270,7 @@ async function checkFileStatus(filePath, newContent) {
|
|
|
278
270
|
} catch {
|
|
279
271
|
return "new" /* New */;
|
|
280
272
|
}
|
|
281
|
-
const existing = await
|
|
273
|
+
const existing = await readFile4(filePath, "utf-8");
|
|
282
274
|
return existing === newContent ? "identical" /* Identical */ : "different" /* Different */;
|
|
283
275
|
}
|
|
284
276
|
function generateDiff(filePath, oldContent, newContent) {
|
|
@@ -286,14 +278,14 @@ function generateDiff(filePath, oldContent, newContent) {
|
|
|
286
278
|
}
|
|
287
279
|
async function readExistingFile(filePath) {
|
|
288
280
|
try {
|
|
289
|
-
return await
|
|
281
|
+
return await readFile4(filePath, "utf-8");
|
|
290
282
|
} catch {
|
|
291
283
|
return null;
|
|
292
284
|
}
|
|
293
285
|
}
|
|
294
286
|
async function writeComponentFile(filePath, content) {
|
|
295
287
|
await mkdir2(dirname(filePath), { recursive: true });
|
|
296
|
-
await
|
|
288
|
+
await writeFile4(filePath, content);
|
|
297
289
|
}
|
|
298
290
|
var init_file_writer = __esm({
|
|
299
291
|
"src/installers/file-writer.ts"() {
|
|
@@ -327,10 +319,10 @@ var init_dep_installer = __esm({
|
|
|
327
319
|
});
|
|
328
320
|
|
|
329
321
|
// src/installers/env-writer.ts
|
|
330
|
-
import * as
|
|
331
|
-
import
|
|
332
|
-
import { readFile as
|
|
333
|
-
import { join as
|
|
322
|
+
import * as p from "@clack/prompts";
|
|
323
|
+
import pc2 from "picocolors";
|
|
324
|
+
import { readFile as readFile5, writeFile as writeFile5, access as access3 } from "fs/promises";
|
|
325
|
+
import { join as join5 } from "path";
|
|
334
326
|
function parseEnvKeys(content) {
|
|
335
327
|
const keys = /* @__PURE__ */ new Set();
|
|
336
328
|
for (const line of content.split("\n")) {
|
|
@@ -345,7 +337,7 @@ function parseEnvKeys(content) {
|
|
|
345
337
|
}
|
|
346
338
|
async function readEnvFile(path) {
|
|
347
339
|
try {
|
|
348
|
-
return await
|
|
340
|
+
return await readFile5(path, "utf-8");
|
|
349
341
|
} catch {
|
|
350
342
|
return "";
|
|
351
343
|
}
|
|
@@ -362,8 +354,8 @@ function collectEnvVars(items) {
|
|
|
362
354
|
async function handleEnvVars(cwd, envVars) {
|
|
363
355
|
const keys = Object.keys(envVars);
|
|
364
356
|
if (keys.length === 0) return;
|
|
365
|
-
const envPath =
|
|
366
|
-
const examplePath =
|
|
357
|
+
const envPath = join5(cwd, ".env");
|
|
358
|
+
const examplePath = join5(cwd, ".env.example");
|
|
367
359
|
const envContent = await readEnvFile(envPath);
|
|
368
360
|
const exampleContent = await readEnvFile(examplePath);
|
|
369
361
|
const envKeys = parseEnvKeys(envContent);
|
|
@@ -378,25 +370,25 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
378
370
|
lines.push(`# ${config.description}${config.url ? ` (${config.url})` : ""}`);
|
|
379
371
|
lines.push(`${key}=`);
|
|
380
372
|
}
|
|
381
|
-
await
|
|
382
|
-
|
|
373
|
+
await writeFile5(examplePath, exampleContent + lines.join("\n") + "\n");
|
|
374
|
+
p.log.info(`Updated ${pc2.cyan(".env.example")} with ${missingFromExample.length} variable(s)`);
|
|
383
375
|
}
|
|
384
376
|
if (missingFromEnv.length === 0) return;
|
|
385
|
-
|
|
386
|
-
|
|
377
|
+
p.log.message("");
|
|
378
|
+
p.log.warn(
|
|
387
379
|
`${missingFromEnv.length} environment variable(s) needed:`
|
|
388
380
|
);
|
|
389
381
|
for (const key of missingFromEnv) {
|
|
390
382
|
const config = envVars[key];
|
|
391
|
-
const req = config.required !== false ?
|
|
392
|
-
|
|
383
|
+
const req = config.required !== false ? pc2.red("*") : "";
|
|
384
|
+
p.log.message(` ${pc2.yellow(key)}${req}: ${config.description}${config.url ? pc2.dim(` -> ${config.url}`) : ""}`);
|
|
393
385
|
}
|
|
394
|
-
const shouldPrompt = await
|
|
386
|
+
const shouldPrompt = await p.confirm({
|
|
395
387
|
message: "Would you like to enter values now?",
|
|
396
388
|
initialValue: true
|
|
397
389
|
});
|
|
398
|
-
if (
|
|
399
|
-
|
|
390
|
+
if (p.isCancel(shouldPrompt) || !shouldPrompt) {
|
|
391
|
+
p.log.info(`Add them to ${pc2.cyan(".env")} when ready.`);
|
|
400
392
|
return;
|
|
401
393
|
}
|
|
402
394
|
const newEntries = [];
|
|
@@ -405,17 +397,17 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
405
397
|
const isSecret = config.secret !== false;
|
|
406
398
|
let value;
|
|
407
399
|
if (isSecret) {
|
|
408
|
-
value = await
|
|
400
|
+
value = await p.password({
|
|
409
401
|
message: `${key}:`
|
|
410
402
|
});
|
|
411
403
|
} else {
|
|
412
|
-
value = await
|
|
404
|
+
value = await p.text({
|
|
413
405
|
message: `${key}:`,
|
|
414
406
|
placeholder: config.description
|
|
415
407
|
});
|
|
416
408
|
}
|
|
417
|
-
if (
|
|
418
|
-
|
|
409
|
+
if (p.isCancel(value)) {
|
|
410
|
+
p.log.info(`Skipped remaining variables. Add them to ${pc2.cyan(".env")} when ready.`);
|
|
419
411
|
break;
|
|
420
412
|
}
|
|
421
413
|
if (value) {
|
|
@@ -427,8 +419,8 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
427
419
|
const lines = [];
|
|
428
420
|
if (existingEnv && !existingEnv.endsWith("\n")) lines.push("");
|
|
429
421
|
lines.push(...newEntries);
|
|
430
|
-
await
|
|
431
|
-
|
|
422
|
+
await writeFile5(envPath, existingEnv + lines.join("\n") + "\n");
|
|
423
|
+
p.log.success(`Wrote ${newEntries.length} variable(s) to ${pc2.cyan(".env")}`);
|
|
432
424
|
}
|
|
433
425
|
}
|
|
434
426
|
var init_env_writer = __esm({
|
|
@@ -438,7 +430,7 @@ var init_env_writer = __esm({
|
|
|
438
430
|
});
|
|
439
431
|
|
|
440
432
|
// src/installers/import-rewriter.ts
|
|
441
|
-
import { relative, join as
|
|
433
|
+
import { relative, join as join6 } from "path";
|
|
442
434
|
function rewriteKitnImports(content, fileType, fileName, aliases) {
|
|
443
435
|
const sourceAliasKey = TYPE_TO_ALIAS_KEY[fileType];
|
|
444
436
|
if (!sourceAliasKey) return content;
|
|
@@ -450,7 +442,7 @@ function rewriteKitnImports(content, fileType, fileName, aliases) {
|
|
|
450
442
|
return `${prefix}@kitn/${type}/${targetPath}${quote}`;
|
|
451
443
|
}
|
|
452
444
|
const targetDir = aliases[type];
|
|
453
|
-
const targetFile =
|
|
445
|
+
const targetFile = join6(targetDir, targetPath);
|
|
454
446
|
let rel = relative(sourceDir, targetFile);
|
|
455
447
|
rel = rel.split("\\").join("/");
|
|
456
448
|
if (!rel.startsWith(".")) {
|
|
@@ -474,76 +466,6 @@ var init_import_rewriter = __esm({
|
|
|
474
466
|
}
|
|
475
467
|
});
|
|
476
468
|
|
|
477
|
-
// src/installers/tsconfig-patcher.ts
|
|
478
|
-
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
479
|
-
import { join as join6 } from "path";
|
|
480
|
-
function stripJsonc(text3) {
|
|
481
|
-
return text3.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([}\]])/g, "$1");
|
|
482
|
-
}
|
|
483
|
-
function patchTsconfig(tsconfigContent, paths) {
|
|
484
|
-
const config = JSON.parse(stripJsonc(tsconfigContent));
|
|
485
|
-
if (!config.compilerOptions) {
|
|
486
|
-
config.compilerOptions = {};
|
|
487
|
-
}
|
|
488
|
-
if (!config.compilerOptions.paths) {
|
|
489
|
-
config.compilerOptions.paths = {};
|
|
490
|
-
}
|
|
491
|
-
for (const [key, value] of Object.entries(paths)) {
|
|
492
|
-
config.compilerOptions.paths[key] = value;
|
|
493
|
-
}
|
|
494
|
-
return JSON.stringify(config, null, 2) + "\n";
|
|
495
|
-
}
|
|
496
|
-
async function patchProjectTsconfig(projectDir, paths) {
|
|
497
|
-
const tsconfigPath = join6(projectDir, "tsconfig.json");
|
|
498
|
-
let content;
|
|
499
|
-
try {
|
|
500
|
-
content = await readFile5(tsconfigPath, "utf-8");
|
|
501
|
-
} catch {
|
|
502
|
-
content = "{}";
|
|
503
|
-
}
|
|
504
|
-
const patched = patchTsconfig(content, paths);
|
|
505
|
-
await writeFile5(tsconfigPath, patched);
|
|
506
|
-
}
|
|
507
|
-
var init_tsconfig_patcher = __esm({
|
|
508
|
-
"src/installers/tsconfig-patcher.ts"() {
|
|
509
|
-
"use strict";
|
|
510
|
-
}
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
// src/installers/barrel-manager.ts
|
|
514
|
-
function createBarrelFile() {
|
|
515
|
-
return `${BARREL_COMMENT}
|
|
516
|
-
${EXPORT_LINE}
|
|
517
|
-
`;
|
|
518
|
-
}
|
|
519
|
-
function addImportToBarrel(content, importPath) {
|
|
520
|
-
const importLine = `import "${importPath}";`;
|
|
521
|
-
if (content.includes(importLine)) return content;
|
|
522
|
-
const exportIndex = content.indexOf(EXPORT_LINE);
|
|
523
|
-
if (exportIndex === -1) {
|
|
524
|
-
return `${content.trimEnd()}
|
|
525
|
-
${importLine}
|
|
526
|
-
${EXPORT_LINE}
|
|
527
|
-
`;
|
|
528
|
-
}
|
|
529
|
-
const before = content.slice(0, exportIndex);
|
|
530
|
-
const after = content.slice(exportIndex);
|
|
531
|
-
return `${before}${importLine}
|
|
532
|
-
${after}`;
|
|
533
|
-
}
|
|
534
|
-
function removeImportFromBarrel(content, importPath) {
|
|
535
|
-
const importLine = `import "${importPath}";`;
|
|
536
|
-
return content.split("\n").filter((line) => line.trim() !== importLine).join("\n");
|
|
537
|
-
}
|
|
538
|
-
var EXPORT_LINE, BARREL_COMMENT;
|
|
539
|
-
var init_barrel_manager = __esm({
|
|
540
|
-
"src/installers/barrel-manager.ts"() {
|
|
541
|
-
"use strict";
|
|
542
|
-
EXPORT_LINE = 'export { registerWithPlugin } from "@kitnai/core";';
|
|
543
|
-
BARREL_COMMENT = "// Managed by kitn CLI \u2014 components auto-imported below";
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
|
|
547
469
|
// src/utils/hash.ts
|
|
548
470
|
import { createHash } from "crypto";
|
|
549
471
|
function contentHash(content) {
|
|
@@ -674,22 +596,22 @@ var add_exports = {};
|
|
|
674
596
|
__export(add_exports, {
|
|
675
597
|
addCommand: () => addCommand
|
|
676
598
|
});
|
|
677
|
-
import * as
|
|
678
|
-
import
|
|
599
|
+
import * as p2 from "@clack/prompts";
|
|
600
|
+
import pc3 from "picocolors";
|
|
679
601
|
import { join as join7 } from "path";
|
|
680
602
|
import { existsSync } from "fs";
|
|
681
603
|
import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir3 } from "fs/promises";
|
|
682
604
|
import { relative as relative2 } from "path";
|
|
683
605
|
async function addCommand(components, opts) {
|
|
684
|
-
|
|
606
|
+
p2.intro(pc3.bgCyan(pc3.black(" kitn add ")));
|
|
685
607
|
const cwd = process.cwd();
|
|
686
608
|
const config = await readConfig(cwd);
|
|
687
609
|
if (!config) {
|
|
688
|
-
|
|
610
|
+
p2.log.error("No kitn.json found. Run `kitn init` first.");
|
|
689
611
|
process.exit(1);
|
|
690
612
|
}
|
|
691
613
|
if (components.length === 0) {
|
|
692
|
-
|
|
614
|
+
p2.log.error("Please specify at least one component to add.");
|
|
693
615
|
process.exit(1);
|
|
694
616
|
}
|
|
695
617
|
const resolvedComponents = components.map((c) => {
|
|
@@ -701,7 +623,7 @@ async function addCommand(components, opts) {
|
|
|
701
623
|
});
|
|
702
624
|
const refs = resolvedComponents.map(parseComponentRef);
|
|
703
625
|
const fetcher = new RegistryFetcher(config.registries);
|
|
704
|
-
const s =
|
|
626
|
+
const s = p2.spinner();
|
|
705
627
|
s.start("Resolving dependencies...");
|
|
706
628
|
let resolved;
|
|
707
629
|
try {
|
|
@@ -714,16 +636,16 @@ async function addCommand(components, opts) {
|
|
|
714
636
|
return fetcher.fetchItem(name, dir, ref.namespace, ref.version);
|
|
715
637
|
});
|
|
716
638
|
} catch (err) {
|
|
717
|
-
s.stop(
|
|
718
|
-
|
|
639
|
+
s.stop(pc3.red("Failed to resolve dependencies"));
|
|
640
|
+
p2.log.error(err.message);
|
|
719
641
|
process.exit(1);
|
|
720
642
|
}
|
|
721
643
|
s.stop(`Resolved ${resolved.length} component(s)`);
|
|
722
|
-
|
|
644
|
+
p2.log.info("Components to install:");
|
|
723
645
|
for (const item of resolved) {
|
|
724
646
|
const isExplicit = resolvedComponents.includes(item.name) || components.includes(item.name);
|
|
725
|
-
const label = isExplicit ? item.name : `${item.name} ${
|
|
726
|
-
|
|
647
|
+
const label = isExplicit ? item.name : `${item.name} ${pc3.dim("(dependency)")}`;
|
|
648
|
+
p2.log.message(` ${pc3.cyan(label)}`);
|
|
727
649
|
}
|
|
728
650
|
const created = [];
|
|
729
651
|
const updated = [];
|
|
@@ -752,15 +674,15 @@ async function addCommand(components, opts) {
|
|
|
752
674
|
} else {
|
|
753
675
|
const existing = await readExistingFile(targetPath);
|
|
754
676
|
const diff = generateDiff(relativePath, existing ?? "", file.content);
|
|
755
|
-
|
|
756
|
-
const action = await
|
|
677
|
+
p2.log.message(pc3.dim(diff));
|
|
678
|
+
const action = await p2.select({
|
|
757
679
|
message: `${relativePath} already exists and differs. What to do?`,
|
|
758
680
|
options: [
|
|
759
681
|
{ value: "skip", label: "Keep local version" },
|
|
760
682
|
{ value: "overwrite", label: "Overwrite with registry version" }
|
|
761
683
|
]
|
|
762
684
|
});
|
|
763
|
-
if (!
|
|
685
|
+
if (!p2.isCancel(action) && action === "overwrite") {
|
|
764
686
|
await writeComponentFile(targetPath, file.content);
|
|
765
687
|
updated.push(relativePath);
|
|
766
688
|
} else {
|
|
@@ -770,15 +692,6 @@ async function addCommand(components, opts) {
|
|
|
770
692
|
break;
|
|
771
693
|
}
|
|
772
694
|
}
|
|
773
|
-
if (item.tsconfig) {
|
|
774
|
-
const resolvedPaths = {};
|
|
775
|
-
const installDir = item.installDir ?? item.name;
|
|
776
|
-
for (const [key, values] of Object.entries(item.tsconfig)) {
|
|
777
|
-
resolvedPaths[key] = values.map((v) => `./${join7(baseDir2, installDir, v)}`);
|
|
778
|
-
}
|
|
779
|
-
await patchProjectTsconfig(cwd, resolvedPaths);
|
|
780
|
-
p3.log.info(`Patched tsconfig.json with paths: ${Object.keys(resolvedPaths).join(", ")}`);
|
|
781
|
-
}
|
|
782
695
|
const installed = config.installed ?? {};
|
|
783
696
|
const allContent = item.files.map((f) => f.content).join("\n");
|
|
784
697
|
const ref = refs.find((r) => r.name === item.name) ?? { namespace: "@kitn", name: item.name, version: void 0 };
|
|
@@ -825,15 +738,15 @@ async function addCommand(components, opts) {
|
|
|
825
738
|
} else {
|
|
826
739
|
const existing = await readExistingFile(targetPath);
|
|
827
740
|
const diff = generateDiff(relativePath, existing ?? "", content);
|
|
828
|
-
|
|
829
|
-
const action = await
|
|
741
|
+
p2.log.message(pc3.dim(diff));
|
|
742
|
+
const action = await p2.select({
|
|
830
743
|
message: `${relativePath} already exists and differs. What to do?`,
|
|
831
744
|
options: [
|
|
832
745
|
{ value: "skip", label: "Keep local version" },
|
|
833
746
|
{ value: "overwrite", label: "Overwrite with registry version" }
|
|
834
747
|
]
|
|
835
748
|
});
|
|
836
|
-
if (!
|
|
749
|
+
if (!p2.isCancel(action) && action === "overwrite") {
|
|
837
750
|
await writeComponentFile(targetPath, content);
|
|
838
751
|
updated.push(relativePath);
|
|
839
752
|
} else {
|
|
@@ -912,21 +825,16 @@ async function addCommand(components, opts) {
|
|
|
912
825
|
barrelContent = addImportToBarrel(barrelContent, importPath);
|
|
913
826
|
}
|
|
914
827
|
await writeFile6(barrelPath, barrelContent);
|
|
915
|
-
|
|
828
|
+
p2.log.info(`Updated barrel file: ${join7(baseDir, "index.ts")}`);
|
|
916
829
|
if (!barrelExisted) {
|
|
917
|
-
|
|
830
|
+
p2.note(
|
|
918
831
|
[
|
|
919
|
-
`import {
|
|
920
|
-
`import { registerWithPlugin } from "./ai";`,
|
|
832
|
+
`import { ai } from "./${baseDir}/plugin.js";`,
|
|
921
833
|
``,
|
|
922
|
-
`
|
|
923
|
-
`
|
|
924
|
-
`});`,
|
|
925
|
-
``,
|
|
926
|
-
`registerWithPlugin(plugin);`,
|
|
927
|
-
`app.route("/api", plugin.app);`
|
|
834
|
+
`app.route("/api", ai.router);`,
|
|
835
|
+
`await ai.initialize();`
|
|
928
836
|
].join("\n"),
|
|
929
|
-
"Add this to your
|
|
837
|
+
"Add this to your server entry point"
|
|
930
838
|
);
|
|
931
839
|
}
|
|
932
840
|
}
|
|
@@ -940,58 +848,49 @@ async function addCommand(components, opts) {
|
|
|
940
848
|
installDependencies(pm, uniqueDeps, cwd);
|
|
941
849
|
s.stop("Dependencies installed");
|
|
942
850
|
} catch {
|
|
943
|
-
s.stop(
|
|
851
|
+
s.stop(pc3.yellow("Some dependencies failed to install"));
|
|
944
852
|
}
|
|
945
853
|
}
|
|
946
854
|
}
|
|
947
855
|
if (created.length > 0) {
|
|
948
|
-
|
|
949
|
-
for (const f of created)
|
|
856
|
+
p2.log.success(`Created ${created.length} file(s):`);
|
|
857
|
+
for (const f of created) p2.log.message(` ${pc3.green("+")} ${f}`);
|
|
950
858
|
}
|
|
951
859
|
if (updated.length > 0) {
|
|
952
|
-
|
|
953
|
-
for (const f of updated)
|
|
860
|
+
p2.log.success(`Updated ${updated.length} file(s):`);
|
|
861
|
+
for (const f of updated) p2.log.message(` ${pc3.yellow("~")} ${f}`);
|
|
954
862
|
}
|
|
955
863
|
if (skipped.length > 0) {
|
|
956
|
-
|
|
957
|
-
for (const f of skipped)
|
|
864
|
+
p2.log.info(`Skipped ${skipped.length} file(s):`);
|
|
865
|
+
for (const f of skipped) p2.log.message(` ${pc3.dim("-")} ${f}`);
|
|
958
866
|
}
|
|
959
867
|
const allEnvVars = collectEnvVars(resolved);
|
|
960
868
|
await handleEnvVars(cwd, allEnvVars);
|
|
961
869
|
for (const item of resolved) {
|
|
962
870
|
if (item.docs) {
|
|
963
|
-
|
|
871
|
+
p2.log.info(`${pc3.bold(item.name)}: ${item.docs}`);
|
|
964
872
|
}
|
|
965
873
|
}
|
|
966
874
|
const installedNames = new Set(resolved.map((r) => r.name));
|
|
967
875
|
const hints = [];
|
|
968
876
|
if (installedNames.has("core") && !installedNames.has(config.framework ?? "hono")) {
|
|
969
|
-
hints.push(`Run ${
|
|
877
|
+
hints.push(`Run ${pc3.cyan(`kitn add routes`)} to install the HTTP adapter.`);
|
|
970
878
|
}
|
|
971
879
|
const fw = config.framework ?? "hono";
|
|
972
880
|
if (installedNames.has(fw) || installedNames.has("core") && installedNames.has(fw)) {
|
|
973
|
-
hints.push(`
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
hints.push(pc4.dim(` const plugin = createAIPlugin({`));
|
|
981
|
-
hints.push(pc4.dim(` model: (model) => yourProvider(model ?? "default-model"),`));
|
|
982
|
-
hints.push(pc4.dim(` });`));
|
|
983
|
-
hints.push(pc4.dim(``));
|
|
984
|
-
hints.push(pc4.dim(` const app = new Hono();`));
|
|
985
|
-
hints.push(pc4.dim(` app.route("/api", plugin.app);`));
|
|
986
|
-
hints.push(pc4.dim(` await plugin.initialize();`));
|
|
987
|
-
hints.push("");
|
|
988
|
-
}
|
|
881
|
+
hints.push(`Configure your AI provider in ${pc3.bold(baseDir + "/plugin.ts")}, then add to your server:`);
|
|
882
|
+
hints.push("");
|
|
883
|
+
hints.push(pc3.dim(` import { ai } from "./${baseDir}/plugin.js";`));
|
|
884
|
+
hints.push(pc3.dim(``));
|
|
885
|
+
hints.push(pc3.dim(` app.route("/api", ai.router);`));
|
|
886
|
+
hints.push(pc3.dim(` await ai.initialize();`));
|
|
887
|
+
hints.push("");
|
|
989
888
|
}
|
|
990
889
|
if (hints.length > 0) {
|
|
991
|
-
|
|
992
|
-
for (const hint of hints)
|
|
890
|
+
p2.log.message(pc3.bold("\nNext steps:"));
|
|
891
|
+
for (const hint of hints) p2.log.message(hint);
|
|
993
892
|
}
|
|
994
|
-
|
|
893
|
+
p2.outro(pc3.green("Done!"));
|
|
995
894
|
}
|
|
996
895
|
var init_add = __esm({
|
|
997
896
|
"src/commands/add.ts"() {
|
|
@@ -1004,7 +903,6 @@ var init_add = __esm({
|
|
|
1004
903
|
init_dep_installer();
|
|
1005
904
|
init_env_writer();
|
|
1006
905
|
init_import_rewriter();
|
|
1007
|
-
init_tsconfig_patcher();
|
|
1008
906
|
init_barrel_manager();
|
|
1009
907
|
init_hash();
|
|
1010
908
|
init_parse_ref();
|
|
@@ -1012,6 +910,126 @@ var init_add = __esm({
|
|
|
1012
910
|
}
|
|
1013
911
|
});
|
|
1014
912
|
|
|
913
|
+
// src/commands/init.ts
|
|
914
|
+
var init_exports = {};
|
|
915
|
+
__export(init_exports, {
|
|
916
|
+
initCommand: () => initCommand
|
|
917
|
+
});
|
|
918
|
+
import * as p3 from "@clack/prompts";
|
|
919
|
+
import pc4 from "picocolors";
|
|
920
|
+
import { mkdir as mkdir4, writeFile as writeFile7 } from "fs/promises";
|
|
921
|
+
import { join as join8 } from "path";
|
|
922
|
+
async function initCommand() {
|
|
923
|
+
p3.intro(pc4.bgCyan(pc4.black(" kitn init ")));
|
|
924
|
+
const cwd = process.cwd();
|
|
925
|
+
const existing = await readConfig(cwd);
|
|
926
|
+
if (existing) {
|
|
927
|
+
p3.log.warn("kitn.json already exists in this directory.");
|
|
928
|
+
const shouldContinue = await p3.confirm({
|
|
929
|
+
message: "Overwrite existing configuration?",
|
|
930
|
+
initialValue: false
|
|
931
|
+
});
|
|
932
|
+
if (p3.isCancel(shouldContinue) || !shouldContinue) {
|
|
933
|
+
p3.cancel("Init cancelled.");
|
|
934
|
+
process.exit(0);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
const runtime = await p3.select({
|
|
938
|
+
message: "Which runtime do you use?",
|
|
939
|
+
options: [
|
|
940
|
+
{ value: "bun", label: "Bun", hint: "recommended" },
|
|
941
|
+
{ value: "node", label: "Node.js" },
|
|
942
|
+
{ value: "deno", label: "Deno" }
|
|
943
|
+
]
|
|
944
|
+
});
|
|
945
|
+
if (p3.isCancel(runtime)) {
|
|
946
|
+
p3.cancel("Init cancelled.");
|
|
947
|
+
process.exit(0);
|
|
948
|
+
}
|
|
949
|
+
const base = await p3.text({
|
|
950
|
+
message: "Where should kitn components be installed?",
|
|
951
|
+
initialValue: "src/ai",
|
|
952
|
+
placeholder: "src/ai"
|
|
953
|
+
});
|
|
954
|
+
if (p3.isCancel(base)) {
|
|
955
|
+
p3.cancel("Init cancelled.");
|
|
956
|
+
process.exit(0);
|
|
957
|
+
}
|
|
958
|
+
const baseDir = base;
|
|
959
|
+
const config = {
|
|
960
|
+
runtime,
|
|
961
|
+
framework: "hono",
|
|
962
|
+
aliases: {
|
|
963
|
+
base: baseDir,
|
|
964
|
+
agents: `${baseDir}/agents`,
|
|
965
|
+
tools: `${baseDir}/tools`,
|
|
966
|
+
skills: `${baseDir}/skills`,
|
|
967
|
+
storage: `${baseDir}/storage`
|
|
968
|
+
},
|
|
969
|
+
registries: {
|
|
970
|
+
"@kitn": "https://kitn-ai.github.io/registry/r/{type}/{name}.json"
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
const s = p3.spinner();
|
|
974
|
+
s.start("Writing kitn.json");
|
|
975
|
+
await writeConfig(cwd, config);
|
|
976
|
+
s.stop("Created kitn.json");
|
|
977
|
+
await patchProjectTsconfig(
|
|
978
|
+
cwd,
|
|
979
|
+
{ "@kitn/*": [`./${baseDir}/*`] },
|
|
980
|
+
["@kitn", "@kitnai"]
|
|
981
|
+
);
|
|
982
|
+
p3.log.info(`Patched tsconfig.json with path: ${pc4.bold("@kitn/*")}`);
|
|
983
|
+
p3.log.info("Installing core engine and routes...");
|
|
984
|
+
await addCommand(["core", "routes"], { overwrite: true });
|
|
985
|
+
const aiDir = join8(cwd, baseDir);
|
|
986
|
+
await mkdir4(aiDir, { recursive: true });
|
|
987
|
+
const barrelPath = join8(aiDir, "index.ts");
|
|
988
|
+
await writeFile7(barrelPath, createBarrelFile());
|
|
989
|
+
const pluginPath = join8(aiDir, "plugin.ts");
|
|
990
|
+
await writeFile7(pluginPath, PLUGIN_TEMPLATE);
|
|
991
|
+
p3.log.success(`Created ${pc4.bold(baseDir + "/plugin.ts")} \u2014 configure your AI provider there`);
|
|
992
|
+
p3.note(
|
|
993
|
+
[
|
|
994
|
+
`import { ai } from "./${baseDir}/plugin.js";`,
|
|
995
|
+
``,
|
|
996
|
+
`app.route("/api", ai.router);`,
|
|
997
|
+
`await ai.initialize();`
|
|
998
|
+
].join("\n"),
|
|
999
|
+
"Add this to your server entry point:"
|
|
1000
|
+
);
|
|
1001
|
+
p3.outro("Done!");
|
|
1002
|
+
}
|
|
1003
|
+
var PLUGIN_TEMPLATE;
|
|
1004
|
+
var init_init = __esm({
|
|
1005
|
+
"src/commands/init.ts"() {
|
|
1006
|
+
"use strict";
|
|
1007
|
+
init_config();
|
|
1008
|
+
init_tsconfig_patcher();
|
|
1009
|
+
init_barrel_manager();
|
|
1010
|
+
init_add();
|
|
1011
|
+
PLUGIN_TEMPLATE = `import { createAIPlugin } from "@kitn/routes";
|
|
1012
|
+
import { registerWithPlugin } from "./index.js";
|
|
1013
|
+
|
|
1014
|
+
export const ai = createAIPlugin({
|
|
1015
|
+
// To enable agent chat, add an AI provider:
|
|
1016
|
+
// https://sdk.vercel.ai/providers/ai-sdk-providers
|
|
1017
|
+
//
|
|
1018
|
+
// Example with OpenRouter (access to many models):
|
|
1019
|
+
// import { openrouter } from "@openrouter/ai-sdk-provider";
|
|
1020
|
+
// model: (id) => openrouter(id ?? "openai/gpt-4o-mini"),
|
|
1021
|
+
//
|
|
1022
|
+
// Example with OpenAI directly:
|
|
1023
|
+
// import { openai } from "@ai-sdk/openai";
|
|
1024
|
+
// model: (id) => openai(id ?? "gpt-4o-mini"),
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
// Flush all auto-registered components into the plugin
|
|
1028
|
+
registerWithPlugin(ai);
|
|
1029
|
+
`;
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1015
1033
|
// src/commands/list.ts
|
|
1016
1034
|
var list_exports = {};
|
|
1017
1035
|
__export(list_exports, {
|
|
@@ -1019,13 +1037,19 @@ __export(list_exports, {
|
|
|
1019
1037
|
});
|
|
1020
1038
|
import * as p4 from "@clack/prompts";
|
|
1021
1039
|
import pc5 from "picocolors";
|
|
1022
|
-
async function listCommand(opts) {
|
|
1040
|
+
async function listCommand(typeFilter, opts) {
|
|
1023
1041
|
const cwd = process.cwd();
|
|
1024
1042
|
const config = await readConfig(cwd);
|
|
1025
1043
|
if (!config) {
|
|
1026
1044
|
p4.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1027
1045
|
process.exit(1);
|
|
1028
1046
|
}
|
|
1047
|
+
const rawType = typeFilter ?? opts.type;
|
|
1048
|
+
const resolvedType = rawType ? TYPE_ALIASES[rawType.toLowerCase()] : void 0;
|
|
1049
|
+
if (rawType && !resolvedType) {
|
|
1050
|
+
p4.log.error(`Unknown type ${pc5.bold(rawType)}. Valid types: agent, tool, skill, storage, package`);
|
|
1051
|
+
process.exit(1);
|
|
1052
|
+
}
|
|
1029
1053
|
const fetcher = new RegistryFetcher(config.registries);
|
|
1030
1054
|
const namespacesToFetch = opts.registry ? [opts.registry] : Object.keys(config.registries);
|
|
1031
1055
|
if (opts.registry && !config.registries[opts.registry]) {
|
|
@@ -1033,7 +1057,7 @@ async function listCommand(opts) {
|
|
|
1033
1057
|
process.exit(1);
|
|
1034
1058
|
}
|
|
1035
1059
|
const s = p4.spinner();
|
|
1036
|
-
s.start("Fetching registry
|
|
1060
|
+
s.start("Fetching registry...");
|
|
1037
1061
|
const allItems = [];
|
|
1038
1062
|
const errors = [];
|
|
1039
1063
|
for (const namespace of namespacesToFetch) {
|
|
@@ -1051,52 +1075,96 @@ async function listCommand(opts) {
|
|
|
1051
1075
|
for (const e of errors) p4.log.error(e);
|
|
1052
1076
|
process.exit(1);
|
|
1053
1077
|
}
|
|
1054
|
-
s.stop(`Found ${allItems.length} components
|
|
1078
|
+
s.stop(`Found ${allItems.length} components`);
|
|
1055
1079
|
for (const e of errors) {
|
|
1056
1080
|
p4.log.warn(`${pc5.yellow("\u26A0")} Failed to fetch ${e}`);
|
|
1057
1081
|
}
|
|
1058
1082
|
const installed = config.installed ?? {};
|
|
1059
1083
|
const typeGroups = /* @__PURE__ */ new Map();
|
|
1060
1084
|
for (const item of allItems) {
|
|
1061
|
-
if (opts.type && !item.type.endsWith(opts.type)) continue;
|
|
1062
1085
|
const group = item.type.replace("kitn:", "");
|
|
1086
|
+
if (resolvedType && group !== resolvedType) continue;
|
|
1087
|
+
if (!resolvedType && group === "package") continue;
|
|
1063
1088
|
if (!typeGroups.has(group)) typeGroups.set(group, []);
|
|
1064
1089
|
typeGroups.get(group).push(item);
|
|
1065
1090
|
}
|
|
1091
|
+
let maxName = 0;
|
|
1092
|
+
for (const items of typeGroups.values()) {
|
|
1093
|
+
for (const item of items) {
|
|
1094
|
+
const displayName = item.namespace === "@kitn" ? item.name : `${item.namespace}/${item.name}`;
|
|
1095
|
+
if (displayName.length > maxName) maxName = displayName.length;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
const cols = process.stdout.columns ?? 80;
|
|
1099
|
+
const versionLen = opts.verbose ? 10 : 0;
|
|
1100
|
+
const prefixLen = 4 + maxName + 2 + versionLen;
|
|
1066
1101
|
let installedCount = 0;
|
|
1067
1102
|
let updateCount = 0;
|
|
1103
|
+
let shownCount = 0;
|
|
1068
1104
|
for (const [group, items] of typeGroups) {
|
|
1069
|
-
|
|
1070
|
-
|
|
1105
|
+
items.sort((a, b) => {
|
|
1106
|
+
const aInst = !!(installed[a.name] ?? installed[`${a.namespace}/${a.name}`]);
|
|
1107
|
+
const bInst = !!(installed[b.name] ?? installed[`${b.namespace}/${b.name}`]);
|
|
1108
|
+
if (aInst !== bInst) return aInst ? -1 : 1;
|
|
1109
|
+
return a.name.localeCompare(b.name);
|
|
1110
|
+
});
|
|
1111
|
+
const label = group.charAt(0).toUpperCase() + group.slice(1) + "s";
|
|
1112
|
+
console.log(`
|
|
1113
|
+
${pc5.bold(label)} ${pc5.dim(`(${items.length})`)}`);
|
|
1071
1114
|
for (const item of items) {
|
|
1072
1115
|
const displayName = item.namespace === "@kitn" ? item.name : `${item.namespace}/${item.name}`;
|
|
1073
1116
|
const inst = installed[item.name] ?? installed[displayName];
|
|
1074
1117
|
if (opts.installed && !inst) continue;
|
|
1075
|
-
const
|
|
1118
|
+
const maxDescLen = Math.max(20, cols - prefixLen);
|
|
1119
|
+
let desc = item.description;
|
|
1120
|
+
if (desc.length > maxDescLen) {
|
|
1121
|
+
desc = desc.slice(0, maxDescLen - 1) + "\u2026";
|
|
1122
|
+
}
|
|
1123
|
+
let line;
|
|
1124
|
+
const nameCol = displayName.padEnd(maxName + 2);
|
|
1125
|
+
const version = opts.verbose ? `${pc5.dim(`v${item.version ?? "1.0.0"}`)} ` : "";
|
|
1076
1126
|
if (inst) {
|
|
1077
1127
|
installedCount++;
|
|
1078
|
-
const status = pc5.green("\u2713");
|
|
1079
1128
|
const hasUpdate = item.version && inst.version !== item.version;
|
|
1080
|
-
const updateTag = hasUpdate ? pc5.yellow(` \u2B06 v${item.version} available`) : "";
|
|
1081
1129
|
if (hasUpdate) updateCount++;
|
|
1082
|
-
|
|
1130
|
+
const updateTag = hasUpdate ? pc5.yellow(` \u2191${item.version}`) : "";
|
|
1131
|
+
line = ` ${pc5.green("\u2713")} ${nameCol}${version}${pc5.dim(desc)}${updateTag}`;
|
|
1083
1132
|
} else {
|
|
1084
|
-
|
|
1085
|
-
p4.log.message(` ${status} ${displayName.padEnd(20)} ${version} ${pc5.dim(item.description)}`);
|
|
1133
|
+
line = ` ${pc5.dim("\u25CB")} ${nameCol}${version}${pc5.dim(desc)}`;
|
|
1086
1134
|
}
|
|
1135
|
+
console.log(line);
|
|
1136
|
+
shownCount++;
|
|
1087
1137
|
}
|
|
1088
1138
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1139
|
+
if (shownCount === 0 && resolvedType) {
|
|
1140
|
+
console.log(pc5.dim(`
|
|
1141
|
+
No ${resolvedType} components found.`));
|
|
1142
|
+
}
|
|
1143
|
+
const totalShown = [...typeGroups.values()].reduce((sum, items) => sum + items.length, 0);
|
|
1144
|
+
const parts = [`${installedCount} installed`, `${totalShown - installedCount} available`];
|
|
1145
|
+
if (updateCount > 0) parts.push(pc5.yellow(`${updateCount} update${updateCount === 1 ? "" : "s"}`));
|
|
1146
|
+
console.log(`
|
|
1147
|
+
${pc5.dim(parts.join(" \xB7 "))}
|
|
1148
|
+
`);
|
|
1094
1149
|
}
|
|
1150
|
+
var TYPE_ALIASES;
|
|
1095
1151
|
var init_list = __esm({
|
|
1096
1152
|
"src/commands/list.ts"() {
|
|
1097
1153
|
"use strict";
|
|
1098
1154
|
init_config();
|
|
1099
1155
|
init_fetcher();
|
|
1156
|
+
TYPE_ALIASES = {
|
|
1157
|
+
agent: "agent",
|
|
1158
|
+
agents: "agent",
|
|
1159
|
+
tool: "tool",
|
|
1160
|
+
tools: "tool",
|
|
1161
|
+
skill: "skill",
|
|
1162
|
+
skills: "skill",
|
|
1163
|
+
storage: "storage",
|
|
1164
|
+
storages: "storage",
|
|
1165
|
+
package: "package",
|
|
1166
|
+
packages: "package"
|
|
1167
|
+
};
|
|
1100
1168
|
}
|
|
1101
1169
|
});
|
|
1102
1170
|
|
|
@@ -1106,7 +1174,7 @@ __export(diff_exports, {
|
|
|
1106
1174
|
diffCommand: () => diffCommand
|
|
1107
1175
|
});
|
|
1108
1176
|
import * as p5 from "@clack/prompts";
|
|
1109
|
-
import { join as
|
|
1177
|
+
import { join as join9 } from "path";
|
|
1110
1178
|
async function diffCommand(componentName) {
|
|
1111
1179
|
const cwd = process.cwd();
|
|
1112
1180
|
const config = await readConfig(cwd);
|
|
@@ -1136,8 +1204,8 @@ async function diffCommand(componentName) {
|
|
|
1136
1204
|
for (const file of registryItem.files) {
|
|
1137
1205
|
if (indexItem.type === "kitn:package") {
|
|
1138
1206
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1139
|
-
const localPath =
|
|
1140
|
-
const relativePath =
|
|
1207
|
+
const localPath = join9(cwd, baseDir, file.path);
|
|
1208
|
+
const relativePath = join9(baseDir, file.path);
|
|
1141
1209
|
const localContent = await readExistingFile(localPath);
|
|
1142
1210
|
if (localContent === null) {
|
|
1143
1211
|
p5.log.warn(`${relativePath}: file missing locally`);
|
|
@@ -1161,7 +1229,7 @@ async function diffCommand(componentName) {
|
|
|
1161
1229
|
return "storage";
|
|
1162
1230
|
}
|
|
1163
1231
|
})();
|
|
1164
|
-
const localPath =
|
|
1232
|
+
const localPath = join9(cwd, config.aliases[aliasKey], fileName);
|
|
1165
1233
|
const localContent = await readExistingFile(localPath);
|
|
1166
1234
|
if (localContent === null) {
|
|
1167
1235
|
p5.log.warn(`${fileName}: file missing locally`);
|
|
@@ -1195,8 +1263,8 @@ __export(remove_exports, {
|
|
|
1195
1263
|
});
|
|
1196
1264
|
import * as p6 from "@clack/prompts";
|
|
1197
1265
|
import pc6 from "picocolors";
|
|
1198
|
-
import { join as
|
|
1199
|
-
import { unlink, readFile as readFile7, writeFile as
|
|
1266
|
+
import { join as join10, relative as relative3, dirname as dirname3 } from "path";
|
|
1267
|
+
import { unlink, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
1200
1268
|
import { existsSync as existsSync2 } from "fs";
|
|
1201
1269
|
async function removeCommand(componentName) {
|
|
1202
1270
|
const cwd = process.cwd();
|
|
@@ -1224,15 +1292,15 @@ async function removeCommand(componentName) {
|
|
|
1224
1292
|
const deleted = [];
|
|
1225
1293
|
for (const filePath of installed.files) {
|
|
1226
1294
|
try {
|
|
1227
|
-
await unlink(
|
|
1295
|
+
await unlink(join10(cwd, filePath));
|
|
1228
1296
|
deleted.push(filePath);
|
|
1229
1297
|
} catch {
|
|
1230
1298
|
p6.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
1231
1299
|
}
|
|
1232
1300
|
}
|
|
1233
1301
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1234
|
-
const barrelPath =
|
|
1235
|
-
const barrelDir =
|
|
1302
|
+
const barrelPath = join10(cwd, baseDir, "index.ts");
|
|
1303
|
+
const barrelDir = join10(cwd, baseDir);
|
|
1236
1304
|
const barrelEligibleDirs = /* @__PURE__ */ new Set([
|
|
1237
1305
|
config.aliases.agents,
|
|
1238
1306
|
config.aliases.tools,
|
|
@@ -1244,7 +1312,7 @@ async function removeCommand(componentName) {
|
|
|
1244
1312
|
for (const filePath of deleted) {
|
|
1245
1313
|
const fileDir = dirname3(filePath);
|
|
1246
1314
|
if (!barrelEligibleDirs.has(fileDir)) continue;
|
|
1247
|
-
const importPath = "./" + relative3(barrelDir,
|
|
1315
|
+
const importPath = "./" + relative3(barrelDir, join10(cwd, filePath)).replace(/\\/g, "/");
|
|
1248
1316
|
const updated = removeImportFromBarrel(barrelContent, importPath);
|
|
1249
1317
|
if (updated !== barrelContent) {
|
|
1250
1318
|
barrelContent = updated;
|
|
@@ -1252,8 +1320,8 @@ async function removeCommand(componentName) {
|
|
|
1252
1320
|
}
|
|
1253
1321
|
}
|
|
1254
1322
|
if (barrelChanged) {
|
|
1255
|
-
await
|
|
1256
|
-
p6.log.info(`Updated barrel file: ${
|
|
1323
|
+
await writeFile8(barrelPath, barrelContent);
|
|
1324
|
+
p6.log.info(`Updated barrel file: ${join10(baseDir, "index.ts")}`);
|
|
1257
1325
|
}
|
|
1258
1326
|
}
|
|
1259
1327
|
delete config.installed[installedKey];
|
|
@@ -1307,8 +1375,8 @@ var init_update = __esm({
|
|
|
1307
1375
|
});
|
|
1308
1376
|
|
|
1309
1377
|
// src/registry/build-output.ts
|
|
1310
|
-
import { readdir, writeFile as
|
|
1311
|
-
import { join as
|
|
1378
|
+
import { readdir, writeFile as writeFile9, mkdir as mkdir5, access as access4 } from "fs/promises";
|
|
1379
|
+
import { join as join11, resolve } from "path";
|
|
1312
1380
|
async function fileExists(path) {
|
|
1313
1381
|
try {
|
|
1314
1382
|
await access4(path);
|
|
@@ -1325,13 +1393,13 @@ async function walkForRegistryJson(dir) {
|
|
|
1325
1393
|
} catch {
|
|
1326
1394
|
return results;
|
|
1327
1395
|
}
|
|
1328
|
-
if (await fileExists(
|
|
1396
|
+
if (await fileExists(join11(dir, "registry.json"))) {
|
|
1329
1397
|
results.push(dir);
|
|
1330
1398
|
return results;
|
|
1331
1399
|
}
|
|
1332
1400
|
for (const entry of entries) {
|
|
1333
1401
|
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
|
|
1334
|
-
const subResults = await walkForRegistryJson(
|
|
1402
|
+
const subResults = await walkForRegistryJson(join11(dir, entry.name));
|
|
1335
1403
|
results.push(...subResults);
|
|
1336
1404
|
}
|
|
1337
1405
|
}
|
|
@@ -1343,7 +1411,7 @@ async function scanForComponents(cwd, paths) {
|
|
|
1343
1411
|
const results = [];
|
|
1344
1412
|
for (const p12 of paths) {
|
|
1345
1413
|
const absPath = resolve(resolvedCwd, p12);
|
|
1346
|
-
if (await fileExists(
|
|
1414
|
+
if (await fileExists(join11(absPath, "registry.json"))) {
|
|
1347
1415
|
results.push(absPath);
|
|
1348
1416
|
continue;
|
|
1349
1417
|
}
|
|
@@ -1355,8 +1423,8 @@ async function scanForComponents(cwd, paths) {
|
|
|
1355
1423
|
}
|
|
1356
1424
|
for (const entry of entries) {
|
|
1357
1425
|
if (entry.isDirectory()) {
|
|
1358
|
-
const subDir =
|
|
1359
|
-
if (await fileExists(
|
|
1426
|
+
const subDir = join11(absPath, entry.name);
|
|
1427
|
+
if (await fileExists(join11(subDir, "registry.json"))) {
|
|
1360
1428
|
results.push(subDir);
|
|
1361
1429
|
}
|
|
1362
1430
|
}
|
|
@@ -1381,21 +1449,21 @@ async function writeRegistryOutput(outputDir, items) {
|
|
|
1381
1449
|
const indexItems = [];
|
|
1382
1450
|
for (const item of items) {
|
|
1383
1451
|
const dir = typeToDir[item.type];
|
|
1384
|
-
const typeDir =
|
|
1385
|
-
await
|
|
1452
|
+
const typeDir = join11(resolvedOutput, dir);
|
|
1453
|
+
await mkdir5(typeDir, { recursive: true });
|
|
1386
1454
|
const itemJson = JSON.stringify(item, null, 2);
|
|
1387
|
-
const latestPath =
|
|
1455
|
+
const latestPath = join11(typeDir, `${item.name}.json`);
|
|
1388
1456
|
const latestRelative = `${dir}/${item.name}.json`;
|
|
1389
|
-
await
|
|
1457
|
+
await writeFile9(latestPath, itemJson, "utf-8");
|
|
1390
1458
|
written.push(latestRelative);
|
|
1391
1459
|
if (item.version) {
|
|
1392
1460
|
const versionedFilename = `${item.name}@${item.version}.json`;
|
|
1393
|
-
const versionedPath =
|
|
1461
|
+
const versionedPath = join11(typeDir, versionedFilename);
|
|
1394
1462
|
const versionedRelative = `${dir}/${versionedFilename}`;
|
|
1395
1463
|
if (await fileExists(versionedPath)) {
|
|
1396
1464
|
skipped.push(versionedRelative);
|
|
1397
1465
|
} else {
|
|
1398
|
-
await
|
|
1466
|
+
await writeFile9(versionedPath, itemJson, "utf-8");
|
|
1399
1467
|
written.push(versionedRelative);
|
|
1400
1468
|
}
|
|
1401
1469
|
}
|
|
@@ -1430,8 +1498,8 @@ async function writeRegistryOutput(outputDir, items) {
|
|
|
1430
1498
|
version: "1",
|
|
1431
1499
|
items: indexItems
|
|
1432
1500
|
};
|
|
1433
|
-
const indexPath =
|
|
1434
|
-
await
|
|
1501
|
+
const indexPath = join11(resolvedOutput, "registry.json");
|
|
1502
|
+
await writeFile9(indexPath, JSON.stringify(index, null, 2), "utf-8");
|
|
1435
1503
|
written.push("registry.json");
|
|
1436
1504
|
return { written, skipped };
|
|
1437
1505
|
}
|
|
@@ -1454,7 +1522,7 @@ var init_build_output = __esm({
|
|
|
1454
1522
|
|
|
1455
1523
|
// src/registry/builder.ts
|
|
1456
1524
|
import { readFile as readFile9, readdir as readdir2 } from "fs/promises";
|
|
1457
|
-
import { join as
|
|
1525
|
+
import { join as join12, relative as relative5 } from "path";
|
|
1458
1526
|
function isExcludedDevDep(name) {
|
|
1459
1527
|
return EXCLUDED_DEV_DEPS.has(name) || name.startsWith("@types/");
|
|
1460
1528
|
}
|
|
@@ -1466,7 +1534,7 @@ async function readTsFiles(dir, baseDir, exclude) {
|
|
|
1466
1534
|
const results = [];
|
|
1467
1535
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
1468
1536
|
for (const entry of entries) {
|
|
1469
|
-
const fullPath =
|
|
1537
|
+
const fullPath = join12(dir, entry.name);
|
|
1470
1538
|
const relPath = relative5(baseDir, fullPath);
|
|
1471
1539
|
if (entry.isDirectory()) {
|
|
1472
1540
|
const nested = await readTsFiles(fullPath, baseDir, exclude);
|
|
@@ -1484,7 +1552,7 @@ async function readTsFiles(dir, baseDir, exclude) {
|
|
|
1484
1552
|
async function buildComponent(componentDir) {
|
|
1485
1553
|
let rawConfig;
|
|
1486
1554
|
try {
|
|
1487
|
-
rawConfig = await readFile9(
|
|
1555
|
+
rawConfig = await readFile9(join12(componentDir, "registry.json"), "utf-8");
|
|
1488
1556
|
} catch {
|
|
1489
1557
|
throw new Error(
|
|
1490
1558
|
`No registry.json found in ${componentDir}. Every component must have a registry.json file.`
|
|
@@ -1500,7 +1568,7 @@ async function buildComponent(componentDir) {
|
|
|
1500
1568
|
}
|
|
1501
1569
|
let pkg = null;
|
|
1502
1570
|
try {
|
|
1503
|
-
const rawPkg = await readFile9(
|
|
1571
|
+
const rawPkg = await readFile9(join12(componentDir, "package.json"), "utf-8");
|
|
1504
1572
|
pkg = JSON.parse(rawPkg);
|
|
1505
1573
|
} catch {
|
|
1506
1574
|
}
|
|
@@ -1557,7 +1625,7 @@ async function buildComponent(componentDir) {
|
|
|
1557
1625
|
let files;
|
|
1558
1626
|
if (isPackage) {
|
|
1559
1627
|
const sourceDir = config.sourceDir ?? "src";
|
|
1560
|
-
const sourcePath =
|
|
1628
|
+
const sourcePath = join12(componentDir, sourceDir);
|
|
1561
1629
|
const exclude = config.exclude ?? [];
|
|
1562
1630
|
let tsFiles;
|
|
1563
1631
|
try {
|
|
@@ -1580,7 +1648,7 @@ async function buildComponent(componentDir) {
|
|
|
1580
1648
|
}
|
|
1581
1649
|
files = await Promise.all(
|
|
1582
1650
|
config.files.map(async (filePath) => {
|
|
1583
|
-
const fullPath =
|
|
1651
|
+
const fullPath = join12(componentDir, filePath);
|
|
1584
1652
|
let content;
|
|
1585
1653
|
try {
|
|
1586
1654
|
content = await readFile9(fullPath, "utf-8");
|
|
@@ -1716,8 +1784,8 @@ __export(create_exports, {
|
|
|
1716
1784
|
});
|
|
1717
1785
|
import * as p9 from "@clack/prompts";
|
|
1718
1786
|
import pc8 from "picocolors";
|
|
1719
|
-
import { join as
|
|
1720
|
-
import { mkdir as
|
|
1787
|
+
import { join as join13 } from "path";
|
|
1788
|
+
import { mkdir as mkdir6, writeFile as writeFile10 } from "fs/promises";
|
|
1721
1789
|
function toCamelCase(str) {
|
|
1722
1790
|
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1723
1791
|
}
|
|
@@ -1743,7 +1811,7 @@ function generateRegistryJson(type, name, sourceFile) {
|
|
|
1743
1811
|
}
|
|
1744
1812
|
function generateAgentSource(name) {
|
|
1745
1813
|
const camel = toCamelCase(name);
|
|
1746
|
-
return `import { registerAgent } from "@
|
|
1814
|
+
return `import { registerAgent } from "@kitn/core";
|
|
1747
1815
|
|
|
1748
1816
|
const SYSTEM_PROMPT = "You are a helpful assistant.";
|
|
1749
1817
|
|
|
@@ -1757,7 +1825,7 @@ registerAgent({
|
|
|
1757
1825
|
}
|
|
1758
1826
|
function generateToolSource(name) {
|
|
1759
1827
|
const camel = toCamelCase(name);
|
|
1760
|
-
return `import { registerTool } from "@
|
|
1828
|
+
return `import { registerTool } from "@kitn/core";
|
|
1761
1829
|
import { tool } from "ai";
|
|
1762
1830
|
import { z } from "zod";
|
|
1763
1831
|
|
|
@@ -1794,7 +1862,7 @@ Describe what this skill does and how to use it.
|
|
|
1794
1862
|
}
|
|
1795
1863
|
function generateStorageSource(name) {
|
|
1796
1864
|
const camel = toCamelCase("create-" + name);
|
|
1797
|
-
return `import type { StorageProvider } from "@
|
|
1865
|
+
return `import type { StorageProvider } from "@kitn/core";
|
|
1798
1866
|
|
|
1799
1867
|
export function ${camel}(config?: Record<string, unknown>): StorageProvider {
|
|
1800
1868
|
// TODO: implement storage provider
|
|
@@ -1818,16 +1886,16 @@ async function createComponent(type, name, opts) {
|
|
|
1818
1886
|
);
|
|
1819
1887
|
}
|
|
1820
1888
|
const cwd = opts?.cwd ?? process.cwd();
|
|
1821
|
-
const dir =
|
|
1889
|
+
const dir = join13(cwd, name);
|
|
1822
1890
|
if (await dirExists(dir)) {
|
|
1823
1891
|
throw new Error(`Directory "${name}" already exists`);
|
|
1824
1892
|
}
|
|
1825
|
-
await
|
|
1893
|
+
await mkdir6(dir, { recursive: true });
|
|
1826
1894
|
const validType = type;
|
|
1827
1895
|
const sourceFile = validType === "skill" ? "README.md" : `${name}.ts`;
|
|
1828
1896
|
const registryJson = generateRegistryJson(validType, name, sourceFile);
|
|
1829
|
-
await
|
|
1830
|
-
|
|
1897
|
+
await writeFile10(
|
|
1898
|
+
join13(dir, "registry.json"),
|
|
1831
1899
|
JSON.stringify(registryJson, null, 2) + "\n"
|
|
1832
1900
|
);
|
|
1833
1901
|
let source;
|
|
@@ -1845,7 +1913,7 @@ async function createComponent(type, name, opts) {
|
|
|
1845
1913
|
source = generateStorageSource(name);
|
|
1846
1914
|
break;
|
|
1847
1915
|
}
|
|
1848
|
-
await
|
|
1916
|
+
await writeFile10(join13(dir, sourceFile), source);
|
|
1849
1917
|
return { dir, files: ["registry.json", sourceFile] };
|
|
1850
1918
|
}
|
|
1851
1919
|
async function createCommand(type, name) {
|
|
@@ -2148,7 +2216,7 @@ function startUpdateCheck(currentVersion) {
|
|
|
2148
2216
|
}
|
|
2149
2217
|
|
|
2150
2218
|
// src/index.ts
|
|
2151
|
-
var VERSION = true ? "0.1.
|
|
2219
|
+
var VERSION = true ? "0.1.11" : "0.0.0-dev";
|
|
2152
2220
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
2153
2221
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
2154
2222
|
program.command("init").description("Initialize kitn in your project").action(async () => {
|
|
@@ -2159,9 +2227,9 @@ program.command("add").description("Add components from the kitn registry").argu
|
|
|
2159
2227
|
const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
2160
2228
|
await addCommand2(components, opts);
|
|
2161
2229
|
});
|
|
2162
|
-
program.command("list").description("List available and installed components").option("-i, --installed", "only show installed components").option("-t, --type <type>", "filter by type (agent, tool, skill, storage, package)").option("-r, --registry <namespace>", "only show components from this registry").action(async (opts) => {
|
|
2230
|
+
program.command("list").argument("[type]", "filter by type (agents, tools, skills, storages, packages)").description("List available and installed components").option("-i, --installed", "only show installed components").option("-t, --type <type>", "filter by type (agent, tool, skill, storage, package)").option("-r, --registry <namespace>", "only show components from this registry").option("-v, --verbose", "show version numbers").action(async (type, opts) => {
|
|
2163
2231
|
const { listCommand: listCommand2 } = await Promise.resolve().then(() => (init_list(), list_exports));
|
|
2164
|
-
await listCommand2(opts);
|
|
2232
|
+
await listCommand2(type, opts);
|
|
2165
2233
|
});
|
|
2166
2234
|
program.command("diff").description("Show differences between local and registry version").argument("<component>", "component name").action(async (component) => {
|
|
2167
2235
|
const { diffCommand: diffCommand2 } = await Promise.resolve().then(() => (init_diff(), diff_exports));
|