@kitnai/cli 0.1.23 → 0.1.24
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 +344 -79
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,9 +9,72 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/utils/detect.ts
|
|
13
|
+
import { access } from "fs/promises";
|
|
14
|
+
import { join } from "path";
|
|
15
|
+
async function detectPackageManager(dir) {
|
|
16
|
+
for (const [lockfile, pm] of LOCKFILE_MAP) {
|
|
17
|
+
try {
|
|
18
|
+
await access(join(dir, lockfile));
|
|
19
|
+
return pm;
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function getRunCommand(pm) {
|
|
26
|
+
switch (pm) {
|
|
27
|
+
case "bun":
|
|
28
|
+
return "bunx";
|
|
29
|
+
case "pnpm":
|
|
30
|
+
return "pnpm dlx";
|
|
31
|
+
case "yarn":
|
|
32
|
+
return "yarn dlx";
|
|
33
|
+
case "npm":
|
|
34
|
+
return "npx";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function detectCliInstaller() {
|
|
38
|
+
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
39
|
+
if (userAgent.startsWith("bun/")) return "bun";
|
|
40
|
+
if (userAgent.startsWith("pnpm/")) return "pnpm";
|
|
41
|
+
if (userAgent.startsWith("yarn/")) return "yarn";
|
|
42
|
+
if (userAgent.startsWith("npm/")) return "npm";
|
|
43
|
+
const invoker = process.env._ ?? "";
|
|
44
|
+
if (invoker.includes("bun")) return "bun";
|
|
45
|
+
if (invoker.includes("pnpm")) return "pnpm";
|
|
46
|
+
if (invoker.includes("yarn")) return "yarn";
|
|
47
|
+
return "npm";
|
|
48
|
+
}
|
|
49
|
+
function getGlobalInstallCommand(pm, pkg) {
|
|
50
|
+
switch (pm) {
|
|
51
|
+
case "bun":
|
|
52
|
+
return `bun install -g ${pkg}`;
|
|
53
|
+
case "pnpm":
|
|
54
|
+
return `pnpm add -g ${pkg}`;
|
|
55
|
+
case "yarn":
|
|
56
|
+
return `yarn global add ${pkg}`;
|
|
57
|
+
case "npm":
|
|
58
|
+
return `npm install -g ${pkg}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
var LOCKFILE_MAP;
|
|
62
|
+
var init_detect = __esm({
|
|
63
|
+
"src/utils/detect.ts"() {
|
|
64
|
+
"use strict";
|
|
65
|
+
LOCKFILE_MAP = [
|
|
66
|
+
["bun.lock", "bun"],
|
|
67
|
+
["bun.lockb", "bun"],
|
|
68
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
69
|
+
["yarn.lock", "yarn"],
|
|
70
|
+
["package-lock.json", "npm"]
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
12
75
|
// src/utils/update-check.ts
|
|
13
76
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
14
|
-
import { join } from "path";
|
|
77
|
+
import { join as join2 } from "path";
|
|
15
78
|
import { homedir } from "os";
|
|
16
79
|
import pc from "picocolors";
|
|
17
80
|
async function readCache() {
|
|
@@ -65,10 +128,13 @@ function startUpdateCheck(currentVersion) {
|
|
|
65
128
|
}
|
|
66
129
|
}
|
|
67
130
|
if (latest && isNewer(latest, currentVersion)) {
|
|
131
|
+
const pm = detectCliInstaller();
|
|
132
|
+
const runCmd = `${getRunCommand(pm)} @kitnai/cli@latest`;
|
|
133
|
+
const installCmd = getGlobalInstallCommand(pm, "@kitnai/cli");
|
|
68
134
|
message = [
|
|
69
135
|
"",
|
|
70
136
|
pc.yellow(` Update available: ${pc.dim(currentVersion)} \u2192 ${pc.green(latest)}`),
|
|
71
|
-
pc.dim(` Run ${pc.cyan(
|
|
137
|
+
pc.dim(` Run ${pc.cyan(runCmd)} or ${pc.cyan(installCmd)}`),
|
|
72
138
|
""
|
|
73
139
|
].join("\n");
|
|
74
140
|
}
|
|
@@ -83,15 +149,16 @@ var CACHE_DIR, CACHE_FILE, CHECK_INTERVAL;
|
|
|
83
149
|
var init_update_check = __esm({
|
|
84
150
|
"src/utils/update-check.ts"() {
|
|
85
151
|
"use strict";
|
|
86
|
-
|
|
87
|
-
|
|
152
|
+
init_detect();
|
|
153
|
+
CACHE_DIR = join2(homedir(), ".kitn");
|
|
154
|
+
CACHE_FILE = join2(CACHE_DIR, "update-check.json");
|
|
88
155
|
CHECK_INTERVAL = 60 * 60 * 1e3;
|
|
89
156
|
}
|
|
90
157
|
});
|
|
91
158
|
|
|
92
159
|
// src/utils/config.ts
|
|
93
160
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
94
|
-
import { join as
|
|
161
|
+
import { join as join3 } from "path";
|
|
95
162
|
import { z } from "zod";
|
|
96
163
|
function getRegistryUrl(entry) {
|
|
97
164
|
return typeof entry === "string" ? entry : entry.url;
|
|
@@ -102,7 +169,7 @@ function resolveRoutesAlias(config) {
|
|
|
102
169
|
}
|
|
103
170
|
async function readConfig(projectDir) {
|
|
104
171
|
try {
|
|
105
|
-
const raw = await readFile2(
|
|
172
|
+
const raw = await readFile2(join3(projectDir, CONFIG_FILE), "utf-8");
|
|
106
173
|
return configSchema.parse(JSON.parse(raw));
|
|
107
174
|
} catch {
|
|
108
175
|
return null;
|
|
@@ -110,16 +177,16 @@ async function readConfig(projectDir) {
|
|
|
110
177
|
}
|
|
111
178
|
async function writeConfig(projectDir, config) {
|
|
112
179
|
const data = { $schema: "https://kitn.dev/schema/config.json", ...config };
|
|
113
|
-
await writeFile2(
|
|
180
|
+
await writeFile2(join3(projectDir, CONFIG_FILE), JSON.stringify(data, null, 2) + "\n");
|
|
114
181
|
}
|
|
115
182
|
function getInstallPath(config, type, fileName, namespace) {
|
|
116
183
|
const aliasKey = typeToAliasKey[type];
|
|
117
184
|
const base = config.aliases[aliasKey];
|
|
118
185
|
if (namespace && namespace !== "@kitn") {
|
|
119
186
|
const nsDir = namespace.replace("@", "");
|
|
120
|
-
return
|
|
187
|
+
return join3(base, nsDir, fileName);
|
|
121
188
|
}
|
|
122
|
-
return
|
|
189
|
+
return join3(base, fileName);
|
|
123
190
|
}
|
|
124
191
|
var componentType, installedComponentSchema, registryEntrySchema, registryValueSchema, configSchema, FRAMEWORK_TO_ADAPTER, CONFIG_FILE, typeToAliasKey;
|
|
125
192
|
var init_config = __esm({
|
|
@@ -128,10 +195,13 @@ var init_config = __esm({
|
|
|
128
195
|
componentType = z.enum(["kitn:agent", "kitn:tool", "kitn:skill", "kitn:storage", "kitn:package"]);
|
|
129
196
|
installedComponentSchema = z.object({
|
|
130
197
|
registry: z.string().optional(),
|
|
198
|
+
type: componentType,
|
|
199
|
+
slot: z.string().optional(),
|
|
131
200
|
version: z.string(),
|
|
132
201
|
installedAt: z.string(),
|
|
133
202
|
files: z.array(z.string()),
|
|
134
|
-
hash: z.string()
|
|
203
|
+
hash: z.string(),
|
|
204
|
+
registryDependencies: z.array(z.string()).optional()
|
|
135
205
|
});
|
|
136
206
|
registryEntrySchema = z.object({
|
|
137
207
|
url: z.string(),
|
|
@@ -170,7 +240,7 @@ var init_config = __esm({
|
|
|
170
240
|
|
|
171
241
|
// src/installers/tsconfig-patcher.ts
|
|
172
242
|
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
173
|
-
import { join as
|
|
243
|
+
import { join as join4 } from "path";
|
|
174
244
|
function stripJsonc(text3) {
|
|
175
245
|
return text3.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([}\]])/g, "$1");
|
|
176
246
|
}
|
|
@@ -209,7 +279,7 @@ function patchTsconfig(tsconfigContent, paths, removePrefixes) {
|
|
|
209
279
|
return JSON.stringify(config, null, 2) + "\n";
|
|
210
280
|
}
|
|
211
281
|
async function patchProjectTsconfig(projectDir, paths, removePrefixes) {
|
|
212
|
-
const tsconfigPath =
|
|
282
|
+
const tsconfigPath = join4(projectDir, "tsconfig.json");
|
|
213
283
|
let content;
|
|
214
284
|
try {
|
|
215
285
|
content = await readFile3(tsconfigPath, "utf-8");
|
|
@@ -259,33 +329,6 @@ var init_barrel_manager = __esm({
|
|
|
259
329
|
}
|
|
260
330
|
});
|
|
261
331
|
|
|
262
|
-
// src/utils/detect.ts
|
|
263
|
-
import { access } from "fs/promises";
|
|
264
|
-
import { join as join4 } from "path";
|
|
265
|
-
async function detectPackageManager(dir) {
|
|
266
|
-
for (const [lockfile, pm] of LOCKFILE_MAP) {
|
|
267
|
-
try {
|
|
268
|
-
await access(join4(dir, lockfile));
|
|
269
|
-
return pm;
|
|
270
|
-
} catch {
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
var LOCKFILE_MAP;
|
|
276
|
-
var init_detect = __esm({
|
|
277
|
-
"src/utils/detect.ts"() {
|
|
278
|
-
"use strict";
|
|
279
|
-
LOCKFILE_MAP = [
|
|
280
|
-
["bun.lock", "bun"],
|
|
281
|
-
["bun.lockb", "bun"],
|
|
282
|
-
["pnpm-lock.yaml", "pnpm"],
|
|
283
|
-
["yarn.lock", "yarn"],
|
|
284
|
-
["package-lock.json", "npm"]
|
|
285
|
-
];
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
|
|
289
332
|
// src/registry/fetcher.ts
|
|
290
333
|
function urlOf(entry) {
|
|
291
334
|
return typeof entry === "string" ? entry : entry.url;
|
|
@@ -729,6 +772,7 @@ var init_schema = __esm({
|
|
|
729
772
|
tsconfig: z2.record(z2.string(), z2.array(z2.string())).optional(),
|
|
730
773
|
envVars: z2.record(z2.string(), envVarConfigSchema).optional(),
|
|
731
774
|
categories: z2.array(z2.string()).optional(),
|
|
775
|
+
slot: z2.string().optional(),
|
|
732
776
|
docs: z2.string().optional(),
|
|
733
777
|
changelog: z2.array(changelogEntrySchema).optional()
|
|
734
778
|
});
|
|
@@ -746,6 +790,7 @@ var init_schema = __esm({
|
|
|
746
790
|
tsconfig: z2.record(z2.string(), z2.array(z2.string())).optional(),
|
|
747
791
|
docs: z2.string().optional(),
|
|
748
792
|
categories: z2.array(z2.string()).optional(),
|
|
793
|
+
slot: z2.string().optional(),
|
|
749
794
|
version: z2.string().optional(),
|
|
750
795
|
updatedAt: z2.string().optional(),
|
|
751
796
|
changelog: z2.array(changelogEntrySchema).optional()
|
|
@@ -756,6 +801,7 @@ var init_schema = __esm({
|
|
|
756
801
|
description: z2.string(),
|
|
757
802
|
registryDependencies: z2.array(z2.string()).optional(),
|
|
758
803
|
categories: z2.array(z2.string()).optional(),
|
|
804
|
+
slot: z2.string().optional(),
|
|
759
805
|
version: z2.string().optional(),
|
|
760
806
|
versions: z2.array(z2.string()).optional(),
|
|
761
807
|
updatedAt: z2.string().optional()
|
|
@@ -782,9 +828,9 @@ __export(add_exports, {
|
|
|
782
828
|
});
|
|
783
829
|
import * as p2 from "@clack/prompts";
|
|
784
830
|
import pc3 from "picocolors";
|
|
785
|
-
import { join as join7 } from "path";
|
|
831
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
786
832
|
import { existsSync } from "fs";
|
|
787
|
-
import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir3 } from "fs/promises";
|
|
833
|
+
import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir3, unlink } from "fs/promises";
|
|
788
834
|
import { relative as relative2 } from "path";
|
|
789
835
|
async function addCommand(components, opts) {
|
|
790
836
|
p2.intro(pc3.bgCyan(pc3.black(" kitn add ")));
|
|
@@ -795,8 +841,63 @@ async function addCommand(components, opts) {
|
|
|
795
841
|
process.exit(1);
|
|
796
842
|
}
|
|
797
843
|
if (components.length === 0) {
|
|
798
|
-
|
|
799
|
-
|
|
844
|
+
const fetcher2 = new RegistryFetcher(config.registries);
|
|
845
|
+
const s2 = p2.spinner();
|
|
846
|
+
s2.start("Fetching registry...");
|
|
847
|
+
const allItems = [];
|
|
848
|
+
for (const namespace of Object.keys(config.registries)) {
|
|
849
|
+
try {
|
|
850
|
+
const index = await fetcher2.fetchIndex(namespace);
|
|
851
|
+
for (const item of index.items) {
|
|
852
|
+
allItems.push({ name: item.name, type: item.type, description: item.description, namespace });
|
|
853
|
+
}
|
|
854
|
+
} catch {
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
s2.stop(`Found ${allItems.length} component(s)`);
|
|
858
|
+
if (allItems.length === 0) {
|
|
859
|
+
p2.log.warn("No components found in configured registries.");
|
|
860
|
+
process.exit(0);
|
|
861
|
+
}
|
|
862
|
+
const installed = new Set(Object.keys(config.installed ?? {}));
|
|
863
|
+
const typeLabels = {
|
|
864
|
+
"kitn:agent": "Agents",
|
|
865
|
+
"kitn:tool": "Tools",
|
|
866
|
+
"kitn:skill": "Skills",
|
|
867
|
+
"kitn:storage": "Storage",
|
|
868
|
+
"kitn:package": "Packages"
|
|
869
|
+
};
|
|
870
|
+
const groups = /* @__PURE__ */ new Map();
|
|
871
|
+
for (const item of allItems) {
|
|
872
|
+
if (!groups.has(item.type)) groups.set(item.type, []);
|
|
873
|
+
groups.get(item.type).push(item);
|
|
874
|
+
}
|
|
875
|
+
const options = [];
|
|
876
|
+
for (const [type, items] of groups) {
|
|
877
|
+
const label = typeLabels[type] ?? type;
|
|
878
|
+
options.push({ value: `__separator_${type}`, label: pc3.bold(`\u2500\u2500 ${label} ${"\u2500".repeat(Math.max(0, 40 - label.length))}`), hint: "" });
|
|
879
|
+
for (const item of items) {
|
|
880
|
+
const isInstalled = installed.has(item.name);
|
|
881
|
+
options.push({
|
|
882
|
+
value: item.name,
|
|
883
|
+
label: isInstalled ? pc3.dim(`${item.name} (installed)`) : item.name,
|
|
884
|
+
hint: item.description
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
const selected = await p2.multiselect({
|
|
889
|
+
message: "Select components to install:",
|
|
890
|
+
options
|
|
891
|
+
});
|
|
892
|
+
if (p2.isCancel(selected)) {
|
|
893
|
+
p2.cancel("Cancelled.");
|
|
894
|
+
process.exit(0);
|
|
895
|
+
}
|
|
896
|
+
components = selected.filter((s3) => !s3.startsWith("__separator_"));
|
|
897
|
+
if (components.length === 0) {
|
|
898
|
+
p2.log.warn("No components selected.");
|
|
899
|
+
process.exit(0);
|
|
900
|
+
}
|
|
800
901
|
}
|
|
801
902
|
let typeFilter;
|
|
802
903
|
const firstAlias = resolveTypeAlias(components[0]);
|
|
@@ -952,6 +1053,68 @@ async function addCommand(components, opts) {
|
|
|
952
1053
|
process.exit(1);
|
|
953
1054
|
}
|
|
954
1055
|
s.stop(`Resolved ${resolved.length} component(s)`);
|
|
1056
|
+
const slotReplacements = /* @__PURE__ */ new Map();
|
|
1057
|
+
for (const item of resolved) {
|
|
1058
|
+
if (!item.slot) continue;
|
|
1059
|
+
const existing = Object.entries(config.installed ?? {}).find(
|
|
1060
|
+
([key, entry]) => key !== item.name && entry.slot === item.slot
|
|
1061
|
+
);
|
|
1062
|
+
if (!existing) continue;
|
|
1063
|
+
const [existingKey] = existing;
|
|
1064
|
+
const action = await p2.select({
|
|
1065
|
+
message: `${pc3.bold(existingKey)} already fills the ${pc3.cyan(item.slot)} slot. What would you like to do?`,
|
|
1066
|
+
options: [
|
|
1067
|
+
{ value: "replace", label: `Replace ${existingKey} with ${item.name}` },
|
|
1068
|
+
{ value: "add", label: `Add alongside ${existingKey}` }
|
|
1069
|
+
]
|
|
1070
|
+
});
|
|
1071
|
+
if (p2.isCancel(action)) {
|
|
1072
|
+
p2.cancel("Cancelled.");
|
|
1073
|
+
process.exit(0);
|
|
1074
|
+
}
|
|
1075
|
+
if (action === "replace") {
|
|
1076
|
+
slotReplacements.set(existingKey, item.name);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
if (slotReplacements.size > 0) {
|
|
1080
|
+
const baseDir2 = config.aliases.base ?? "src/ai";
|
|
1081
|
+
for (const [oldKey] of slotReplacements) {
|
|
1082
|
+
const oldEntry = config.installed[oldKey];
|
|
1083
|
+
if (!oldEntry) continue;
|
|
1084
|
+
for (const filePath of oldEntry.files) {
|
|
1085
|
+
try {
|
|
1086
|
+
await unlink(join7(cwd, filePath));
|
|
1087
|
+
} catch {
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
const barrelPath2 = join7(cwd, baseDir2, "index.ts");
|
|
1091
|
+
const barrelEligibleDirs = /* @__PURE__ */ new Set([
|
|
1092
|
+
config.aliases.agents,
|
|
1093
|
+
config.aliases.tools,
|
|
1094
|
+
config.aliases.skills
|
|
1095
|
+
]);
|
|
1096
|
+
if (existsSync(barrelPath2)) {
|
|
1097
|
+
let barrelContent = await readFile6(barrelPath2, "utf-8");
|
|
1098
|
+
let barrelChanged = false;
|
|
1099
|
+
for (const filePath of oldEntry.files) {
|
|
1100
|
+
const fileDir = dirname3(filePath);
|
|
1101
|
+
if (!barrelEligibleDirs.has(fileDir)) continue;
|
|
1102
|
+
const barrelDir2 = join7(cwd, baseDir2);
|
|
1103
|
+
const importPath = "./" + relative2(barrelDir2, join7(cwd, filePath)).replace(/\\/g, "/");
|
|
1104
|
+
const updated2 = removeImportFromBarrel(barrelContent, importPath);
|
|
1105
|
+
if (updated2 !== barrelContent) {
|
|
1106
|
+
barrelContent = updated2;
|
|
1107
|
+
barrelChanged = true;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
if (barrelChanged) {
|
|
1111
|
+
await writeFile6(barrelPath2, barrelContent);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
delete config.installed[oldKey];
|
|
1115
|
+
p2.log.info(`Replaced ${pc3.dim(oldKey)} \u2192 ${pc3.cyan(slotReplacements.get(oldKey))}`);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
955
1118
|
p2.log.info("Components to install:\n" + resolved.map((item) => {
|
|
956
1119
|
const isExplicit = expandedNames.includes(item.name) || components.includes(item.name);
|
|
957
1120
|
const label = isExplicit ? item.name : `${item.name} ${pc3.dim("(dependency)")}`;
|
|
@@ -1017,10 +1180,13 @@ async function addCommand(components, opts) {
|
|
|
1017
1180
|
const installedKey = ref.namespace === "@kitn" ? item.name : `${ref.namespace}/${item.name}`;
|
|
1018
1181
|
installed[installedKey] = {
|
|
1019
1182
|
registry: ref.namespace,
|
|
1183
|
+
type: item.type,
|
|
1184
|
+
...item.slot && { slot: item.slot },
|
|
1020
1185
|
version: item.version ?? "1.0.0",
|
|
1021
1186
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1022
1187
|
files: item.files.map((f) => join7(baseDir2, f.path)),
|
|
1023
|
-
hash: contentHash(allContent)
|
|
1188
|
+
hash: contentHash(allContent),
|
|
1189
|
+
registryDependencies: item.registryDependencies
|
|
1024
1190
|
};
|
|
1025
1191
|
config.installed = installed;
|
|
1026
1192
|
} else {
|
|
@@ -1086,13 +1252,16 @@ async function addCommand(components, opts) {
|
|
|
1086
1252
|
const installedKey = ns === "@kitn" ? item.name : `${ns}/${item.name}`;
|
|
1087
1253
|
installed[installedKey] = {
|
|
1088
1254
|
registry: ns,
|
|
1255
|
+
type: item.type,
|
|
1256
|
+
...item.slot && { slot: item.slot },
|
|
1089
1257
|
version: item.version ?? "1.0.0",
|
|
1090
1258
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1091
1259
|
files: item.files.map((f) => {
|
|
1092
1260
|
const fileName = f.path.split("/").pop();
|
|
1093
1261
|
return getInstallPath(config, item.type, fileName, ns);
|
|
1094
1262
|
}),
|
|
1095
|
-
hash: contentHash(allContent)
|
|
1263
|
+
hash: contentHash(allContent),
|
|
1264
|
+
registryDependencies: item.registryDependencies
|
|
1096
1265
|
};
|
|
1097
1266
|
config.installed = installed;
|
|
1098
1267
|
}
|
|
@@ -1609,36 +1778,16 @@ __export(remove_exports, {
|
|
|
1609
1778
|
});
|
|
1610
1779
|
import * as p6 from "@clack/prompts";
|
|
1611
1780
|
import pc6 from "picocolors";
|
|
1612
|
-
import { join as join10, relative as relative3, dirname as
|
|
1613
|
-
import { unlink, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
1781
|
+
import { join as join10, relative as relative3, dirname as dirname4 } from "path";
|
|
1782
|
+
import { unlink as unlink2, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
1614
1783
|
import { existsSync as existsSync2 } from "fs";
|
|
1615
|
-
async function
|
|
1616
|
-
const cwd = process.cwd();
|
|
1617
|
-
const config = await readConfig(cwd);
|
|
1618
|
-
if (!config) {
|
|
1619
|
-
p6.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1620
|
-
process.exit(1);
|
|
1621
|
-
}
|
|
1622
|
-
const input = componentName === "routes" ? resolveRoutesAlias(config) : componentName;
|
|
1623
|
-
const ref = parseComponentRef(input);
|
|
1624
|
-
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
1784
|
+
async function removeSingleComponent(installedKey, config, cwd) {
|
|
1625
1785
|
const installed = config.installed?.[installedKey];
|
|
1626
|
-
if (!installed)
|
|
1627
|
-
p6.log.error(`Component '${ref.name}' is not installed.`);
|
|
1628
|
-
process.exit(1);
|
|
1629
|
-
}
|
|
1630
|
-
const shouldRemove = await p6.confirm({
|
|
1631
|
-
message: `Remove ${ref.name}? This will delete ${installed.files.length} file(s).`,
|
|
1632
|
-
initialValue: false
|
|
1633
|
-
});
|
|
1634
|
-
if (p6.isCancel(shouldRemove) || !shouldRemove) {
|
|
1635
|
-
p6.cancel("Remove cancelled.");
|
|
1636
|
-
process.exit(0);
|
|
1637
|
-
}
|
|
1786
|
+
if (!installed) return;
|
|
1638
1787
|
const deleted = [];
|
|
1639
1788
|
for (const filePath of installed.files) {
|
|
1640
1789
|
try {
|
|
1641
|
-
await
|
|
1790
|
+
await unlink2(join10(cwd, filePath));
|
|
1642
1791
|
deleted.push(filePath);
|
|
1643
1792
|
} catch {
|
|
1644
1793
|
p6.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
@@ -1656,7 +1805,7 @@ async function removeCommand(componentName) {
|
|
|
1656
1805
|
let barrelContent = await readFile7(barrelPath, "utf-8");
|
|
1657
1806
|
let barrelChanged = false;
|
|
1658
1807
|
for (const filePath of deleted) {
|
|
1659
|
-
const fileDir =
|
|
1808
|
+
const fileDir = dirname4(filePath);
|
|
1660
1809
|
if (!barrelEligibleDirs.has(fileDir)) continue;
|
|
1661
1810
|
const importPath = "./" + relative3(barrelDir, join10(cwd, filePath)).replace(/\\/g, "/");
|
|
1662
1811
|
const updated = removeImportFromBarrel(barrelContent, importPath);
|
|
@@ -1671,14 +1820,113 @@ async function removeCommand(componentName) {
|
|
|
1671
1820
|
}
|
|
1672
1821
|
}
|
|
1673
1822
|
delete config.installed[installedKey];
|
|
1823
|
+
if (deleted.length > 0) {
|
|
1824
|
+
p6.log.success(`Removed ${installedKey}:
|
|
1825
|
+
` + deleted.map((f) => ` ${pc6.red("-")} ${f}`).join("\n"));
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
async function offerOrphanRemoval(removedDeps, config, cwd) {
|
|
1829
|
+
if (removedDeps.size === 0) return;
|
|
1830
|
+
const remaining = Object.entries(config.installed ?? {});
|
|
1831
|
+
const neededDeps = /* @__PURE__ */ new Set();
|
|
1832
|
+
for (const [, entry] of remaining) {
|
|
1833
|
+
const deps = entry.registryDependencies;
|
|
1834
|
+
if (deps) {
|
|
1835
|
+
for (const dep of deps) {
|
|
1836
|
+
neededDeps.add(dep);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
const orphans = [...removedDeps].filter(
|
|
1841
|
+
(dep) => dep !== "core" && !neededDeps.has(dep) && config.installed?.[dep]
|
|
1842
|
+
);
|
|
1843
|
+
if (orphans.length === 0) return;
|
|
1844
|
+
const selected = await p6.multiselect({
|
|
1845
|
+
message: "The following dependencies are no longer used. Remove them?",
|
|
1846
|
+
options: orphans.map((dep) => ({
|
|
1847
|
+
value: dep,
|
|
1848
|
+
label: dep,
|
|
1849
|
+
hint: `${config.installed[dep].files.length} file(s)`
|
|
1850
|
+
})),
|
|
1851
|
+
initialValues: orphans
|
|
1852
|
+
// all checked by default
|
|
1853
|
+
});
|
|
1854
|
+
if (p6.isCancel(selected)) return;
|
|
1855
|
+
for (const key of selected) {
|
|
1856
|
+
await removeSingleComponent(key, config, cwd);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
async function removeCommand(componentName) {
|
|
1860
|
+
const cwd = process.cwd();
|
|
1861
|
+
const config = await readConfig(cwd);
|
|
1862
|
+
if (!config) {
|
|
1863
|
+
p6.log.error("No kitn.json found. Run `kitn init` first.");
|
|
1864
|
+
process.exit(1);
|
|
1865
|
+
}
|
|
1866
|
+
if (!componentName) {
|
|
1867
|
+
const installed2 = config.installed ?? {};
|
|
1868
|
+
const installedKeys = Object.keys(installed2);
|
|
1869
|
+
if (installedKeys.length === 0) {
|
|
1870
|
+
p6.log.warn("No components installed.");
|
|
1871
|
+
process.exit(0);
|
|
1872
|
+
}
|
|
1873
|
+
const selected = await p6.multiselect({
|
|
1874
|
+
message: "Select components to remove:",
|
|
1875
|
+
options: installedKeys.map((key) => ({
|
|
1876
|
+
value: key,
|
|
1877
|
+
label: key,
|
|
1878
|
+
hint: `${installed2[key].files.length} file(s)`
|
|
1879
|
+
}))
|
|
1880
|
+
});
|
|
1881
|
+
if (p6.isCancel(selected)) {
|
|
1882
|
+
p6.cancel("Cancelled.");
|
|
1883
|
+
process.exit(0);
|
|
1884
|
+
}
|
|
1885
|
+
const selectedKeys = selected;
|
|
1886
|
+
if (selectedKeys.length === 0) {
|
|
1887
|
+
p6.log.warn("No components selected.");
|
|
1888
|
+
process.exit(0);
|
|
1889
|
+
}
|
|
1890
|
+
for (const key of selectedKeys) {
|
|
1891
|
+
await removeSingleComponent(key, config, cwd);
|
|
1892
|
+
}
|
|
1893
|
+
const allRemovedDeps = /* @__PURE__ */ new Set();
|
|
1894
|
+
for (const key of selectedKeys) {
|
|
1895
|
+
const entry = installed2[key];
|
|
1896
|
+
if (entry?.registryDependencies) {
|
|
1897
|
+
for (const dep of entry.registryDependencies) {
|
|
1898
|
+
allRemovedDeps.add(dep);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
await offerOrphanRemoval(allRemovedDeps, config, cwd);
|
|
1903
|
+
await writeConfig(cwd, config);
|
|
1904
|
+
p6.outro(pc6.green("Done!"));
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
const input = componentName === "routes" ? resolveRoutesAlias(config) : componentName;
|
|
1908
|
+
const ref = parseComponentRef(input);
|
|
1909
|
+
const installedKey = ref.namespace === "@kitn" ? ref.name : `${ref.namespace}/${ref.name}`;
|
|
1910
|
+
const installed = config.installed?.[installedKey];
|
|
1911
|
+
if (!installed) {
|
|
1912
|
+
p6.log.error(`Component '${ref.name}' is not installed.`);
|
|
1913
|
+
process.exit(1);
|
|
1914
|
+
}
|
|
1915
|
+
const shouldRemove = await p6.confirm({
|
|
1916
|
+
message: `Remove ${ref.name}? This will delete ${installed.files.length} file(s).`,
|
|
1917
|
+
initialValue: false
|
|
1918
|
+
});
|
|
1919
|
+
if (p6.isCancel(shouldRemove) || !shouldRemove) {
|
|
1920
|
+
p6.cancel("Remove cancelled.");
|
|
1921
|
+
process.exit(0);
|
|
1922
|
+
}
|
|
1923
|
+
await removeSingleComponent(installedKey, config, cwd);
|
|
1924
|
+
const removedDeps = new Set(installed.registryDependencies ?? []);
|
|
1925
|
+
await offerOrphanRemoval(removedDeps, config, cwd);
|
|
1674
1926
|
if (Object.keys(config.installed).length === 0) {
|
|
1675
1927
|
delete config.installed;
|
|
1676
1928
|
}
|
|
1677
1929
|
await writeConfig(cwd, config);
|
|
1678
|
-
if (deleted.length > 0) {
|
|
1679
|
-
p6.log.success(`Removed ${ref.name}:
|
|
1680
|
-
` + deleted.map((f) => ` ${pc6.red("-")} ${f}`).join("\n"));
|
|
1681
|
-
}
|
|
1682
1930
|
}
|
|
1683
1931
|
var init_remove = __esm({
|
|
1684
1932
|
"src/commands/remove.ts"() {
|
|
@@ -2013,6 +2261,7 @@ var check_exports = {};
|
|
|
2013
2261
|
__export(check_exports, {
|
|
2014
2262
|
checkCommand: () => checkCommand
|
|
2015
2263
|
});
|
|
2264
|
+
import { execSync as execSync2 } from "child_process";
|
|
2016
2265
|
import * as p10 from "@clack/prompts";
|
|
2017
2266
|
import pc9 from "picocolors";
|
|
2018
2267
|
async function checkCommand(currentVersion) {
|
|
@@ -2028,7 +2277,22 @@ async function checkCommand(currentVersion) {
|
|
|
2028
2277
|
}
|
|
2029
2278
|
if (isNewer(latest, currentVersion)) {
|
|
2030
2279
|
s.stop(pc9.yellow(`Update available: ${currentVersion} \u2192 ${latest}`));
|
|
2031
|
-
|
|
2280
|
+
const pm = detectCliInstaller();
|
|
2281
|
+
const installCmd = getGlobalInstallCommand(pm, "@kitnai/cli");
|
|
2282
|
+
const shouldUpdate = await p10.confirm({ message: "Update now?" });
|
|
2283
|
+
if (p10.isCancel(shouldUpdate) || !shouldUpdate) {
|
|
2284
|
+
p10.log.message(` Run: ${pc9.cyan(installCmd)}`);
|
|
2285
|
+
} else {
|
|
2286
|
+
const us = p10.spinner();
|
|
2287
|
+
us.start("Updating...");
|
|
2288
|
+
try {
|
|
2289
|
+
execSync2(installCmd, { stdio: "pipe" });
|
|
2290
|
+
us.stop(pc9.green(`Updated to v${latest}`));
|
|
2291
|
+
} catch {
|
|
2292
|
+
us.stop(pc9.red("Update failed"));
|
|
2293
|
+
p10.log.message(` Run manually: ${pc9.cyan(installCmd)}`);
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2032
2296
|
} else {
|
|
2033
2297
|
s.stop(pc9.green("You're on the latest version"));
|
|
2034
2298
|
}
|
|
@@ -2037,6 +2301,7 @@ async function checkCommand(currentVersion) {
|
|
|
2037
2301
|
var init_check = __esm({
|
|
2038
2302
|
"src/commands/check.ts"() {
|
|
2039
2303
|
"use strict";
|
|
2304
|
+
init_detect();
|
|
2040
2305
|
init_update_check();
|
|
2041
2306
|
}
|
|
2042
2307
|
});
|
|
@@ -2140,14 +2405,14 @@ var init_registry = __esm({
|
|
|
2140
2405
|
// src/index.ts
|
|
2141
2406
|
init_update_check();
|
|
2142
2407
|
import { Command } from "commander";
|
|
2143
|
-
var VERSION = true ? "0.1.
|
|
2408
|
+
var VERSION = true ? "0.1.24" : "0.0.0-dev";
|
|
2144
2409
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
2145
2410
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
2146
2411
|
program.command("init").description("Initialize kitn in your project").option("-r, --runtime <runtime>", "runtime to use (bun, node, deno)").option("-f, --framework <framework>", "HTTP framework (hono, hono-openapi, elysia)").option("-b, --base <path>", "base directory for components (default: src/ai)").option("-y, --yes", "accept all defaults without prompting").action(async (opts) => {
|
|
2147
2412
|
const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
2148
2413
|
await initCommand2(opts);
|
|
2149
2414
|
});
|
|
2150
|
-
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) => {
|
|
2415
|
+
program.command("add").alias("install").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) => {
|
|
2151
2416
|
const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
2152
2417
|
await addCommand2(components, opts);
|
|
2153
2418
|
});
|
|
@@ -2159,7 +2424,7 @@ program.command("diff").description("Show differences between local and registry
|
|
|
2159
2424
|
const { diffCommand: diffCommand2 } = await Promise.resolve().then(() => (init_diff(), diff_exports));
|
|
2160
2425
|
await diffCommand2(component);
|
|
2161
2426
|
});
|
|
2162
|
-
program.command("remove").description("Remove an installed component").argument("
|
|
2427
|
+
program.command("remove").alias("uninstall").description("Remove an installed component").argument("[component]", "component name to remove (interactive if omitted)").action(async (component) => {
|
|
2163
2428
|
const { removeCommand: removeCommand2 } = await Promise.resolve().then(() => (init_remove(), remove_exports));
|
|
2164
2429
|
await removeCommand2(component);
|
|
2165
2430
|
});
|