@kitnai/cli 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -0
- package/dist/index.js +697 -234
- package/dist/index.js.map +1 -1
- package/package.json +11 -3
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
|
-
|
|
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
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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(
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
208
|
-
|
|
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[
|
|
227
|
-
if (!template) throw new Error(
|
|
228
|
-
|
|
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[
|
|
239
|
-
if (!template) throw new Error(
|
|
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,79 @@ 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 stripJsonc(text2) {
|
|
387
|
+
return text2.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([}\]])/g, "$1");
|
|
388
|
+
}
|
|
389
|
+
function patchTsconfig(tsconfigContent, paths) {
|
|
390
|
+
const config = JSON.parse(stripJsonc(tsconfigContent));
|
|
391
|
+
if (!config.compilerOptions) {
|
|
392
|
+
config.compilerOptions = {};
|
|
393
|
+
}
|
|
394
|
+
if (!config.compilerOptions.paths) {
|
|
395
|
+
config.compilerOptions.paths = {};
|
|
396
|
+
}
|
|
397
|
+
for (const [key, value] of Object.entries(paths)) {
|
|
398
|
+
config.compilerOptions.paths[key] = value;
|
|
399
|
+
}
|
|
400
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
401
|
+
}
|
|
402
|
+
async function patchProjectTsconfig(projectDir, paths) {
|
|
403
|
+
const tsconfigPath = join4(projectDir, "tsconfig.json");
|
|
404
|
+
let content;
|
|
405
|
+
try {
|
|
406
|
+
content = await readFile3(tsconfigPath, "utf-8");
|
|
407
|
+
} catch {
|
|
408
|
+
content = "{}";
|
|
409
|
+
}
|
|
410
|
+
const patched = patchTsconfig(content, paths);
|
|
411
|
+
await writeFile3(tsconfigPath, patched);
|
|
412
|
+
}
|
|
413
|
+
var init_tsconfig_patcher = __esm({
|
|
414
|
+
"src/installers/tsconfig-patcher.ts"() {
|
|
415
|
+
"use strict";
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
|
|
362
419
|
// src/utils/hash.ts
|
|
363
420
|
import { createHash } from "crypto";
|
|
364
421
|
function contentHash(content) {
|
|
@@ -370,18 +427,52 @@ var init_hash = __esm({
|
|
|
370
427
|
}
|
|
371
428
|
});
|
|
372
429
|
|
|
430
|
+
// src/utils/parse-ref.ts
|
|
431
|
+
function parseComponentRef(input) {
|
|
432
|
+
let namespace = "@kitn";
|
|
433
|
+
let rest = input;
|
|
434
|
+
if (rest.startsWith("@")) {
|
|
435
|
+
const slashIdx = rest.indexOf("/");
|
|
436
|
+
if (slashIdx === -1) {
|
|
437
|
+
throw new Error(`Invalid component reference: ${input}. Expected @namespace/name`);
|
|
438
|
+
}
|
|
439
|
+
namespace = rest.slice(0, slashIdx);
|
|
440
|
+
rest = rest.slice(slashIdx + 1);
|
|
441
|
+
}
|
|
442
|
+
const atIdx = rest.indexOf("@");
|
|
443
|
+
if (atIdx === -1) {
|
|
444
|
+
return { namespace, name: rest, version: void 0 };
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
namespace,
|
|
448
|
+
name: rest.slice(0, atIdx),
|
|
449
|
+
version: rest.slice(atIdx + 1)
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
var init_parse_ref = __esm({
|
|
453
|
+
"src/utils/parse-ref.ts"() {
|
|
454
|
+
"use strict";
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
373
458
|
// src/registry/schema.ts
|
|
374
459
|
import { z as z2 } from "zod";
|
|
375
|
-
var componentType2, registryFileSchema, registryItemSchema, registryIndexItemSchema, registryIndexSchema, typeToDir;
|
|
460
|
+
var componentType2, registryFileSchema, changelogEntrySchema, registryItemSchema, registryIndexItemSchema, registryIndexSchema, typeToDir;
|
|
376
461
|
var init_schema = __esm({
|
|
377
462
|
"src/registry/schema.ts"() {
|
|
378
463
|
"use strict";
|
|
379
|
-
componentType2 = z2.enum(["kitn:agent", "kitn:tool", "kitn:skill", "kitn:storage"]);
|
|
464
|
+
componentType2 = z2.enum(["kitn:agent", "kitn:tool", "kitn:skill", "kitn:storage", "kitn:package"]);
|
|
380
465
|
registryFileSchema = z2.object({
|
|
381
466
|
path: z2.string(),
|
|
382
467
|
content: z2.string(),
|
|
383
468
|
type: componentType2
|
|
384
469
|
});
|
|
470
|
+
changelogEntrySchema = z2.object({
|
|
471
|
+
version: z2.string(),
|
|
472
|
+
date: z2.string(),
|
|
473
|
+
type: z2.enum(["feature", "fix", "breaking", "initial"]),
|
|
474
|
+
note: z2.string()
|
|
475
|
+
});
|
|
385
476
|
registryItemSchema = z2.object({
|
|
386
477
|
$schema: z2.string().optional(),
|
|
387
478
|
name: z2.string(),
|
|
@@ -392,9 +483,13 @@ var init_schema = __esm({
|
|
|
392
483
|
registryDependencies: z2.array(z2.string()).optional(),
|
|
393
484
|
envVars: z2.record(z2.string(), z2.string()).optional(),
|
|
394
485
|
files: z2.array(registryFileSchema),
|
|
486
|
+
installDir: z2.string().optional(),
|
|
487
|
+
tsconfig: z2.record(z2.string(), z2.array(z2.string())).optional(),
|
|
395
488
|
docs: z2.string().optional(),
|
|
396
489
|
categories: z2.array(z2.string()).optional(),
|
|
397
|
-
version: z2.string().optional()
|
|
490
|
+
version: z2.string().optional(),
|
|
491
|
+
updatedAt: z2.string().optional(),
|
|
492
|
+
changelog: z2.array(changelogEntrySchema).optional()
|
|
398
493
|
});
|
|
399
494
|
registryIndexItemSchema = z2.object({
|
|
400
495
|
name: z2.string(),
|
|
@@ -402,7 +497,9 @@ var init_schema = __esm({
|
|
|
402
497
|
description: z2.string(),
|
|
403
498
|
registryDependencies: z2.array(z2.string()).optional(),
|
|
404
499
|
categories: z2.array(z2.string()).optional(),
|
|
405
|
-
version: z2.string().optional()
|
|
500
|
+
version: z2.string().optional(),
|
|
501
|
+
versions: z2.array(z2.string()).optional(),
|
|
502
|
+
updatedAt: z2.string().optional()
|
|
406
503
|
});
|
|
407
504
|
registryIndexSchema = z2.object({
|
|
408
505
|
$schema: z2.string().optional(),
|
|
@@ -413,7 +510,8 @@ var init_schema = __esm({
|
|
|
413
510
|
"kitn:agent": "agents",
|
|
414
511
|
"kitn:tool": "tools",
|
|
415
512
|
"kitn:skill": "skills",
|
|
416
|
-
"kitn:storage": "storage"
|
|
513
|
+
"kitn:storage": "storage",
|
|
514
|
+
"kitn:package": "package"
|
|
417
515
|
};
|
|
418
516
|
}
|
|
419
517
|
});
|
|
@@ -425,7 +523,7 @@ __export(add_exports, {
|
|
|
425
523
|
});
|
|
426
524
|
import * as p2 from "@clack/prompts";
|
|
427
525
|
import pc3 from "picocolors";
|
|
428
|
-
import { join as
|
|
526
|
+
import { join as join5 } from "path";
|
|
429
527
|
async function addCommand(components, opts) {
|
|
430
528
|
p2.intro(pc3.bgCyan(pc3.black(" kitn add ")));
|
|
431
529
|
const cwd = process.cwd();
|
|
@@ -438,17 +536,26 @@ async function addCommand(components, opts) {
|
|
|
438
536
|
p2.log.error("Please specify at least one component to add.");
|
|
439
537
|
process.exit(1);
|
|
440
538
|
}
|
|
539
|
+
const resolvedComponents = components.map((c) => {
|
|
540
|
+
if (c === "routes") {
|
|
541
|
+
const fw = config.framework ?? "hono";
|
|
542
|
+
return fw;
|
|
543
|
+
}
|
|
544
|
+
return c;
|
|
545
|
+
});
|
|
546
|
+
const refs = resolvedComponents.map(parseComponentRef);
|
|
441
547
|
const fetcher = new RegistryFetcher(config.registries);
|
|
442
548
|
const s = p2.spinner();
|
|
443
549
|
s.start("Resolving dependencies...");
|
|
444
550
|
let resolved;
|
|
445
551
|
try {
|
|
446
|
-
resolved = await resolveDependencies(
|
|
447
|
-
const
|
|
552
|
+
resolved = await resolveDependencies(resolvedComponents, async (name) => {
|
|
553
|
+
const ref = refs.find((r) => r.name === name) ?? { namespace: "@kitn", name, version: void 0 };
|
|
554
|
+
const index = await fetcher.fetchIndex(ref.namespace);
|
|
448
555
|
const indexItem = index.items.find((i) => i.name === name);
|
|
449
|
-
if (!indexItem) throw new Error(`Component '${name}' not found in registry`);
|
|
556
|
+
if (!indexItem) throw new Error(`Component '${name}' not found in ${ref.namespace} registry`);
|
|
450
557
|
const dir = typeToDir[indexItem.type];
|
|
451
|
-
return fetcher.fetchItem(name, dir);
|
|
558
|
+
return fetcher.fetchItem(name, dir, ref.namespace, ref.version);
|
|
452
559
|
});
|
|
453
560
|
} catch (err) {
|
|
454
561
|
s.stop(pc3.red("Failed to resolve dependencies"));
|
|
@@ -458,7 +565,7 @@ async function addCommand(components, opts) {
|
|
|
458
565
|
s.stop(`Resolved ${resolved.length} component(s)`);
|
|
459
566
|
p2.log.info("Components to install:");
|
|
460
567
|
for (const item of resolved) {
|
|
461
|
-
const isExplicit = components.includes(item.name);
|
|
568
|
+
const isExplicit = resolvedComponents.includes(item.name) || components.includes(item.name);
|
|
462
569
|
const label = isExplicit ? item.name : `${item.name} ${pc3.dim("(dependency)")}`;
|
|
463
570
|
p2.log.message(` ${pc3.cyan(label)}`);
|
|
464
571
|
}
|
|
@@ -472,62 +579,68 @@ async function addCommand(components, opts) {
|
|
|
472
579
|
if (item.envVars) {
|
|
473
580
|
allEnvWarnings.push(...checkEnvVars(item.envVars));
|
|
474
581
|
}
|
|
475
|
-
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
case "
|
|
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) {
|
|
582
|
+
if (item.type === "kitn:package") {
|
|
583
|
+
const baseDir = config.aliases.base ?? "src/ai";
|
|
584
|
+
for (const file of item.files) {
|
|
585
|
+
const targetPath = join5(cwd, baseDir, file.path);
|
|
586
|
+
const relativePath = join5(baseDir, file.path);
|
|
587
|
+
const status = await checkFileStatus(targetPath, file.content);
|
|
588
|
+
switch (status) {
|
|
589
|
+
case "new" /* New */:
|
|
502
590
|
await writeComponentFile(targetPath, file.content);
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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") {
|
|
591
|
+
created.push(relativePath);
|
|
592
|
+
break;
|
|
593
|
+
case "identical" /* Identical */:
|
|
594
|
+
skipped.push(relativePath);
|
|
595
|
+
break;
|
|
596
|
+
case "different" /* Different */:
|
|
597
|
+
if (opts.overwrite) {
|
|
516
598
|
await writeComponentFile(targetPath, file.content);
|
|
517
599
|
updated.push(relativePath);
|
|
518
600
|
} else {
|
|
519
|
-
|
|
601
|
+
const existing = await readExistingFile(targetPath);
|
|
602
|
+
const diff = generateDiff(relativePath, existing ?? "", file.content);
|
|
603
|
+
p2.log.message(pc3.dim(diff));
|
|
604
|
+
const action = await p2.select({
|
|
605
|
+
message: `${relativePath} already exists and differs. What to do?`,
|
|
606
|
+
options: [
|
|
607
|
+
{ value: "skip", label: "Keep local version" },
|
|
608
|
+
{ value: "overwrite", label: "Overwrite with registry version" }
|
|
609
|
+
]
|
|
610
|
+
});
|
|
611
|
+
if (!p2.isCancel(action) && action === "overwrite") {
|
|
612
|
+
await writeComponentFile(targetPath, file.content);
|
|
613
|
+
updated.push(relativePath);
|
|
614
|
+
} else {
|
|
615
|
+
skipped.push(relativePath);
|
|
616
|
+
}
|
|
520
617
|
}
|
|
521
|
-
|
|
522
|
-
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
523
620
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
621
|
+
if (item.tsconfig) {
|
|
622
|
+
const resolvedPaths = {};
|
|
623
|
+
const installDir = item.installDir ?? item.name;
|
|
624
|
+
for (const [key, values] of Object.entries(item.tsconfig)) {
|
|
625
|
+
resolvedPaths[key] = values.map((v) => `./${join5(baseDir, installDir, v)}`);
|
|
626
|
+
}
|
|
627
|
+
await patchProjectTsconfig(cwd, resolvedPaths);
|
|
628
|
+
p2.log.info(`Patched tsconfig.json with paths: ${Object.keys(resolvedPaths).join(", ")}`);
|
|
629
|
+
}
|
|
630
|
+
const installed = config.installed ?? {};
|
|
631
|
+
const allContent = item.files.map((f) => f.content).join("\n");
|
|
632
|
+
const ref = refs.find((r) => r.name === item.name) ?? { namespace: "@kitn", name: item.name, version: void 0 };
|
|
633
|
+
const installedKey = ref.namespace === "@kitn" ? item.name : `${ref.namespace}/${item.name}`;
|
|
634
|
+
installed[installedKey] = {
|
|
635
|
+
registry: ref.namespace,
|
|
636
|
+
version: item.version ?? "1.0.0",
|
|
637
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
638
|
+
files: item.files.map((f) => join5(baseDir, f.path)),
|
|
639
|
+
hash: contentHash(allContent)
|
|
640
|
+
};
|
|
641
|
+
config.installed = installed;
|
|
642
|
+
} else {
|
|
643
|
+
for (const file of item.files) {
|
|
531
644
|
const aliasKey = (() => {
|
|
532
645
|
switch (item.type) {
|
|
533
646
|
case "kitn:agent":
|
|
@@ -540,12 +653,75 @@ async function addCommand(components, opts) {
|
|
|
540
653
|
return "storage";
|
|
541
654
|
}
|
|
542
655
|
})();
|
|
543
|
-
const fileName =
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
656
|
+
const fileName = file.path.split("/").pop();
|
|
657
|
+
const targetPath = join5(cwd, config.aliases[aliasKey], fileName);
|
|
658
|
+
const relativePath = join5(config.aliases[aliasKey], fileName);
|
|
659
|
+
const content = rewriteKitnImports(file.content, item.type, fileName, config.aliases);
|
|
660
|
+
const status = await checkFileStatus(targetPath, content);
|
|
661
|
+
switch (status) {
|
|
662
|
+
case "new" /* New */:
|
|
663
|
+
await writeComponentFile(targetPath, content);
|
|
664
|
+
created.push(relativePath);
|
|
665
|
+
break;
|
|
666
|
+
case "identical" /* Identical */:
|
|
667
|
+
skipped.push(relativePath);
|
|
668
|
+
break;
|
|
669
|
+
case "different" /* Different */:
|
|
670
|
+
if (opts.overwrite) {
|
|
671
|
+
await writeComponentFile(targetPath, content);
|
|
672
|
+
updated.push(relativePath);
|
|
673
|
+
} else {
|
|
674
|
+
const existing = await readExistingFile(targetPath);
|
|
675
|
+
const diff = generateDiff(relativePath, existing ?? "", content);
|
|
676
|
+
p2.log.message(pc3.dim(diff));
|
|
677
|
+
const action = await p2.select({
|
|
678
|
+
message: `${relativePath} already exists and differs. What to do?`,
|
|
679
|
+
options: [
|
|
680
|
+
{ value: "skip", label: "Keep local version" },
|
|
681
|
+
{ value: "overwrite", label: "Overwrite with registry version" }
|
|
682
|
+
]
|
|
683
|
+
});
|
|
684
|
+
if (!p2.isCancel(action) && action === "overwrite") {
|
|
685
|
+
await writeComponentFile(targetPath, content);
|
|
686
|
+
updated.push(relativePath);
|
|
687
|
+
} else {
|
|
688
|
+
skipped.push(relativePath);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
const installed = config.installed ?? {};
|
|
695
|
+
const allContent = item.files.map((f) => {
|
|
696
|
+
const fn = f.path.split("/").pop();
|
|
697
|
+
return rewriteKitnImports(f.content, item.type, fn, config.aliases);
|
|
698
|
+
}).join("\n");
|
|
699
|
+
const ref = refs.find((r) => r.name === item.name) ?? { namespace: "@kitn", name: item.name, version: void 0 };
|
|
700
|
+
const installedKey = ref.namespace === "@kitn" ? item.name : `${ref.namespace}/${item.name}`;
|
|
701
|
+
installed[installedKey] = {
|
|
702
|
+
registry: ref.namespace,
|
|
703
|
+
version: item.version ?? "1.0.0",
|
|
704
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
705
|
+
files: item.files.map((f) => {
|
|
706
|
+
const aliasKey = (() => {
|
|
707
|
+
switch (item.type) {
|
|
708
|
+
case "kitn:agent":
|
|
709
|
+
return "agents";
|
|
710
|
+
case "kitn:tool":
|
|
711
|
+
return "tools";
|
|
712
|
+
case "kitn:skill":
|
|
713
|
+
return "skills";
|
|
714
|
+
case "kitn:storage":
|
|
715
|
+
return "storage";
|
|
716
|
+
}
|
|
717
|
+
})();
|
|
718
|
+
const fileName = f.path.split("/").pop();
|
|
719
|
+
return join5(config.aliases[aliasKey], fileName);
|
|
720
|
+
}),
|
|
721
|
+
hash: contentHash(allContent)
|
|
722
|
+
};
|
|
723
|
+
config.installed = installed;
|
|
724
|
+
}
|
|
549
725
|
}
|
|
550
726
|
await writeConfig(cwd, config);
|
|
551
727
|
const uniqueDeps = [...new Set(allDeps)];
|
|
@@ -594,7 +770,10 @@ var init_add = __esm({
|
|
|
594
770
|
init_file_writer();
|
|
595
771
|
init_dep_installer();
|
|
596
772
|
init_env_checker();
|
|
773
|
+
init_import_rewriter();
|
|
774
|
+
init_tsconfig_patcher();
|
|
597
775
|
init_hash();
|
|
776
|
+
init_parse_ref();
|
|
598
777
|
init_schema();
|
|
599
778
|
}
|
|
600
779
|
});
|
|
@@ -614,40 +793,70 @@ async function listCommand(opts) {
|
|
|
614
793
|
process.exit(1);
|
|
615
794
|
}
|
|
616
795
|
const fetcher = new RegistryFetcher(config.registries);
|
|
796
|
+
const namespacesToFetch = opts.registry ? [opts.registry] : Object.keys(config.registries);
|
|
797
|
+
if (opts.registry && !config.registries[opts.registry]) {
|
|
798
|
+
p3.log.error(`Registry ${pc4.bold(opts.registry)} is not configured. Run ${pc4.bold("kitn registry list")} to see configured registries.`);
|
|
799
|
+
process.exit(1);
|
|
800
|
+
}
|
|
617
801
|
const s = p3.spinner();
|
|
618
802
|
s.start("Fetching registry index...");
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
803
|
+
const allItems = [];
|
|
804
|
+
const errors = [];
|
|
805
|
+
for (const namespace of namespacesToFetch) {
|
|
806
|
+
try {
|
|
807
|
+
const index = await fetcher.fetchIndex(namespace);
|
|
808
|
+
for (const item of index.items) {
|
|
809
|
+
allItems.push({ ...item, namespace });
|
|
810
|
+
}
|
|
811
|
+
} catch (err) {
|
|
812
|
+
errors.push(`${namespace}: ${err.message}`);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
if (allItems.length === 0 && errors.length > 0) {
|
|
816
|
+
s.stop(pc4.red("Failed to fetch registries"));
|
|
817
|
+
for (const e of errors) p3.log.error(e);
|
|
625
818
|
process.exit(1);
|
|
626
819
|
}
|
|
627
|
-
s.stop(`Found ${
|
|
628
|
-
const
|
|
820
|
+
s.stop(`Found ${allItems.length} components across ${namespacesToFetch.length - errors.length} ${namespacesToFetch.length - errors.length === 1 ? "registry" : "registries"}`);
|
|
821
|
+
for (const e of errors) {
|
|
822
|
+
p3.log.warn(`${pc4.yellow("\u26A0")} Failed to fetch ${e}`);
|
|
823
|
+
}
|
|
824
|
+
const installed = config.installed ?? {};
|
|
629
825
|
const typeGroups = /* @__PURE__ */ new Map();
|
|
630
|
-
for (const item of
|
|
826
|
+
for (const item of allItems) {
|
|
631
827
|
if (opts.type && !item.type.endsWith(opts.type)) continue;
|
|
632
|
-
const
|
|
633
|
-
if (!typeGroups.has(
|
|
634
|
-
typeGroups.get(
|
|
828
|
+
const group = item.type.replace("kitn:", "");
|
|
829
|
+
if (!typeGroups.has(group)) typeGroups.set(group, []);
|
|
830
|
+
typeGroups.get(group).push(item);
|
|
635
831
|
}
|
|
636
|
-
|
|
832
|
+
let installedCount = 0;
|
|
833
|
+
let updateCount = 0;
|
|
834
|
+
for (const [group, items] of typeGroups) {
|
|
637
835
|
p3.log.message(pc4.bold(`
|
|
638
|
-
${
|
|
836
|
+
${group.charAt(0).toUpperCase() + group.slice(1)}s:`));
|
|
639
837
|
for (const item of items) {
|
|
640
|
-
const
|
|
838
|
+
const displayName = item.namespace === "@kitn" ? item.name : `${item.namespace}/${item.name}`;
|
|
839
|
+
const inst = installed[item.name] ?? installed[displayName];
|
|
641
840
|
if (opts.installed && !inst) continue;
|
|
841
|
+
const version = pc4.dim(`v${item.version ?? "1.0.0"}`);
|
|
642
842
|
if (inst) {
|
|
843
|
+
installedCount++;
|
|
643
844
|
const status = pc4.green("\u2713");
|
|
644
|
-
|
|
845
|
+
const hasUpdate = item.version && inst.version !== item.version;
|
|
846
|
+
const updateTag = hasUpdate ? pc4.yellow(` \u2B06 v${item.version} available`) : "";
|
|
847
|
+
if (hasUpdate) updateCount++;
|
|
848
|
+
p3.log.message(` ${status} ${displayName.padEnd(20)} ${version} ${pc4.dim(item.description)}${updateTag}`);
|
|
645
849
|
} else {
|
|
646
850
|
const status = pc4.dim("\u25CB");
|
|
647
|
-
p3.log.message(` ${status} ${
|
|
851
|
+
p3.log.message(` ${status} ${displayName.padEnd(20)} ${version} ${pc4.dim(item.description)}`);
|
|
648
852
|
}
|
|
649
853
|
}
|
|
650
854
|
}
|
|
855
|
+
const availableCount = allItems.length - installedCount;
|
|
856
|
+
const parts = [`${installedCount} installed`, `${availableCount} available`];
|
|
857
|
+
if (updateCount > 0) parts.push(`${updateCount} update${updateCount === 1 ? "" : "s"} available`);
|
|
858
|
+
p3.log.message("");
|
|
859
|
+
p3.log.message(pc4.dim(` ${parts.join(", ")}`));
|
|
651
860
|
}
|
|
652
861
|
var init_list = __esm({
|
|
653
862
|
"src/commands/list.ts"() {
|
|
@@ -663,7 +872,7 @@ __export(diff_exports, {
|
|
|
663
872
|
diffCommand: () => diffCommand
|
|
664
873
|
});
|
|
665
874
|
import * as p4 from "@clack/prompts";
|
|
666
|
-
import { join as
|
|
875
|
+
import { join as join6 } from "path";
|
|
667
876
|
async function diffCommand(componentName) {
|
|
668
877
|
const cwd = process.cwd();
|
|
669
878
|
const config = await readConfig(cwd);
|
|
@@ -671,48 +880,67 @@ async function diffCommand(componentName) {
|
|
|
671
880
|
p4.log.error("No kitn.json found. Run `kitn init` first.");
|
|
672
881
|
process.exit(1);
|
|
673
882
|
}
|
|
674
|
-
const
|
|
883
|
+
const input = componentName === "routes" ? config.framework ?? "hono" : componentName;
|
|
884
|
+
const ref = parseComponentRef(input);
|
|
885
|
+
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
886
|
+
const installed = config.installed?.[installedKey];
|
|
675
887
|
if (!installed) {
|
|
676
|
-
p4.log.error(`Component '${
|
|
888
|
+
p4.log.error(`Component '${ref.name}' is not installed.`);
|
|
677
889
|
process.exit(1);
|
|
678
890
|
}
|
|
891
|
+
const namespace = installed.registry ?? ref.namespace;
|
|
679
892
|
const fetcher = new RegistryFetcher(config.registries);
|
|
680
|
-
const index = await fetcher.fetchIndex();
|
|
681
|
-
const indexItem = index.items.find((i) => i.name ===
|
|
893
|
+
const index = await fetcher.fetchIndex(namespace);
|
|
894
|
+
const indexItem = index.items.find((i) => i.name === ref.name);
|
|
682
895
|
if (!indexItem) {
|
|
683
|
-
p4.log.error(`Component '${
|
|
896
|
+
p4.log.error(`Component '${ref.name}' not found in ${namespace} registry.`);
|
|
684
897
|
process.exit(1);
|
|
685
898
|
}
|
|
686
899
|
const dir = typeToDir[indexItem.type];
|
|
687
|
-
const registryItem = await fetcher.fetchItem(
|
|
900
|
+
const registryItem = await fetcher.fetchItem(ref.name, dir, namespace, ref.version);
|
|
688
901
|
let hasDiff = false;
|
|
689
902
|
for (const file of registryItem.files) {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
903
|
+
if (indexItem.type === "kitn:package") {
|
|
904
|
+
const baseDir = config.aliases.base ?? "src/ai";
|
|
905
|
+
const localPath = join6(cwd, baseDir, file.path);
|
|
906
|
+
const relativePath = join6(baseDir, file.path);
|
|
907
|
+
const localContent = await readExistingFile(localPath);
|
|
908
|
+
if (localContent === null) {
|
|
909
|
+
p4.log.warn(`${relativePath}: file missing locally`);
|
|
910
|
+
hasDiff = true;
|
|
911
|
+
} else if (localContent !== file.content) {
|
|
912
|
+
const diff = generateDiff(relativePath, localContent, file.content);
|
|
913
|
+
console.log(diff);
|
|
914
|
+
hasDiff = true;
|
|
915
|
+
}
|
|
916
|
+
} else {
|
|
917
|
+
const fileName = file.path.split("/").pop();
|
|
918
|
+
const aliasKey = (() => {
|
|
919
|
+
switch (indexItem.type) {
|
|
920
|
+
case "kitn:agent":
|
|
921
|
+
return "agents";
|
|
922
|
+
case "kitn:tool":
|
|
923
|
+
return "tools";
|
|
924
|
+
case "kitn:skill":
|
|
925
|
+
return "skills";
|
|
926
|
+
case "kitn:storage":
|
|
927
|
+
return "storage";
|
|
928
|
+
}
|
|
929
|
+
})();
|
|
930
|
+
const localPath = join6(cwd, config.aliases[aliasKey], fileName);
|
|
931
|
+
const localContent = await readExistingFile(localPath);
|
|
932
|
+
if (localContent === null) {
|
|
933
|
+
p4.log.warn(`${fileName}: file missing locally`);
|
|
934
|
+
hasDiff = true;
|
|
935
|
+
} else if (localContent !== file.content) {
|
|
936
|
+
const diff = generateDiff(fileName, localContent, file.content);
|
|
937
|
+
console.log(diff);
|
|
938
|
+
hasDiff = true;
|
|
701
939
|
}
|
|
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
940
|
}
|
|
713
941
|
}
|
|
714
942
|
if (!hasDiff) {
|
|
715
|
-
p4.log.success(`${
|
|
943
|
+
p4.log.success(`${ref.name}: up to date, no differences.`);
|
|
716
944
|
}
|
|
717
945
|
}
|
|
718
946
|
var init_diff = __esm({
|
|
@@ -721,6 +949,7 @@ var init_diff = __esm({
|
|
|
721
949
|
init_config();
|
|
722
950
|
init_fetcher();
|
|
723
951
|
init_file_writer();
|
|
952
|
+
init_parse_ref();
|
|
724
953
|
init_schema();
|
|
725
954
|
}
|
|
726
955
|
});
|
|
@@ -732,7 +961,7 @@ __export(remove_exports, {
|
|
|
732
961
|
});
|
|
733
962
|
import * as p5 from "@clack/prompts";
|
|
734
963
|
import pc5 from "picocolors";
|
|
735
|
-
import { join as
|
|
964
|
+
import { join as join7 } from "path";
|
|
736
965
|
import { unlink } from "fs/promises";
|
|
737
966
|
async function removeCommand(componentName) {
|
|
738
967
|
const cwd = process.cwd();
|
|
@@ -741,13 +970,16 @@ async function removeCommand(componentName) {
|
|
|
741
970
|
p5.log.error("No kitn.json found. Run `kitn init` first.");
|
|
742
971
|
process.exit(1);
|
|
743
972
|
}
|
|
744
|
-
const
|
|
973
|
+
const input = componentName === "routes" ? config.framework ?? "hono" : componentName;
|
|
974
|
+
const ref = parseComponentRef(input);
|
|
975
|
+
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
976
|
+
const installed = config.installed?.[installedKey];
|
|
745
977
|
if (!installed) {
|
|
746
|
-
p5.log.error(`Component '${
|
|
978
|
+
p5.log.error(`Component '${ref.name}' is not installed.`);
|
|
747
979
|
process.exit(1);
|
|
748
980
|
}
|
|
749
981
|
const shouldRemove = await p5.confirm({
|
|
750
|
-
message: `Remove ${
|
|
982
|
+
message: `Remove ${ref.name}? This will delete ${installed.files.length} file(s).`,
|
|
751
983
|
initialValue: false
|
|
752
984
|
});
|
|
753
985
|
if (p5.isCancel(shouldRemove) || !shouldRemove) {
|
|
@@ -757,19 +989,19 @@ async function removeCommand(componentName) {
|
|
|
757
989
|
const deleted = [];
|
|
758
990
|
for (const filePath of installed.files) {
|
|
759
991
|
try {
|
|
760
|
-
await unlink(
|
|
992
|
+
await unlink(join7(cwd, filePath));
|
|
761
993
|
deleted.push(filePath);
|
|
762
994
|
} catch {
|
|
763
995
|
p5.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
764
996
|
}
|
|
765
997
|
}
|
|
766
|
-
delete config.
|
|
767
|
-
if (Object.keys(config.
|
|
768
|
-
delete config.
|
|
998
|
+
delete config.installed[installedKey];
|
|
999
|
+
if (Object.keys(config.installed).length === 0) {
|
|
1000
|
+
delete config.installed;
|
|
769
1001
|
}
|
|
770
1002
|
await writeConfig(cwd, config);
|
|
771
1003
|
if (deleted.length > 0) {
|
|
772
|
-
p5.log.success(`Removed ${
|
|
1004
|
+
p5.log.success(`Removed ${ref.name}:`);
|
|
773
1005
|
for (const f of deleted) p5.log.message(` ${pc5.red("-")} ${f}`);
|
|
774
1006
|
}
|
|
775
1007
|
}
|
|
@@ -777,6 +1009,7 @@ var init_remove = __esm({
|
|
|
777
1009
|
"src/commands/remove.ts"() {
|
|
778
1010
|
"use strict";
|
|
779
1011
|
init_config();
|
|
1012
|
+
init_parse_ref();
|
|
780
1013
|
}
|
|
781
1014
|
});
|
|
782
1015
|
|
|
@@ -785,13 +1018,226 @@ var update_exports = {};
|
|
|
785
1018
|
__export(update_exports, {
|
|
786
1019
|
updateCommand: () => updateCommand
|
|
787
1020
|
});
|
|
1021
|
+
import * as p6 from "@clack/prompts";
|
|
788
1022
|
async function updateCommand(components) {
|
|
1023
|
+
if (components.length === 0) {
|
|
1024
|
+
const cwd = process.cwd();
|
|
1025
|
+
const config = await readConfig(cwd);
|
|
1026
|
+
if (!config) {
|
|
1027
|
+
p6.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1028
|
+
process.exit(1);
|
|
1029
|
+
}
|
|
1030
|
+
const installed = config.installed;
|
|
1031
|
+
if (!installed || Object.keys(installed).length === 0) {
|
|
1032
|
+
p6.log.info("No installed components to update.");
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
components = Object.keys(installed);
|
|
1036
|
+
}
|
|
789
1037
|
await addCommand(components, { overwrite: true });
|
|
790
1038
|
}
|
|
791
1039
|
var init_update = __esm({
|
|
792
1040
|
"src/commands/update.ts"() {
|
|
793
1041
|
"use strict";
|
|
794
1042
|
init_add();
|
|
1043
|
+
init_config();
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
// src/commands/info.ts
|
|
1048
|
+
var info_exports = {};
|
|
1049
|
+
__export(info_exports, {
|
|
1050
|
+
infoCommand: () => infoCommand
|
|
1051
|
+
});
|
|
1052
|
+
import * as p7 from "@clack/prompts";
|
|
1053
|
+
import pc6 from "picocolors";
|
|
1054
|
+
async function infoCommand(component) {
|
|
1055
|
+
const cwd = process.cwd();
|
|
1056
|
+
const config = await readConfig(cwd);
|
|
1057
|
+
if (!config) {
|
|
1058
|
+
p7.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1059
|
+
process.exit(1);
|
|
1060
|
+
}
|
|
1061
|
+
const ref = parseComponentRef(component);
|
|
1062
|
+
const fetcher = new RegistryFetcher(config.registries);
|
|
1063
|
+
const s = p7.spinner();
|
|
1064
|
+
s.start("Fetching component info...");
|
|
1065
|
+
let index;
|
|
1066
|
+
try {
|
|
1067
|
+
index = await fetcher.fetchIndex(ref.namespace);
|
|
1068
|
+
} catch (err) {
|
|
1069
|
+
s.stop(pc6.red("Failed to fetch registry"));
|
|
1070
|
+
p7.log.error(err.message);
|
|
1071
|
+
process.exit(1);
|
|
1072
|
+
}
|
|
1073
|
+
const indexItem = index.items.find((i) => i.name === ref.name);
|
|
1074
|
+
if (!indexItem) {
|
|
1075
|
+
s.stop(pc6.red("Component not found"));
|
|
1076
|
+
p7.log.error(`Component '${ref.name}' not found in registry.`);
|
|
1077
|
+
process.exit(1);
|
|
1078
|
+
}
|
|
1079
|
+
const dir = typeToDir[indexItem.type];
|
|
1080
|
+
let item;
|
|
1081
|
+
try {
|
|
1082
|
+
item = await fetcher.fetchItem(ref.name, dir, ref.namespace, ref.version);
|
|
1083
|
+
} catch (err) {
|
|
1084
|
+
s.stop(pc6.red("Failed to fetch component"));
|
|
1085
|
+
p7.log.error(err.message);
|
|
1086
|
+
process.exit(1);
|
|
1087
|
+
}
|
|
1088
|
+
s.stop("Component found");
|
|
1089
|
+
const version = item.version ?? indexItem.version ?? "unknown";
|
|
1090
|
+
const typeName = indexItem.type.replace("kitn:", "");
|
|
1091
|
+
console.log();
|
|
1092
|
+
console.log(
|
|
1093
|
+
` ${pc6.bold(item.name)} ${pc6.cyan(`v${version}`)}${" ".repeat(Math.max(1, 40 - item.name.length - version.length - 2))}${pc6.dim(ref.namespace)}`
|
|
1094
|
+
);
|
|
1095
|
+
console.log(` ${pc6.dim(item.description)}`);
|
|
1096
|
+
console.log();
|
|
1097
|
+
console.log(` ${pc6.dim("Type:")} ${typeName}`);
|
|
1098
|
+
if (item.dependencies?.length) {
|
|
1099
|
+
console.log(
|
|
1100
|
+
` ${pc6.dim("Dependencies:")} ${item.dependencies.join(", ")}`
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
if (item.registryDependencies?.length) {
|
|
1104
|
+
console.log(
|
|
1105
|
+
` ${pc6.dim("Registry deps:")} ${item.registryDependencies.join(", ")}`
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
if (item.categories?.length) {
|
|
1109
|
+
console.log(
|
|
1110
|
+
` ${pc6.dim("Categories:")} ${item.categories.join(", ")}`
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
if (item.updatedAt) {
|
|
1114
|
+
console.log(` ${pc6.dim("Updated:")} ${item.updatedAt}`);
|
|
1115
|
+
}
|
|
1116
|
+
const versions = indexItem.versions;
|
|
1117
|
+
if (versions?.length) {
|
|
1118
|
+
console.log(` ${pc6.dim("Versions:")} ${versions.join(", ")}`);
|
|
1119
|
+
}
|
|
1120
|
+
if (item.changelog?.length) {
|
|
1121
|
+
console.log();
|
|
1122
|
+
console.log(` ${pc6.bold("Changelog:")}`);
|
|
1123
|
+
for (const entry of item.changelog) {
|
|
1124
|
+
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);
|
|
1125
|
+
console.log(
|
|
1126
|
+
` ${pc6.cyan(entry.version)} ${pc6.dim(entry.date)} ${tag} ${entry.note}`
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
console.log();
|
|
1131
|
+
const fileCount = item.files.length;
|
|
1132
|
+
console.log(` ${pc6.bold(`Files:`)} ${pc6.dim(`(${fileCount})`)}`);
|
|
1133
|
+
const maxShown = 10;
|
|
1134
|
+
for (const file of item.files.slice(0, maxShown)) {
|
|
1135
|
+
console.log(` ${pc6.dim(file.path)}`);
|
|
1136
|
+
}
|
|
1137
|
+
if (fileCount > maxShown) {
|
|
1138
|
+
console.log(` ${pc6.dim(`... and ${fileCount - maxShown} more`)}`);
|
|
1139
|
+
}
|
|
1140
|
+
const installed = config.installed?.[item.name];
|
|
1141
|
+
if (installed) {
|
|
1142
|
+
console.log();
|
|
1143
|
+
console.log(
|
|
1144
|
+
` ${pc6.green("Installed")} ${pc6.dim(`v${installed.version}`)}`
|
|
1145
|
+
);
|
|
1146
|
+
if (version !== installed.version) {
|
|
1147
|
+
console.log(
|
|
1148
|
+
` ${pc6.yellow("Update available:")} ${pc6.dim(`v${installed.version}`)} \u2192 ${pc6.cyan(`v${version}`)}`
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
console.log();
|
|
1153
|
+
}
|
|
1154
|
+
var init_info = __esm({
|
|
1155
|
+
"src/commands/info.ts"() {
|
|
1156
|
+
"use strict";
|
|
1157
|
+
init_config();
|
|
1158
|
+
init_parse_ref();
|
|
1159
|
+
init_fetcher();
|
|
1160
|
+
init_schema();
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
// src/commands/registry.ts
|
|
1165
|
+
var registry_exports = {};
|
|
1166
|
+
__export(registry_exports, {
|
|
1167
|
+
registryAddCommand: () => registryAddCommand,
|
|
1168
|
+
registryListCommand: () => registryListCommand,
|
|
1169
|
+
registryRemoveCommand: () => registryRemoveCommand
|
|
1170
|
+
});
|
|
1171
|
+
import * as p8 from "@clack/prompts";
|
|
1172
|
+
import pc7 from "picocolors";
|
|
1173
|
+
async function registryAddCommand(namespace, url, opts = {}) {
|
|
1174
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
1175
|
+
const config = await readConfig(cwd);
|
|
1176
|
+
if (!config) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
1177
|
+
if (!namespace.startsWith("@")) {
|
|
1178
|
+
throw new Error("Namespace must start with @ (e.g. @myteam)");
|
|
1179
|
+
}
|
|
1180
|
+
if (!url.includes("{type}")) {
|
|
1181
|
+
throw new Error("URL template must include {type} placeholder");
|
|
1182
|
+
}
|
|
1183
|
+
if (!url.includes("{name}")) {
|
|
1184
|
+
throw new Error("URL template must include {name} placeholder");
|
|
1185
|
+
}
|
|
1186
|
+
if (config.registries[namespace] && !opts.overwrite) {
|
|
1187
|
+
throw new Error(`Registry '${namespace}' is already configured. Use --overwrite to replace.`);
|
|
1188
|
+
}
|
|
1189
|
+
config.registries[namespace] = url;
|
|
1190
|
+
await writeConfig(cwd, config);
|
|
1191
|
+
p8.log.success(`Added registry ${pc7.bold(namespace)}`);
|
|
1192
|
+
p8.log.message(pc7.dim(` ${url}`));
|
|
1193
|
+
}
|
|
1194
|
+
async function registryRemoveCommand(namespace, opts = {}) {
|
|
1195
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
1196
|
+
const config = await readConfig(cwd);
|
|
1197
|
+
if (!config) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
1198
|
+
if (!config.registries[namespace]) {
|
|
1199
|
+
throw new Error(`Registry '${namespace}' is not configured.`);
|
|
1200
|
+
}
|
|
1201
|
+
if (namespace === "@kitn" && !opts.force) {
|
|
1202
|
+
throw new Error("Cannot remove the default @kitn registry. Use --force to override.");
|
|
1203
|
+
}
|
|
1204
|
+
const affectedComponents = [];
|
|
1205
|
+
if (config.installed) {
|
|
1206
|
+
for (const [name, entry] of Object.entries(config.installed)) {
|
|
1207
|
+
if (entry.registry === namespace) {
|
|
1208
|
+
affectedComponents.push(name);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
delete config.registries[namespace];
|
|
1213
|
+
await writeConfig(cwd, config);
|
|
1214
|
+
p8.log.success(`Removed registry ${pc7.bold(namespace)}`);
|
|
1215
|
+
if (affectedComponents.length > 0) {
|
|
1216
|
+
p8.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:`);
|
|
1217
|
+
for (const name of affectedComponents) {
|
|
1218
|
+
p8.log.message(` ${pc7.yellow("!")} ${name}`);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
return { affectedComponents };
|
|
1222
|
+
}
|
|
1223
|
+
async function registryListCommand(opts = {}) {
|
|
1224
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
1225
|
+
const config = await readConfig(cwd);
|
|
1226
|
+
if (!config) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
1227
|
+
const entries = Object.entries(config.registries).map(([namespace, url]) => ({ namespace, url }));
|
|
1228
|
+
if (entries.length === 0) {
|
|
1229
|
+
p8.log.message(pc7.dim(" No registries configured."));
|
|
1230
|
+
} else {
|
|
1231
|
+
for (const { namespace, url } of entries) {
|
|
1232
|
+
p8.log.message(` ${pc7.bold(namespace.padEnd(16))} ${pc7.dim(url)}`);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return entries;
|
|
1236
|
+
}
|
|
1237
|
+
var init_registry = __esm({
|
|
1238
|
+
"src/commands/registry.ts"() {
|
|
1239
|
+
"use strict";
|
|
1240
|
+
init_config();
|
|
795
1241
|
}
|
|
796
1242
|
});
|
|
797
1243
|
|
|
@@ -806,7 +1252,7 @@ program.command("add").description("Add components from the kitn registry").argu
|
|
|
806
1252
|
const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
807
1253
|
await addCommand2(components, opts);
|
|
808
1254
|
});
|
|
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) => {
|
|
1255
|
+
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
1256
|
const { listCommand: listCommand2 } = await Promise.resolve().then(() => (init_list(), list_exports));
|
|
811
1257
|
await listCommand2(opts);
|
|
812
1258
|
});
|
|
@@ -822,5 +1268,22 @@ program.command("update").description("Update installed components to latest reg
|
|
|
822
1268
|
const { updateCommand: updateCommand2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
823
1269
|
await updateCommand2(components);
|
|
824
1270
|
});
|
|
1271
|
+
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) => {
|
|
1272
|
+
const { infoCommand: infoCommand2 } = await Promise.resolve().then(() => (init_info(), info_exports));
|
|
1273
|
+
await infoCommand2(component);
|
|
1274
|
+
});
|
|
1275
|
+
var registry = program.command("registry").description("Manage component registries");
|
|
1276
|
+
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) => {
|
|
1277
|
+
const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
1278
|
+
await registryAddCommand2(namespace, url, opts);
|
|
1279
|
+
});
|
|
1280
|
+
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) => {
|
|
1281
|
+
const { registryRemoveCommand: registryRemoveCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
1282
|
+
await registryRemoveCommand2(namespace, opts);
|
|
1283
|
+
});
|
|
1284
|
+
registry.command("list").description("List all configured registries").action(async () => {
|
|
1285
|
+
const { registryListCommand: registryListCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
1286
|
+
await registryListCommand2();
|
|
1287
|
+
});
|
|
825
1288
|
program.parse();
|
|
826
1289
|
//# sourceMappingURL=index.js.map
|