@kitnai/cli 0.1.0 → 0.1.2

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
@@ -29,8 +29,9 @@ var componentType, installedComponentSchema, configSchema, CONFIG_FILE;
29
29
  var init_config = __esm({
30
30
  "src/utils/config.ts"() {
31
31
  "use strict";
32
- componentType = z.enum(["kitn:agent", "kitn:tool", "kitn:skill", "kitn:storage"]);
32
+ componentType = z.enum(["kitn:agent", "kitn:tool", "kitn:skill", "kitn:storage", "kitn:package"]);
33
33
  installedComponentSchema = z.object({
34
+ registry: z.string().optional(),
34
35
  version: z.string(),
35
36
  installedAt: z.string(),
36
37
  files: z.array(z.string()),
@@ -39,71 +40,21 @@ var init_config = __esm({
39
40
  configSchema = z.object({
40
41
  $schema: z.string().optional(),
41
42
  runtime: z.enum(["bun", "node", "deno"]),
43
+ framework: z.enum(["hono", "cloudflare", "elysia", "fastify", "express"]).optional(),
42
44
  aliases: z.object({
45
+ base: z.string().optional(),
43
46
  agents: z.string(),
44
47
  tools: z.string(),
45
48
  skills: z.string(),
46
49
  storage: z.string()
47
50
  }),
48
51
  registries: z.record(z.string(), z.string()),
49
- _installed: z.record(z.string(), installedComponentSchema).optional()
52
+ installed: z.record(z.string(), installedComponentSchema).optional()
50
53
  });
51
54
  CONFIG_FILE = "kitn.json";
52
55
  }
53
56
  });
54
57
 
55
- // src/utils/detect.ts
56
- import { access } from "fs/promises";
57
- import { join as join2 } from "path";
58
- async function detectPackageManager(dir) {
59
- for (const [lockfile, pm] of LOCKFILE_MAP) {
60
- try {
61
- await access(join2(dir, lockfile));
62
- return pm;
63
- } catch {
64
- }
65
- }
66
- return null;
67
- }
68
- var LOCKFILE_MAP;
69
- var init_detect = __esm({
70
- "src/utils/detect.ts"() {
71
- "use strict";
72
- LOCKFILE_MAP = [
73
- ["bun.lock", "bun"],
74
- ["bun.lockb", "bun"],
75
- ["pnpm-lock.yaml", "pnpm"],
76
- ["yarn.lock", "yarn"],
77
- ["package-lock.json", "npm"]
78
- ];
79
- }
80
- });
81
-
82
- // src/installers/dep-installer.ts
83
- import { execSync } from "child_process";
84
- function installDependencies(pm, deps, projectDir) {
85
- if (deps.length === 0) return;
86
- const pkgs = deps.join(" ");
87
- const cmd = (() => {
88
- switch (pm) {
89
- case "bun":
90
- return `bun add ${pkgs}`;
91
- case "pnpm":
92
- return `pnpm add ${pkgs}`;
93
- case "yarn":
94
- return `yarn add ${pkgs}`;
95
- case "npm":
96
- return `npm install ${pkgs}`;
97
- }
98
- })();
99
- execSync(cmd, { cwd: projectDir, stdio: "pipe" });
100
- }
101
- var init_dep_installer = __esm({
102
- "src/installers/dep-installer.ts"() {
103
- "use strict";
104
- }
105
- });
106
-
107
58
  // src/commands/init.ts
108
59
  var init_exports = {};
109
60
  __export(init_exports, {
@@ -138,74 +89,81 @@ async function initCommand() {
138
89
  p.cancel("Init cancelled.");
139
90
  process.exit(0);
140
91
  }
141
- const aliases = await p.group({
142
- agents: () => p.text({
143
- message: "Where should agents be installed?",
144
- initialValue: "src/agents",
145
- placeholder: "src/agents"
146
- }),
147
- tools: () => p.text({
148
- message: "Where should tools be installed?",
149
- initialValue: "src/tools",
150
- placeholder: "src/tools"
151
- }),
152
- skills: () => p.text({
153
- message: "Where should skills be installed?",
154
- initialValue: "src/skills",
155
- placeholder: "src/skills"
156
- }),
157
- storage: () => p.text({
158
- message: "Where should storage adapters be installed?",
159
- initialValue: "src/storage",
160
- placeholder: "src/storage"
161
- })
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);
105
+ }
106
+ const base = await p.text({
107
+ message: "Where should kitn packages be installed?",
108
+ initialValue: "src/ai",
109
+ placeholder: "src/ai"
162
110
  });
163
- if (p.isCancel(aliases)) {
111
+ if (p.isCancel(base)) {
164
112
  p.cancel("Init cancelled.");
165
113
  process.exit(0);
166
114
  }
115
+ const baseDir = base;
167
116
  const config = {
168
117
  runtime,
118
+ framework,
169
119
  aliases: {
170
- agents: aliases.agents,
171
- tools: aliases.tools,
172
- skills: aliases.skills,
173
- storage: aliases.storage
120
+ base: baseDir,
121
+ agents: `${baseDir}/agents`,
122
+ tools: `${baseDir}/tools`,
123
+ skills: `${baseDir}/skills`,
124
+ storage: `${baseDir}/storage`
174
125
  },
175
126
  registries: {
176
- "@kitn": "https://kitn-ai.github.io/kitn/r/{type}/{name}.json"
127
+ "@kitn": "https://kitn-ai.github.io/registry/r/{type}/{name}.json"
177
128
  }
178
129
  };
179
130
  const s = p.spinner();
180
131
  s.start("Writing kitn.json");
181
132
  await writeConfig(cwd, config);
182
133
  s.stop("Created kitn.json");
183
- const pm = await detectPackageManager(cwd);
184
- if (pm) {
185
- const shouldInstall = await p.confirm({
186
- message: `Install @kitnai/server using ${pm}?`,
187
- initialValue: true
188
- });
189
- if (!p.isCancel(shouldInstall) && shouldInstall) {
190
- s.start("Installing @kitnai/server...");
191
- try {
192
- installDependencies(pm, ["@kitnai/server"], cwd);
193
- s.stop("Installed @kitnai/server");
194
- } catch {
195
- s.stop(pc.yellow("Failed to install @kitnai/server \u2014 you can install it manually"));
196
- }
197
- }
198
- } else {
199
- p.log.info("No package manager detected. Install @kitnai/server manually.");
200
- }
201
- p.outro(pc.green("Done! Run `kitn add <component>` to add your first component."));
134
+ p.outro(pc.green("Done! Run `kitn add core` to install the engine, then `kitn add routes` for HTTP routes."));
202
135
  }
203
136
  var init_init = __esm({
204
137
  "src/commands/init.ts"() {
205
138
  "use strict";
206
139
  init_config();
207
- init_detect();
208
- init_dep_installer();
140
+ }
141
+ });
142
+
143
+ // src/utils/detect.ts
144
+ import { access } from "fs/promises";
145
+ import { join as join2 } from "path";
146
+ async function detectPackageManager(dir) {
147
+ for (const [lockfile, pm] of LOCKFILE_MAP) {
148
+ try {
149
+ await access(join2(dir, lockfile));
150
+ return pm;
151
+ } catch {
152
+ }
153
+ }
154
+ return null;
155
+ }
156
+ var LOCKFILE_MAP;
157
+ var init_detect = __esm({
158
+ "src/utils/detect.ts"() {
159
+ "use strict";
160
+ LOCKFILE_MAP = [
161
+ ["bun.lock", "bun"],
162
+ ["bun.lockb", "bun"],
163
+ ["pnpm-lock.yaml", "pnpm"],
164
+ ["yarn.lock", "yarn"],
165
+ ["package-lock.json", "npm"]
166
+ ];
209
167
  }
210
168
  });
211
169
 
@@ -222,21 +180,22 @@ var init_fetcher = __esm({
222
180
  this.registries = registries;
223
181
  this.fetchFn = fetchFn ?? this.defaultFetch;
224
182
  }
225
- resolveUrl(name, typeDir) {
226
- const template = this.registries["@kitn"];
227
- if (!template) throw new Error("No @kitn registry configured");
228
- return template.replace("{name}", name).replace("{type}", typeDir);
183
+ resolveUrl(name, typeDir, namespace = "@kitn", version) {
184
+ const template = this.registries[namespace];
185
+ if (!template) throw new Error(`No registry configured for ${namespace}`);
186
+ const fileName = version ? `${name}@${version}` : name;
187
+ return template.replace("{name}", fileName).replace("{type}", typeDir);
229
188
  }
230
- async fetchItem(name, typeDir) {
231
- const url = this.resolveUrl(name, typeDir);
189
+ async fetchItem(name, typeDir, namespace = "@kitn", version) {
190
+ const url = this.resolveUrl(name, typeDir, namespace, version);
232
191
  if (!this.cache.has(url)) {
233
192
  this.cache.set(url, this.fetchFn(url));
234
193
  }
235
194
  return this.cache.get(url);
236
195
  }
237
- async fetchIndex() {
238
- const template = this.registries["@kitn"];
239
- if (!template) throw new Error("No @kitn registry configured");
196
+ async fetchIndex(namespace = "@kitn") {
197
+ const template = this.registries[namespace];
198
+ if (!template) throw new Error(`No registry configured for ${namespace}`);
240
199
  const baseUrl = template.replace("{type}/{name}.json", "registry.json");
241
200
  const res = await fetch(baseUrl);
242
201
  if (!res.ok) throw new Error(`Failed to fetch registry index: ${res.statusText}`);
@@ -342,6 +301,31 @@ var init_file_writer = __esm({
342
301
  }
343
302
  });
344
303
 
304
+ // src/installers/dep-installer.ts
305
+ import { execSync } from "child_process";
306
+ function installDependencies(pm, deps, projectDir) {
307
+ if (deps.length === 0) return;
308
+ const pkgs = deps.join(" ");
309
+ const cmd = (() => {
310
+ switch (pm) {
311
+ case "bun":
312
+ return `bun add ${pkgs}`;
313
+ case "pnpm":
314
+ return `pnpm add ${pkgs}`;
315
+ case "yarn":
316
+ return `yarn add ${pkgs}`;
317
+ case "npm":
318
+ return `npm install ${pkgs}`;
319
+ }
320
+ })();
321
+ execSync(cmd, { cwd: projectDir, stdio: "pipe" });
322
+ }
323
+ var init_dep_installer = __esm({
324
+ "src/installers/dep-installer.ts"() {
325
+ "use strict";
326
+ }
327
+ });
328
+
345
329
  // src/installers/env-checker.ts
346
330
  import pc2 from "picocolors";
347
331
  function checkEnvVars(envVars) {
@@ -359,6 +343,76 @@ var init_env_checker = __esm({
359
343
  }
360
344
  });
361
345
 
346
+ // src/installers/import-rewriter.ts
347
+ import { relative, join as join3 } from "path";
348
+ function rewriteKitnImports(content, fileType, fileName, aliases) {
349
+ const sourceAliasKey = TYPE_TO_ALIAS_KEY[fileType];
350
+ if (!sourceAliasKey) return content;
351
+ const sourceDir = aliases[sourceAliasKey];
352
+ return content.replace(
353
+ /((?:import|export)\s+.*?\s+from\s+["'])@kitn\/([\w-]+)\/([^"']+)(["'])/g,
354
+ (_match, prefix, type, targetPath, quote) => {
355
+ if (!KNOWN_TYPES.includes(type)) {
356
+ return `${prefix}@kitn/${type}/${targetPath}${quote}`;
357
+ }
358
+ const targetDir = aliases[type];
359
+ const targetFile = join3(targetDir, targetPath);
360
+ let rel = relative(sourceDir, targetFile);
361
+ rel = rel.split("\\").join("/");
362
+ if (!rel.startsWith(".")) {
363
+ rel = `./${rel}`;
364
+ }
365
+ return `${prefix}${rel}${quote}`;
366
+ }
367
+ );
368
+ }
369
+ var KNOWN_TYPES, TYPE_TO_ALIAS_KEY;
370
+ var init_import_rewriter = __esm({
371
+ "src/installers/import-rewriter.ts"() {
372
+ "use strict";
373
+ KNOWN_TYPES = ["agents", "tools", "skills", "storage"];
374
+ TYPE_TO_ALIAS_KEY = {
375
+ "kitn:agent": "agents",
376
+ "kitn:tool": "tools",
377
+ "kitn:skill": "skills",
378
+ "kitn:storage": "storage"
379
+ };
380
+ }
381
+ });
382
+
383
+ // src/installers/tsconfig-patcher.ts
384
+ import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
385
+ import { join as join4 } from "path";
386
+ function patchTsconfig(tsconfigContent, paths) {
387
+ const config = JSON.parse(tsconfigContent);
388
+ if (!config.compilerOptions) {
389
+ config.compilerOptions = {};
390
+ }
391
+ if (!config.compilerOptions.paths) {
392
+ config.compilerOptions.paths = {};
393
+ }
394
+ for (const [key, value] of Object.entries(paths)) {
395
+ config.compilerOptions.paths[key] = value;
396
+ }
397
+ return JSON.stringify(config, null, 2) + "\n";
398
+ }
399
+ async function patchProjectTsconfig(projectDir, paths) {
400
+ const tsconfigPath = join4(projectDir, "tsconfig.json");
401
+ let content;
402
+ try {
403
+ content = await readFile3(tsconfigPath, "utf-8");
404
+ } catch {
405
+ content = "{}";
406
+ }
407
+ const patched = patchTsconfig(content, paths);
408
+ await writeFile3(tsconfigPath, patched);
409
+ }
410
+ var init_tsconfig_patcher = __esm({
411
+ "src/installers/tsconfig-patcher.ts"() {
412
+ "use strict";
413
+ }
414
+ });
415
+
362
416
  // src/utils/hash.ts
363
417
  import { createHash } from "crypto";
364
418
  function contentHash(content) {
@@ -370,18 +424,52 @@ var init_hash = __esm({
370
424
  }
371
425
  });
372
426
 
427
+ // src/utils/parse-ref.ts
428
+ function parseComponentRef(input) {
429
+ let namespace = "@kitn";
430
+ let rest = input;
431
+ if (rest.startsWith("@")) {
432
+ const slashIdx = rest.indexOf("/");
433
+ if (slashIdx === -1) {
434
+ throw new Error(`Invalid component reference: ${input}. Expected @namespace/name`);
435
+ }
436
+ namespace = rest.slice(0, slashIdx);
437
+ rest = rest.slice(slashIdx + 1);
438
+ }
439
+ const atIdx = rest.indexOf("@");
440
+ if (atIdx === -1) {
441
+ return { namespace, name: rest, version: void 0 };
442
+ }
443
+ return {
444
+ namespace,
445
+ name: rest.slice(0, atIdx),
446
+ version: rest.slice(atIdx + 1)
447
+ };
448
+ }
449
+ var init_parse_ref = __esm({
450
+ "src/utils/parse-ref.ts"() {
451
+ "use strict";
452
+ }
453
+ });
454
+
373
455
  // src/registry/schema.ts
374
456
  import { z as z2 } from "zod";
375
- var componentType2, registryFileSchema, registryItemSchema, registryIndexItemSchema, registryIndexSchema, typeToDir;
457
+ var componentType2, registryFileSchema, changelogEntrySchema, registryItemSchema, registryIndexItemSchema, registryIndexSchema, typeToDir;
376
458
  var init_schema = __esm({
377
459
  "src/registry/schema.ts"() {
378
460
  "use strict";
379
- componentType2 = z2.enum(["kitn:agent", "kitn:tool", "kitn:skill", "kitn:storage"]);
461
+ componentType2 = z2.enum(["kitn:agent", "kitn:tool", "kitn:skill", "kitn:storage", "kitn:package"]);
380
462
  registryFileSchema = z2.object({
381
463
  path: z2.string(),
382
464
  content: z2.string(),
383
465
  type: componentType2
384
466
  });
467
+ changelogEntrySchema = z2.object({
468
+ version: z2.string(),
469
+ date: z2.string(),
470
+ type: z2.enum(["feature", "fix", "breaking", "initial"]),
471
+ note: z2.string()
472
+ });
385
473
  registryItemSchema = z2.object({
386
474
  $schema: z2.string().optional(),
387
475
  name: z2.string(),
@@ -392,9 +480,13 @@ var init_schema = __esm({
392
480
  registryDependencies: z2.array(z2.string()).optional(),
393
481
  envVars: z2.record(z2.string(), z2.string()).optional(),
394
482
  files: z2.array(registryFileSchema),
483
+ installDir: z2.string().optional(),
484
+ tsconfig: z2.record(z2.string(), z2.array(z2.string())).optional(),
395
485
  docs: z2.string().optional(),
396
486
  categories: z2.array(z2.string()).optional(),
397
- version: z2.string().optional()
487
+ version: z2.string().optional(),
488
+ updatedAt: z2.string().optional(),
489
+ changelog: z2.array(changelogEntrySchema).optional()
398
490
  });
399
491
  registryIndexItemSchema = z2.object({
400
492
  name: z2.string(),
@@ -402,7 +494,9 @@ var init_schema = __esm({
402
494
  description: z2.string(),
403
495
  registryDependencies: z2.array(z2.string()).optional(),
404
496
  categories: z2.array(z2.string()).optional(),
405
- version: z2.string().optional()
497
+ version: z2.string().optional(),
498
+ versions: z2.array(z2.string()).optional(),
499
+ updatedAt: z2.string().optional()
406
500
  });
407
501
  registryIndexSchema = z2.object({
408
502
  $schema: z2.string().optional(),
@@ -413,7 +507,8 @@ var init_schema = __esm({
413
507
  "kitn:agent": "agents",
414
508
  "kitn:tool": "tools",
415
509
  "kitn:skill": "skills",
416
- "kitn:storage": "storage"
510
+ "kitn:storage": "storage",
511
+ "kitn:package": "package"
417
512
  };
418
513
  }
419
514
  });
@@ -425,7 +520,7 @@ __export(add_exports, {
425
520
  });
426
521
  import * as p2 from "@clack/prompts";
427
522
  import pc3 from "picocolors";
428
- import { join as join3 } from "path";
523
+ import { join as join5 } from "path";
429
524
  async function addCommand(components, opts) {
430
525
  p2.intro(pc3.bgCyan(pc3.black(" kitn add ")));
431
526
  const cwd = process.cwd();
@@ -438,17 +533,26 @@ async function addCommand(components, opts) {
438
533
  p2.log.error("Please specify at least one component to add.");
439
534
  process.exit(1);
440
535
  }
536
+ const resolvedComponents = components.map((c) => {
537
+ if (c === "routes") {
538
+ const fw = config.framework ?? "hono";
539
+ return fw;
540
+ }
541
+ return c;
542
+ });
543
+ const refs = resolvedComponents.map(parseComponentRef);
441
544
  const fetcher = new RegistryFetcher(config.registries);
442
545
  const s = p2.spinner();
443
546
  s.start("Resolving dependencies...");
444
547
  let resolved;
445
548
  try {
446
- resolved = await resolveDependencies(components, async (name) => {
447
- const index = await fetcher.fetchIndex();
549
+ resolved = await resolveDependencies(resolvedComponents, async (name) => {
550
+ const ref = refs.find((r) => r.name === name) ?? { namespace: "@kitn", name, version: void 0 };
551
+ const index = await fetcher.fetchIndex(ref.namespace);
448
552
  const indexItem = index.items.find((i) => i.name === name);
449
- if (!indexItem) throw new Error(`Component '${name}' not found in registry`);
553
+ if (!indexItem) throw new Error(`Component '${name}' not found in ${ref.namespace} registry`);
450
554
  const dir = typeToDir[indexItem.type];
451
- return fetcher.fetchItem(name, dir);
555
+ return fetcher.fetchItem(name, dir, ref.namespace, ref.version);
452
556
  });
453
557
  } catch (err) {
454
558
  s.stop(pc3.red("Failed to resolve dependencies"));
@@ -458,7 +562,7 @@ async function addCommand(components, opts) {
458
562
  s.stop(`Resolved ${resolved.length} component(s)`);
459
563
  p2.log.info("Components to install:");
460
564
  for (const item of resolved) {
461
- const isExplicit = components.includes(item.name);
565
+ const isExplicit = resolvedComponents.includes(item.name) || components.includes(item.name);
462
566
  const label = isExplicit ? item.name : `${item.name} ${pc3.dim("(dependency)")}`;
463
567
  p2.log.message(` ${pc3.cyan(label)}`);
464
568
  }
@@ -472,62 +576,68 @@ async function addCommand(components, opts) {
472
576
  if (item.envVars) {
473
577
  allEnvWarnings.push(...checkEnvVars(item.envVars));
474
578
  }
475
- for (const file of item.files) {
476
- const aliasKey = (() => {
477
- switch (item.type) {
478
- case "kitn:agent":
479
- return "agents";
480
- case "kitn:tool":
481
- return "tools";
482
- case "kitn:skill":
483
- return "skills";
484
- case "kitn:storage":
485
- return "storage";
486
- }
487
- })();
488
- const fileName = file.path.split("/").pop();
489
- const targetPath = join3(cwd, config.aliases[aliasKey], fileName);
490
- const relativePath = join3(config.aliases[aliasKey], fileName);
491
- const status = await checkFileStatus(targetPath, file.content);
492
- switch (status) {
493
- case "new" /* New */:
494
- await writeComponentFile(targetPath, file.content);
495
- created.push(relativePath);
496
- break;
497
- case "identical" /* Identical */:
498
- skipped.push(relativePath);
499
- break;
500
- case "different" /* Different */:
501
- if (opts.overwrite) {
579
+ if (item.type === "kitn:package") {
580
+ const baseDir = config.aliases.base ?? "src/ai";
581
+ for (const file of item.files) {
582
+ const targetPath = join5(cwd, baseDir, file.path);
583
+ const relativePath = join5(baseDir, file.path);
584
+ const status = await checkFileStatus(targetPath, file.content);
585
+ switch (status) {
586
+ case "new" /* New */:
502
587
  await writeComponentFile(targetPath, file.content);
503
- updated.push(relativePath);
504
- } else {
505
- const existing = await readExistingFile(targetPath);
506
- const diff = generateDiff(relativePath, existing ?? "", file.content);
507
- p2.log.message(pc3.dim(diff));
508
- const action = await p2.select({
509
- message: `${relativePath} already exists and differs. What to do?`,
510
- options: [
511
- { value: "skip", label: "Keep local version" },
512
- { value: "overwrite", label: "Overwrite with registry version" }
513
- ]
514
- });
515
- if (!p2.isCancel(action) && action === "overwrite") {
588
+ created.push(relativePath);
589
+ break;
590
+ case "identical" /* Identical */:
591
+ skipped.push(relativePath);
592
+ break;
593
+ case "different" /* Different */:
594
+ if (opts.overwrite) {
516
595
  await writeComponentFile(targetPath, file.content);
517
596
  updated.push(relativePath);
518
597
  } else {
519
- skipped.push(relativePath);
598
+ const existing = await readExistingFile(targetPath);
599
+ const diff = generateDiff(relativePath, existing ?? "", file.content);
600
+ p2.log.message(pc3.dim(diff));
601
+ const action = await p2.select({
602
+ message: `${relativePath} already exists and differs. What to do?`,
603
+ options: [
604
+ { value: "skip", label: "Keep local version" },
605
+ { value: "overwrite", label: "Overwrite with registry version" }
606
+ ]
607
+ });
608
+ if (!p2.isCancel(action) && action === "overwrite") {
609
+ await writeComponentFile(targetPath, file.content);
610
+ updated.push(relativePath);
611
+ } else {
612
+ skipped.push(relativePath);
613
+ }
520
614
  }
521
- }
522
- break;
615
+ break;
616
+ }
523
617
  }
524
- }
525
- const installed = config._installed ?? {};
526
- const allContent = item.files.map((f) => f.content).join("\n");
527
- installed[item.name] = {
528
- version: item.version ?? "1.0.0",
529
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
530
- files: item.files.map((f) => {
618
+ if (item.tsconfig) {
619
+ const resolvedPaths = {};
620
+ const installDir = item.installDir ?? item.name;
621
+ for (const [key, values] of Object.entries(item.tsconfig)) {
622
+ resolvedPaths[key] = values.map((v) => `./${join5(baseDir, installDir, v)}`);
623
+ }
624
+ await patchProjectTsconfig(cwd, resolvedPaths);
625
+ p2.log.info(`Patched tsconfig.json with paths: ${Object.keys(resolvedPaths).join(", ")}`);
626
+ }
627
+ const installed = config.installed ?? {};
628
+ const allContent = item.files.map((f) => f.content).join("\n");
629
+ const ref = refs.find((r) => r.name === item.name) ?? { namespace: "@kitn", name: item.name, version: void 0 };
630
+ const installedKey = ref.namespace === "@kitn" ? item.name : `${ref.namespace}/${item.name}`;
631
+ installed[installedKey] = {
632
+ registry: ref.namespace,
633
+ version: item.version ?? "1.0.0",
634
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
635
+ files: item.files.map((f) => join5(baseDir, f.path)),
636
+ hash: contentHash(allContent)
637
+ };
638
+ config.installed = installed;
639
+ } else {
640
+ for (const file of item.files) {
531
641
  const aliasKey = (() => {
532
642
  switch (item.type) {
533
643
  case "kitn:agent":
@@ -540,12 +650,75 @@ async function addCommand(components, opts) {
540
650
  return "storage";
541
651
  }
542
652
  })();
543
- const fileName = f.path.split("/").pop();
544
- return join3(config.aliases[aliasKey], fileName);
545
- }),
546
- hash: contentHash(allContent)
547
- };
548
- config._installed = installed;
653
+ const fileName = file.path.split("/").pop();
654
+ const targetPath = join5(cwd, config.aliases[aliasKey], fileName);
655
+ const relativePath = join5(config.aliases[aliasKey], fileName);
656
+ const content = rewriteKitnImports(file.content, item.type, fileName, config.aliases);
657
+ const status = await checkFileStatus(targetPath, content);
658
+ switch (status) {
659
+ case "new" /* New */:
660
+ await writeComponentFile(targetPath, content);
661
+ created.push(relativePath);
662
+ break;
663
+ case "identical" /* Identical */:
664
+ skipped.push(relativePath);
665
+ break;
666
+ case "different" /* Different */:
667
+ if (opts.overwrite) {
668
+ await writeComponentFile(targetPath, content);
669
+ updated.push(relativePath);
670
+ } else {
671
+ const existing = await readExistingFile(targetPath);
672
+ const diff = generateDiff(relativePath, existing ?? "", content);
673
+ p2.log.message(pc3.dim(diff));
674
+ const action = await p2.select({
675
+ message: `${relativePath} already exists and differs. What to do?`,
676
+ options: [
677
+ { value: "skip", label: "Keep local version" },
678
+ { value: "overwrite", label: "Overwrite with registry version" }
679
+ ]
680
+ });
681
+ if (!p2.isCancel(action) && action === "overwrite") {
682
+ await writeComponentFile(targetPath, content);
683
+ updated.push(relativePath);
684
+ } else {
685
+ skipped.push(relativePath);
686
+ }
687
+ }
688
+ break;
689
+ }
690
+ }
691
+ const installed = config.installed ?? {};
692
+ const allContent = item.files.map((f) => {
693
+ const fn = f.path.split("/").pop();
694
+ return rewriteKitnImports(f.content, item.type, fn, config.aliases);
695
+ }).join("\n");
696
+ const ref = refs.find((r) => r.name === item.name) ?? { namespace: "@kitn", name: item.name, version: void 0 };
697
+ const installedKey = ref.namespace === "@kitn" ? item.name : `${ref.namespace}/${item.name}`;
698
+ installed[installedKey] = {
699
+ registry: ref.namespace,
700
+ version: item.version ?? "1.0.0",
701
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
702
+ files: item.files.map((f) => {
703
+ const aliasKey = (() => {
704
+ switch (item.type) {
705
+ case "kitn:agent":
706
+ return "agents";
707
+ case "kitn:tool":
708
+ return "tools";
709
+ case "kitn:skill":
710
+ return "skills";
711
+ case "kitn:storage":
712
+ return "storage";
713
+ }
714
+ })();
715
+ const fileName = f.path.split("/").pop();
716
+ return join5(config.aliases[aliasKey], fileName);
717
+ }),
718
+ hash: contentHash(allContent)
719
+ };
720
+ config.installed = installed;
721
+ }
549
722
  }
550
723
  await writeConfig(cwd, config);
551
724
  const uniqueDeps = [...new Set(allDeps)];
@@ -594,7 +767,10 @@ var init_add = __esm({
594
767
  init_file_writer();
595
768
  init_dep_installer();
596
769
  init_env_checker();
770
+ init_import_rewriter();
771
+ init_tsconfig_patcher();
597
772
  init_hash();
773
+ init_parse_ref();
598
774
  init_schema();
599
775
  }
600
776
  });
@@ -614,40 +790,70 @@ async function listCommand(opts) {
614
790
  process.exit(1);
615
791
  }
616
792
  const fetcher = new RegistryFetcher(config.registries);
793
+ const namespacesToFetch = opts.registry ? [opts.registry] : Object.keys(config.registries);
794
+ if (opts.registry && !config.registries[opts.registry]) {
795
+ p3.log.error(`Registry ${pc4.bold(opts.registry)} is not configured. Run ${pc4.bold("kitn registry list")} to see configured registries.`);
796
+ process.exit(1);
797
+ }
617
798
  const s = p3.spinner();
618
799
  s.start("Fetching registry index...");
619
- let index;
620
- try {
621
- index = await fetcher.fetchIndex();
622
- } catch (err) {
623
- s.stop(pc4.red("Failed to fetch registry"));
624
- p3.log.error(err.message);
800
+ const allItems = [];
801
+ const errors = [];
802
+ for (const namespace of namespacesToFetch) {
803
+ try {
804
+ const index = await fetcher.fetchIndex(namespace);
805
+ for (const item of index.items) {
806
+ allItems.push({ ...item, namespace });
807
+ }
808
+ } catch (err) {
809
+ errors.push(`${namespace}: ${err.message}`);
810
+ }
811
+ }
812
+ if (allItems.length === 0 && errors.length > 0) {
813
+ s.stop(pc4.red("Failed to fetch registries"));
814
+ for (const e of errors) p3.log.error(e);
625
815
  process.exit(1);
626
816
  }
627
- s.stop(`Found ${index.items.length} components`);
628
- const installed = config._installed ?? {};
817
+ s.stop(`Found ${allItems.length} components across ${namespacesToFetch.length - errors.length} ${namespacesToFetch.length - errors.length === 1 ? "registry" : "registries"}`);
818
+ for (const e of errors) {
819
+ p3.log.warn(`${pc4.yellow("\u26A0")} Failed to fetch ${e}`);
820
+ }
821
+ const installed = config.installed ?? {};
629
822
  const typeGroups = /* @__PURE__ */ new Map();
630
- for (const item of index.items) {
823
+ for (const item of allItems) {
631
824
  if (opts.type && !item.type.endsWith(opts.type)) continue;
632
- const group2 = item.type.replace("kitn:", "");
633
- if (!typeGroups.has(group2)) typeGroups.set(group2, []);
634
- typeGroups.get(group2).push(item);
825
+ const group = item.type.replace("kitn:", "");
826
+ if (!typeGroups.has(group)) typeGroups.set(group, []);
827
+ typeGroups.get(group).push(item);
635
828
  }
636
- for (const [group2, items] of typeGroups) {
829
+ let installedCount = 0;
830
+ let updateCount = 0;
831
+ for (const [group, items] of typeGroups) {
637
832
  p3.log.message(pc4.bold(`
638
- ${group2.charAt(0).toUpperCase() + group2.slice(1)}s:`));
833
+ ${group.charAt(0).toUpperCase() + group.slice(1)}s:`));
639
834
  for (const item of items) {
640
- const inst = installed[item.name];
835
+ const displayName = item.namespace === "@kitn" ? item.name : `${item.namespace}/${item.name}`;
836
+ const inst = installed[item.name] ?? installed[displayName];
641
837
  if (opts.installed && !inst) continue;
838
+ const version = pc4.dim(`v${item.version ?? "1.0.0"}`);
642
839
  if (inst) {
840
+ installedCount++;
643
841
  const status = pc4.green("\u2713");
644
- p3.log.message(` ${status} ${item.name} ${pc4.dim(item.description)}`);
842
+ const hasUpdate = item.version && inst.version !== item.version;
843
+ const updateTag = hasUpdate ? pc4.yellow(` \u2B06 v${item.version} available`) : "";
844
+ if (hasUpdate) updateCount++;
845
+ p3.log.message(` ${status} ${displayName.padEnd(20)} ${version} ${pc4.dim(item.description)}${updateTag}`);
645
846
  } else {
646
847
  const status = pc4.dim("\u25CB");
647
- p3.log.message(` ${status} ${item.name} ${pc4.dim(item.description)}`);
848
+ p3.log.message(` ${status} ${displayName.padEnd(20)} ${version} ${pc4.dim(item.description)}`);
648
849
  }
649
850
  }
650
851
  }
852
+ const availableCount = allItems.length - installedCount;
853
+ const parts = [`${installedCount} installed`, `${availableCount} available`];
854
+ if (updateCount > 0) parts.push(`${updateCount} update${updateCount === 1 ? "" : "s"} available`);
855
+ p3.log.message("");
856
+ p3.log.message(pc4.dim(` ${parts.join(", ")}`));
651
857
  }
652
858
  var init_list = __esm({
653
859
  "src/commands/list.ts"() {
@@ -663,7 +869,7 @@ __export(diff_exports, {
663
869
  diffCommand: () => diffCommand
664
870
  });
665
871
  import * as p4 from "@clack/prompts";
666
- import { join as join4 } from "path";
872
+ import { join as join6 } from "path";
667
873
  async function diffCommand(componentName) {
668
874
  const cwd = process.cwd();
669
875
  const config = await readConfig(cwd);
@@ -671,48 +877,67 @@ async function diffCommand(componentName) {
671
877
  p4.log.error("No kitn.json found. Run `kitn init` first.");
672
878
  process.exit(1);
673
879
  }
674
- const installed = config._installed?.[componentName];
880
+ const input = componentName === "routes" ? config.framework ?? "hono" : componentName;
881
+ const ref = parseComponentRef(input);
882
+ const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
883
+ const installed = config.installed?.[installedKey];
675
884
  if (!installed) {
676
- p4.log.error(`Component '${componentName}' is not installed.`);
885
+ p4.log.error(`Component '${ref.name}' is not installed.`);
677
886
  process.exit(1);
678
887
  }
888
+ const namespace = installed.registry ?? ref.namespace;
679
889
  const fetcher = new RegistryFetcher(config.registries);
680
- const index = await fetcher.fetchIndex();
681
- const indexItem = index.items.find((i) => i.name === componentName);
890
+ const index = await fetcher.fetchIndex(namespace);
891
+ const indexItem = index.items.find((i) => i.name === ref.name);
682
892
  if (!indexItem) {
683
- p4.log.error(`Component '${componentName}' not found in registry.`);
893
+ p4.log.error(`Component '${ref.name}' not found in ${namespace} registry.`);
684
894
  process.exit(1);
685
895
  }
686
896
  const dir = typeToDir[indexItem.type];
687
- const registryItem = await fetcher.fetchItem(componentName, dir);
897
+ const registryItem = await fetcher.fetchItem(ref.name, dir, namespace, ref.version);
688
898
  let hasDiff = false;
689
899
  for (const file of registryItem.files) {
690
- const fileName = file.path.split("/").pop();
691
- const aliasKey = (() => {
692
- switch (indexItem.type) {
693
- case "kitn:agent":
694
- return "agents";
695
- case "kitn:tool":
696
- return "tools";
697
- case "kitn:skill":
698
- return "skills";
699
- case "kitn:storage":
700
- return "storage";
900
+ if (indexItem.type === "kitn:package") {
901
+ const baseDir = config.aliases.base ?? "src/ai";
902
+ const localPath = join6(cwd, baseDir, file.path);
903
+ const relativePath = join6(baseDir, file.path);
904
+ const localContent = await readExistingFile(localPath);
905
+ if (localContent === null) {
906
+ p4.log.warn(`${relativePath}: file missing locally`);
907
+ hasDiff = true;
908
+ } else if (localContent !== file.content) {
909
+ const diff = generateDiff(relativePath, localContent, file.content);
910
+ console.log(diff);
911
+ hasDiff = true;
912
+ }
913
+ } else {
914
+ const fileName = file.path.split("/").pop();
915
+ const aliasKey = (() => {
916
+ switch (indexItem.type) {
917
+ case "kitn:agent":
918
+ return "agents";
919
+ case "kitn:tool":
920
+ return "tools";
921
+ case "kitn:skill":
922
+ return "skills";
923
+ case "kitn:storage":
924
+ return "storage";
925
+ }
926
+ })();
927
+ const localPath = join6(cwd, config.aliases[aliasKey], fileName);
928
+ const localContent = await readExistingFile(localPath);
929
+ if (localContent === null) {
930
+ p4.log.warn(`${fileName}: file missing locally`);
931
+ hasDiff = true;
932
+ } else if (localContent !== file.content) {
933
+ const diff = generateDiff(fileName, localContent, file.content);
934
+ console.log(diff);
935
+ hasDiff = true;
701
936
  }
702
- })();
703
- const localPath = join4(cwd, config.aliases[aliasKey], fileName);
704
- const localContent = await readExistingFile(localPath);
705
- if (localContent === null) {
706
- p4.log.warn(`${fileName}: file missing locally`);
707
- hasDiff = true;
708
- } else if (localContent !== file.content) {
709
- const diff = generateDiff(fileName, localContent, file.content);
710
- console.log(diff);
711
- hasDiff = true;
712
937
  }
713
938
  }
714
939
  if (!hasDiff) {
715
- p4.log.success(`${componentName}: up to date, no differences.`);
940
+ p4.log.success(`${ref.name}: up to date, no differences.`);
716
941
  }
717
942
  }
718
943
  var init_diff = __esm({
@@ -721,6 +946,7 @@ var init_diff = __esm({
721
946
  init_config();
722
947
  init_fetcher();
723
948
  init_file_writer();
949
+ init_parse_ref();
724
950
  init_schema();
725
951
  }
726
952
  });
@@ -732,7 +958,7 @@ __export(remove_exports, {
732
958
  });
733
959
  import * as p5 from "@clack/prompts";
734
960
  import pc5 from "picocolors";
735
- import { join as join5 } from "path";
961
+ import { join as join7 } from "path";
736
962
  import { unlink } from "fs/promises";
737
963
  async function removeCommand(componentName) {
738
964
  const cwd = process.cwd();
@@ -741,13 +967,16 @@ async function removeCommand(componentName) {
741
967
  p5.log.error("No kitn.json found. Run `kitn init` first.");
742
968
  process.exit(1);
743
969
  }
744
- const installed = config._installed?.[componentName];
970
+ const input = componentName === "routes" ? config.framework ?? "hono" : componentName;
971
+ const ref = parseComponentRef(input);
972
+ const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
973
+ const installed = config.installed?.[installedKey];
745
974
  if (!installed) {
746
- p5.log.error(`Component '${componentName}' is not installed.`);
975
+ p5.log.error(`Component '${ref.name}' is not installed.`);
747
976
  process.exit(1);
748
977
  }
749
978
  const shouldRemove = await p5.confirm({
750
- message: `Remove ${componentName}? This will delete ${installed.files.length} file(s).`,
979
+ message: `Remove ${ref.name}? This will delete ${installed.files.length} file(s).`,
751
980
  initialValue: false
752
981
  });
753
982
  if (p5.isCancel(shouldRemove) || !shouldRemove) {
@@ -757,19 +986,19 @@ async function removeCommand(componentName) {
757
986
  const deleted = [];
758
987
  for (const filePath of installed.files) {
759
988
  try {
760
- await unlink(join5(cwd, filePath));
989
+ await unlink(join7(cwd, filePath));
761
990
  deleted.push(filePath);
762
991
  } catch {
763
992
  p5.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
764
993
  }
765
994
  }
766
- delete config._installed[componentName];
767
- if (Object.keys(config._installed).length === 0) {
768
- delete config._installed;
995
+ delete config.installed[installedKey];
996
+ if (Object.keys(config.installed).length === 0) {
997
+ delete config.installed;
769
998
  }
770
999
  await writeConfig(cwd, config);
771
1000
  if (deleted.length > 0) {
772
- p5.log.success(`Removed ${componentName}:`);
1001
+ p5.log.success(`Removed ${ref.name}:`);
773
1002
  for (const f of deleted) p5.log.message(` ${pc5.red("-")} ${f}`);
774
1003
  }
775
1004
  }
@@ -777,6 +1006,7 @@ var init_remove = __esm({
777
1006
  "src/commands/remove.ts"() {
778
1007
  "use strict";
779
1008
  init_config();
1009
+ init_parse_ref();
780
1010
  }
781
1011
  });
782
1012
 
@@ -785,13 +1015,226 @@ var update_exports = {};
785
1015
  __export(update_exports, {
786
1016
  updateCommand: () => updateCommand
787
1017
  });
1018
+ import * as p6 from "@clack/prompts";
788
1019
  async function updateCommand(components) {
1020
+ if (components.length === 0) {
1021
+ const cwd = process.cwd();
1022
+ const config = await readConfig(cwd);
1023
+ if (!config) {
1024
+ p6.log.error("No kitn.json found. Run `kitn init` first.");
1025
+ process.exit(1);
1026
+ }
1027
+ const installed = config.installed;
1028
+ if (!installed || Object.keys(installed).length === 0) {
1029
+ p6.log.info("No installed components to update.");
1030
+ return;
1031
+ }
1032
+ components = Object.keys(installed);
1033
+ }
789
1034
  await addCommand(components, { overwrite: true });
790
1035
  }
791
1036
  var init_update = __esm({
792
1037
  "src/commands/update.ts"() {
793
1038
  "use strict";
794
1039
  init_add();
1040
+ init_config();
1041
+ }
1042
+ });
1043
+
1044
+ // src/commands/info.ts
1045
+ var info_exports = {};
1046
+ __export(info_exports, {
1047
+ infoCommand: () => infoCommand
1048
+ });
1049
+ import * as p7 from "@clack/prompts";
1050
+ import pc6 from "picocolors";
1051
+ async function infoCommand(component) {
1052
+ const cwd = process.cwd();
1053
+ const config = await readConfig(cwd);
1054
+ if (!config) {
1055
+ p7.log.error("No kitn.json found. Run `kitn init` first.");
1056
+ process.exit(1);
1057
+ }
1058
+ const ref = parseComponentRef(component);
1059
+ const fetcher = new RegistryFetcher(config.registries);
1060
+ const s = p7.spinner();
1061
+ s.start("Fetching component info...");
1062
+ let index;
1063
+ try {
1064
+ index = await fetcher.fetchIndex(ref.namespace);
1065
+ } catch (err) {
1066
+ s.stop(pc6.red("Failed to fetch registry"));
1067
+ p7.log.error(err.message);
1068
+ process.exit(1);
1069
+ }
1070
+ const indexItem = index.items.find((i) => i.name === ref.name);
1071
+ if (!indexItem) {
1072
+ s.stop(pc6.red("Component not found"));
1073
+ p7.log.error(`Component '${ref.name}' not found in registry.`);
1074
+ process.exit(1);
1075
+ }
1076
+ const dir = typeToDir[indexItem.type];
1077
+ let item;
1078
+ try {
1079
+ item = await fetcher.fetchItem(ref.name, dir, ref.namespace, ref.version);
1080
+ } catch (err) {
1081
+ s.stop(pc6.red("Failed to fetch component"));
1082
+ p7.log.error(err.message);
1083
+ process.exit(1);
1084
+ }
1085
+ s.stop("Component found");
1086
+ const version = item.version ?? indexItem.version ?? "unknown";
1087
+ const typeName = indexItem.type.replace("kitn:", "");
1088
+ console.log();
1089
+ console.log(
1090
+ ` ${pc6.bold(item.name)} ${pc6.cyan(`v${version}`)}${" ".repeat(Math.max(1, 40 - item.name.length - version.length - 2))}${pc6.dim(ref.namespace)}`
1091
+ );
1092
+ console.log(` ${pc6.dim(item.description)}`);
1093
+ console.log();
1094
+ console.log(` ${pc6.dim("Type:")} ${typeName}`);
1095
+ if (item.dependencies?.length) {
1096
+ console.log(
1097
+ ` ${pc6.dim("Dependencies:")} ${item.dependencies.join(", ")}`
1098
+ );
1099
+ }
1100
+ if (item.registryDependencies?.length) {
1101
+ console.log(
1102
+ ` ${pc6.dim("Registry deps:")} ${item.registryDependencies.join(", ")}`
1103
+ );
1104
+ }
1105
+ if (item.categories?.length) {
1106
+ console.log(
1107
+ ` ${pc6.dim("Categories:")} ${item.categories.join(", ")}`
1108
+ );
1109
+ }
1110
+ if (item.updatedAt) {
1111
+ console.log(` ${pc6.dim("Updated:")} ${item.updatedAt}`);
1112
+ }
1113
+ const versions = indexItem.versions;
1114
+ if (versions?.length) {
1115
+ console.log(` ${pc6.dim("Versions:")} ${versions.join(", ")}`);
1116
+ }
1117
+ if (item.changelog?.length) {
1118
+ console.log();
1119
+ console.log(` ${pc6.bold("Changelog:")}`);
1120
+ for (const entry of item.changelog) {
1121
+ const tag = entry.type === "feature" ? pc6.green(entry.type) : entry.type === "fix" ? pc6.yellow(entry.type) : entry.type === "breaking" ? pc6.red(entry.type) : pc6.dim(entry.type);
1122
+ console.log(
1123
+ ` ${pc6.cyan(entry.version)} ${pc6.dim(entry.date)} ${tag} ${entry.note}`
1124
+ );
1125
+ }
1126
+ }
1127
+ console.log();
1128
+ const fileCount = item.files.length;
1129
+ console.log(` ${pc6.bold(`Files:`)} ${pc6.dim(`(${fileCount})`)}`);
1130
+ const maxShown = 10;
1131
+ for (const file of item.files.slice(0, maxShown)) {
1132
+ console.log(` ${pc6.dim(file.path)}`);
1133
+ }
1134
+ if (fileCount > maxShown) {
1135
+ console.log(` ${pc6.dim(`... and ${fileCount - maxShown} more`)}`);
1136
+ }
1137
+ const installed = config.installed?.[item.name];
1138
+ if (installed) {
1139
+ console.log();
1140
+ console.log(
1141
+ ` ${pc6.green("Installed")} ${pc6.dim(`v${installed.version}`)}`
1142
+ );
1143
+ if (version !== installed.version) {
1144
+ console.log(
1145
+ ` ${pc6.yellow("Update available:")} ${pc6.dim(`v${installed.version}`)} \u2192 ${pc6.cyan(`v${version}`)}`
1146
+ );
1147
+ }
1148
+ }
1149
+ console.log();
1150
+ }
1151
+ var init_info = __esm({
1152
+ "src/commands/info.ts"() {
1153
+ "use strict";
1154
+ init_config();
1155
+ init_parse_ref();
1156
+ init_fetcher();
1157
+ init_schema();
1158
+ }
1159
+ });
1160
+
1161
+ // src/commands/registry.ts
1162
+ var registry_exports = {};
1163
+ __export(registry_exports, {
1164
+ registryAddCommand: () => registryAddCommand,
1165
+ registryListCommand: () => registryListCommand,
1166
+ registryRemoveCommand: () => registryRemoveCommand
1167
+ });
1168
+ import * as p8 from "@clack/prompts";
1169
+ import pc7 from "picocolors";
1170
+ async function registryAddCommand(namespace, url, opts = {}) {
1171
+ const cwd = opts.cwd ?? process.cwd();
1172
+ const config = await readConfig(cwd);
1173
+ if (!config) throw new Error("No kitn.json found. Run `kitn init` first.");
1174
+ if (!namespace.startsWith("@")) {
1175
+ throw new Error("Namespace must start with @ (e.g. @myteam)");
1176
+ }
1177
+ if (!url.includes("{type}")) {
1178
+ throw new Error("URL template must include {type} placeholder");
1179
+ }
1180
+ if (!url.includes("{name}")) {
1181
+ throw new Error("URL template must include {name} placeholder");
1182
+ }
1183
+ if (config.registries[namespace] && !opts.overwrite) {
1184
+ throw new Error(`Registry '${namespace}' is already configured. Use --overwrite to replace.`);
1185
+ }
1186
+ config.registries[namespace] = url;
1187
+ await writeConfig(cwd, config);
1188
+ p8.log.success(`Added registry ${pc7.bold(namespace)}`);
1189
+ p8.log.message(pc7.dim(` ${url}`));
1190
+ }
1191
+ async function registryRemoveCommand(namespace, opts = {}) {
1192
+ const cwd = opts.cwd ?? process.cwd();
1193
+ const config = await readConfig(cwd);
1194
+ if (!config) throw new Error("No kitn.json found. Run `kitn init` first.");
1195
+ if (!config.registries[namespace]) {
1196
+ throw new Error(`Registry '${namespace}' is not configured.`);
1197
+ }
1198
+ if (namespace === "@kitn" && !opts.force) {
1199
+ throw new Error("Cannot remove the default @kitn registry. Use --force to override.");
1200
+ }
1201
+ const affectedComponents = [];
1202
+ if (config.installed) {
1203
+ for (const [name, entry] of Object.entries(config.installed)) {
1204
+ if (entry.registry === namespace) {
1205
+ affectedComponents.push(name);
1206
+ }
1207
+ }
1208
+ }
1209
+ delete config.registries[namespace];
1210
+ await writeConfig(cwd, config);
1211
+ p8.log.success(`Removed registry ${pc7.bold(namespace)}`);
1212
+ if (affectedComponents.length > 0) {
1213
+ p8.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:`);
1214
+ for (const name of affectedComponents) {
1215
+ p8.log.message(` ${pc7.yellow("!")} ${name}`);
1216
+ }
1217
+ }
1218
+ return { affectedComponents };
1219
+ }
1220
+ async function registryListCommand(opts = {}) {
1221
+ const cwd = opts.cwd ?? process.cwd();
1222
+ const config = await readConfig(cwd);
1223
+ if (!config) throw new Error("No kitn.json found. Run `kitn init` first.");
1224
+ const entries = Object.entries(config.registries).map(([namespace, url]) => ({ namespace, url }));
1225
+ if (entries.length === 0) {
1226
+ p8.log.message(pc7.dim(" No registries configured."));
1227
+ } else {
1228
+ for (const { namespace, url } of entries) {
1229
+ p8.log.message(` ${pc7.bold(namespace.padEnd(16))} ${pc7.dim(url)}`);
1230
+ }
1231
+ }
1232
+ return entries;
1233
+ }
1234
+ var init_registry = __esm({
1235
+ "src/commands/registry.ts"() {
1236
+ "use strict";
1237
+ init_config();
795
1238
  }
796
1239
  });
797
1240
 
@@ -806,7 +1249,7 @@ program.command("add").description("Add components from the kitn registry").argu
806
1249
  const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
807
1250
  await addCommand2(components, opts);
808
1251
  });
809
- 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)").action(async (opts) => {
1252
+ 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) => {
810
1253
  const { listCommand: listCommand2 } = await Promise.resolve().then(() => (init_list(), list_exports));
811
1254
  await listCommand2(opts);
812
1255
  });
@@ -822,5 +1265,22 @@ program.command("update").description("Update installed components to latest reg
822
1265
  const { updateCommand: updateCommand2 } = await Promise.resolve().then(() => (init_update(), update_exports));
823
1266
  await updateCommand2(components);
824
1267
  });
1268
+ 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) => {
1269
+ const { infoCommand: infoCommand2 } = await Promise.resolve().then(() => (init_info(), info_exports));
1270
+ await infoCommand2(component);
1271
+ });
1272
+ var registry = program.command("registry").description("Manage component registries");
1273
+ registry.command("add").description("Add a component registry").argument("<namespace>", "registry namespace (e.g. @myteam)").argument("<url>", "URL template with {type} and {name} placeholders").option("-o, --overwrite", "overwrite if namespace already exists").action(async (namespace, url, opts) => {
1274
+ const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
1275
+ await registryAddCommand2(namespace, url, opts);
1276
+ });
1277
+ registry.command("remove").description("Remove a component registry").argument("<namespace>", "registry namespace to remove (e.g. @myteam)").option("-f, --force", "allow removing the default @kitn registry").action(async (namespace, opts) => {
1278
+ const { registryRemoveCommand: registryRemoveCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
1279
+ await registryRemoveCommand2(namespace, opts);
1280
+ });
1281
+ registry.command("list").description("List all configured registries").action(async () => {
1282
+ const { registryListCommand: registryListCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
1283
+ await registryListCommand2();
1284
+ });
825
1285
  program.parse();
826
1286
  //# sourceMappingURL=index.js.map