@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/dist/index.js CHANGED
@@ -55,98 +55,90 @@ var init_config = __esm({
55
55
  }
56
56
  });
57
57
 
58
- // src/commands/init.ts
59
- var init_exports = {};
60
- __export(init_exports, {
61
- initCommand: () => initCommand
62
- });
63
- import * as p from "@clack/prompts";
64
- import pc2 from "picocolors";
65
- async function initCommand() {
66
- p.intro(pc2.bgCyan(pc2.black(" kitn ")));
67
- const cwd = process.cwd();
68
- const existing = await readConfig(cwd);
69
- if (existing) {
70
- p.log.warn("kitn.json already exists in this directory.");
71
- const shouldContinue = await p.confirm({
72
- message: "Overwrite existing configuration?",
73
- initialValue: false
74
- });
75
- if (p.isCancel(shouldContinue) || !shouldContinue) {
76
- p.cancel("Init cancelled.");
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 runtime = await p.select({
81
- message: "Which runtime do you use?",
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
- const framework = await p.select({
93
- message: "Which framework are you using?",
94
- options: [
95
- { value: "hono", label: "Hono", hint: "recommended" },
96
- { value: "cloudflare", label: "Cloudflare Workers", hint: "coming soon" },
97
- { value: "elysia", label: "Elysia", hint: "coming soon" },
98
- { value: "fastify", label: "Fastify", hint: "coming soon" },
99
- { value: "express", label: "Express", hint: "coming soon" }
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 base = await p.text({
107
- message: "Where should kitn packages be installed?",
108
- initialValue: "src/ai",
109
- placeholder: "src/ai"
110
- });
111
- if (p.isCancel(base)) {
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
- const baseDir = base;
116
- const config = {
117
- runtime,
118
- framework,
119
- aliases: {
120
- base: baseDir,
121
- agents: `${baseDir}/agents`,
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
- var init_init = __esm({
137
- "src/commands/init.ts"() {
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
- init_config();
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 join3 } from "path";
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(join3(dir, lockfile));
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 readFile3, writeFile as writeFile3, mkdir as mkdir2, access as access2 } from "fs/promises";
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 readFile3(filePath, "utf-8");
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 readFile3(filePath, "utf-8");
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 writeFile3(filePath, content);
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 p2 from "@clack/prompts";
331
- import pc3 from "picocolors";
332
- import { readFile as readFile4, writeFile as writeFile4, access as access3 } from "fs/promises";
333
- import { join as join4 } from "path";
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 readFile4(path, "utf-8");
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 = join4(cwd, ".env");
366
- const examplePath = join4(cwd, ".env.example");
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 writeFile4(examplePath, exampleContent + lines.join("\n") + "\n");
382
- p2.log.info(`Updated ${pc3.cyan(".env.example")} with ${missingFromExample.length} variable(s)`);
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
- p2.log.message("");
386
- p2.log.warn(
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 ? pc3.red("*") : "";
392
- p2.log.message(` ${pc3.yellow(key)}${req}: ${config.description}${config.url ? pc3.dim(` -> ${config.url}`) : ""}`);
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 p2.confirm({
386
+ const shouldPrompt = await p.confirm({
395
387
  message: "Would you like to enter values now?",
396
388
  initialValue: true
397
389
  });
398
- if (p2.isCancel(shouldPrompt) || !shouldPrompt) {
399
- p2.log.info(`Add them to ${pc3.cyan(".env")} when ready.`);
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 p2.password({
400
+ value = await p.password({
409
401
  message: `${key}:`
410
402
  });
411
403
  } else {
412
- value = await p2.text({
404
+ value = await p.text({
413
405
  message: `${key}:`,
414
406
  placeholder: config.description
415
407
  });
416
408
  }
417
- if (p2.isCancel(value)) {
418
- p2.log.info(`Skipped remaining variables. Add them to ${pc3.cyan(".env")} when ready.`);
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 writeFile4(envPath, existingEnv + lines.join("\n") + "\n");
431
- p2.log.success(`Wrote ${newEntries.length} variable(s) to ${pc3.cyan(".env")}`);
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 join5 } from "path";
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 = join5(targetDir, targetPath);
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 p3 from "@clack/prompts";
678
- import pc4 from "picocolors";
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
- p3.intro(pc4.bgCyan(pc4.black(" kitn add ")));
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
- p3.log.error("No kitn.json found. Run `kitn init` first.");
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
- p3.log.error("Please specify at least one component to add.");
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 = p3.spinner();
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(pc4.red("Failed to resolve dependencies"));
718
- p3.log.error(err.message);
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
- p3.log.info("Components to install:");
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} ${pc4.dim("(dependency)")}`;
726
- p3.log.message(` ${pc4.cyan(label)}`);
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
- p3.log.message(pc4.dim(diff));
756
- const action = await p3.select({
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 (!p3.isCancel(action) && action === "overwrite") {
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
- p3.log.message(pc4.dim(diff));
829
- const action = await p3.select({
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 (!p3.isCancel(action) && action === "overwrite") {
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
- p3.log.info(`Updated barrel file: ${join7(baseDir, "index.ts")}`);
828
+ p2.log.info(`Updated barrel file: ${join7(baseDir, "index.ts")}`);
916
829
  if (!barrelExisted) {
917
- p3.note(
830
+ p2.note(
918
831
  [
919
- `import { createAIPlugin } from "@kitnai/hono";`,
920
- `import { registerWithPlugin } from "./ai";`,
832
+ `import { ai } from "./${baseDir}/plugin.js";`,
921
833
  ``,
922
- `const plugin = createAIPlugin({`,
923
- ` model: (model) => yourProvider(model ?? "default-model"),`,
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 app setup"
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(pc4.yellow("Some dependencies failed to install"));
851
+ s.stop(pc3.yellow("Some dependencies failed to install"));
944
852
  }
945
853
  }
946
854
  }
947
855
  if (created.length > 0) {
948
- p3.log.success(`Created ${created.length} file(s):`);
949
- for (const f of created) p3.log.message(` ${pc4.green("+")} ${f}`);
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
- p3.log.success(`Updated ${updated.length} file(s):`);
953
- for (const f of updated) p3.log.message(` ${pc4.yellow("~")} ${f}`);
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
- p3.log.info(`Skipped ${skipped.length} file(s):`);
957
- for (const f of skipped) p3.log.message(` ${pc4.dim("-")} ${f}`);
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
- p3.log.info(`${pc4.bold(item.name)}: ${item.docs}`);
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 ${pc4.cyan(`kitn add routes`)} to install the HTTP adapter.`);
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(`Add this to your server entry point:`);
974
- if (fw === "hono") {
975
- hints.push("");
976
- hints.push(pc4.dim(` import { Hono } from "hono";`));
977
- hints.push(pc4.dim(` import { createAIPlugin } from "@kitnai/hono";`));
978
- hints.push(pc4.dim(` import { yourProvider } from "your-ai-provider";`));
979
- hints.push(pc4.dim(``));
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
- p3.log.message(pc4.bold("\nNext steps:"));
992
- for (const hint of hints) p3.log.message(hint);
890
+ p2.log.message(pc3.bold("\nNext steps:"));
891
+ for (const hint of hints) p2.log.message(hint);
993
892
  }
994
- p3.outro(pc4.green("Done!"));
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 index...");
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 across ${namespacesToFetch.length - errors.length} ${namespacesToFetch.length - errors.length === 1 ? "registry" : "registries"}`);
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
- p4.log.message(pc5.bold(`
1070
- ${group.charAt(0).toUpperCase() + group.slice(1)}s:`));
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 version = pc5.dim(`v${item.version ?? "1.0.0"}`);
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
- p4.log.message(` ${status} ${displayName.padEnd(20)} ${version} ${pc5.dim(item.description)}${updateTag}`);
1130
+ const updateTag = hasUpdate ? pc5.yellow(` \u2191${item.version}`) : "";
1131
+ line = ` ${pc5.green("\u2713")} ${nameCol}${version}${pc5.dim(desc)}${updateTag}`;
1083
1132
  } else {
1084
- const status = pc5.dim("\u25CB");
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
- const availableCount = allItems.length - installedCount;
1090
- const parts = [`${installedCount} installed`, `${availableCount} available`];
1091
- if (updateCount > 0) parts.push(`${updateCount} update${updateCount === 1 ? "" : "s"} available`);
1092
- p4.log.message("");
1093
- p4.log.message(pc5.dim(` ${parts.join(", ")}`));
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 join8 } from "path";
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 = join8(cwd, baseDir, file.path);
1140
- const relativePath = join8(baseDir, file.path);
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 = join8(cwd, config.aliases[aliasKey], fileName);
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 join9, relative as relative3, dirname as dirname3 } from "path";
1199
- import { unlink, readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
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(join9(cwd, filePath));
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 = join9(cwd, baseDir, "index.ts");
1235
- const barrelDir = join9(cwd, baseDir);
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, join9(cwd, filePath)).replace(/\\/g, "/");
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 writeFile7(barrelPath, barrelContent);
1256
- p6.log.info(`Updated barrel file: ${join9(baseDir, "index.ts")}`);
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 writeFile8, mkdir as mkdir4, access as access4 } from "fs/promises";
1311
- import { join as join10, resolve } from "path";
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(join10(dir, "registry.json"))) {
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(join10(dir, entry.name));
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(join10(absPath, "registry.json"))) {
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 = join10(absPath, entry.name);
1359
- if (await fileExists(join10(subDir, "registry.json"))) {
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 = join10(resolvedOutput, dir);
1385
- await mkdir4(typeDir, { recursive: true });
1452
+ const typeDir = join11(resolvedOutput, dir);
1453
+ await mkdir5(typeDir, { recursive: true });
1386
1454
  const itemJson = JSON.stringify(item, null, 2);
1387
- const latestPath = join10(typeDir, `${item.name}.json`);
1455
+ const latestPath = join11(typeDir, `${item.name}.json`);
1388
1456
  const latestRelative = `${dir}/${item.name}.json`;
1389
- await writeFile8(latestPath, itemJson, "utf-8");
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 = join10(typeDir, versionedFilename);
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 writeFile8(versionedPath, itemJson, "utf-8");
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 = join10(resolvedOutput, "registry.json");
1434
- await writeFile8(indexPath, JSON.stringify(index, null, 2), "utf-8");
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 join11, relative as relative5 } from "path";
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 = join11(dir, entry.name);
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(join11(componentDir, "registry.json"), "utf-8");
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(join11(componentDir, "package.json"), "utf-8");
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 = join11(componentDir, sourceDir);
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 = join11(componentDir, filePath);
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 join12 } from "path";
1720
- import { mkdir as mkdir5, writeFile as writeFile9 } from "fs/promises";
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 "@kitnai/core";
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 "@kitnai/core";
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 "@kitnai/core";
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 = join12(cwd, name);
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 mkdir5(dir, { recursive: true });
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 writeFile9(
1830
- join12(dir, "registry.json"),
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 writeFile9(join12(dir, sourceFile), source);
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.9" : "0.0.0-dev";
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));