@kitnai/cli 0.1.33 → 0.1.35
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 +259 -214
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1188,8 +1188,8 @@ async function addCommand(components, opts) {
|
|
|
1188
1188
|
if (!opts.yes && process.stdin.isTTY) {
|
|
1189
1189
|
const totalComponents = resolved.length;
|
|
1190
1190
|
const summary = totalDeps > 0 ? `Install ${totalComponents} component(s) and ${totalDeps} npm package(s)?` : `Install ${totalComponents} component(s)?`;
|
|
1191
|
-
const
|
|
1192
|
-
if (p2.isCancel(
|
|
1191
|
+
const confirm7 = await p2.confirm({ message: summary });
|
|
1192
|
+
if (p2.isCancel(confirm7) || !confirm7) {
|
|
1193
1193
|
p2.cancel("Cancelled.");
|
|
1194
1194
|
process.exit(0);
|
|
1195
1195
|
}
|
|
@@ -1441,8 +1441,14 @@ var init_add = __esm({
|
|
|
1441
1441
|
});
|
|
1442
1442
|
|
|
1443
1443
|
// src/installers/rules-generator.ts
|
|
1444
|
-
import { writeFile as writeFile7, mkdir as mkdir4 } from "fs/promises";
|
|
1444
|
+
import { writeFile as writeFile7, mkdir as mkdir4, readFile as readFile7 } from "fs/promises";
|
|
1445
1445
|
import { join as join8, dirname as dirname4 } from "path";
|
|
1446
|
+
async function loadFallbackTemplate() {
|
|
1447
|
+
if (!_fallbackTemplate) {
|
|
1448
|
+
_fallbackTemplate = await readFile7(TEMPLATE_PATH, "utf-8");
|
|
1449
|
+
}
|
|
1450
|
+
return _fallbackTemplate;
|
|
1451
|
+
}
|
|
1446
1452
|
function deriveRulesBaseUrl(registries) {
|
|
1447
1453
|
const kitnEntry = registries["@kitn"];
|
|
1448
1454
|
if (!kitnEntry) {
|
|
@@ -1468,7 +1474,7 @@ async function fetchRulesTemplate(registries) {
|
|
|
1468
1474
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1469
1475
|
return await res.text();
|
|
1470
1476
|
} catch {
|
|
1471
|
-
return
|
|
1477
|
+
return loadFallbackTemplate();
|
|
1472
1478
|
}
|
|
1473
1479
|
}
|
|
1474
1480
|
function renderTemplate(template, aliases) {
|
|
@@ -1503,11 +1509,12 @@ async function generateRulesFiles(cwd, config2, selectedToolIds) {
|
|
|
1503
1509
|
}
|
|
1504
1510
|
return written;
|
|
1505
1511
|
}
|
|
1506
|
-
var FALLBACK_CONFIG,
|
|
1512
|
+
var TEMPLATE_PATH, FALLBACK_CONFIG, _fallbackTemplate;
|
|
1507
1513
|
var init_rules_generator = __esm({
|
|
1508
1514
|
"src/installers/rules-generator.ts"() {
|
|
1509
1515
|
"use strict";
|
|
1510
1516
|
init_config();
|
|
1517
|
+
TEMPLATE_PATH = join8(import.meta.dirname, "rules-template.md");
|
|
1511
1518
|
FALLBACK_CONFIG = {
|
|
1512
1519
|
version: "1.0.0",
|
|
1513
1520
|
tools: [
|
|
@@ -1525,7 +1532,7 @@ var init_rules_generator = __esm({
|
|
|
1525
1532
|
format: "mdc",
|
|
1526
1533
|
frontmatter: {
|
|
1527
1534
|
description: "kitn AI agent framework conventions and patterns",
|
|
1528
|
-
globs: "src/ai/**/*.ts, kitn.json"
|
|
1535
|
+
globs: "src/ai/**/*.ts, src/ai/**/*.md, kitn.json, kitn.lock"
|
|
1529
1536
|
}
|
|
1530
1537
|
},
|
|
1531
1538
|
{
|
|
@@ -1548,34 +1555,6 @@ var init_rules_generator = __esm({
|
|
|
1548
1555
|
}
|
|
1549
1556
|
]
|
|
1550
1557
|
};
|
|
1551
|
-
FALLBACK_TEMPLATE = `# kitn AI Agent Framework
|
|
1552
|
-
|
|
1553
|
-
This project uses **kitn** to build multi-agent AI systems.
|
|
1554
|
-
|
|
1555
|
-
## Project Structure
|
|
1556
|
-
|
|
1557
|
-
AI components live under \`{base}\`:
|
|
1558
|
-
|
|
1559
|
-
- \`{agents}/\` \u2014 Agent definitions
|
|
1560
|
-
- \`{tools}/\` \u2014 Tool definitions
|
|
1561
|
-
- \`{skills}/\` \u2014 Skill files (markdown)
|
|
1562
|
-
- \`{storage}/\` \u2014 Storage providers
|
|
1563
|
-
- \`{crons}/\` \u2014 Cron job definitions
|
|
1564
|
-
|
|
1565
|
-
## Patterns
|
|
1566
|
-
|
|
1567
|
-
- Agents: \`registerAgent({ name, system, tools })\` from \`@kitn/core\`
|
|
1568
|
-
- Tools: \`tool()\` from \`ai\` + \`registerTool()\` from \`@kitn/core\`
|
|
1569
|
-
- Always use \`.js\` extension in relative imports
|
|
1570
|
-
- Use \`@kitn/core\` for core imports, \`ai\` for Vercel AI SDK
|
|
1571
|
-
|
|
1572
|
-
## CLI
|
|
1573
|
-
|
|
1574
|
-
- \`kitn add <name>\` \u2014 install from registry
|
|
1575
|
-
- \`kitn create <type> <name>\` \u2014 scaffold locally
|
|
1576
|
-
- \`kitn link tool <name> --to <agent>\` \u2014 wire a tool to an agent
|
|
1577
|
-
- \`kitn list\` \u2014 browse components
|
|
1578
|
-
`;
|
|
1579
1558
|
}
|
|
1580
1559
|
});
|
|
1581
1560
|
|
|
@@ -1586,11 +1565,11 @@ __export(init_exports, {
|
|
|
1586
1565
|
});
|
|
1587
1566
|
import * as p3 from "@clack/prompts";
|
|
1588
1567
|
import pc4 from "picocolors";
|
|
1589
|
-
import { mkdir as mkdir5, readFile as
|
|
1568
|
+
import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
|
|
1590
1569
|
import { join as join9 } from "path";
|
|
1591
1570
|
async function detectFramework(cwd) {
|
|
1592
1571
|
try {
|
|
1593
|
-
const pkg = JSON.parse(await
|
|
1572
|
+
const pkg = JSON.parse(await readFile8(join9(cwd, "package.json"), "utf-8"));
|
|
1594
1573
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1595
1574
|
if (deps["elysia"]) return "elysia";
|
|
1596
1575
|
if (deps["@hono/zod-openapi"]) return "hono-openapi";
|
|
@@ -1678,15 +1657,15 @@ async function initCommand(opts = {}) {
|
|
|
1678
1657
|
framework = detected;
|
|
1679
1658
|
p3.log.info(`Detected ${pc4.bold(detected)} from package.json`);
|
|
1680
1659
|
if (!opts.yes) {
|
|
1681
|
-
const
|
|
1660
|
+
const confirm7 = await p3.confirm({
|
|
1682
1661
|
message: `Use ${detected}?`,
|
|
1683
1662
|
initialValue: true
|
|
1684
1663
|
});
|
|
1685
|
-
if (p3.isCancel(
|
|
1664
|
+
if (p3.isCancel(confirm7)) {
|
|
1686
1665
|
p3.cancel("Init cancelled.");
|
|
1687
1666
|
process.exit(0);
|
|
1688
1667
|
}
|
|
1689
|
-
if (!
|
|
1668
|
+
if (!confirm7) {
|
|
1690
1669
|
const selected = await p3.select({
|
|
1691
1670
|
message: "Which HTTP framework do you use?",
|
|
1692
1671
|
options: [
|
|
@@ -2057,7 +2036,7 @@ __export(remove_exports, {
|
|
|
2057
2036
|
import * as p6 from "@clack/prompts";
|
|
2058
2037
|
import pc6 from "picocolors";
|
|
2059
2038
|
import { join as join11, relative as relative3, dirname as dirname5 } from "path";
|
|
2060
|
-
import { unlink as unlink3, readFile as
|
|
2039
|
+
import { unlink as unlink3, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
|
|
2061
2040
|
import { existsSync as existsSync2 } from "fs";
|
|
2062
2041
|
async function removeSingleComponent(installedKey, lock, config2, cwd) {
|
|
2063
2042
|
const entry = lock[installedKey];
|
|
@@ -2080,7 +2059,7 @@ async function removeSingleComponent(installedKey, lock, config2, cwd) {
|
|
|
2080
2059
|
config2.aliases.skills
|
|
2081
2060
|
]);
|
|
2082
2061
|
if (existsSync2(barrelPath) && deleted.length > 0) {
|
|
2083
|
-
let barrelContent = await
|
|
2062
|
+
let barrelContent = await readFile9(barrelPath, "utf-8");
|
|
2084
2063
|
let barrelChanged = false;
|
|
2085
2064
|
for (const filePath of deleted) {
|
|
2086
2065
|
const fileDir = dirname5(filePath);
|
|
@@ -2265,7 +2244,7 @@ import * as p8 from "@clack/prompts";
|
|
|
2265
2244
|
import pc7 from "picocolors";
|
|
2266
2245
|
import { join as join12, relative as relative4 } from "path";
|
|
2267
2246
|
import { existsSync as existsSync3 } from "fs";
|
|
2268
|
-
import { readFile as
|
|
2247
|
+
import { readFile as readFile10, writeFile as writeFile10, mkdir as mkdir6 } from "fs/promises";
|
|
2269
2248
|
function generateAgentSource(name) {
|
|
2270
2249
|
const camel = toCamelCase(name);
|
|
2271
2250
|
return `import { registerAgent } from "@kitn/core";
|
|
@@ -2403,7 +2382,7 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2403
2382
|
const barrelPath = join12(cwd, baseDir, "index.ts");
|
|
2404
2383
|
let barrelContent;
|
|
2405
2384
|
if (existsSync3(barrelPath)) {
|
|
2406
|
-
barrelContent = await
|
|
2385
|
+
barrelContent = await readFile10(barrelPath, "utf-8");
|
|
2407
2386
|
} else {
|
|
2408
2387
|
barrelContent = createBarrelFile();
|
|
2409
2388
|
await mkdir6(join12(cwd, baseDir), { recursive: true });
|
|
@@ -2455,7 +2434,7 @@ var init_create = __esm({
|
|
|
2455
2434
|
});
|
|
2456
2435
|
|
|
2457
2436
|
// src/utils/component-resolver.ts
|
|
2458
|
-
import { readdir, readFile as
|
|
2437
|
+
import { readdir, readFile as readFile11 } from "fs/promises";
|
|
2459
2438
|
import { join as join13, relative as relative5 } from "path";
|
|
2460
2439
|
function stripSuffix(name, suffix) {
|
|
2461
2440
|
if (name.endsWith(`-${suffix}`)) {
|
|
@@ -2477,7 +2456,7 @@ async function findFile(dir, candidates) {
|
|
|
2477
2456
|
for (const name of candidates) {
|
|
2478
2457
|
const filePath = join13(dir, `${name}.ts`);
|
|
2479
2458
|
try {
|
|
2480
|
-
await
|
|
2459
|
+
await readFile11(filePath);
|
|
2481
2460
|
return filePath;
|
|
2482
2461
|
} catch {
|
|
2483
2462
|
}
|
|
@@ -2512,7 +2491,7 @@ async function resolveToolByName(name, config2, cwd) {
|
|
|
2512
2491
|
if (toolFile) {
|
|
2513
2492
|
const filePath2 = join13(cwd, toolFile);
|
|
2514
2493
|
try {
|
|
2515
|
-
const source2 = await
|
|
2494
|
+
const source2 = await readFile11(filePath2, "utf-8");
|
|
2516
2495
|
const exportName2 = parseExportName(source2);
|
|
2517
2496
|
if (exportName2) {
|
|
2518
2497
|
return {
|
|
@@ -2530,7 +2509,7 @@ async function resolveToolByName(name, config2, cwd) {
|
|
|
2530
2509
|
const uniqueCandidates = [...new Set(candidates)];
|
|
2531
2510
|
const filePath = await findFile(tDir, uniqueCandidates);
|
|
2532
2511
|
if (!filePath) return null;
|
|
2533
|
-
const source = await
|
|
2512
|
+
const source = await readFile11(filePath, "utf-8");
|
|
2534
2513
|
const exportName = parseExportName(source);
|
|
2535
2514
|
if (!exportName) return null;
|
|
2536
2515
|
return {
|
|
@@ -2551,7 +2530,7 @@ async function resolveAgentByName(name, config2, cwd) {
|
|
|
2551
2530
|
if (agentFile) {
|
|
2552
2531
|
const filePath2 = join13(cwd, agentFile);
|
|
2553
2532
|
try {
|
|
2554
|
-
const source2 = await
|
|
2533
|
+
const source2 = await readFile11(filePath2, "utf-8");
|
|
2555
2534
|
const agentName2 = parseAgentName(source2);
|
|
2556
2535
|
return {
|
|
2557
2536
|
filePath: filePath2,
|
|
@@ -2566,7 +2545,7 @@ async function resolveAgentByName(name, config2, cwd) {
|
|
|
2566
2545
|
const uniqueCandidates = [...new Set(candidates)];
|
|
2567
2546
|
const filePath = await findFile(aDir, uniqueCandidates);
|
|
2568
2547
|
if (!filePath) return null;
|
|
2569
|
-
const source = await
|
|
2548
|
+
const source = await readFile11(filePath, "utf-8");
|
|
2570
2549
|
const agentName = parseAgentName(source);
|
|
2571
2550
|
const fallbackName = filePath.split("/").pop().replace(/\.ts$/, "");
|
|
2572
2551
|
return {
|
|
@@ -2854,7 +2833,7 @@ __export(link_exports, {
|
|
|
2854
2833
|
});
|
|
2855
2834
|
import * as p9 from "@clack/prompts";
|
|
2856
2835
|
import pc8 from "picocolors";
|
|
2857
|
-
import { readFile as
|
|
2836
|
+
import { readFile as readFile12, writeFile as writeFile11 } from "fs/promises";
|
|
2858
2837
|
import { basename } from "path";
|
|
2859
2838
|
async function linkCommand(type, name, opts) {
|
|
2860
2839
|
p9.intro(pc8.bgCyan(pc8.black(" kitn link ")));
|
|
@@ -2924,7 +2903,7 @@ async function linkCommand(type, name, opts) {
|
|
|
2924
2903
|
);
|
|
2925
2904
|
process.exit(1);
|
|
2926
2905
|
}
|
|
2927
|
-
const agentContent = await
|
|
2906
|
+
const agentContent = await readFile12(agent.filePath, "utf-8");
|
|
2928
2907
|
const toolRef = {
|
|
2929
2908
|
exportName: tool.exportName,
|
|
2930
2909
|
importPath: tool.importPath
|
|
@@ -2970,7 +2949,7 @@ __export(unlink_exports, {
|
|
|
2970
2949
|
});
|
|
2971
2950
|
import * as p10 from "@clack/prompts";
|
|
2972
2951
|
import pc9 from "picocolors";
|
|
2973
|
-
import { readFile as
|
|
2952
|
+
import { readFile as readFile13, writeFile as writeFile12 } from "fs/promises";
|
|
2974
2953
|
import { basename as basename2 } from "path";
|
|
2975
2954
|
async function unlinkCommand(type, name, opts) {
|
|
2976
2955
|
p10.intro(pc9.bgCyan(pc9.black(" kitn unlink ")));
|
|
@@ -3040,7 +3019,7 @@ async function unlinkCommand(type, name, opts) {
|
|
|
3040
3019
|
);
|
|
3041
3020
|
process.exit(1);
|
|
3042
3021
|
}
|
|
3043
|
-
const agentContent = await
|
|
3022
|
+
const agentContent = await readFile13(agent.filePath, "utf-8");
|
|
3044
3023
|
const toolRef = {
|
|
3045
3024
|
exportName: tool.exportName,
|
|
3046
3025
|
importPath: tool.importPath
|
|
@@ -3308,13 +3287,13 @@ __export(config_exports, {
|
|
|
3308
3287
|
});
|
|
3309
3288
|
import * as p14 from "@clack/prompts";
|
|
3310
3289
|
import pc13 from "picocolors";
|
|
3311
|
-
import { readFile as
|
|
3290
|
+
import { readFile as readFile14, writeFile as writeFile13, mkdir as mkdir7 } from "fs/promises";
|
|
3312
3291
|
import { join as join14 } from "path";
|
|
3313
3292
|
import { homedir as homedir2 } from "os";
|
|
3314
3293
|
import { existsSync as existsSync4 } from "fs";
|
|
3315
3294
|
async function readUserConfig() {
|
|
3316
3295
|
try {
|
|
3317
|
-
const raw = await
|
|
3296
|
+
const raw = await readFile14(CONFIG_FILE2, "utf-8");
|
|
3318
3297
|
return JSON.parse(raw);
|
|
3319
3298
|
} catch {
|
|
3320
3299
|
return {};
|
|
@@ -3371,16 +3350,144 @@ var init_config2 = __esm({
|
|
|
3371
3350
|
}
|
|
3372
3351
|
});
|
|
3373
3352
|
|
|
3353
|
+
// src/commands/registry.ts
|
|
3354
|
+
var registry_exports = {};
|
|
3355
|
+
__export(registry_exports, {
|
|
3356
|
+
registryAddCommand: () => registryAddCommand,
|
|
3357
|
+
registryListCommand: () => registryListCommand,
|
|
3358
|
+
registryRemoveCommand: () => registryRemoveCommand
|
|
3359
|
+
});
|
|
3360
|
+
import * as p15 from "@clack/prompts";
|
|
3361
|
+
import pc14 from "picocolors";
|
|
3362
|
+
async function registryAddCommand(namespace, url, opts = {}) {
|
|
3363
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
3364
|
+
const config2 = await readConfig(cwd);
|
|
3365
|
+
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3366
|
+
if (!namespace.startsWith("@")) {
|
|
3367
|
+
throw new Error("Namespace must start with @ (e.g. @myteam)");
|
|
3368
|
+
}
|
|
3369
|
+
if (!url.includes("{type}")) {
|
|
3370
|
+
throw new Error("URL template must include {type} placeholder");
|
|
3371
|
+
}
|
|
3372
|
+
if (!url.includes("{name}")) {
|
|
3373
|
+
throw new Error("URL template must include {name} placeholder");
|
|
3374
|
+
}
|
|
3375
|
+
if (config2.registries[namespace] && !opts.overwrite) {
|
|
3376
|
+
throw new Error(`Registry '${namespace}' is already configured. Use --overwrite to replace.`);
|
|
3377
|
+
}
|
|
3378
|
+
if (opts.homepage || opts.description) {
|
|
3379
|
+
const entry = { url };
|
|
3380
|
+
if (opts.homepage) entry.homepage = opts.homepage;
|
|
3381
|
+
if (opts.description) entry.description = opts.description;
|
|
3382
|
+
config2.registries[namespace] = entry;
|
|
3383
|
+
} else {
|
|
3384
|
+
config2.registries[namespace] = url;
|
|
3385
|
+
}
|
|
3386
|
+
await writeConfig(cwd, config2);
|
|
3387
|
+
p15.log.success(`Added registry ${pc14.bold(namespace)}`);
|
|
3388
|
+
p15.log.message(pc14.dim(` ${url}`));
|
|
3389
|
+
if (opts.homepage) p15.log.message(pc14.dim(` Homepage: ${opts.homepage}`));
|
|
3390
|
+
if (opts.description) p15.log.message(pc14.dim(` ${opts.description}`));
|
|
3391
|
+
}
|
|
3392
|
+
async function registryRemoveCommand(namespace, opts = {}) {
|
|
3393
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
3394
|
+
const config2 = await readConfig(cwd);
|
|
3395
|
+
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3396
|
+
if (!config2.registries[namespace]) {
|
|
3397
|
+
throw new Error(`Registry '${namespace}' is not configured.`);
|
|
3398
|
+
}
|
|
3399
|
+
if (namespace === "@kitn" && !opts.force) {
|
|
3400
|
+
throw new Error("Cannot remove the default @kitn registry. Use --force to override.");
|
|
3401
|
+
}
|
|
3402
|
+
const lock = await readLock(cwd);
|
|
3403
|
+
const affectedComponents = [];
|
|
3404
|
+
for (const [name, entry] of Object.entries(lock)) {
|
|
3405
|
+
if (entry.registry === namespace) {
|
|
3406
|
+
affectedComponents.push(name);
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
delete config2.registries[namespace];
|
|
3410
|
+
await writeConfig(cwd, config2);
|
|
3411
|
+
p15.log.success(`Removed registry ${pc14.bold(namespace)}`);
|
|
3412
|
+
if (affectedComponents.length > 0) {
|
|
3413
|
+
p15.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:
|
|
3414
|
+
` + affectedComponents.map((name) => ` ${pc14.yellow("!")} ${name}`).join("\n"));
|
|
3415
|
+
}
|
|
3416
|
+
return { affectedComponents };
|
|
3417
|
+
}
|
|
3418
|
+
async function registryListCommand(opts = {}) {
|
|
3419
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
3420
|
+
const config2 = await readConfig(cwd);
|
|
3421
|
+
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3422
|
+
const entries = Object.entries(config2.registries).map(([namespace, value]) => {
|
|
3423
|
+
const url = getRegistryUrl(value);
|
|
3424
|
+
const homepage = typeof value === "object" ? value.homepage : void 0;
|
|
3425
|
+
const description = typeof value === "object" ? value.description : void 0;
|
|
3426
|
+
return { namespace, url, homepage, description };
|
|
3427
|
+
});
|
|
3428
|
+
if (entries.length === 0) {
|
|
3429
|
+
p15.log.message(pc14.dim(" No registries configured."));
|
|
3430
|
+
} else {
|
|
3431
|
+
const lines = [];
|
|
3432
|
+
for (const { namespace, url, homepage, description } of entries) {
|
|
3433
|
+
lines.push(` ${pc14.bold(namespace.padEnd(16))} ${pc14.dim(url)}`);
|
|
3434
|
+
if (description) lines.push(` ${" ".repeat(16)} ${description}`);
|
|
3435
|
+
if (homepage) lines.push(` ${" ".repeat(16)} ${pc14.dim(homepage)}`);
|
|
3436
|
+
}
|
|
3437
|
+
p15.log.message(lines.join("\n"));
|
|
3438
|
+
}
|
|
3439
|
+
return entries;
|
|
3440
|
+
}
|
|
3441
|
+
var init_registry = __esm({
|
|
3442
|
+
"src/commands/registry.ts"() {
|
|
3443
|
+
"use strict";
|
|
3444
|
+
init_config();
|
|
3445
|
+
}
|
|
3446
|
+
});
|
|
3447
|
+
|
|
3374
3448
|
// src/commands/chat.ts
|
|
3375
3449
|
var chat_exports = {};
|
|
3376
3450
|
__export(chat_exports, {
|
|
3377
3451
|
buildRequestPayload: () => buildRequestPayload,
|
|
3378
3452
|
chatCommand: () => chatCommand,
|
|
3453
|
+
fetchGlobalRegistries: () => fetchGlobalRegistries,
|
|
3379
3454
|
formatPlan: () => formatPlan,
|
|
3380
3455
|
resolveServiceUrl: () => resolveServiceUrl
|
|
3381
3456
|
});
|
|
3382
|
-
import * as
|
|
3383
|
-
import
|
|
3457
|
+
import * as p16 from "@clack/prompts";
|
|
3458
|
+
import pc15 from "picocolors";
|
|
3459
|
+
async function fetchGlobalRegistries(configuredNamespaces) {
|
|
3460
|
+
let directory;
|
|
3461
|
+
try {
|
|
3462
|
+
const res = await fetch(GLOBAL_REGISTRY_URL);
|
|
3463
|
+
if (!res.ok) return [];
|
|
3464
|
+
directory = await res.json();
|
|
3465
|
+
} catch {
|
|
3466
|
+
return [];
|
|
3467
|
+
}
|
|
3468
|
+
const unconfigured = directory.filter(
|
|
3469
|
+
(entry) => !configuredNamespaces.includes(entry.name)
|
|
3470
|
+
);
|
|
3471
|
+
if (unconfigured.length === 0) return [];
|
|
3472
|
+
const results = [];
|
|
3473
|
+
for (const entry of unconfigured) {
|
|
3474
|
+
try {
|
|
3475
|
+
const indexUrl = entry.url.replace("{type}/{name}.json", "registry.json");
|
|
3476
|
+
const res = await fetch(indexUrl);
|
|
3477
|
+
if (!res.ok) continue;
|
|
3478
|
+
const index = await res.json();
|
|
3479
|
+
const items = (index.items ?? []).map((item) => ({
|
|
3480
|
+
name: item.name,
|
|
3481
|
+
type: item.type,
|
|
3482
|
+
description: item.description,
|
|
3483
|
+
registryDependencies: item.registryDependencies
|
|
3484
|
+
}));
|
|
3485
|
+
results.push({ namespace: entry.name, url: entry.url, items });
|
|
3486
|
+
} catch {
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
return results;
|
|
3490
|
+
}
|
|
3384
3491
|
async function resolveServiceUrl(urlOverride, chatServiceConfig) {
|
|
3385
3492
|
if (urlOverride) return urlOverride;
|
|
3386
3493
|
if (process.env.KITN_CHAT_URL) return process.env.KITN_CHAT_URL;
|
|
@@ -3405,55 +3512,71 @@ function formatPlan(plan) {
|
|
|
3405
3512
|
function formatStepLabel(step) {
|
|
3406
3513
|
switch (step.action) {
|
|
3407
3514
|
case "add":
|
|
3408
|
-
return `Add ${
|
|
3515
|
+
return `Add ${pc15.cyan(step.component)}`;
|
|
3409
3516
|
case "remove":
|
|
3410
|
-
return `Remove ${
|
|
3517
|
+
return `Remove ${pc15.red(step.component)}`;
|
|
3411
3518
|
case "create":
|
|
3412
|
-
return `Create ${
|
|
3519
|
+
return `Create ${pc15.green(step.name)} ${pc15.dim(`(${step.type})`)}`;
|
|
3413
3520
|
case "link":
|
|
3414
|
-
return `Link ${
|
|
3521
|
+
return `Link ${pc15.cyan(step.toolName)} \u2192 ${pc15.cyan(step.agentName)}`;
|
|
3415
3522
|
case "unlink":
|
|
3416
|
-
return `Unlink ${
|
|
3523
|
+
return `Unlink ${pc15.red(step.toolName)} from ${pc15.cyan(step.agentName)}`;
|
|
3524
|
+
case "registry-add":
|
|
3525
|
+
return `Add registry ${pc15.magenta(step.namespace)}`;
|
|
3417
3526
|
}
|
|
3418
3527
|
}
|
|
3419
3528
|
async function chatCommand(message, opts) {
|
|
3420
3529
|
const cwd = process.cwd();
|
|
3421
3530
|
const config2 = await readConfig(cwd);
|
|
3422
3531
|
if (!config2) {
|
|
3423
|
-
|
|
3532
|
+
p16.log.error("No kitn.json found. Run `kitn init` first.");
|
|
3424
3533
|
process.exit(1);
|
|
3425
3534
|
}
|
|
3426
3535
|
if (!message) {
|
|
3427
|
-
|
|
3536
|
+
p16.log.error('Please provide a message. Usage: kitn chat "add a weather tool"');
|
|
3428
3537
|
process.exit(1);
|
|
3429
3538
|
}
|
|
3430
|
-
|
|
3431
|
-
const s =
|
|
3539
|
+
p16.intro(pc15.bold("kitn assistant"));
|
|
3540
|
+
const s = p16.spinner();
|
|
3432
3541
|
s.start("Gathering project context...");
|
|
3433
3542
|
let registryIndex;
|
|
3434
3543
|
let installed;
|
|
3544
|
+
let globalRegistryIndex;
|
|
3435
3545
|
try {
|
|
3546
|
+
const configuredNamespaces = Object.keys(config2.registries);
|
|
3436
3547
|
const fetcher = new RegistryFetcher(config2.registries);
|
|
3437
|
-
const indices = [
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3548
|
+
const [indices, globalEntries, lock] = await Promise.all([
|
|
3549
|
+
Promise.all(
|
|
3550
|
+
configuredNamespaces.map(async (ns) => {
|
|
3551
|
+
try {
|
|
3552
|
+
return await fetcher.fetchIndex(ns);
|
|
3553
|
+
} catch {
|
|
3554
|
+
return null;
|
|
3555
|
+
}
|
|
3556
|
+
})
|
|
3557
|
+
),
|
|
3558
|
+
fetchGlobalRegistries(configuredNamespaces),
|
|
3559
|
+
readLock(cwd)
|
|
3560
|
+
]);
|
|
3561
|
+
registryIndex = indices.filter(Boolean).flatMap((index) => (index.items ?? []).map((item) => ({
|
|
3562
|
+
name: item.name,
|
|
3563
|
+
type: item.type,
|
|
3564
|
+
description: item.description,
|
|
3565
|
+
registryDependencies: item.registryDependencies
|
|
3566
|
+
})));
|
|
3447
3567
|
installed = Object.keys(lock);
|
|
3568
|
+
globalRegistryIndex = globalEntries.length > 0 ? globalEntries : void 0;
|
|
3448
3569
|
} catch {
|
|
3449
|
-
s.stop(
|
|
3450
|
-
|
|
3570
|
+
s.stop(pc15.red("Failed to gather context"));
|
|
3571
|
+
p16.log.error("Could not read project context. Check your kitn.json and network connection.");
|
|
3451
3572
|
process.exit(1);
|
|
3452
3573
|
}
|
|
3453
3574
|
s.stop("Context gathered");
|
|
3454
3575
|
s.start("Thinking...");
|
|
3455
3576
|
const serviceUrl = await resolveServiceUrl(opts?.url, config2.chatService);
|
|
3456
|
-
const
|
|
3577
|
+
const metadata = { registryIndex, installed };
|
|
3578
|
+
if (globalRegistryIndex) metadata.globalRegistryIndex = globalRegistryIndex;
|
|
3579
|
+
const payload = buildRequestPayload(message, metadata);
|
|
3457
3580
|
let response;
|
|
3458
3581
|
try {
|
|
3459
3582
|
const headers = {
|
|
@@ -3468,69 +3591,85 @@ async function chatCommand(message, opts) {
|
|
|
3468
3591
|
body: JSON.stringify(payload)
|
|
3469
3592
|
});
|
|
3470
3593
|
} catch (err) {
|
|
3471
|
-
s.stop(
|
|
3472
|
-
|
|
3594
|
+
s.stop(pc15.red("Connection failed"));
|
|
3595
|
+
p16.log.error(`Could not reach chat service at ${serviceUrl}. ${err.message ?? ""}`);
|
|
3473
3596
|
process.exit(1);
|
|
3474
3597
|
}
|
|
3475
3598
|
if (!response.ok) {
|
|
3476
|
-
s.stop(
|
|
3477
|
-
|
|
3599
|
+
s.stop(pc15.red("Request failed"));
|
|
3600
|
+
p16.log.error(`Chat service returned ${response.status}: ${response.statusText}`);
|
|
3478
3601
|
process.exit(1);
|
|
3479
3602
|
}
|
|
3480
3603
|
let data;
|
|
3481
3604
|
try {
|
|
3482
3605
|
data = await response.json();
|
|
3483
3606
|
} catch {
|
|
3484
|
-
s.stop(
|
|
3485
|
-
|
|
3607
|
+
s.stop(pc15.red("Invalid response"));
|
|
3608
|
+
p16.log.error("Chat service returned an invalid response.");
|
|
3486
3609
|
process.exit(1);
|
|
3487
3610
|
}
|
|
3488
3611
|
s.stop("Done");
|
|
3489
3612
|
if (data.rejected) {
|
|
3490
|
-
|
|
3491
|
-
|
|
3613
|
+
p16.log.warn(data.text ?? "Request was rejected by the assistant.");
|
|
3614
|
+
p16.outro("Try rephrasing your request.");
|
|
3492
3615
|
return;
|
|
3493
3616
|
}
|
|
3494
3617
|
if (!data.plan) {
|
|
3495
|
-
|
|
3496
|
-
|
|
3618
|
+
p16.log.info(data.text ?? "No actionable plan returned.");
|
|
3619
|
+
p16.outro("Nothing to do.");
|
|
3497
3620
|
return;
|
|
3498
3621
|
}
|
|
3499
|
-
|
|
3622
|
+
p16.log.message(formatPlan(data.plan));
|
|
3500
3623
|
const steps = data.plan.steps;
|
|
3501
|
-
const action = await p15.select({
|
|
3502
|
-
message: "How would you like to proceed?",
|
|
3503
|
-
options: [
|
|
3504
|
-
{ value: "all", label: "Yes, run all steps" },
|
|
3505
|
-
{ value: "select", label: "Select which steps to run" },
|
|
3506
|
-
{ value: "cancel", label: "Cancel" }
|
|
3507
|
-
]
|
|
3508
|
-
});
|
|
3509
|
-
if (p15.isCancel(action) || action === "cancel") {
|
|
3510
|
-
p15.cancel("Cancelled.");
|
|
3511
|
-
return;
|
|
3512
|
-
}
|
|
3513
3624
|
let selectedSteps;
|
|
3514
|
-
if (
|
|
3515
|
-
const
|
|
3516
|
-
message:
|
|
3517
|
-
options: steps.map((step, i) => ({
|
|
3518
|
-
value: i,
|
|
3519
|
-
label: `${formatStepLabel(step)} - ${step.reason}`
|
|
3520
|
-
}))
|
|
3625
|
+
if (steps.length === 1) {
|
|
3626
|
+
const confirm7 = await p16.confirm({
|
|
3627
|
+
message: `Run: ${formatStepLabel(steps[0])}?`
|
|
3521
3628
|
});
|
|
3522
|
-
if (
|
|
3523
|
-
|
|
3629
|
+
if (p16.isCancel(confirm7) || !confirm7) {
|
|
3630
|
+
p16.cancel("Cancelled.");
|
|
3524
3631
|
return;
|
|
3525
3632
|
}
|
|
3526
|
-
selectedSteps = choices.map((i) => steps[i]);
|
|
3527
|
-
} else {
|
|
3528
3633
|
selectedSteps = steps;
|
|
3634
|
+
} else {
|
|
3635
|
+
const action = await p16.select({
|
|
3636
|
+
message: "How would you like to proceed?",
|
|
3637
|
+
options: [
|
|
3638
|
+
{ value: "all", label: "Yes, run all steps" },
|
|
3639
|
+
{ value: "select", label: "Select which steps to run" },
|
|
3640
|
+
{ value: "cancel", label: "Cancel" }
|
|
3641
|
+
]
|
|
3642
|
+
});
|
|
3643
|
+
if (p16.isCancel(action) || action === "cancel") {
|
|
3644
|
+
p16.cancel("Cancelled.");
|
|
3645
|
+
return;
|
|
3646
|
+
}
|
|
3647
|
+
if (action === "select") {
|
|
3648
|
+
const choices = await p16.multiselect({
|
|
3649
|
+
message: "Select steps to run:",
|
|
3650
|
+
options: steps.map((step, i) => ({
|
|
3651
|
+
value: i,
|
|
3652
|
+
label: `${formatStepLabel(step)} - ${step.reason}`
|
|
3653
|
+
}))
|
|
3654
|
+
});
|
|
3655
|
+
if (p16.isCancel(choices)) {
|
|
3656
|
+
p16.cancel("Cancelled.");
|
|
3657
|
+
return;
|
|
3658
|
+
}
|
|
3659
|
+
selectedSteps = choices.map((i) => steps[i]);
|
|
3660
|
+
} else {
|
|
3661
|
+
selectedSteps = steps;
|
|
3662
|
+
}
|
|
3529
3663
|
}
|
|
3530
3664
|
for (const step of selectedSteps) {
|
|
3531
3665
|
s.start(`Running: ${formatStepLabel(step)}...`);
|
|
3532
3666
|
try {
|
|
3533
3667
|
switch (step.action) {
|
|
3668
|
+
case "registry-add": {
|
|
3669
|
+
const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
3670
|
+
await registryAddCommand2(step.namespace, step.url, { overwrite: true });
|
|
3671
|
+
break;
|
|
3672
|
+
}
|
|
3534
3673
|
case "add": {
|
|
3535
3674
|
const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
3536
3675
|
await addCommand2([step.component], { yes: true });
|
|
@@ -3557,15 +3696,15 @@ async function chatCommand(message, opts) {
|
|
|
3557
3696
|
break;
|
|
3558
3697
|
}
|
|
3559
3698
|
}
|
|
3560
|
-
s.stop(
|
|
3699
|
+
s.stop(pc15.green(`Done: ${formatStepLabel(step)}`));
|
|
3561
3700
|
} catch (err) {
|
|
3562
|
-
s.stop(
|
|
3563
|
-
|
|
3701
|
+
s.stop(pc15.red(`Failed: ${formatStepLabel(step)}`));
|
|
3702
|
+
p16.log.error(err.message ?? "Unknown error");
|
|
3564
3703
|
}
|
|
3565
3704
|
}
|
|
3566
|
-
|
|
3705
|
+
p16.outro(pc15.green("All done! Run your dev server to test the new components."));
|
|
3567
3706
|
}
|
|
3568
|
-
var DEFAULT_SERVICE_URL;
|
|
3707
|
+
var DEFAULT_SERVICE_URL, GLOBAL_REGISTRY_URL;
|
|
3569
3708
|
var init_chat = __esm({
|
|
3570
3709
|
"src/commands/chat.ts"() {
|
|
3571
3710
|
"use strict";
|
|
@@ -3573,108 +3712,14 @@ var init_chat = __esm({
|
|
|
3573
3712
|
init_fetcher();
|
|
3574
3713
|
init_config2();
|
|
3575
3714
|
DEFAULT_SERVICE_URL = "https://chat.kitn.dev";
|
|
3576
|
-
|
|
3577
|
-
});
|
|
3578
|
-
|
|
3579
|
-
// src/commands/registry.ts
|
|
3580
|
-
var registry_exports = {};
|
|
3581
|
-
__export(registry_exports, {
|
|
3582
|
-
registryAddCommand: () => registryAddCommand,
|
|
3583
|
-
registryListCommand: () => registryListCommand,
|
|
3584
|
-
registryRemoveCommand: () => registryRemoveCommand
|
|
3585
|
-
});
|
|
3586
|
-
import * as p16 from "@clack/prompts";
|
|
3587
|
-
import pc15 from "picocolors";
|
|
3588
|
-
async function registryAddCommand(namespace, url, opts = {}) {
|
|
3589
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
3590
|
-
const config2 = await readConfig(cwd);
|
|
3591
|
-
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3592
|
-
if (!namespace.startsWith("@")) {
|
|
3593
|
-
throw new Error("Namespace must start with @ (e.g. @myteam)");
|
|
3594
|
-
}
|
|
3595
|
-
if (!url.includes("{type}")) {
|
|
3596
|
-
throw new Error("URL template must include {type} placeholder");
|
|
3597
|
-
}
|
|
3598
|
-
if (!url.includes("{name}")) {
|
|
3599
|
-
throw new Error("URL template must include {name} placeholder");
|
|
3600
|
-
}
|
|
3601
|
-
if (config2.registries[namespace] && !opts.overwrite) {
|
|
3602
|
-
throw new Error(`Registry '${namespace}' is already configured. Use --overwrite to replace.`);
|
|
3603
|
-
}
|
|
3604
|
-
if (opts.homepage || opts.description) {
|
|
3605
|
-
const entry = { url };
|
|
3606
|
-
if (opts.homepage) entry.homepage = opts.homepage;
|
|
3607
|
-
if (opts.description) entry.description = opts.description;
|
|
3608
|
-
config2.registries[namespace] = entry;
|
|
3609
|
-
} else {
|
|
3610
|
-
config2.registries[namespace] = url;
|
|
3611
|
-
}
|
|
3612
|
-
await writeConfig(cwd, config2);
|
|
3613
|
-
p16.log.success(`Added registry ${pc15.bold(namespace)}`);
|
|
3614
|
-
p16.log.message(pc15.dim(` ${url}`));
|
|
3615
|
-
if (opts.homepage) p16.log.message(pc15.dim(` Homepage: ${opts.homepage}`));
|
|
3616
|
-
if (opts.description) p16.log.message(pc15.dim(` ${opts.description}`));
|
|
3617
|
-
}
|
|
3618
|
-
async function registryRemoveCommand(namespace, opts = {}) {
|
|
3619
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
3620
|
-
const config2 = await readConfig(cwd);
|
|
3621
|
-
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3622
|
-
if (!config2.registries[namespace]) {
|
|
3623
|
-
throw new Error(`Registry '${namespace}' is not configured.`);
|
|
3624
|
-
}
|
|
3625
|
-
if (namespace === "@kitn" && !opts.force) {
|
|
3626
|
-
throw new Error("Cannot remove the default @kitn registry. Use --force to override.");
|
|
3627
|
-
}
|
|
3628
|
-
const lock = await readLock(cwd);
|
|
3629
|
-
const affectedComponents = [];
|
|
3630
|
-
for (const [name, entry] of Object.entries(lock)) {
|
|
3631
|
-
if (entry.registry === namespace) {
|
|
3632
|
-
affectedComponents.push(name);
|
|
3633
|
-
}
|
|
3634
|
-
}
|
|
3635
|
-
delete config2.registries[namespace];
|
|
3636
|
-
await writeConfig(cwd, config2);
|
|
3637
|
-
p16.log.success(`Removed registry ${pc15.bold(namespace)}`);
|
|
3638
|
-
if (affectedComponents.length > 0) {
|
|
3639
|
-
p16.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:
|
|
3640
|
-
` + affectedComponents.map((name) => ` ${pc15.yellow("!")} ${name}`).join("\n"));
|
|
3641
|
-
}
|
|
3642
|
-
return { affectedComponents };
|
|
3643
|
-
}
|
|
3644
|
-
async function registryListCommand(opts = {}) {
|
|
3645
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
3646
|
-
const config2 = await readConfig(cwd);
|
|
3647
|
-
if (!config2) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
3648
|
-
const entries = Object.entries(config2.registries).map(([namespace, value]) => {
|
|
3649
|
-
const url = getRegistryUrl(value);
|
|
3650
|
-
const homepage = typeof value === "object" ? value.homepage : void 0;
|
|
3651
|
-
const description = typeof value === "object" ? value.description : void 0;
|
|
3652
|
-
return { namespace, url, homepage, description };
|
|
3653
|
-
});
|
|
3654
|
-
if (entries.length === 0) {
|
|
3655
|
-
p16.log.message(pc15.dim(" No registries configured."));
|
|
3656
|
-
} else {
|
|
3657
|
-
const lines = [];
|
|
3658
|
-
for (const { namespace, url, homepage, description } of entries) {
|
|
3659
|
-
lines.push(` ${pc15.bold(namespace.padEnd(16))} ${pc15.dim(url)}`);
|
|
3660
|
-
if (description) lines.push(` ${" ".repeat(16)} ${description}`);
|
|
3661
|
-
if (homepage) lines.push(` ${" ".repeat(16)} ${pc15.dim(homepage)}`);
|
|
3662
|
-
}
|
|
3663
|
-
p16.log.message(lines.join("\n"));
|
|
3664
|
-
}
|
|
3665
|
-
return entries;
|
|
3666
|
-
}
|
|
3667
|
-
var init_registry = __esm({
|
|
3668
|
-
"src/commands/registry.ts"() {
|
|
3669
|
-
"use strict";
|
|
3670
|
-
init_config();
|
|
3715
|
+
GLOBAL_REGISTRY_URL = "https://kitn-ai.github.io/registry/registries.json";
|
|
3671
3716
|
}
|
|
3672
3717
|
});
|
|
3673
3718
|
|
|
3674
3719
|
// src/index.ts
|
|
3675
3720
|
init_update_check();
|
|
3676
3721
|
import { Command } from "commander";
|
|
3677
|
-
var VERSION = true ? "0.1.
|
|
3722
|
+
var VERSION = true ? "0.1.35" : "0.0.0-dev";
|
|
3678
3723
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
3679
3724
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
3680
3725
|
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) => {
|