@kitnai/cli 0.1.6 → 0.1.8
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/dist/index.js +815 -137
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -215,7 +215,7 @@ async function resolveDependencies(names, fetchItem) {
|
|
|
215
215
|
const visited = /* @__PURE__ */ new Set();
|
|
216
216
|
const items = /* @__PURE__ */ new Map();
|
|
217
217
|
const edges = [];
|
|
218
|
-
async function
|
|
218
|
+
async function resolve3(name) {
|
|
219
219
|
if (visited.has(name)) return;
|
|
220
220
|
visited.add(name);
|
|
221
221
|
const item = await fetchItem(name);
|
|
@@ -223,11 +223,11 @@ async function resolveDependencies(names, fetchItem) {
|
|
|
223
223
|
const deps = item.registryDependencies ?? [];
|
|
224
224
|
for (const dep of deps) {
|
|
225
225
|
edges.push([dep, name]);
|
|
226
|
-
await
|
|
226
|
+
await resolve3(dep);
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
for (const name of names) {
|
|
230
|
-
await
|
|
230
|
+
await resolve3(name);
|
|
231
231
|
}
|
|
232
232
|
return topologicalSort(items, edges);
|
|
233
233
|
}
|
|
@@ -326,25 +326,119 @@ var init_dep_installer = __esm({
|
|
|
326
326
|
}
|
|
327
327
|
});
|
|
328
328
|
|
|
329
|
-
// src/installers/env-
|
|
329
|
+
// src/installers/env-writer.ts
|
|
330
|
+
import * as p2 from "@clack/prompts";
|
|
330
331
|
import pc3 from "picocolors";
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
332
|
+
import { readFile as readFile4, writeFile as writeFile4, access as access3 } from "fs/promises";
|
|
333
|
+
import { join as join4 } from "path";
|
|
334
|
+
function parseEnvKeys(content) {
|
|
335
|
+
const keys = /* @__PURE__ */ new Set();
|
|
336
|
+
for (const line of content.split("\n")) {
|
|
337
|
+
const trimmed = line.trim();
|
|
338
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
339
|
+
const eqIndex = trimmed.indexOf("=");
|
|
340
|
+
if (eqIndex > 0) {
|
|
341
|
+
keys.add(trimmed.slice(0, eqIndex).trim());
|
|
336
342
|
}
|
|
337
343
|
}
|
|
338
|
-
return
|
|
344
|
+
return keys;
|
|
345
|
+
}
|
|
346
|
+
async function readEnvFile(path) {
|
|
347
|
+
try {
|
|
348
|
+
return await readFile4(path, "utf-8");
|
|
349
|
+
} catch {
|
|
350
|
+
return "";
|
|
351
|
+
}
|
|
339
352
|
}
|
|
340
|
-
|
|
341
|
-
|
|
353
|
+
function collectEnvVars(items) {
|
|
354
|
+
const merged = {};
|
|
355
|
+
for (const item of items) {
|
|
356
|
+
if (item.envVars) {
|
|
357
|
+
Object.assign(merged, item.envVars);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return merged;
|
|
361
|
+
}
|
|
362
|
+
async function handleEnvVars(cwd, envVars) {
|
|
363
|
+
const keys = Object.keys(envVars);
|
|
364
|
+
if (keys.length === 0) return;
|
|
365
|
+
const envPath = join4(cwd, ".env");
|
|
366
|
+
const examplePath = join4(cwd, ".env.example");
|
|
367
|
+
const envContent = await readEnvFile(envPath);
|
|
368
|
+
const exampleContent = await readEnvFile(examplePath);
|
|
369
|
+
const envKeys = parseEnvKeys(envContent);
|
|
370
|
+
const exampleKeys = parseEnvKeys(exampleContent);
|
|
371
|
+
const missingFromExample = keys.filter((k) => !exampleKeys.has(k));
|
|
372
|
+
const missingFromEnv = keys.filter((k) => !envKeys.has(k) && !process.env[k]);
|
|
373
|
+
if (missingFromExample.length > 0) {
|
|
374
|
+
const lines = [];
|
|
375
|
+
if (exampleContent && !exampleContent.endsWith("\n")) lines.push("");
|
|
376
|
+
for (const key of missingFromExample) {
|
|
377
|
+
const config = envVars[key];
|
|
378
|
+
lines.push(`# ${config.description}${config.url ? ` (${config.url})` : ""}`);
|
|
379
|
+
lines.push(`${key}=`);
|
|
380
|
+
}
|
|
381
|
+
await writeFile4(examplePath, exampleContent + lines.join("\n") + "\n");
|
|
382
|
+
p2.log.info(`Updated ${pc3.cyan(".env.example")} with ${missingFromExample.length} variable(s)`);
|
|
383
|
+
}
|
|
384
|
+
if (missingFromEnv.length === 0) return;
|
|
385
|
+
p2.log.message("");
|
|
386
|
+
p2.log.warn(
|
|
387
|
+
`${missingFromEnv.length} environment variable(s) needed:`
|
|
388
|
+
);
|
|
389
|
+
for (const key of missingFromEnv) {
|
|
390
|
+
const config = envVars[key];
|
|
391
|
+
const req = config.required !== false ? pc3.red("*") : "";
|
|
392
|
+
p2.log.message(` ${pc3.yellow(key)}${req}: ${config.description}${config.url ? pc3.dim(` -> ${config.url}`) : ""}`);
|
|
393
|
+
}
|
|
394
|
+
const shouldPrompt = await p2.confirm({
|
|
395
|
+
message: "Would you like to enter values now?",
|
|
396
|
+
initialValue: true
|
|
397
|
+
});
|
|
398
|
+
if (p2.isCancel(shouldPrompt) || !shouldPrompt) {
|
|
399
|
+
p2.log.info(`Add them to ${pc3.cyan(".env")} when ready.`);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const newEntries = [];
|
|
403
|
+
for (const key of missingFromEnv) {
|
|
404
|
+
const config = envVars[key];
|
|
405
|
+
const isSecret = config.secret !== false;
|
|
406
|
+
let value;
|
|
407
|
+
if (isSecret) {
|
|
408
|
+
value = await p2.password({
|
|
409
|
+
message: `${key}:`
|
|
410
|
+
});
|
|
411
|
+
} else {
|
|
412
|
+
value = await p2.text({
|
|
413
|
+
message: `${key}:`,
|
|
414
|
+
placeholder: config.description
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
if (p2.isCancel(value)) {
|
|
418
|
+
p2.log.info(`Skipped remaining variables. Add them to ${pc3.cyan(".env")} when ready.`);
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
if (value) {
|
|
422
|
+
newEntries.push(`${key}=${value}`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (newEntries.length > 0) {
|
|
426
|
+
const existingEnv = await readEnvFile(envPath);
|
|
427
|
+
const lines = [];
|
|
428
|
+
if (existingEnv && !existingEnv.endsWith("\n")) lines.push("");
|
|
429
|
+
lines.push(...newEntries);
|
|
430
|
+
await writeFile4(envPath, existingEnv + lines.join("\n") + "\n");
|
|
431
|
+
p2.log.success(`Wrote ${newEntries.length} variable(s) to ${pc3.cyan(".env")}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
var init_env_writer = __esm({
|
|
435
|
+
"src/installers/env-writer.ts"() {
|
|
342
436
|
"use strict";
|
|
343
437
|
}
|
|
344
438
|
});
|
|
345
439
|
|
|
346
440
|
// src/installers/import-rewriter.ts
|
|
347
|
-
import { relative, join as
|
|
441
|
+
import { relative, join as join5 } from "path";
|
|
348
442
|
function rewriteKitnImports(content, fileType, fileName, aliases) {
|
|
349
443
|
const sourceAliasKey = TYPE_TO_ALIAS_KEY[fileType];
|
|
350
444
|
if (!sourceAliasKey) return content;
|
|
@@ -356,7 +450,7 @@ function rewriteKitnImports(content, fileType, fileName, aliases) {
|
|
|
356
450
|
return `${prefix}@kitn/${type}/${targetPath}${quote}`;
|
|
357
451
|
}
|
|
358
452
|
const targetDir = aliases[type];
|
|
359
|
-
const targetFile =
|
|
453
|
+
const targetFile = join5(targetDir, targetPath);
|
|
360
454
|
let rel = relative(sourceDir, targetFile);
|
|
361
455
|
rel = rel.split("\\").join("/");
|
|
362
456
|
if (!rel.startsWith(".")) {
|
|
@@ -381,10 +475,10 @@ var init_import_rewriter = __esm({
|
|
|
381
475
|
});
|
|
382
476
|
|
|
383
477
|
// src/installers/tsconfig-patcher.ts
|
|
384
|
-
import { readFile as
|
|
385
|
-
import { join as
|
|
386
|
-
function stripJsonc(
|
|
387
|
-
return
|
|
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");
|
|
388
482
|
}
|
|
389
483
|
function patchTsconfig(tsconfigContent, paths) {
|
|
390
484
|
const config = JSON.parse(stripJsonc(tsconfigContent));
|
|
@@ -400,15 +494,15 @@ function patchTsconfig(tsconfigContent, paths) {
|
|
|
400
494
|
return JSON.stringify(config, null, 2) + "\n";
|
|
401
495
|
}
|
|
402
496
|
async function patchProjectTsconfig(projectDir, paths) {
|
|
403
|
-
const tsconfigPath =
|
|
497
|
+
const tsconfigPath = join6(projectDir, "tsconfig.json");
|
|
404
498
|
let content;
|
|
405
499
|
try {
|
|
406
|
-
content = await
|
|
500
|
+
content = await readFile5(tsconfigPath, "utf-8");
|
|
407
501
|
} catch {
|
|
408
502
|
content = "{}";
|
|
409
503
|
}
|
|
410
504
|
const patched = patchTsconfig(content, paths);
|
|
411
|
-
await
|
|
505
|
+
await writeFile5(tsconfigPath, patched);
|
|
412
506
|
}
|
|
413
507
|
var init_tsconfig_patcher = __esm({
|
|
414
508
|
"src/installers/tsconfig-patcher.ts"() {
|
|
@@ -457,7 +551,7 @@ var init_parse_ref = __esm({
|
|
|
457
551
|
|
|
458
552
|
// src/registry/schema.ts
|
|
459
553
|
import { z as z2 } from "zod";
|
|
460
|
-
var componentType2, registryFileSchema, changelogEntrySchema, registryItemSchema, registryIndexItemSchema, registryIndexSchema, typeToDir;
|
|
554
|
+
var componentType2, registryFileSchema, changelogEntrySchema, envVarConfigSchema, componentConfigSchema, registryItemSchema, registryIndexItemSchema, registryIndexSchema, typeToDir;
|
|
461
555
|
var init_schema = __esm({
|
|
462
556
|
"src/registry/schema.ts"() {
|
|
463
557
|
"use strict";
|
|
@@ -473,6 +567,31 @@ var init_schema = __esm({
|
|
|
473
567
|
type: z2.enum(["feature", "fix", "breaking", "initial"]),
|
|
474
568
|
note: z2.string()
|
|
475
569
|
});
|
|
570
|
+
envVarConfigSchema = z2.object({
|
|
571
|
+
description: z2.string(),
|
|
572
|
+
required: z2.boolean().optional(),
|
|
573
|
+
secret: z2.boolean().optional(),
|
|
574
|
+
url: z2.string().optional()
|
|
575
|
+
});
|
|
576
|
+
componentConfigSchema = z2.object({
|
|
577
|
+
$schema: z2.string().optional(),
|
|
578
|
+
type: componentType2,
|
|
579
|
+
name: z2.string().optional(),
|
|
580
|
+
version: z2.string().optional(),
|
|
581
|
+
description: z2.string().optional(),
|
|
582
|
+
dependencies: z2.array(z2.string()).optional(),
|
|
583
|
+
devDependencies: z2.array(z2.string()).optional(),
|
|
584
|
+
registryDependencies: z2.array(z2.string()).optional(),
|
|
585
|
+
files: z2.array(z2.string()).optional(),
|
|
586
|
+
sourceDir: z2.string().optional(),
|
|
587
|
+
exclude: z2.array(z2.string()).optional(),
|
|
588
|
+
installDir: z2.string().optional(),
|
|
589
|
+
tsconfig: z2.record(z2.string(), z2.array(z2.string())).optional(),
|
|
590
|
+
envVars: z2.record(z2.string(), envVarConfigSchema).optional(),
|
|
591
|
+
categories: z2.array(z2.string()).optional(),
|
|
592
|
+
docs: z2.string().optional(),
|
|
593
|
+
changelog: z2.array(changelogEntrySchema).optional()
|
|
594
|
+
});
|
|
476
595
|
registryItemSchema = z2.object({
|
|
477
596
|
$schema: z2.string().optional(),
|
|
478
597
|
name: z2.string(),
|
|
@@ -481,7 +600,7 @@ var init_schema = __esm({
|
|
|
481
600
|
dependencies: z2.array(z2.string()).optional(),
|
|
482
601
|
devDependencies: z2.array(z2.string()).optional(),
|
|
483
602
|
registryDependencies: z2.array(z2.string()).optional(),
|
|
484
|
-
envVars: z2.record(z2.string(),
|
|
603
|
+
envVars: z2.record(z2.string(), envVarConfigSchema).optional(),
|
|
485
604
|
files: z2.array(registryFileSchema),
|
|
486
605
|
installDir: z2.string().optional(),
|
|
487
606
|
tsconfig: z2.record(z2.string(), z2.array(z2.string())).optional(),
|
|
@@ -521,19 +640,19 @@ var add_exports = {};
|
|
|
521
640
|
__export(add_exports, {
|
|
522
641
|
addCommand: () => addCommand
|
|
523
642
|
});
|
|
524
|
-
import * as
|
|
643
|
+
import * as p3 from "@clack/prompts";
|
|
525
644
|
import pc4 from "picocolors";
|
|
526
|
-
import { join as
|
|
645
|
+
import { join as join7 } from "path";
|
|
527
646
|
async function addCommand(components, opts) {
|
|
528
|
-
|
|
647
|
+
p3.intro(pc4.bgCyan(pc4.black(" kitn add ")));
|
|
529
648
|
const cwd = process.cwd();
|
|
530
649
|
const config = await readConfig(cwd);
|
|
531
650
|
if (!config) {
|
|
532
|
-
|
|
651
|
+
p3.log.error("No kitn.json found. Run `kitn init` first.");
|
|
533
652
|
process.exit(1);
|
|
534
653
|
}
|
|
535
654
|
if (components.length === 0) {
|
|
536
|
-
|
|
655
|
+
p3.log.error("Please specify at least one component to add.");
|
|
537
656
|
process.exit(1);
|
|
538
657
|
}
|
|
539
658
|
const resolvedComponents = components.map((c) => {
|
|
@@ -545,7 +664,7 @@ async function addCommand(components, opts) {
|
|
|
545
664
|
});
|
|
546
665
|
const refs = resolvedComponents.map(parseComponentRef);
|
|
547
666
|
const fetcher = new RegistryFetcher(config.registries);
|
|
548
|
-
const s =
|
|
667
|
+
const s = p3.spinner();
|
|
549
668
|
s.start("Resolving dependencies...");
|
|
550
669
|
let resolved;
|
|
551
670
|
try {
|
|
@@ -559,31 +678,27 @@ async function addCommand(components, opts) {
|
|
|
559
678
|
});
|
|
560
679
|
} catch (err) {
|
|
561
680
|
s.stop(pc4.red("Failed to resolve dependencies"));
|
|
562
|
-
|
|
681
|
+
p3.log.error(err.message);
|
|
563
682
|
process.exit(1);
|
|
564
683
|
}
|
|
565
684
|
s.stop(`Resolved ${resolved.length} component(s)`);
|
|
566
|
-
|
|
685
|
+
p3.log.info("Components to install:");
|
|
567
686
|
for (const item of resolved) {
|
|
568
687
|
const isExplicit = resolvedComponents.includes(item.name) || components.includes(item.name);
|
|
569
688
|
const label = isExplicit ? item.name : `${item.name} ${pc4.dim("(dependency)")}`;
|
|
570
|
-
|
|
689
|
+
p3.log.message(` ${pc4.cyan(label)}`);
|
|
571
690
|
}
|
|
572
691
|
const created = [];
|
|
573
692
|
const updated = [];
|
|
574
693
|
const skipped = [];
|
|
575
694
|
const allDeps = [];
|
|
576
|
-
const allEnvWarnings = [];
|
|
577
695
|
for (const item of resolved) {
|
|
578
696
|
if (item.dependencies) allDeps.push(...item.dependencies);
|
|
579
|
-
if (item.envVars) {
|
|
580
|
-
allEnvWarnings.push(...checkEnvVars(item.envVars));
|
|
581
|
-
}
|
|
582
697
|
if (item.type === "kitn:package") {
|
|
583
698
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
584
699
|
for (const file of item.files) {
|
|
585
|
-
const targetPath =
|
|
586
|
-
const relativePath =
|
|
700
|
+
const targetPath = join7(cwd, baseDir, file.path);
|
|
701
|
+
const relativePath = join7(baseDir, file.path);
|
|
587
702
|
const status = await checkFileStatus(targetPath, file.content);
|
|
588
703
|
switch (status) {
|
|
589
704
|
case "new" /* New */:
|
|
@@ -600,15 +715,15 @@ async function addCommand(components, opts) {
|
|
|
600
715
|
} else {
|
|
601
716
|
const existing = await readExistingFile(targetPath);
|
|
602
717
|
const diff = generateDiff(relativePath, existing ?? "", file.content);
|
|
603
|
-
|
|
604
|
-
const action = await
|
|
718
|
+
p3.log.message(pc4.dim(diff));
|
|
719
|
+
const action = await p3.select({
|
|
605
720
|
message: `${relativePath} already exists and differs. What to do?`,
|
|
606
721
|
options: [
|
|
607
722
|
{ value: "skip", label: "Keep local version" },
|
|
608
723
|
{ value: "overwrite", label: "Overwrite with registry version" }
|
|
609
724
|
]
|
|
610
725
|
});
|
|
611
|
-
if (!
|
|
726
|
+
if (!p3.isCancel(action) && action === "overwrite") {
|
|
612
727
|
await writeComponentFile(targetPath, file.content);
|
|
613
728
|
updated.push(relativePath);
|
|
614
729
|
} else {
|
|
@@ -622,10 +737,10 @@ async function addCommand(components, opts) {
|
|
|
622
737
|
const resolvedPaths = {};
|
|
623
738
|
const installDir = item.installDir ?? item.name;
|
|
624
739
|
for (const [key, values] of Object.entries(item.tsconfig)) {
|
|
625
|
-
resolvedPaths[key] = values.map((v) => `./${
|
|
740
|
+
resolvedPaths[key] = values.map((v) => `./${join7(baseDir, installDir, v)}`);
|
|
626
741
|
}
|
|
627
742
|
await patchProjectTsconfig(cwd, resolvedPaths);
|
|
628
|
-
|
|
743
|
+
p3.log.info(`Patched tsconfig.json with paths: ${Object.keys(resolvedPaths).join(", ")}`);
|
|
629
744
|
}
|
|
630
745
|
const installed = config.installed ?? {};
|
|
631
746
|
const allContent = item.files.map((f) => f.content).join("\n");
|
|
@@ -635,7 +750,7 @@ async function addCommand(components, opts) {
|
|
|
635
750
|
registry: ref.namespace,
|
|
636
751
|
version: item.version ?? "1.0.0",
|
|
637
752
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
638
|
-
files: item.files.map((f) =>
|
|
753
|
+
files: item.files.map((f) => join7(baseDir, f.path)),
|
|
639
754
|
hash: contentHash(allContent)
|
|
640
755
|
};
|
|
641
756
|
config.installed = installed;
|
|
@@ -654,8 +769,8 @@ async function addCommand(components, opts) {
|
|
|
654
769
|
}
|
|
655
770
|
})();
|
|
656
771
|
const fileName = file.path.split("/").pop();
|
|
657
|
-
const targetPath =
|
|
658
|
-
const relativePath =
|
|
772
|
+
const targetPath = join7(cwd, config.aliases[aliasKey], fileName);
|
|
773
|
+
const relativePath = join7(config.aliases[aliasKey], fileName);
|
|
659
774
|
const content = rewriteKitnImports(file.content, item.type, fileName, config.aliases);
|
|
660
775
|
const status = await checkFileStatus(targetPath, content);
|
|
661
776
|
switch (status) {
|
|
@@ -673,15 +788,15 @@ async function addCommand(components, opts) {
|
|
|
673
788
|
} else {
|
|
674
789
|
const existing = await readExistingFile(targetPath);
|
|
675
790
|
const diff = generateDiff(relativePath, existing ?? "", content);
|
|
676
|
-
|
|
677
|
-
const action = await
|
|
791
|
+
p3.log.message(pc4.dim(diff));
|
|
792
|
+
const action = await p3.select({
|
|
678
793
|
message: `${relativePath} already exists and differs. What to do?`,
|
|
679
794
|
options: [
|
|
680
795
|
{ value: "skip", label: "Keep local version" },
|
|
681
796
|
{ value: "overwrite", label: "Overwrite with registry version" }
|
|
682
797
|
]
|
|
683
798
|
});
|
|
684
|
-
if (!
|
|
799
|
+
if (!p3.isCancel(action) && action === "overwrite") {
|
|
685
800
|
await writeComponentFile(targetPath, content);
|
|
686
801
|
updated.push(relativePath);
|
|
687
802
|
} else {
|
|
@@ -716,7 +831,7 @@ async function addCommand(components, opts) {
|
|
|
716
831
|
}
|
|
717
832
|
})();
|
|
718
833
|
const fileName = f.path.split("/").pop();
|
|
719
|
-
return
|
|
834
|
+
return join7(config.aliases[aliasKey], fileName);
|
|
720
835
|
}),
|
|
721
836
|
hash: contentHash(allContent)
|
|
722
837
|
};
|
|
@@ -738,24 +853,22 @@ async function addCommand(components, opts) {
|
|
|
738
853
|
}
|
|
739
854
|
}
|
|
740
855
|
if (created.length > 0) {
|
|
741
|
-
|
|
742
|
-
for (const f of created)
|
|
856
|
+
p3.log.success(`Created ${created.length} file(s):`);
|
|
857
|
+
for (const f of created) p3.log.message(` ${pc4.green("+")} ${f}`);
|
|
743
858
|
}
|
|
744
859
|
if (updated.length > 0) {
|
|
745
|
-
|
|
746
|
-
for (const f of updated)
|
|
860
|
+
p3.log.success(`Updated ${updated.length} file(s):`);
|
|
861
|
+
for (const f of updated) p3.log.message(` ${pc4.yellow("~")} ${f}`);
|
|
747
862
|
}
|
|
748
863
|
if (skipped.length > 0) {
|
|
749
|
-
|
|
750
|
-
for (const f of skipped)
|
|
751
|
-
}
|
|
752
|
-
if (allEnvWarnings.length > 0) {
|
|
753
|
-
p2.log.warn("Missing environment variables:");
|
|
754
|
-
for (const w of allEnvWarnings) p2.log.message(w);
|
|
864
|
+
p3.log.info(`Skipped ${skipped.length} file(s):`);
|
|
865
|
+
for (const f of skipped) p3.log.message(` ${pc4.dim("-")} ${f}`);
|
|
755
866
|
}
|
|
867
|
+
const allEnvVars = collectEnvVars(resolved);
|
|
868
|
+
await handleEnvVars(cwd, allEnvVars);
|
|
756
869
|
for (const item of resolved) {
|
|
757
870
|
if (item.docs) {
|
|
758
|
-
|
|
871
|
+
p3.log.info(`${pc4.bold(item.name)}: ${item.docs}`);
|
|
759
872
|
}
|
|
760
873
|
}
|
|
761
874
|
const installedNames = new Set(resolved.map((r) => r.name));
|
|
@@ -783,10 +896,10 @@ async function addCommand(components, opts) {
|
|
|
783
896
|
}
|
|
784
897
|
}
|
|
785
898
|
if (hints.length > 0) {
|
|
786
|
-
|
|
787
|
-
for (const hint of hints)
|
|
899
|
+
p3.log.message(pc4.bold("\nNext steps:"));
|
|
900
|
+
for (const hint of hints) p3.log.message(hint);
|
|
788
901
|
}
|
|
789
|
-
|
|
902
|
+
p3.outro(pc4.green("Done!"));
|
|
790
903
|
}
|
|
791
904
|
var init_add = __esm({
|
|
792
905
|
"src/commands/add.ts"() {
|
|
@@ -797,7 +910,7 @@ var init_add = __esm({
|
|
|
797
910
|
init_resolver();
|
|
798
911
|
init_file_writer();
|
|
799
912
|
init_dep_installer();
|
|
800
|
-
|
|
913
|
+
init_env_writer();
|
|
801
914
|
init_import_rewriter();
|
|
802
915
|
init_tsconfig_patcher();
|
|
803
916
|
init_hash();
|
|
@@ -811,22 +924,22 @@ var list_exports = {};
|
|
|
811
924
|
__export(list_exports, {
|
|
812
925
|
listCommand: () => listCommand
|
|
813
926
|
});
|
|
814
|
-
import * as
|
|
927
|
+
import * as p4 from "@clack/prompts";
|
|
815
928
|
import pc5 from "picocolors";
|
|
816
929
|
async function listCommand(opts) {
|
|
817
930
|
const cwd = process.cwd();
|
|
818
931
|
const config = await readConfig(cwd);
|
|
819
932
|
if (!config) {
|
|
820
|
-
|
|
933
|
+
p4.log.error("No kitn.json found. Run `kitn init` first.");
|
|
821
934
|
process.exit(1);
|
|
822
935
|
}
|
|
823
936
|
const fetcher = new RegistryFetcher(config.registries);
|
|
824
937
|
const namespacesToFetch = opts.registry ? [opts.registry] : Object.keys(config.registries);
|
|
825
938
|
if (opts.registry && !config.registries[opts.registry]) {
|
|
826
|
-
|
|
939
|
+
p4.log.error(`Registry ${pc5.bold(opts.registry)} is not configured. Run ${pc5.bold("kitn registry list")} to see configured registries.`);
|
|
827
940
|
process.exit(1);
|
|
828
941
|
}
|
|
829
|
-
const s =
|
|
942
|
+
const s = p4.spinner();
|
|
830
943
|
s.start("Fetching registry index...");
|
|
831
944
|
const allItems = [];
|
|
832
945
|
const errors = [];
|
|
@@ -842,12 +955,12 @@ async function listCommand(opts) {
|
|
|
842
955
|
}
|
|
843
956
|
if (allItems.length === 0 && errors.length > 0) {
|
|
844
957
|
s.stop(pc5.red("Failed to fetch registries"));
|
|
845
|
-
for (const e of errors)
|
|
958
|
+
for (const e of errors) p4.log.error(e);
|
|
846
959
|
process.exit(1);
|
|
847
960
|
}
|
|
848
961
|
s.stop(`Found ${allItems.length} components across ${namespacesToFetch.length - errors.length} ${namespacesToFetch.length - errors.length === 1 ? "registry" : "registries"}`);
|
|
849
962
|
for (const e of errors) {
|
|
850
|
-
|
|
963
|
+
p4.log.warn(`${pc5.yellow("\u26A0")} Failed to fetch ${e}`);
|
|
851
964
|
}
|
|
852
965
|
const installed = config.installed ?? {};
|
|
853
966
|
const typeGroups = /* @__PURE__ */ new Map();
|
|
@@ -860,7 +973,7 @@ async function listCommand(opts) {
|
|
|
860
973
|
let installedCount = 0;
|
|
861
974
|
let updateCount = 0;
|
|
862
975
|
for (const [group, items] of typeGroups) {
|
|
863
|
-
|
|
976
|
+
p4.log.message(pc5.bold(`
|
|
864
977
|
${group.charAt(0).toUpperCase() + group.slice(1)}s:`));
|
|
865
978
|
for (const item of items) {
|
|
866
979
|
const displayName = item.namespace === "@kitn" ? item.name : `${item.namespace}/${item.name}`;
|
|
@@ -873,18 +986,18 @@ ${group.charAt(0).toUpperCase() + group.slice(1)}s:`));
|
|
|
873
986
|
const hasUpdate = item.version && inst.version !== item.version;
|
|
874
987
|
const updateTag = hasUpdate ? pc5.yellow(` \u2B06 v${item.version} available`) : "";
|
|
875
988
|
if (hasUpdate) updateCount++;
|
|
876
|
-
|
|
989
|
+
p4.log.message(` ${status} ${displayName.padEnd(20)} ${version} ${pc5.dim(item.description)}${updateTag}`);
|
|
877
990
|
} else {
|
|
878
991
|
const status = pc5.dim("\u25CB");
|
|
879
|
-
|
|
992
|
+
p4.log.message(` ${status} ${displayName.padEnd(20)} ${version} ${pc5.dim(item.description)}`);
|
|
880
993
|
}
|
|
881
994
|
}
|
|
882
995
|
}
|
|
883
996
|
const availableCount = allItems.length - installedCount;
|
|
884
997
|
const parts = [`${installedCount} installed`, `${availableCount} available`];
|
|
885
998
|
if (updateCount > 0) parts.push(`${updateCount} update${updateCount === 1 ? "" : "s"} available`);
|
|
886
|
-
|
|
887
|
-
|
|
999
|
+
p4.log.message("");
|
|
1000
|
+
p4.log.message(pc5.dim(` ${parts.join(", ")}`));
|
|
888
1001
|
}
|
|
889
1002
|
var init_list = __esm({
|
|
890
1003
|
"src/commands/list.ts"() {
|
|
@@ -899,13 +1012,13 @@ var diff_exports = {};
|
|
|
899
1012
|
__export(diff_exports, {
|
|
900
1013
|
diffCommand: () => diffCommand
|
|
901
1014
|
});
|
|
902
|
-
import * as
|
|
903
|
-
import { join as
|
|
1015
|
+
import * as p5 from "@clack/prompts";
|
|
1016
|
+
import { join as join8 } from "path";
|
|
904
1017
|
async function diffCommand(componentName) {
|
|
905
1018
|
const cwd = process.cwd();
|
|
906
1019
|
const config = await readConfig(cwd);
|
|
907
1020
|
if (!config) {
|
|
908
|
-
|
|
1021
|
+
p5.log.error("No kitn.json found. Run `kitn init` first.");
|
|
909
1022
|
process.exit(1);
|
|
910
1023
|
}
|
|
911
1024
|
const input = componentName === "routes" ? config.framework ?? "hono" : componentName;
|
|
@@ -913,7 +1026,7 @@ async function diffCommand(componentName) {
|
|
|
913
1026
|
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
914
1027
|
const installed = config.installed?.[installedKey];
|
|
915
1028
|
if (!installed) {
|
|
916
|
-
|
|
1029
|
+
p5.log.error(`Component '${ref.name}' is not installed.`);
|
|
917
1030
|
process.exit(1);
|
|
918
1031
|
}
|
|
919
1032
|
const namespace = installed.registry ?? ref.namespace;
|
|
@@ -921,7 +1034,7 @@ async function diffCommand(componentName) {
|
|
|
921
1034
|
const index = await fetcher.fetchIndex(namespace);
|
|
922
1035
|
const indexItem = index.items.find((i) => i.name === ref.name);
|
|
923
1036
|
if (!indexItem) {
|
|
924
|
-
|
|
1037
|
+
p5.log.error(`Component '${ref.name}' not found in ${namespace} registry.`);
|
|
925
1038
|
process.exit(1);
|
|
926
1039
|
}
|
|
927
1040
|
const dir = typeToDir[indexItem.type];
|
|
@@ -930,11 +1043,11 @@ async function diffCommand(componentName) {
|
|
|
930
1043
|
for (const file of registryItem.files) {
|
|
931
1044
|
if (indexItem.type === "kitn:package") {
|
|
932
1045
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
933
|
-
const localPath =
|
|
934
|
-
const relativePath =
|
|
1046
|
+
const localPath = join8(cwd, baseDir, file.path);
|
|
1047
|
+
const relativePath = join8(baseDir, file.path);
|
|
935
1048
|
const localContent = await readExistingFile(localPath);
|
|
936
1049
|
if (localContent === null) {
|
|
937
|
-
|
|
1050
|
+
p5.log.warn(`${relativePath}: file missing locally`);
|
|
938
1051
|
hasDiff = true;
|
|
939
1052
|
} else if (localContent !== file.content) {
|
|
940
1053
|
const diff = generateDiff(relativePath, localContent, file.content);
|
|
@@ -955,10 +1068,10 @@ async function diffCommand(componentName) {
|
|
|
955
1068
|
return "storage";
|
|
956
1069
|
}
|
|
957
1070
|
})();
|
|
958
|
-
const localPath =
|
|
1071
|
+
const localPath = join8(cwd, config.aliases[aliasKey], fileName);
|
|
959
1072
|
const localContent = await readExistingFile(localPath);
|
|
960
1073
|
if (localContent === null) {
|
|
961
|
-
|
|
1074
|
+
p5.log.warn(`${fileName}: file missing locally`);
|
|
962
1075
|
hasDiff = true;
|
|
963
1076
|
} else if (localContent !== file.content) {
|
|
964
1077
|
const diff = generateDiff(fileName, localContent, file.content);
|
|
@@ -968,7 +1081,7 @@ async function diffCommand(componentName) {
|
|
|
968
1081
|
}
|
|
969
1082
|
}
|
|
970
1083
|
if (!hasDiff) {
|
|
971
|
-
|
|
1084
|
+
p5.log.success(`${ref.name}: up to date, no differences.`);
|
|
972
1085
|
}
|
|
973
1086
|
}
|
|
974
1087
|
var init_diff = __esm({
|
|
@@ -987,15 +1100,15 @@ var remove_exports = {};
|
|
|
987
1100
|
__export(remove_exports, {
|
|
988
1101
|
removeCommand: () => removeCommand
|
|
989
1102
|
});
|
|
990
|
-
import * as
|
|
1103
|
+
import * as p6 from "@clack/prompts";
|
|
991
1104
|
import pc6 from "picocolors";
|
|
992
|
-
import { join as
|
|
1105
|
+
import { join as join9 } from "path";
|
|
993
1106
|
import { unlink } from "fs/promises";
|
|
994
1107
|
async function removeCommand(componentName) {
|
|
995
1108
|
const cwd = process.cwd();
|
|
996
1109
|
const config = await readConfig(cwd);
|
|
997
1110
|
if (!config) {
|
|
998
|
-
|
|
1111
|
+
p6.log.error("No kitn.json found. Run `kitn init` first.");
|
|
999
1112
|
process.exit(1);
|
|
1000
1113
|
}
|
|
1001
1114
|
const input = componentName === "routes" ? config.framework ?? "hono" : componentName;
|
|
@@ -1003,24 +1116,24 @@ async function removeCommand(componentName) {
|
|
|
1003
1116
|
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
1004
1117
|
const installed = config.installed?.[installedKey];
|
|
1005
1118
|
if (!installed) {
|
|
1006
|
-
|
|
1119
|
+
p6.log.error(`Component '${ref.name}' is not installed.`);
|
|
1007
1120
|
process.exit(1);
|
|
1008
1121
|
}
|
|
1009
|
-
const shouldRemove = await
|
|
1122
|
+
const shouldRemove = await p6.confirm({
|
|
1010
1123
|
message: `Remove ${ref.name}? This will delete ${installed.files.length} file(s).`,
|
|
1011
1124
|
initialValue: false
|
|
1012
1125
|
});
|
|
1013
|
-
if (
|
|
1014
|
-
|
|
1126
|
+
if (p6.isCancel(shouldRemove) || !shouldRemove) {
|
|
1127
|
+
p6.cancel("Remove cancelled.");
|
|
1015
1128
|
process.exit(0);
|
|
1016
1129
|
}
|
|
1017
1130
|
const deleted = [];
|
|
1018
1131
|
for (const filePath of installed.files) {
|
|
1019
1132
|
try {
|
|
1020
|
-
await unlink(
|
|
1133
|
+
await unlink(join9(cwd, filePath));
|
|
1021
1134
|
deleted.push(filePath);
|
|
1022
1135
|
} catch {
|
|
1023
|
-
|
|
1136
|
+
p6.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
1024
1137
|
}
|
|
1025
1138
|
}
|
|
1026
1139
|
delete config.installed[installedKey];
|
|
@@ -1029,8 +1142,8 @@ async function removeCommand(componentName) {
|
|
|
1029
1142
|
}
|
|
1030
1143
|
await writeConfig(cwd, config);
|
|
1031
1144
|
if (deleted.length > 0) {
|
|
1032
|
-
|
|
1033
|
-
for (const f of deleted)
|
|
1145
|
+
p6.log.success(`Removed ${ref.name}:`);
|
|
1146
|
+
for (const f of deleted) p6.log.message(` ${pc6.red("-")} ${f}`);
|
|
1034
1147
|
}
|
|
1035
1148
|
}
|
|
1036
1149
|
var init_remove = __esm({
|
|
@@ -1046,18 +1159,18 @@ var update_exports = {};
|
|
|
1046
1159
|
__export(update_exports, {
|
|
1047
1160
|
updateCommand: () => updateCommand
|
|
1048
1161
|
});
|
|
1049
|
-
import * as
|
|
1162
|
+
import * as p7 from "@clack/prompts";
|
|
1050
1163
|
async function updateCommand(components) {
|
|
1051
1164
|
if (components.length === 0) {
|
|
1052
1165
|
const cwd = process.cwd();
|
|
1053
1166
|
const config = await readConfig(cwd);
|
|
1054
1167
|
if (!config) {
|
|
1055
|
-
|
|
1168
|
+
p7.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1056
1169
|
process.exit(1);
|
|
1057
1170
|
}
|
|
1058
1171
|
const installed = config.installed;
|
|
1059
1172
|
if (!installed || Object.keys(installed).length === 0) {
|
|
1060
|
-
|
|
1173
|
+
p7.log.info("No installed components to update.");
|
|
1061
1174
|
return;
|
|
1062
1175
|
}
|
|
1063
1176
|
components = Object.keys(installed);
|
|
@@ -1072,36 +1185,593 @@ var init_update = __esm({
|
|
|
1072
1185
|
}
|
|
1073
1186
|
});
|
|
1074
1187
|
|
|
1188
|
+
// src/registry/build-output.ts
|
|
1189
|
+
import { readdir, writeFile as writeFile6, mkdir as mkdir3, access as access4 } from "fs/promises";
|
|
1190
|
+
import { join as join10, resolve } from "path";
|
|
1191
|
+
async function fileExists(path) {
|
|
1192
|
+
try {
|
|
1193
|
+
await access4(path);
|
|
1194
|
+
return true;
|
|
1195
|
+
} catch {
|
|
1196
|
+
return false;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
async function walkForRegistryJson(dir) {
|
|
1200
|
+
const results = [];
|
|
1201
|
+
let entries;
|
|
1202
|
+
try {
|
|
1203
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
1204
|
+
} catch {
|
|
1205
|
+
return results;
|
|
1206
|
+
}
|
|
1207
|
+
if (await fileExists(join10(dir, "registry.json"))) {
|
|
1208
|
+
results.push(dir);
|
|
1209
|
+
return results;
|
|
1210
|
+
}
|
|
1211
|
+
for (const entry of entries) {
|
|
1212
|
+
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
|
|
1213
|
+
const subResults = await walkForRegistryJson(join10(dir, entry.name));
|
|
1214
|
+
results.push(...subResults);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
return results;
|
|
1218
|
+
}
|
|
1219
|
+
async function scanForComponents(cwd, paths) {
|
|
1220
|
+
const resolvedCwd = resolve(cwd);
|
|
1221
|
+
if (paths && paths.length > 0) {
|
|
1222
|
+
const results = [];
|
|
1223
|
+
for (const p12 of paths) {
|
|
1224
|
+
const absPath = resolve(resolvedCwd, p12);
|
|
1225
|
+
if (await fileExists(join10(absPath, "registry.json"))) {
|
|
1226
|
+
results.push(absPath);
|
|
1227
|
+
continue;
|
|
1228
|
+
}
|
|
1229
|
+
let entries;
|
|
1230
|
+
try {
|
|
1231
|
+
entries = await readdir(absPath, { withFileTypes: true });
|
|
1232
|
+
} catch {
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
for (const entry of entries) {
|
|
1236
|
+
if (entry.isDirectory()) {
|
|
1237
|
+
const subDir = join10(absPath, entry.name);
|
|
1238
|
+
if (await fileExists(join10(subDir, "registry.json"))) {
|
|
1239
|
+
results.push(subDir);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
return results;
|
|
1245
|
+
}
|
|
1246
|
+
return walkForRegistryJson(resolvedCwd);
|
|
1247
|
+
}
|
|
1248
|
+
function parseVersionFromFilename(name, componentName) {
|
|
1249
|
+
const prefix = `${componentName}@`;
|
|
1250
|
+
const suffix = ".json";
|
|
1251
|
+
if (name.startsWith(prefix) && name.endsWith(suffix)) {
|
|
1252
|
+
return name.slice(prefix.length, -suffix.length);
|
|
1253
|
+
}
|
|
1254
|
+
return null;
|
|
1255
|
+
}
|
|
1256
|
+
async function writeRegistryOutput(outputDir, items) {
|
|
1257
|
+
const written = [];
|
|
1258
|
+
const skipped = [];
|
|
1259
|
+
const resolvedOutput = resolve(outputDir);
|
|
1260
|
+
const indexItems = [];
|
|
1261
|
+
for (const item of items) {
|
|
1262
|
+
const dir = typeToDir[item.type];
|
|
1263
|
+
const typeDir = join10(resolvedOutput, dir);
|
|
1264
|
+
await mkdir3(typeDir, { recursive: true });
|
|
1265
|
+
const itemJson = JSON.stringify(item, null, 2);
|
|
1266
|
+
const latestPath = join10(typeDir, `${item.name}.json`);
|
|
1267
|
+
const latestRelative = `${dir}/${item.name}.json`;
|
|
1268
|
+
await writeFile6(latestPath, itemJson, "utf-8");
|
|
1269
|
+
written.push(latestRelative);
|
|
1270
|
+
if (item.version) {
|
|
1271
|
+
const versionedFilename = `${item.name}@${item.version}.json`;
|
|
1272
|
+
const versionedPath = join10(typeDir, versionedFilename);
|
|
1273
|
+
const versionedRelative = `${dir}/${versionedFilename}`;
|
|
1274
|
+
if (await fileExists(versionedPath)) {
|
|
1275
|
+
skipped.push(versionedRelative);
|
|
1276
|
+
} else {
|
|
1277
|
+
await writeFile6(versionedPath, itemJson, "utf-8");
|
|
1278
|
+
written.push(versionedRelative);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
const versions = [];
|
|
1282
|
+
let entries;
|
|
1283
|
+
try {
|
|
1284
|
+
entries = await readdir(typeDir);
|
|
1285
|
+
} catch {
|
|
1286
|
+
entries = [];
|
|
1287
|
+
}
|
|
1288
|
+
for (const filename of entries) {
|
|
1289
|
+
const ver = parseVersionFromFilename(filename, item.name);
|
|
1290
|
+
if (ver) {
|
|
1291
|
+
versions.push(ver);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
versions.sort();
|
|
1295
|
+
indexItems.push({
|
|
1296
|
+
name: item.name,
|
|
1297
|
+
type: item.type,
|
|
1298
|
+
description: item.description,
|
|
1299
|
+
...item.registryDependencies && item.registryDependencies.length > 0 && {
|
|
1300
|
+
registryDependencies: item.registryDependencies
|
|
1301
|
+
},
|
|
1302
|
+
...item.categories && item.categories.length > 0 && { categories: item.categories },
|
|
1303
|
+
...item.version && { version: item.version },
|
|
1304
|
+
...versions.length > 0 && { versions },
|
|
1305
|
+
...item.updatedAt && { updatedAt: item.updatedAt }
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
const index = {
|
|
1309
|
+
version: "1",
|
|
1310
|
+
items: indexItems
|
|
1311
|
+
};
|
|
1312
|
+
const indexPath = join10(resolvedOutput, "registry.json");
|
|
1313
|
+
await writeFile6(indexPath, JSON.stringify(index, null, 2), "utf-8");
|
|
1314
|
+
written.push("registry.json");
|
|
1315
|
+
return { written, skipped };
|
|
1316
|
+
}
|
|
1317
|
+
var SKIP_DIRS;
|
|
1318
|
+
var init_build_output = __esm({
|
|
1319
|
+
"src/registry/build-output.ts"() {
|
|
1320
|
+
"use strict";
|
|
1321
|
+
init_schema();
|
|
1322
|
+
SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1323
|
+
"node_modules",
|
|
1324
|
+
"dist",
|
|
1325
|
+
".git",
|
|
1326
|
+
"r",
|
|
1327
|
+
"test",
|
|
1328
|
+
"tests",
|
|
1329
|
+
".claude"
|
|
1330
|
+
]);
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
// src/registry/builder.ts
|
|
1335
|
+
import { readFile as readFile7, readdir as readdir2 } from "fs/promises";
|
|
1336
|
+
import { join as join11, relative as relative3 } from "path";
|
|
1337
|
+
function isExcludedDevDep(name) {
|
|
1338
|
+
return EXCLUDED_DEV_DEPS.has(name) || name.startsWith("@types/");
|
|
1339
|
+
}
|
|
1340
|
+
function stripScope(name) {
|
|
1341
|
+
const match = name.match(/^@[^/]+\/(.+)$/);
|
|
1342
|
+
return match ? match[1] : name;
|
|
1343
|
+
}
|
|
1344
|
+
async function readTsFiles(dir, baseDir, exclude) {
|
|
1345
|
+
const results = [];
|
|
1346
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
1347
|
+
for (const entry of entries) {
|
|
1348
|
+
const fullPath = join11(dir, entry.name);
|
|
1349
|
+
const relPath = relative3(baseDir, fullPath);
|
|
1350
|
+
if (entry.isDirectory()) {
|
|
1351
|
+
const nested = await readTsFiles(fullPath, baseDir, exclude);
|
|
1352
|
+
results.push(...nested);
|
|
1353
|
+
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
1354
|
+
if (exclude.includes(relPath)) {
|
|
1355
|
+
continue;
|
|
1356
|
+
}
|
|
1357
|
+
const content = await readFile7(fullPath, "utf-8");
|
|
1358
|
+
results.push({ relativePath: relPath, content });
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
return results;
|
|
1362
|
+
}
|
|
1363
|
+
async function buildComponent(componentDir) {
|
|
1364
|
+
let rawConfig;
|
|
1365
|
+
try {
|
|
1366
|
+
rawConfig = await readFile7(join11(componentDir, "registry.json"), "utf-8");
|
|
1367
|
+
} catch {
|
|
1368
|
+
throw new Error(
|
|
1369
|
+
`No registry.json found in ${componentDir}. Every component must have a registry.json file.`
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
1372
|
+
let config;
|
|
1373
|
+
try {
|
|
1374
|
+
config = componentConfigSchema.parse(JSON.parse(rawConfig));
|
|
1375
|
+
} catch (err) {
|
|
1376
|
+
throw new Error(
|
|
1377
|
+
`Invalid registry.json in ${componentDir}: ${err instanceof Error ? err.message : String(err)}`
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
let pkg = null;
|
|
1381
|
+
try {
|
|
1382
|
+
const rawPkg = await readFile7(join11(componentDir, "package.json"), "utf-8");
|
|
1383
|
+
pkg = JSON.parse(rawPkg);
|
|
1384
|
+
} catch {
|
|
1385
|
+
}
|
|
1386
|
+
const name = config.name ?? (pkg?.name ? stripScope(pkg.name) : void 0);
|
|
1387
|
+
const version = config.version ?? pkg?.version;
|
|
1388
|
+
const description = config.description ?? pkg?.description;
|
|
1389
|
+
if (!name) {
|
|
1390
|
+
throw new Error(
|
|
1391
|
+
`Component in ${componentDir} is missing a name. Provide "name" in registry.json or have a package.json with a "name" field.`
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
if (!description) {
|
|
1395
|
+
throw new Error(
|
|
1396
|
+
`Component in ${componentDir} is missing a description. Provide "description" in registry.json or have a package.json with a "description" field.`
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
let dependencies = config.dependencies;
|
|
1400
|
+
let devDependencies = config.devDependencies;
|
|
1401
|
+
if (pkg && !config.dependencies) {
|
|
1402
|
+
const deps = [];
|
|
1403
|
+
if (pkg.dependencies) {
|
|
1404
|
+
for (const [depName, depVersion] of Object.entries(pkg.dependencies)) {
|
|
1405
|
+
if (depVersion !== "workspace:*") {
|
|
1406
|
+
deps.push(depName);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
if (pkg.peerDependencies) {
|
|
1411
|
+
for (const [depName, depVersion] of Object.entries(pkg.peerDependencies)) {
|
|
1412
|
+
if (depVersion !== "workspace:*") {
|
|
1413
|
+
deps.push(depName);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
if (deps.length > 0) {
|
|
1418
|
+
dependencies = deps;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
if (pkg && !config.devDependencies) {
|
|
1422
|
+
const devDeps = [];
|
|
1423
|
+
if (pkg.devDependencies) {
|
|
1424
|
+
for (const depName of Object.keys(pkg.devDependencies)) {
|
|
1425
|
+
if (!isExcludedDevDep(depName)) {
|
|
1426
|
+
devDeps.push(depName);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
if (devDeps.length > 0) {
|
|
1431
|
+
devDependencies = devDeps;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
const isPackage = config.type === "kitn:package";
|
|
1435
|
+
const dirPrefix = config.installDir ?? typeToDir[config.type];
|
|
1436
|
+
let files;
|
|
1437
|
+
if (isPackage) {
|
|
1438
|
+
const sourceDir = config.sourceDir ?? "src";
|
|
1439
|
+
const sourcePath = join11(componentDir, sourceDir);
|
|
1440
|
+
const exclude = config.exclude ?? [];
|
|
1441
|
+
let tsFiles;
|
|
1442
|
+
try {
|
|
1443
|
+
tsFiles = await readTsFiles(sourcePath, sourcePath, exclude);
|
|
1444
|
+
} catch {
|
|
1445
|
+
throw new Error(
|
|
1446
|
+
`Cannot read source directory "${sourceDir}" in ${componentDir}. Make sure it exists.`
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
files = tsFiles.map((f) => ({
|
|
1450
|
+
path: `${dirPrefix}/${f.relativePath}`,
|
|
1451
|
+
content: f.content,
|
|
1452
|
+
type: config.type
|
|
1453
|
+
}));
|
|
1454
|
+
} else {
|
|
1455
|
+
if (!config.files || config.files.length === 0) {
|
|
1456
|
+
throw new Error(
|
|
1457
|
+
`Component "${name}" (type: ${config.type}) has no "files" array in registry.json. Standalone components must list their source files.`
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
files = await Promise.all(
|
|
1461
|
+
config.files.map(async (filePath) => {
|
|
1462
|
+
const fullPath = join11(componentDir, filePath);
|
|
1463
|
+
let content;
|
|
1464
|
+
try {
|
|
1465
|
+
content = await readFile7(fullPath, "utf-8");
|
|
1466
|
+
} catch {
|
|
1467
|
+
throw new Error(
|
|
1468
|
+
`Cannot read file "${filePath}" referenced in registry.json for component "${name}". Make sure the file exists at ${fullPath}.`
|
|
1469
|
+
);
|
|
1470
|
+
}
|
|
1471
|
+
return {
|
|
1472
|
+
path: `${dirPrefix}/${filePath}`,
|
|
1473
|
+
content,
|
|
1474
|
+
type: config.type
|
|
1475
|
+
};
|
|
1476
|
+
})
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
const item = {
|
|
1480
|
+
name,
|
|
1481
|
+
type: config.type,
|
|
1482
|
+
description,
|
|
1483
|
+
files
|
|
1484
|
+
};
|
|
1485
|
+
if (version) item.version = version;
|
|
1486
|
+
if (dependencies && dependencies.length > 0) item.dependencies = dependencies;
|
|
1487
|
+
if (devDependencies && devDependencies.length > 0) item.devDependencies = devDependencies;
|
|
1488
|
+
if (config.registryDependencies && config.registryDependencies.length > 0) {
|
|
1489
|
+
item.registryDependencies = config.registryDependencies;
|
|
1490
|
+
}
|
|
1491
|
+
if (config.envVars) item.envVars = config.envVars;
|
|
1492
|
+
if (config.tsconfig) item.tsconfig = config.tsconfig;
|
|
1493
|
+
if (config.docs) item.docs = config.docs;
|
|
1494
|
+
if (config.categories && config.categories.length > 0) item.categories = config.categories;
|
|
1495
|
+
if (config.changelog && config.changelog.length > 0) item.changelog = config.changelog;
|
|
1496
|
+
if (isPackage && config.installDir) item.installDir = config.installDir;
|
|
1497
|
+
try {
|
|
1498
|
+
return registryItemSchema.parse(item);
|
|
1499
|
+
} catch (err) {
|
|
1500
|
+
throw new Error(
|
|
1501
|
+
`Built component "${name}" failed validation: ${err instanceof Error ? err.message : String(err)}`
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
var EXCLUDED_DEV_DEPS;
|
|
1506
|
+
var init_builder = __esm({
|
|
1507
|
+
"src/registry/builder.ts"() {
|
|
1508
|
+
"use strict";
|
|
1509
|
+
init_schema();
|
|
1510
|
+
EXCLUDED_DEV_DEPS = /* @__PURE__ */ new Set([
|
|
1511
|
+
"typescript",
|
|
1512
|
+
"@types/bun",
|
|
1513
|
+
"@types/node",
|
|
1514
|
+
"tsup",
|
|
1515
|
+
"vitest",
|
|
1516
|
+
"jest",
|
|
1517
|
+
"@types/jest"
|
|
1518
|
+
]);
|
|
1519
|
+
}
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
// src/commands/build.ts
|
|
1523
|
+
var build_exports = {};
|
|
1524
|
+
__export(build_exports, {
|
|
1525
|
+
buildCommand: () => buildCommand
|
|
1526
|
+
});
|
|
1527
|
+
import * as p8 from "@clack/prompts";
|
|
1528
|
+
import pc7 from "picocolors";
|
|
1529
|
+
import { resolve as resolve2, relative as relative4 } from "path";
|
|
1530
|
+
async function buildCommand(paths, opts) {
|
|
1531
|
+
p8.intro(pc7.bgCyan(pc7.black(" kitn build ")));
|
|
1532
|
+
const cwd = process.cwd();
|
|
1533
|
+
const outputDir = resolve2(cwd, opts.output ?? "dist/r");
|
|
1534
|
+
const s = p8.spinner();
|
|
1535
|
+
s.start("Scanning for components...");
|
|
1536
|
+
const componentDirs = await scanForComponents(cwd, paths.length > 0 ? paths : void 0);
|
|
1537
|
+
if (componentDirs.length === 0) {
|
|
1538
|
+
s.stop("No components found");
|
|
1539
|
+
p8.log.info(
|
|
1540
|
+
`No directories with ${pc7.bold("registry.json")} found. Run ${pc7.bold("kitn create")} to scaffold a component.`
|
|
1541
|
+
);
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
s.stop(`Found ${componentDirs.length} component(s)`);
|
|
1545
|
+
for (const dir of componentDirs) {
|
|
1546
|
+
p8.log.message(` ${pc7.dim(relative4(cwd, dir))}`);
|
|
1547
|
+
}
|
|
1548
|
+
s.start("Building components...");
|
|
1549
|
+
const items = [];
|
|
1550
|
+
const errors = [];
|
|
1551
|
+
for (const dir of componentDirs) {
|
|
1552
|
+
try {
|
|
1553
|
+
const item = await buildComponent(dir);
|
|
1554
|
+
items.push(item);
|
|
1555
|
+
} catch (err) {
|
|
1556
|
+
errors.push({ dir: relative4(cwd, dir), error: err.message });
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
if (errors.length > 0) {
|
|
1560
|
+
s.stop(pc7.red(`Build failed with ${errors.length} error(s)`));
|
|
1561
|
+
for (const { dir, error } of errors) {
|
|
1562
|
+
p8.log.error(`${pc7.bold(dir)}: ${error}`);
|
|
1563
|
+
}
|
|
1564
|
+
process.exit(1);
|
|
1565
|
+
}
|
|
1566
|
+
const { written, skipped } = await writeRegistryOutput(outputDir, items);
|
|
1567
|
+
s.stop(pc7.green(`Built ${items.length} component(s)`));
|
|
1568
|
+
if (written.length > 0) {
|
|
1569
|
+
p8.log.success(`Wrote ${written.length} file(s):`);
|
|
1570
|
+
for (const f of written) {
|
|
1571
|
+
p8.log.message(` ${pc7.green("+")} ${f}`);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
if (skipped.length > 0) {
|
|
1575
|
+
p8.log.info(`Skipped ${skipped.length} file(s) (already exist):`);
|
|
1576
|
+
for (const f of skipped) {
|
|
1577
|
+
p8.log.message(` ${pc7.dim("-")} ${f}`);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
p8.outro(`Output: ${pc7.cyan(relative4(cwd, outputDir) || ".")}`);
|
|
1581
|
+
}
|
|
1582
|
+
var init_build = __esm({
|
|
1583
|
+
"src/commands/build.ts"() {
|
|
1584
|
+
"use strict";
|
|
1585
|
+
init_build_output();
|
|
1586
|
+
init_builder();
|
|
1587
|
+
}
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
// src/commands/create.ts
|
|
1591
|
+
var create_exports = {};
|
|
1592
|
+
__export(create_exports, {
|
|
1593
|
+
createCommand: () => createCommand,
|
|
1594
|
+
createComponent: () => createComponent
|
|
1595
|
+
});
|
|
1596
|
+
import * as p9 from "@clack/prompts";
|
|
1597
|
+
import pc8 from "picocolors";
|
|
1598
|
+
import { join as join12 } from "path";
|
|
1599
|
+
import { mkdir as mkdir4, writeFile as writeFile7 } from "fs/promises";
|
|
1600
|
+
function toCamelCase(str) {
|
|
1601
|
+
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1602
|
+
}
|
|
1603
|
+
function toTitleCase(str) {
|
|
1604
|
+
return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
1605
|
+
}
|
|
1606
|
+
function generateRegistryJson(type, name, sourceFile) {
|
|
1607
|
+
const base = {
|
|
1608
|
+
$schema: "https://kitn.dev/schema/registry.json",
|
|
1609
|
+
name,
|
|
1610
|
+
type: `kitn:${type}`,
|
|
1611
|
+
version: "0.1.0",
|
|
1612
|
+
description: "",
|
|
1613
|
+
files: [sourceFile],
|
|
1614
|
+
categories: []
|
|
1615
|
+
};
|
|
1616
|
+
if (type === "tool") {
|
|
1617
|
+
base.dependencies = ["ai", "zod"];
|
|
1618
|
+
} else if (type === "agent" || type === "storage") {
|
|
1619
|
+
base.dependencies = [];
|
|
1620
|
+
}
|
|
1621
|
+
return base;
|
|
1622
|
+
}
|
|
1623
|
+
function generateAgentSource(name) {
|
|
1624
|
+
const camel = toCamelCase(name);
|
|
1625
|
+
return `import type { AgentConfig } from "@kitnai/core";
|
|
1626
|
+
|
|
1627
|
+
export const ${camel}Config: AgentConfig = {
|
|
1628
|
+
name: "${name}",
|
|
1629
|
+
description: "",
|
|
1630
|
+
system: "You are a helpful assistant.",
|
|
1631
|
+
tools: [],
|
|
1632
|
+
};
|
|
1633
|
+
`;
|
|
1634
|
+
}
|
|
1635
|
+
function generateToolSource(name) {
|
|
1636
|
+
const camel = toCamelCase(name);
|
|
1637
|
+
return `import { tool } from "ai";
|
|
1638
|
+
import { z } from "zod";
|
|
1639
|
+
|
|
1640
|
+
export const ${camel} = tool({
|
|
1641
|
+
description: "",
|
|
1642
|
+
inputSchema: z.object({
|
|
1643
|
+
input: z.string().describe("Input parameter"),
|
|
1644
|
+
}),
|
|
1645
|
+
execute: async ({ input }) => {
|
|
1646
|
+
// TODO: implement
|
|
1647
|
+
return { result: input };
|
|
1648
|
+
},
|
|
1649
|
+
});
|
|
1650
|
+
`;
|
|
1651
|
+
}
|
|
1652
|
+
function generateSkillSource(name) {
|
|
1653
|
+
const title = toTitleCase(name);
|
|
1654
|
+
return `---
|
|
1655
|
+
name: ${name}
|
|
1656
|
+
description: ""
|
|
1657
|
+
---
|
|
1658
|
+
|
|
1659
|
+
# ${title}
|
|
1660
|
+
|
|
1661
|
+
Describe what this skill does and how to use it.
|
|
1662
|
+
`;
|
|
1663
|
+
}
|
|
1664
|
+
function generateStorageSource(name) {
|
|
1665
|
+
const camel = toCamelCase("create-" + name);
|
|
1666
|
+
return `import type { StorageProvider } from "@kitnai/core";
|
|
1667
|
+
|
|
1668
|
+
export function ${camel}(config?: Record<string, unknown>): StorageProvider {
|
|
1669
|
+
// TODO: implement storage provider
|
|
1670
|
+
throw new Error("Not implemented");
|
|
1671
|
+
}
|
|
1672
|
+
`;
|
|
1673
|
+
}
|
|
1674
|
+
async function dirExists(path) {
|
|
1675
|
+
try {
|
|
1676
|
+
const { stat: stat2 } = await import("fs/promises");
|
|
1677
|
+
const s = await stat2(path);
|
|
1678
|
+
return s.isDirectory();
|
|
1679
|
+
} catch {
|
|
1680
|
+
return false;
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
async function createComponent(type, name, opts) {
|
|
1684
|
+
if (!VALID_TYPES.includes(type)) {
|
|
1685
|
+
throw new Error(
|
|
1686
|
+
`Invalid component type: "${type}". Valid types: ${VALID_TYPES.join(", ")}`
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
1690
|
+
const dir = join12(cwd, name);
|
|
1691
|
+
if (await dirExists(dir)) {
|
|
1692
|
+
throw new Error(`Directory "${name}" already exists`);
|
|
1693
|
+
}
|
|
1694
|
+
await mkdir4(dir, { recursive: true });
|
|
1695
|
+
const validType = type;
|
|
1696
|
+
const sourceFile = validType === "skill" ? "README.md" : `${name}.ts`;
|
|
1697
|
+
const registryJson = generateRegistryJson(validType, name, sourceFile);
|
|
1698
|
+
await writeFile7(
|
|
1699
|
+
join12(dir, "registry.json"),
|
|
1700
|
+
JSON.stringify(registryJson, null, 2) + "\n"
|
|
1701
|
+
);
|
|
1702
|
+
let source;
|
|
1703
|
+
switch (validType) {
|
|
1704
|
+
case "agent":
|
|
1705
|
+
source = generateAgentSource(name);
|
|
1706
|
+
break;
|
|
1707
|
+
case "tool":
|
|
1708
|
+
source = generateToolSource(name);
|
|
1709
|
+
break;
|
|
1710
|
+
case "skill":
|
|
1711
|
+
source = generateSkillSource(name);
|
|
1712
|
+
break;
|
|
1713
|
+
case "storage":
|
|
1714
|
+
source = generateStorageSource(name);
|
|
1715
|
+
break;
|
|
1716
|
+
}
|
|
1717
|
+
await writeFile7(join12(dir, sourceFile), source);
|
|
1718
|
+
return { dir, files: ["registry.json", sourceFile] };
|
|
1719
|
+
}
|
|
1720
|
+
async function createCommand(type, name) {
|
|
1721
|
+
p9.intro(pc8.bgCyan(pc8.black(" kitn create ")));
|
|
1722
|
+
try {
|
|
1723
|
+
const { dir, files } = await createComponent(type, name);
|
|
1724
|
+
p9.log.success(`Created ${pc8.bold(type)} component ${pc8.cyan(name)}`);
|
|
1725
|
+
for (const file of files) {
|
|
1726
|
+
p9.log.message(` ${pc8.green("+")} ${file}`);
|
|
1727
|
+
}
|
|
1728
|
+
const editFile = files.find((f) => f !== "registry.json") ?? files[0];
|
|
1729
|
+
p9.outro(
|
|
1730
|
+
`Edit ${pc8.cyan(`${name}/${editFile}`)}, then run ${pc8.bold("kitn build")}`
|
|
1731
|
+
);
|
|
1732
|
+
} catch (err) {
|
|
1733
|
+
p9.log.error(err.message);
|
|
1734
|
+
process.exit(1);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
var VALID_TYPES;
|
|
1738
|
+
var init_create = __esm({
|
|
1739
|
+
"src/commands/create.ts"() {
|
|
1740
|
+
"use strict";
|
|
1741
|
+
VALID_TYPES = ["agent", "tool", "skill", "storage"];
|
|
1742
|
+
}
|
|
1743
|
+
});
|
|
1744
|
+
|
|
1075
1745
|
// src/commands/info.ts
|
|
1076
1746
|
var info_exports = {};
|
|
1077
1747
|
__export(info_exports, {
|
|
1078
1748
|
infoCommand: () => infoCommand
|
|
1079
1749
|
});
|
|
1080
|
-
import * as
|
|
1081
|
-
import
|
|
1750
|
+
import * as p10 from "@clack/prompts";
|
|
1751
|
+
import pc9 from "picocolors";
|
|
1082
1752
|
async function infoCommand(component) {
|
|
1083
1753
|
const cwd = process.cwd();
|
|
1084
1754
|
const config = await readConfig(cwd);
|
|
1085
1755
|
if (!config) {
|
|
1086
|
-
|
|
1756
|
+
p10.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1087
1757
|
process.exit(1);
|
|
1088
1758
|
}
|
|
1089
1759
|
const ref = parseComponentRef(component);
|
|
1090
1760
|
const fetcher = new RegistryFetcher(config.registries);
|
|
1091
|
-
const s =
|
|
1761
|
+
const s = p10.spinner();
|
|
1092
1762
|
s.start("Fetching component info...");
|
|
1093
1763
|
let index;
|
|
1094
1764
|
try {
|
|
1095
1765
|
index = await fetcher.fetchIndex(ref.namespace);
|
|
1096
1766
|
} catch (err) {
|
|
1097
|
-
s.stop(
|
|
1098
|
-
|
|
1767
|
+
s.stop(pc9.red("Failed to fetch registry"));
|
|
1768
|
+
p10.log.error(err.message);
|
|
1099
1769
|
process.exit(1);
|
|
1100
1770
|
}
|
|
1101
1771
|
const indexItem = index.items.find((i) => i.name === ref.name);
|
|
1102
1772
|
if (!indexItem) {
|
|
1103
|
-
s.stop(
|
|
1104
|
-
|
|
1773
|
+
s.stop(pc9.red("Component not found"));
|
|
1774
|
+
p10.log.error(`Component '${ref.name}' not found in registry.`);
|
|
1105
1775
|
process.exit(1);
|
|
1106
1776
|
}
|
|
1107
1777
|
const dir = typeToDir[indexItem.type];
|
|
@@ -1109,8 +1779,8 @@ async function infoCommand(component) {
|
|
|
1109
1779
|
try {
|
|
1110
1780
|
item = await fetcher.fetchItem(ref.name, dir, ref.namespace, ref.version);
|
|
1111
1781
|
} catch (err) {
|
|
1112
|
-
s.stop(
|
|
1113
|
-
|
|
1782
|
+
s.stop(pc9.red("Failed to fetch component"));
|
|
1783
|
+
p10.log.error(err.message);
|
|
1114
1784
|
process.exit(1);
|
|
1115
1785
|
}
|
|
1116
1786
|
s.stop("Component found");
|
|
@@ -1118,62 +1788,62 @@ async function infoCommand(component) {
|
|
|
1118
1788
|
const typeName = indexItem.type.replace("kitn:", "");
|
|
1119
1789
|
console.log();
|
|
1120
1790
|
console.log(
|
|
1121
|
-
` ${
|
|
1791
|
+
` ${pc9.bold(item.name)} ${pc9.cyan(`v${version}`)}${" ".repeat(Math.max(1, 40 - item.name.length - version.length - 2))}${pc9.dim(ref.namespace)}`
|
|
1122
1792
|
);
|
|
1123
|
-
console.log(` ${
|
|
1793
|
+
console.log(` ${pc9.dim(item.description)}`);
|
|
1124
1794
|
console.log();
|
|
1125
|
-
console.log(` ${
|
|
1795
|
+
console.log(` ${pc9.dim("Type:")} ${typeName}`);
|
|
1126
1796
|
if (item.dependencies?.length) {
|
|
1127
1797
|
console.log(
|
|
1128
|
-
` ${
|
|
1798
|
+
` ${pc9.dim("Dependencies:")} ${item.dependencies.join(", ")}`
|
|
1129
1799
|
);
|
|
1130
1800
|
}
|
|
1131
1801
|
if (item.registryDependencies?.length) {
|
|
1132
1802
|
console.log(
|
|
1133
|
-
` ${
|
|
1803
|
+
` ${pc9.dim("Registry deps:")} ${item.registryDependencies.join(", ")}`
|
|
1134
1804
|
);
|
|
1135
1805
|
}
|
|
1136
1806
|
if (item.categories?.length) {
|
|
1137
1807
|
console.log(
|
|
1138
|
-
` ${
|
|
1808
|
+
` ${pc9.dim("Categories:")} ${item.categories.join(", ")}`
|
|
1139
1809
|
);
|
|
1140
1810
|
}
|
|
1141
1811
|
if (item.updatedAt) {
|
|
1142
|
-
console.log(` ${
|
|
1812
|
+
console.log(` ${pc9.dim("Updated:")} ${item.updatedAt}`);
|
|
1143
1813
|
}
|
|
1144
1814
|
const versions = indexItem.versions;
|
|
1145
1815
|
if (versions?.length) {
|
|
1146
|
-
console.log(` ${
|
|
1816
|
+
console.log(` ${pc9.dim("Versions:")} ${versions.join(", ")}`);
|
|
1147
1817
|
}
|
|
1148
1818
|
if (item.changelog?.length) {
|
|
1149
1819
|
console.log();
|
|
1150
|
-
console.log(` ${
|
|
1820
|
+
console.log(` ${pc9.bold("Changelog:")}`);
|
|
1151
1821
|
for (const entry of item.changelog) {
|
|
1152
|
-
const tag = entry.type === "feature" ?
|
|
1822
|
+
const tag = entry.type === "feature" ? pc9.green(entry.type) : entry.type === "fix" ? pc9.yellow(entry.type) : entry.type === "breaking" ? pc9.red(entry.type) : pc9.dim(entry.type);
|
|
1153
1823
|
console.log(
|
|
1154
|
-
` ${
|
|
1824
|
+
` ${pc9.cyan(entry.version)} ${pc9.dim(entry.date)} ${tag} ${entry.note}`
|
|
1155
1825
|
);
|
|
1156
1826
|
}
|
|
1157
1827
|
}
|
|
1158
1828
|
console.log();
|
|
1159
1829
|
const fileCount = item.files.length;
|
|
1160
|
-
console.log(` ${
|
|
1830
|
+
console.log(` ${pc9.bold(`Files:`)} ${pc9.dim(`(${fileCount})`)}`);
|
|
1161
1831
|
const maxShown = 10;
|
|
1162
1832
|
for (const file of item.files.slice(0, maxShown)) {
|
|
1163
|
-
console.log(` ${
|
|
1833
|
+
console.log(` ${pc9.dim(file.path)}`);
|
|
1164
1834
|
}
|
|
1165
1835
|
if (fileCount > maxShown) {
|
|
1166
|
-
console.log(` ${
|
|
1836
|
+
console.log(` ${pc9.dim(`... and ${fileCount - maxShown} more`)}`);
|
|
1167
1837
|
}
|
|
1168
1838
|
const installed = config.installed?.[item.name];
|
|
1169
1839
|
if (installed) {
|
|
1170
1840
|
console.log();
|
|
1171
1841
|
console.log(
|
|
1172
|
-
` ${
|
|
1842
|
+
` ${pc9.green("Installed")} ${pc9.dim(`v${installed.version}`)}`
|
|
1173
1843
|
);
|
|
1174
1844
|
if (version !== installed.version) {
|
|
1175
1845
|
console.log(
|
|
1176
|
-
` ${
|
|
1846
|
+
` ${pc9.yellow("Update available:")} ${pc9.dim(`v${installed.version}`)} \u2192 ${pc9.cyan(`v${version}`)}`
|
|
1177
1847
|
);
|
|
1178
1848
|
}
|
|
1179
1849
|
}
|
|
@@ -1196,8 +1866,8 @@ __export(registry_exports, {
|
|
|
1196
1866
|
registryListCommand: () => registryListCommand,
|
|
1197
1867
|
registryRemoveCommand: () => registryRemoveCommand
|
|
1198
1868
|
});
|
|
1199
|
-
import * as
|
|
1200
|
-
import
|
|
1869
|
+
import * as p11 from "@clack/prompts";
|
|
1870
|
+
import pc10 from "picocolors";
|
|
1201
1871
|
async function registryAddCommand(namespace, url, opts = {}) {
|
|
1202
1872
|
const cwd = opts.cwd ?? process.cwd();
|
|
1203
1873
|
const config = await readConfig(cwd);
|
|
@@ -1216,8 +1886,8 @@ async function registryAddCommand(namespace, url, opts = {}) {
|
|
|
1216
1886
|
}
|
|
1217
1887
|
config.registries[namespace] = url;
|
|
1218
1888
|
await writeConfig(cwd, config);
|
|
1219
|
-
|
|
1220
|
-
|
|
1889
|
+
p11.log.success(`Added registry ${pc10.bold(namespace)}`);
|
|
1890
|
+
p11.log.message(pc10.dim(` ${url}`));
|
|
1221
1891
|
}
|
|
1222
1892
|
async function registryRemoveCommand(namespace, opts = {}) {
|
|
1223
1893
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -1239,11 +1909,11 @@ async function registryRemoveCommand(namespace, opts = {}) {
|
|
|
1239
1909
|
}
|
|
1240
1910
|
delete config.registries[namespace];
|
|
1241
1911
|
await writeConfig(cwd, config);
|
|
1242
|
-
|
|
1912
|
+
p11.log.success(`Removed registry ${pc10.bold(namespace)}`);
|
|
1243
1913
|
if (affectedComponents.length > 0) {
|
|
1244
|
-
|
|
1914
|
+
p11.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:`);
|
|
1245
1915
|
for (const name of affectedComponents) {
|
|
1246
|
-
|
|
1916
|
+
p11.log.message(` ${pc10.yellow("!")} ${name}`);
|
|
1247
1917
|
}
|
|
1248
1918
|
}
|
|
1249
1919
|
return { affectedComponents };
|
|
@@ -1254,10 +1924,10 @@ async function registryListCommand(opts = {}) {
|
|
|
1254
1924
|
if (!config) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
1255
1925
|
const entries = Object.entries(config.registries).map(([namespace, url]) => ({ namespace, url }));
|
|
1256
1926
|
if (entries.length === 0) {
|
|
1257
|
-
|
|
1927
|
+
p11.log.message(pc10.dim(" No registries configured."));
|
|
1258
1928
|
} else {
|
|
1259
1929
|
for (const { namespace, url } of entries) {
|
|
1260
|
-
|
|
1930
|
+
p11.log.message(` ${pc10.bold(namespace.padEnd(16))} ${pc10.dim(url)}`);
|
|
1261
1931
|
}
|
|
1262
1932
|
}
|
|
1263
1933
|
return entries;
|
|
@@ -1347,7 +2017,7 @@ function startUpdateCheck(currentVersion) {
|
|
|
1347
2017
|
}
|
|
1348
2018
|
|
|
1349
2019
|
// src/index.ts
|
|
1350
|
-
var VERSION = true ? "0.1.
|
|
2020
|
+
var VERSION = true ? "0.1.8" : "0.0.0-dev";
|
|
1351
2021
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
1352
2022
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
1353
2023
|
program.command("init").description("Initialize kitn in your project").action(async () => {
|
|
@@ -1374,6 +2044,14 @@ program.command("update").description("Update installed components to latest reg
|
|
|
1374
2044
|
const { updateCommand: updateCommand2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
1375
2045
|
await updateCommand2(components);
|
|
1376
2046
|
});
|
|
2047
|
+
program.command("build").description("Build registry JSON from components with registry.json files").argument("[paths...]", "directories to build (default: scan from cwd)").option("-o, --output <dir>", "output directory", "dist/r").action(async (paths, opts) => {
|
|
2048
|
+
const { buildCommand: buildCommand2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
2049
|
+
await buildCommand2(paths, opts);
|
|
2050
|
+
});
|
|
2051
|
+
program.command("create").description("Scaffold a new kitn component").argument("<type>", "component type (agent, tool, skill, storage)").argument("<name>", "component name").action(async (type, name) => {
|
|
2052
|
+
const { createCommand: createCommand2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
2053
|
+
await createCommand2(type, name);
|
|
2054
|
+
});
|
|
1377
2055
|
program.command("info").description("Show details about a component").argument("<component>", "component name (e.g. weather-agent, @acme/tool@1.0.0)").action(async (component) => {
|
|
1378
2056
|
const { infoCommand: infoCommand2 } = await Promise.resolve().then(() => (init_info(), info_exports));
|
|
1379
2057
|
await infoCommand2(component);
|