@kitnai/cli 0.1.16 → 0.1.18

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
@@ -9,6 +9,86 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // src/utils/update-check.ts
13
+ import { readFile, writeFile, mkdir } from "fs/promises";
14
+ import { join } from "path";
15
+ import { homedir } from "os";
16
+ import pc from "picocolors";
17
+ async function readCache() {
18
+ try {
19
+ const raw = await readFile(CACHE_FILE, "utf-8");
20
+ return JSON.parse(raw);
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+ async function writeCache(entry) {
26
+ try {
27
+ await mkdir(CACHE_DIR, { recursive: true });
28
+ await writeFile(CACHE_FILE, JSON.stringify(entry));
29
+ } catch {
30
+ }
31
+ }
32
+ async function fetchLatestVersion() {
33
+ try {
34
+ const controller = new AbortController();
35
+ const timeout = setTimeout(() => controller.abort(), 3e3);
36
+ const res = await fetch("https://registry.npmjs.org/@kitnai/cli/latest", {
37
+ signal: controller.signal
38
+ });
39
+ clearTimeout(timeout);
40
+ if (!res.ok) return null;
41
+ const data = await res.json();
42
+ return data.version ?? null;
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+ function isNewer(latest, current) {
48
+ const [lMaj, lMin, lPat] = latest.split(".").map(Number);
49
+ const [cMaj, cMin, cPat] = current.split(".").map(Number);
50
+ if (lMaj !== cMaj) return lMaj > cMaj;
51
+ if (lMin !== cMin) return lMin > cMin;
52
+ return lPat > cPat;
53
+ }
54
+ function startUpdateCheck(currentVersion) {
55
+ let message = "";
56
+ const check = (async () => {
57
+ const cache = await readCache();
58
+ let latest = null;
59
+ if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL) {
60
+ latest = cache.latest;
61
+ } else {
62
+ latest = await fetchLatestVersion();
63
+ if (latest) {
64
+ await writeCache({ latest, checkedAt: Date.now() });
65
+ }
66
+ }
67
+ if (latest && isNewer(latest, currentVersion)) {
68
+ message = [
69
+ "",
70
+ pc.yellow(` Update available: ${pc.dim(currentVersion)} \u2192 ${pc.green(latest)}`),
71
+ pc.dim(` Run ${pc.cyan("npx @kitnai/cli@latest")} or ${pc.cyan("npm i -g @kitnai/cli")}`),
72
+ ""
73
+ ].join("\n");
74
+ }
75
+ })();
76
+ check.catch(() => {
77
+ });
78
+ return () => {
79
+ if (message) process.stderr.write(message);
80
+ };
81
+ }
82
+ var CACHE_DIR, CACHE_FILE, CHECK_INTERVAL;
83
+ var init_update_check = __esm({
84
+ "src/utils/update-check.ts"() {
85
+ "use strict";
86
+ CACHE_DIR = join(homedir(), ".kitn");
87
+ CACHE_FILE = join(CACHE_DIR, "update-check.json");
88
+ CHECK_INTERVAL = 60 * 60 * 1e3;
89
+ }
90
+ });
91
+
12
92
  // src/utils/config.ts
13
93
  import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
14
94
  import { join as join2 } from "path";
@@ -565,6 +645,41 @@ var init_parse_ref = __esm({
565
645
  }
566
646
  });
567
647
 
648
+ // src/utils/type-aliases.ts
649
+ function resolveTypeAlias(input) {
650
+ return TYPE_ALIASES[input.toLowerCase()];
651
+ }
652
+ function toComponentType(shortType) {
653
+ const ct = SHORT_TO_COMPONENT[shortType];
654
+ if (!ct) throw new Error(`Unknown component type: ${shortType}`);
655
+ return ct;
656
+ }
657
+ var TYPE_ALIASES, SHORT_TO_COMPONENT;
658
+ var init_type_aliases = __esm({
659
+ "src/utils/type-aliases.ts"() {
660
+ "use strict";
661
+ TYPE_ALIASES = {
662
+ agent: "agent",
663
+ agents: "agent",
664
+ tool: "tool",
665
+ tools: "tool",
666
+ skill: "skill",
667
+ skills: "skill",
668
+ storage: "storage",
669
+ storages: "storage",
670
+ package: "package",
671
+ packages: "package"
672
+ };
673
+ SHORT_TO_COMPONENT = {
674
+ agent: "kitn:agent",
675
+ tool: "kitn:tool",
676
+ skill: "kitn:skill",
677
+ storage: "kitn:storage",
678
+ package: "kitn:package"
679
+ };
680
+ }
681
+ });
682
+
568
683
  // src/registry/schema.ts
569
684
  import { z as z2 } from "zod";
570
685
  var componentType2, registryFileSchema, changelogEntrySchema, envVarConfigSchema, componentConfigSchema, registryItemSchema, registryIndexItemSchema, registryIndexSchema, typeToDir;
@@ -674,6 +789,29 @@ async function addCommand(components, opts) {
674
789
  p2.log.error("Please specify at least one component to add.");
675
790
  process.exit(1);
676
791
  }
792
+ let typeFilter;
793
+ const firstAlias = resolveTypeAlias(components[0]);
794
+ if (firstAlias) {
795
+ if (components.length === 1) {
796
+ p2.log.error(
797
+ `${pc3.bold(components[0])} looks like a type, not a component name. Usage: ${pc3.cyan(`kitn add ${components[0]} <name>`)}`
798
+ );
799
+ process.exit(1);
800
+ }
801
+ typeFilter = firstAlias;
802
+ components = components.slice(1);
803
+ }
804
+ if (opts.type) {
805
+ const flagAlias = resolveTypeAlias(opts.type);
806
+ if (!flagAlias) {
807
+ p2.log.error(`Unknown type ${pc3.bold(opts.type)}. Valid types: agent, tool, skill, storage, package`);
808
+ process.exit(1);
809
+ }
810
+ if (typeFilter && typeFilter !== flagAlias) {
811
+ p2.log.warn(`Positional type ${pc3.bold(typeFilter)} overridden by --type ${pc3.bold(flagAlias)}`);
812
+ }
813
+ typeFilter = flagAlias;
814
+ }
677
815
  const resolvedComponents = components.map((c) => {
678
816
  if (c === "routes") {
679
817
  const fw2 = config.framework ?? "hono";
@@ -685,12 +823,79 @@ async function addCommand(components, opts) {
685
823
  const fetcher = new RegistryFetcher(config.registries);
686
824
  const s = p2.spinner();
687
825
  s.start("Resolving dependencies...");
826
+ const preResolvedTypes = /* @__PURE__ */ new Map();
827
+ let expandedNames = [...resolvedComponents];
828
+ try {
829
+ const namespacesToFetch = Object.keys(config.registries);
830
+ const allIndexItems = [];
831
+ for (const namespace of namespacesToFetch) {
832
+ try {
833
+ const index = await fetcher.fetchIndex(namespace);
834
+ for (const item of index.items) {
835
+ allIndexItems.push({ name: item.name, type: item.type, namespace });
836
+ }
837
+ } catch {
838
+ }
839
+ }
840
+ const newExpandedNames = [];
841
+ for (const name of resolvedComponents) {
842
+ const ref = refs.find((r) => r.name === name) ?? { namespace: "@kitn", name, version: void 0 };
843
+ let matches = allIndexItems.filter((i) => i.name === name && (ref.namespace === "@kitn" || i.namespace === ref.namespace));
844
+ if (typeFilter) {
845
+ const filteredType = toComponentType(typeFilter);
846
+ matches = matches.filter((m) => m.type === filteredType);
847
+ }
848
+ if (matches.length === 0) {
849
+ newExpandedNames.push(name);
850
+ } else if (matches.length === 1) {
851
+ preResolvedTypes.set(name, matches[0].type);
852
+ newExpandedNames.push(name);
853
+ } else {
854
+ const uniqueTypes = [...new Set(matches.map((m) => m.type))];
855
+ if (uniqueTypes.length === 1) {
856
+ preResolvedTypes.set(name, uniqueTypes[0]);
857
+ newExpandedNames.push(name);
858
+ } else {
859
+ s.stop("Disambiguation needed");
860
+ if (!process.stdin.isTTY) {
861
+ const typeNames = uniqueTypes.map((t) => t.replace("kitn:", "")).join(", ");
862
+ p2.log.error(
863
+ `Multiple components named ${pc3.bold(name)} found (${typeNames}). Specify the type: ${pc3.cyan(`kitn add ${uniqueTypes[0].replace("kitn:", "")} ${name}`)}`
864
+ );
865
+ process.exit(1);
866
+ }
867
+ const selected = await p2.multiselect({
868
+ message: `Multiple types found for ${pc3.bold(name)}. Which do you want to install?`,
869
+ options: uniqueTypes.map((t) => ({
870
+ value: t,
871
+ label: `${name} ${pc3.dim(`(${t.replace("kitn:", "")})`)}`
872
+ }))
873
+ });
874
+ if (p2.isCancel(selected)) {
875
+ p2.cancel("Cancelled.");
876
+ process.exit(0);
877
+ }
878
+ for (const type of selected) {
879
+ preResolvedTypes.set(name, type);
880
+ newExpandedNames.push(name);
881
+ }
882
+ s.start("Resolving dependencies...");
883
+ }
884
+ }
885
+ }
886
+ expandedNames = newExpandedNames;
887
+ } catch (err) {
888
+ s.stop(pc3.red("Failed to resolve dependencies"));
889
+ p2.log.error(err.message);
890
+ process.exit(1);
891
+ }
688
892
  let resolved;
689
893
  try {
690
- resolved = await resolveDependencies(resolvedComponents, async (name) => {
894
+ resolved = await resolveDependencies(expandedNames, async (name) => {
691
895
  const ref = refs.find((r) => r.name === name) ?? { namespace: "@kitn", name, version: void 0 };
692
896
  const index = await fetcher.fetchIndex(ref.namespace);
693
- const indexItem = index.items.find((i) => i.name === name);
897
+ const preResolved = preResolvedTypes.get(name);
898
+ const indexItem = preResolved ? index.items.find((i) => i.name === name && i.type === preResolved) : index.items.find((i) => i.name === name);
694
899
  if (!indexItem) throw new Error(`Component '${name}' not found in ${ref.namespace} registry`);
695
900
  const dir = typeToDir[indexItem.type];
696
901
  return fetcher.fetchItem(name, dir, ref.namespace, ref.version);
@@ -702,7 +907,7 @@ async function addCommand(components, opts) {
702
907
  }
703
908
  s.stop(`Resolved ${resolved.length} component(s)`);
704
909
  p2.log.info("Components to install:\n" + resolved.map((item) => {
705
- const isExplicit = resolvedComponents.includes(item.name) || components.includes(item.name);
910
+ const isExplicit = expandedNames.includes(item.name) || components.includes(item.name);
706
911
  const label = isExplicit ? item.name : `${item.name} ${pc3.dim("(dependency)")}`;
707
912
  return ` ${pc3.cyan(label)}`;
708
913
  }).join("\n"));
@@ -714,6 +919,13 @@ async function addCommand(components, opts) {
714
919
  for (const item of resolved) {
715
920
  if (item.dependencies) allDeps.push(...item.dependencies);
716
921
  if (item.devDependencies) allDevDeps.push(...item.devDependencies);
922
+ const existingInstall = (config.installed ?? {})[item.name];
923
+ if (existingInstall && item.type === "kitn:package") {
924
+ const allContent = item.files.map((f) => f.content).join("\n");
925
+ if (contentHash(allContent) === existingInstall.hash) {
926
+ continue;
927
+ }
928
+ }
717
929
  if (item.type === "kitn:package") {
718
930
  const baseDir2 = config.aliases.base ?? "src/ai";
719
931
  for (const file of item.files) {
@@ -899,7 +1111,7 @@ async function addCommand(components, opts) {
899
1111
  }
900
1112
  }
901
1113
  if (created.length > 0) {
902
- p2.log.success(`Created ${created.length} file(s):
1114
+ p2.log.success(`Added ${created.length} file(s):
903
1115
  ` + created.map((f) => ` ${pc3.green("+")} ${f}`).join("\n"));
904
1116
  }
905
1117
  if (updated.length > 0) {
@@ -917,13 +1129,14 @@ async function addCommand(components, opts) {
917
1129
  p2.log.info(`${pc3.bold(item.name)}: ${item.docs}`);
918
1130
  }
919
1131
  }
920
- const installedNames = new Set(resolved.map((r) => r.name));
1132
+ const resolvedNames = new Set(resolved.map((r) => r.name));
1133
+ const projectInstalled = new Set(Object.keys(config.installed ?? {}));
921
1134
  const hints = [];
922
- if (installedNames.has("core") && !installedNames.has(config.framework ?? "hono")) {
1135
+ const fw = config.framework ?? "hono";
1136
+ if (resolvedNames.has("core") && !resolvedNames.has(fw) && !projectInstalled.has(fw)) {
923
1137
  hints.push(`Run ${pc3.cyan(`kitn add routes`)} to install the HTTP adapter.`);
924
1138
  }
925
- const fw = config.framework ?? "hono";
926
- if (installedNames.has(fw) || installedNames.has("core") && installedNames.has(fw)) {
1139
+ if (resolvedNames.has(fw)) {
927
1140
  hints.push(`Configure your AI provider in ${pc3.bold(baseDir + "/plugin.ts")}, then add to your server:`);
928
1141
  hints.push("");
929
1142
  hints.push(pc3.dim(` import { ai } from "./${baseDir.replace(/^src\//, "")}/plugin";`));
@@ -951,6 +1164,7 @@ var init_add = __esm({
951
1164
  init_barrel_manager();
952
1165
  init_hash();
953
1166
  init_parse_ref();
1167
+ init_type_aliases();
954
1168
  init_schema();
955
1169
  }
956
1170
  });
@@ -964,43 +1178,66 @@ import * as p3 from "@clack/prompts";
964
1178
  import pc4 from "picocolors";
965
1179
  import { mkdir as mkdir4, writeFile as writeFile7 } from "fs/promises";
966
1180
  import { join as join8 } from "path";
967
- async function initCommand() {
1181
+ async function initCommand(opts = {}) {
968
1182
  p3.intro(pc4.bgCyan(pc4.black(" kitn init ")));
969
1183
  const cwd = process.cwd();
970
1184
  const existing = await readConfig(cwd);
971
1185
  if (existing) {
972
- p3.log.warn("kitn.json already exists in this directory.");
973
- const shouldContinue = await p3.confirm({
974
- message: "Overwrite existing configuration?",
975
- initialValue: false
1186
+ if (opts.yes) {
1187
+ p3.log.warn("kitn.json already exists \u2014 overwriting (--yes).");
1188
+ } else {
1189
+ p3.log.warn("kitn.json already exists in this directory.");
1190
+ const shouldContinue = await p3.confirm({
1191
+ message: "Overwrite existing configuration?",
1192
+ initialValue: false
1193
+ });
1194
+ if (p3.isCancel(shouldContinue) || !shouldContinue) {
1195
+ p3.cancel("Init cancelled.");
1196
+ process.exit(0);
1197
+ }
1198
+ }
1199
+ }
1200
+ let runtime;
1201
+ if (opts.runtime) {
1202
+ if (!["bun", "node", "deno"].includes(opts.runtime)) {
1203
+ p3.log.error(`Invalid runtime: ${opts.runtime}. Must be bun, node, or deno.`);
1204
+ process.exit(1);
1205
+ }
1206
+ runtime = opts.runtime;
1207
+ } else if (opts.yes) {
1208
+ runtime = "bun";
1209
+ } else {
1210
+ const selected = await p3.select({
1211
+ message: "Which runtime do you use?",
1212
+ options: [
1213
+ { value: "bun", label: "Bun", hint: "recommended" },
1214
+ { value: "node", label: "Node.js" },
1215
+ { value: "deno", label: "Deno" }
1216
+ ]
976
1217
  });
977
- if (p3.isCancel(shouldContinue) || !shouldContinue) {
1218
+ if (p3.isCancel(selected)) {
978
1219
  p3.cancel("Init cancelled.");
979
1220
  process.exit(0);
980
1221
  }
1222
+ runtime = selected;
981
1223
  }
982
- const runtime = await p3.select({
983
- message: "Which runtime do you use?",
984
- options: [
985
- { value: "bun", label: "Bun", hint: "recommended" },
986
- { value: "node", label: "Node.js" },
987
- { value: "deno", label: "Deno" }
988
- ]
989
- });
990
- if (p3.isCancel(runtime)) {
991
- p3.cancel("Init cancelled.");
992
- process.exit(0);
993
- }
994
- const base = await p3.text({
995
- message: "Where should kitn components be installed?",
996
- initialValue: "src/ai",
997
- placeholder: "src/ai"
998
- });
999
- if (p3.isCancel(base)) {
1000
- p3.cancel("Init cancelled.");
1001
- process.exit(0);
1224
+ let baseDir;
1225
+ if (opts.base) {
1226
+ baseDir = opts.base;
1227
+ } else if (opts.yes) {
1228
+ baseDir = "src/ai";
1229
+ } else {
1230
+ const base = await p3.text({
1231
+ message: "Where should kitn components be installed?",
1232
+ initialValue: "src/ai",
1233
+ placeholder: "src/ai"
1234
+ });
1235
+ if (p3.isCancel(base)) {
1236
+ p3.cancel("Init cancelled.");
1237
+ process.exit(0);
1238
+ }
1239
+ baseDir = base;
1002
1240
  }
1003
- const baseDir = base;
1004
1241
  const config = {
1005
1242
  runtime,
1006
1243
  framework: "hono",
@@ -1047,6 +1284,15 @@ async function initCommand() {
1047
1284
  ].join("\n"),
1048
1285
  "Add this to your server entry point:"
1049
1286
  );
1287
+ p3.log.message(
1288
+ [
1289
+ pc4.bold("Add your first agent:"),
1290
+ ` ${pc4.cyan("kitn add weather-agent")}`,
1291
+ "",
1292
+ pc4.bold("Browse all components:"),
1293
+ ` ${pc4.cyan("kitn list")}`
1294
+ ].join("\n")
1295
+ );
1050
1296
  p3.outro("Done!");
1051
1297
  }
1052
1298
  var PLUGIN_TEMPLATE;
@@ -1094,7 +1340,7 @@ async function listCommand(typeFilter, opts) {
1094
1340
  process.exit(1);
1095
1341
  }
1096
1342
  const rawType = typeFilter ?? opts.type;
1097
- const resolvedType = rawType ? TYPE_ALIASES[rawType.toLowerCase()] : void 0;
1343
+ const resolvedType = rawType ? resolveTypeAlias(rawType) : void 0;
1098
1344
  if (rawType && !resolvedType) {
1099
1345
  p4.log.error(`Unknown type ${pc5.bold(rawType)}. Valid types: agent, tool, skill, storage, package`);
1100
1346
  process.exit(1);
@@ -1196,24 +1442,12 @@ async function listCommand(typeFilter, opts) {
1196
1442
  ${pc5.dim(parts.join(" \xB7 "))}
1197
1443
  `);
1198
1444
  }
1199
- var TYPE_ALIASES;
1200
1445
  var init_list = __esm({
1201
1446
  "src/commands/list.ts"() {
1202
1447
  "use strict";
1203
1448
  init_config();
1449
+ init_type_aliases();
1204
1450
  init_fetcher();
1205
- TYPE_ALIASES = {
1206
- agent: "agent",
1207
- agents: "agent",
1208
- tool: "tool",
1209
- tools: "tool",
1210
- skill: "skill",
1211
- skills: "skill",
1212
- storage: "storage",
1213
- storages: "storage",
1214
- package: "package",
1215
- packages: "package"
1216
- };
1217
1451
  }
1218
1452
  });
1219
1453
 
@@ -1458,8 +1692,8 @@ async function scanForComponents(cwd, paths) {
1458
1692
  const resolvedCwd = resolve(cwd);
1459
1693
  if (paths && paths.length > 0) {
1460
1694
  const results = [];
1461
- for (const p12 of paths) {
1462
- const absPath = resolve(resolvedCwd, p12);
1695
+ for (const p13 of paths) {
1696
+ const absPath = resolve(resolvedCwd, p13);
1463
1697
  if (await fileExists(join11(absPath, "registry.json"))) {
1464
1698
  results.push(absPath);
1465
1699
  continue;
@@ -2099,6 +2333,39 @@ var init_info = __esm({
2099
2333
  }
2100
2334
  });
2101
2335
 
2336
+ // src/commands/check.ts
2337
+ var check_exports = {};
2338
+ __export(check_exports, {
2339
+ checkCommand: () => checkCommand
2340
+ });
2341
+ import * as p11 from "@clack/prompts";
2342
+ import pc10 from "picocolors";
2343
+ async function checkCommand(currentVersion) {
2344
+ p11.intro(pc10.bgCyan(pc10.black(" kitn check ")));
2345
+ p11.log.info(`kitn v${currentVersion}`);
2346
+ const s = p11.spinner();
2347
+ s.start("Checking for updates...");
2348
+ const latest = await fetchLatestVersion();
2349
+ if (!latest) {
2350
+ s.stop(pc10.yellow("Could not reach the npm registry"));
2351
+ p11.outro("Try again later.");
2352
+ return;
2353
+ }
2354
+ if (isNewer(latest, currentVersion)) {
2355
+ s.stop(pc10.yellow(`Update available: ${currentVersion} \u2192 ${latest}`));
2356
+ p11.log.message(` Run: ${pc10.cyan("npm i -g @kitnai/cli")}`);
2357
+ } else {
2358
+ s.stop(pc10.green("You're on the latest version"));
2359
+ }
2360
+ p11.outro("");
2361
+ }
2362
+ var init_check = __esm({
2363
+ "src/commands/check.ts"() {
2364
+ "use strict";
2365
+ init_update_check();
2366
+ }
2367
+ });
2368
+
2102
2369
  // src/commands/registry.ts
2103
2370
  var registry_exports = {};
2104
2371
  __export(registry_exports, {
@@ -2106,8 +2373,8 @@ __export(registry_exports, {
2106
2373
  registryListCommand: () => registryListCommand,
2107
2374
  registryRemoveCommand: () => registryRemoveCommand
2108
2375
  });
2109
- import * as p11 from "@clack/prompts";
2110
- import pc10 from "picocolors";
2376
+ import * as p12 from "@clack/prompts";
2377
+ import pc11 from "picocolors";
2111
2378
  async function registryAddCommand(namespace, url, opts = {}) {
2112
2379
  const cwd = opts.cwd ?? process.cwd();
2113
2380
  const config = await readConfig(cwd);
@@ -2133,10 +2400,10 @@ async function registryAddCommand(namespace, url, opts = {}) {
2133
2400
  config.registries[namespace] = url;
2134
2401
  }
2135
2402
  await writeConfig(cwd, config);
2136
- p11.log.success(`Added registry ${pc10.bold(namespace)}`);
2137
- p11.log.message(pc10.dim(` ${url}`));
2138
- if (opts.homepage) p11.log.message(pc10.dim(` Homepage: ${opts.homepage}`));
2139
- if (opts.description) p11.log.message(pc10.dim(` ${opts.description}`));
2403
+ p12.log.success(`Added registry ${pc11.bold(namespace)}`);
2404
+ p12.log.message(pc11.dim(` ${url}`));
2405
+ if (opts.homepage) p12.log.message(pc11.dim(` Homepage: ${opts.homepage}`));
2406
+ if (opts.description) p12.log.message(pc11.dim(` ${opts.description}`));
2140
2407
  }
2141
2408
  async function registryRemoveCommand(namespace, opts = {}) {
2142
2409
  const cwd = opts.cwd ?? process.cwd();
@@ -2158,10 +2425,10 @@ async function registryRemoveCommand(namespace, opts = {}) {
2158
2425
  }
2159
2426
  delete config.registries[namespace];
2160
2427
  await writeConfig(cwd, config);
2161
- p11.log.success(`Removed registry ${pc10.bold(namespace)}`);
2428
+ p12.log.success(`Removed registry ${pc11.bold(namespace)}`);
2162
2429
  if (affectedComponents.length > 0) {
2163
- p11.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:
2164
- ` + affectedComponents.map((name) => ` ${pc10.yellow("!")} ${name}`).join("\n"));
2430
+ p12.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:
2431
+ ` + affectedComponents.map((name) => ` ${pc11.yellow("!")} ${name}`).join("\n"));
2165
2432
  }
2166
2433
  return { affectedComponents };
2167
2434
  }
@@ -2176,15 +2443,15 @@ async function registryListCommand(opts = {}) {
2176
2443
  return { namespace, url, homepage, description };
2177
2444
  });
2178
2445
  if (entries.length === 0) {
2179
- p11.log.message(pc10.dim(" No registries configured."));
2446
+ p12.log.message(pc11.dim(" No registries configured."));
2180
2447
  } else {
2181
2448
  const lines = [];
2182
2449
  for (const { namespace, url, homepage, description } of entries) {
2183
- lines.push(` ${pc10.bold(namespace.padEnd(16))} ${pc10.dim(url)}`);
2450
+ lines.push(` ${pc11.bold(namespace.padEnd(16))} ${pc11.dim(url)}`);
2184
2451
  if (description) lines.push(` ${" ".repeat(16)} ${description}`);
2185
- if (homepage) lines.push(` ${" ".repeat(16)} ${pc10.dim(homepage)}`);
2452
+ if (homepage) lines.push(` ${" ".repeat(16)} ${pc11.dim(homepage)}`);
2186
2453
  }
2187
- p11.log.message(lines.join("\n"));
2454
+ p12.log.message(lines.join("\n"));
2188
2455
  }
2189
2456
  return entries;
2190
2457
  }
@@ -2196,91 +2463,16 @@ var init_registry = __esm({
2196
2463
  });
2197
2464
 
2198
2465
  // src/index.ts
2466
+ init_update_check();
2199
2467
  import { Command } from "commander";
2200
-
2201
- // src/utils/update-check.ts
2202
- import { readFile, writeFile, mkdir } from "fs/promises";
2203
- import { join } from "path";
2204
- import { homedir } from "os";
2205
- import pc from "picocolors";
2206
- var CACHE_DIR = join(homedir(), ".kitn");
2207
- var CACHE_FILE = join(CACHE_DIR, "update-check.json");
2208
- var CHECK_INTERVAL = 60 * 60 * 1e3;
2209
- async function readCache() {
2210
- try {
2211
- const raw = await readFile(CACHE_FILE, "utf-8");
2212
- return JSON.parse(raw);
2213
- } catch {
2214
- return null;
2215
- }
2216
- }
2217
- async function writeCache(entry) {
2218
- try {
2219
- await mkdir(CACHE_DIR, { recursive: true });
2220
- await writeFile(CACHE_FILE, JSON.stringify(entry));
2221
- } catch {
2222
- }
2223
- }
2224
- async function fetchLatestVersion() {
2225
- try {
2226
- const controller = new AbortController();
2227
- const timeout = setTimeout(() => controller.abort(), 3e3);
2228
- const res = await fetch("https://registry.npmjs.org/@kitnai/cli/latest", {
2229
- signal: controller.signal
2230
- });
2231
- clearTimeout(timeout);
2232
- if (!res.ok) return null;
2233
- const data = await res.json();
2234
- return data.version ?? null;
2235
- } catch {
2236
- return null;
2237
- }
2238
- }
2239
- function isNewer(latest, current) {
2240
- const [lMaj, lMin, lPat] = latest.split(".").map(Number);
2241
- const [cMaj, cMin, cPat] = current.split(".").map(Number);
2242
- if (lMaj !== cMaj) return lMaj > cMaj;
2243
- if (lMin !== cMin) return lMin > cMin;
2244
- return lPat > cPat;
2245
- }
2246
- function startUpdateCheck(currentVersion) {
2247
- let message = "";
2248
- const check = (async () => {
2249
- const cache = await readCache();
2250
- let latest = null;
2251
- if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL) {
2252
- latest = cache.latest;
2253
- } else {
2254
- latest = await fetchLatestVersion();
2255
- if (latest) {
2256
- await writeCache({ latest, checkedAt: Date.now() });
2257
- }
2258
- }
2259
- if (latest && isNewer(latest, currentVersion)) {
2260
- message = [
2261
- "",
2262
- pc.yellow(` Update available: ${pc.dim(currentVersion)} \u2192 ${pc.green(latest)}`),
2263
- pc.dim(` Run ${pc.cyan("npx @kitnai/cli@latest")} or ${pc.cyan("npm i -g @kitnai/cli")}`),
2264
- ""
2265
- ].join("\n");
2266
- }
2267
- })();
2268
- check.catch(() => {
2269
- });
2270
- return () => {
2271
- if (message) process.stderr.write(message);
2272
- };
2273
- }
2274
-
2275
- // src/index.ts
2276
- var VERSION = true ? "0.1.16" : "0.0.0-dev";
2468
+ var VERSION = true ? "0.1.18" : "0.0.0-dev";
2277
2469
  var printUpdateNotice = startUpdateCheck(VERSION);
2278
2470
  var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
2279
- program.command("init").description("Initialize kitn in your project").action(async () => {
2471
+ program.command("init").description("Initialize kitn in your project").option("-r, --runtime <runtime>", "runtime to use (bun, node, deno)").option("-b, --base <path>", "base directory for components (default: src/ai)").option("-y, --yes", "accept all defaults without prompting").action(async (opts) => {
2280
2472
  const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
2281
- await initCommand2();
2473
+ await initCommand2(opts);
2282
2474
  });
2283
- program.command("add").description("Add components from the kitn registry").argument("[components...]", "component names to install").option("-o, --overwrite", "overwrite existing files without prompting").option("-t, --type <type>", "filter by component type").action(async (components, opts) => {
2475
+ program.command("add").description("Add components from the registry (supports type-first: kitn add agent <name>)").argument("[components...]", "component names or type followed by names").option("-o, --overwrite", "overwrite existing files without prompting").option("-t, --type <type>", "filter by component type during resolution").action(async (components, opts) => {
2284
2476
  const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
2285
2477
  await addCommand2(components, opts);
2286
2478
  });
@@ -2312,6 +2504,10 @@ program.command("info").description("Show details about a component").argument("
2312
2504
  const { infoCommand: infoCommand2 } = await Promise.resolve().then(() => (init_info(), info_exports));
2313
2505
  await infoCommand2(component);
2314
2506
  });
2507
+ program.command("check").description("Check for CLI updates").action(async () => {
2508
+ const { checkCommand: checkCommand2 } = await Promise.resolve().then(() => (init_check(), check_exports));
2509
+ await checkCommand2(VERSION);
2510
+ });
2315
2511
  var registry = program.command("registry").description("Manage component registries");
2316
2512
  registry.command("add").description("Add a component registry").argument("<namespace>", "registry namespace (e.g. @myteam)").argument("<url>", "URL template with {type} and {name} placeholders").option("-o, --overwrite", "overwrite if namespace already exists").option("--homepage <url>", "registry homepage URL").option("--description <text>", "short description of the registry").action(async (namespace, url, opts) => {
2317
2513
  const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));