@kitnai/cli 0.1.29 → 0.1.31
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 +52 -1
- package/dist/index.js +991 -99
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -240,7 +240,8 @@ var init_config = __esm({
|
|
|
240
240
|
storage: z.string(),
|
|
241
241
|
crons: z.string().optional()
|
|
242
242
|
}),
|
|
243
|
-
registries: z.record(z.string(), registryValueSchema)
|
|
243
|
+
registries: z.record(z.string(), registryValueSchema),
|
|
244
|
+
aiTools: z.array(z.string()).optional()
|
|
244
245
|
});
|
|
245
246
|
FRAMEWORK_TO_ADAPTER = {
|
|
246
247
|
hono: "hono",
|
|
@@ -1152,14 +1153,33 @@ async function addCommand(components, opts) {
|
|
|
1152
1153
|
const label = isExplicit ? item.name : `${item.name} ${pc3.dim("(dependency)")}`;
|
|
1153
1154
|
return ` ${pc3.cyan(label)}`;
|
|
1154
1155
|
}).join("\n"));
|
|
1155
|
-
const created = [];
|
|
1156
|
-
const updated = [];
|
|
1157
|
-
const skipped = [];
|
|
1158
1156
|
const allDeps = [];
|
|
1159
1157
|
const allDevDeps = [];
|
|
1160
1158
|
for (const item of resolved) {
|
|
1161
1159
|
if (item.dependencies) allDeps.push(...item.dependencies);
|
|
1162
1160
|
if (item.devDependencies) allDevDeps.push(...item.devDependencies);
|
|
1161
|
+
}
|
|
1162
|
+
const uniqueDeps = [...new Set(allDeps)];
|
|
1163
|
+
const uniqueDevDeps = [...new Set(allDevDeps)].filter((d) => !uniqueDeps.includes(d));
|
|
1164
|
+
const totalDeps = uniqueDeps.length + uniqueDevDeps.length;
|
|
1165
|
+
if (totalDeps > 0) {
|
|
1166
|
+
const depLines = uniqueDeps.map((d) => ` ${pc3.cyan(d)}`);
|
|
1167
|
+
const devDepLines = uniqueDevDeps.map((d) => ` ${pc3.dim(d)}`);
|
|
1168
|
+
p2.log.info("Dependencies:\n" + [...depLines, ...devDepLines].join("\n"));
|
|
1169
|
+
}
|
|
1170
|
+
if (!opts.yes && process.stdin.isTTY) {
|
|
1171
|
+
const totalComponents = resolved.length;
|
|
1172
|
+
const summary = totalDeps > 0 ? `Install ${totalComponents} component(s) and ${totalDeps} npm package(s)?` : `Install ${totalComponents} component(s)?`;
|
|
1173
|
+
const confirm6 = await p2.confirm({ message: summary });
|
|
1174
|
+
if (p2.isCancel(confirm6) || !confirm6) {
|
|
1175
|
+
p2.cancel("Cancelled.");
|
|
1176
|
+
process.exit(0);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
const created = [];
|
|
1180
|
+
const updated = [];
|
|
1181
|
+
const skipped = [];
|
|
1182
|
+
for (const item of resolved) {
|
|
1163
1183
|
const existingInstall = lock[item.name];
|
|
1164
1184
|
if (existingInstall && item.type === "kitn:package") {
|
|
1165
1185
|
const allContent = item.files.map((f) => f.content).join("\n");
|
|
@@ -1339,9 +1359,6 @@ async function addCommand(components, opts) {
|
|
|
1339
1359
|
}
|
|
1340
1360
|
await writeConfig(cwd, config);
|
|
1341
1361
|
await writeLock(cwd, lock);
|
|
1342
|
-
const uniqueDeps = [...new Set(allDeps)];
|
|
1343
|
-
const uniqueDevDeps = [...new Set(allDevDeps)].filter((d) => !uniqueDeps.includes(d));
|
|
1344
|
-
const totalDeps = uniqueDeps.length + uniqueDevDeps.length;
|
|
1345
1362
|
if (totalDeps > 0) {
|
|
1346
1363
|
const pm = await detectPackageManager(cwd);
|
|
1347
1364
|
if (pm) {
|
|
@@ -1405,6 +1422,145 @@ var init_add = __esm({
|
|
|
1405
1422
|
}
|
|
1406
1423
|
});
|
|
1407
1424
|
|
|
1425
|
+
// src/installers/rules-generator.ts
|
|
1426
|
+
import { writeFile as writeFile7, mkdir as mkdir4 } from "fs/promises";
|
|
1427
|
+
import { join as join8, dirname as dirname4 } from "path";
|
|
1428
|
+
function deriveRulesBaseUrl(registries) {
|
|
1429
|
+
const kitnEntry = registries["@kitn"];
|
|
1430
|
+
if (!kitnEntry) {
|
|
1431
|
+
throw new Error("No @kitn registry configured");
|
|
1432
|
+
}
|
|
1433
|
+
const url = getRegistryUrl(kitnEntry);
|
|
1434
|
+
return url.replace("{type}/{name}.json", "rules/");
|
|
1435
|
+
}
|
|
1436
|
+
async function fetchRulesConfig(registries) {
|
|
1437
|
+
try {
|
|
1438
|
+
const baseUrl = deriveRulesBaseUrl(registries);
|
|
1439
|
+
const res = await fetch(baseUrl + "config.json");
|
|
1440
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1441
|
+
return await res.json();
|
|
1442
|
+
} catch {
|
|
1443
|
+
return FALLBACK_CONFIG;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
async function fetchRulesTemplate(registries) {
|
|
1447
|
+
try {
|
|
1448
|
+
const baseUrl = deriveRulesBaseUrl(registries);
|
|
1449
|
+
const res = await fetch(baseUrl + "template.md");
|
|
1450
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1451
|
+
return await res.text();
|
|
1452
|
+
} catch {
|
|
1453
|
+
return FALLBACK_TEMPLATE;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
function renderTemplate(template, aliases) {
|
|
1457
|
+
const base = aliases.base ?? "src/ai";
|
|
1458
|
+
return template.replace(/\{base\}/g, base).replace(/\{agents\}/g, aliases.agents).replace(/\{tools\}/g, aliases.tools).replace(/\{skills\}/g, aliases.skills).replace(/\{storage\}/g, aliases.storage).replace(/\{crons\}/g, aliases.crons ?? `${base}/crons`);
|
|
1459
|
+
}
|
|
1460
|
+
function wrapContent(content, tool) {
|
|
1461
|
+
if (tool.format === "mdc" && tool.frontmatter) {
|
|
1462
|
+
const lines = Object.entries(tool.frontmatter).map(
|
|
1463
|
+
([key, value]) => `${key}: ${value}`
|
|
1464
|
+
);
|
|
1465
|
+
return `---
|
|
1466
|
+
${lines.join("\n")}
|
|
1467
|
+
---
|
|
1468
|
+
|
|
1469
|
+
${content}`;
|
|
1470
|
+
}
|
|
1471
|
+
return content;
|
|
1472
|
+
}
|
|
1473
|
+
async function generateRulesFiles(cwd, config, selectedToolIds) {
|
|
1474
|
+
const rulesConfig = await fetchRulesConfig(config.registries);
|
|
1475
|
+
const template = await fetchRulesTemplate(config.registries);
|
|
1476
|
+
const rendered = renderTemplate(template, config.aliases);
|
|
1477
|
+
const toolsToWrite = selectedToolIds ? rulesConfig.tools.filter((t) => selectedToolIds.includes(t.id)) : rulesConfig.tools;
|
|
1478
|
+
const written = [];
|
|
1479
|
+
for (const tool of toolsToWrite) {
|
|
1480
|
+
const content = wrapContent(rendered, tool);
|
|
1481
|
+
const filePath = join8(cwd, tool.filePath);
|
|
1482
|
+
await mkdir4(dirname4(filePath), { recursive: true });
|
|
1483
|
+
await writeFile7(filePath, content);
|
|
1484
|
+
written.push(tool.filePath);
|
|
1485
|
+
}
|
|
1486
|
+
return written;
|
|
1487
|
+
}
|
|
1488
|
+
var FALLBACK_CONFIG, FALLBACK_TEMPLATE;
|
|
1489
|
+
var init_rules_generator = __esm({
|
|
1490
|
+
"src/installers/rules-generator.ts"() {
|
|
1491
|
+
"use strict";
|
|
1492
|
+
init_config();
|
|
1493
|
+
FALLBACK_CONFIG = {
|
|
1494
|
+
version: "1.0.0",
|
|
1495
|
+
tools: [
|
|
1496
|
+
{
|
|
1497
|
+
id: "claude-code",
|
|
1498
|
+
name: "Claude Code",
|
|
1499
|
+
filePath: "AGENTS.md",
|
|
1500
|
+
format: "plain",
|
|
1501
|
+
description: "Also works with any tool that reads AGENTS.md"
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
id: "cursor",
|
|
1505
|
+
name: "Cursor",
|
|
1506
|
+
filePath: ".cursor/rules/kitn.mdc",
|
|
1507
|
+
format: "mdc",
|
|
1508
|
+
frontmatter: {
|
|
1509
|
+
description: "kitn AI agent framework conventions and patterns",
|
|
1510
|
+
globs: "src/ai/**/*.ts, kitn.json"
|
|
1511
|
+
}
|
|
1512
|
+
},
|
|
1513
|
+
{
|
|
1514
|
+
id: "github-copilot",
|
|
1515
|
+
name: "GitHub Copilot",
|
|
1516
|
+
filePath: ".github/copilot-instructions.md",
|
|
1517
|
+
format: "plain"
|
|
1518
|
+
},
|
|
1519
|
+
{
|
|
1520
|
+
id: "cline",
|
|
1521
|
+
name: "Cline",
|
|
1522
|
+
filePath: ".clinerules",
|
|
1523
|
+
format: "plain"
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
id: "windsurf",
|
|
1527
|
+
name: "Windsurf",
|
|
1528
|
+
filePath: ".windsurfrules",
|
|
1529
|
+
format: "plain"
|
|
1530
|
+
}
|
|
1531
|
+
]
|
|
1532
|
+
};
|
|
1533
|
+
FALLBACK_TEMPLATE = `# kitn AI Agent Framework
|
|
1534
|
+
|
|
1535
|
+
This project uses **kitn** to build multi-agent AI systems.
|
|
1536
|
+
|
|
1537
|
+
## Project Structure
|
|
1538
|
+
|
|
1539
|
+
AI components live under \`{base}\`:
|
|
1540
|
+
|
|
1541
|
+
- \`{agents}/\` \u2014 Agent definitions
|
|
1542
|
+
- \`{tools}/\` \u2014 Tool definitions
|
|
1543
|
+
- \`{skills}/\` \u2014 Skill files (markdown)
|
|
1544
|
+
- \`{storage}/\` \u2014 Storage providers
|
|
1545
|
+
- \`{crons}/\` \u2014 Cron job definitions
|
|
1546
|
+
|
|
1547
|
+
## Patterns
|
|
1548
|
+
|
|
1549
|
+
- Agents: \`registerAgent({ name, system, tools })\` from \`@kitn/core\`
|
|
1550
|
+
- Tools: \`tool()\` from \`ai\` + \`registerTool()\` from \`@kitn/core\`
|
|
1551
|
+
- Always use \`.js\` extension in relative imports
|
|
1552
|
+
- Use \`@kitn/core\` for core imports, \`ai\` for Vercel AI SDK
|
|
1553
|
+
|
|
1554
|
+
## CLI
|
|
1555
|
+
|
|
1556
|
+
- \`kitn add <name>\` \u2014 install from registry
|
|
1557
|
+
- \`kitn create <type> <name>\` \u2014 scaffold locally
|
|
1558
|
+
- \`kitn link tool <name> --to <agent>\` \u2014 wire a tool to an agent
|
|
1559
|
+
- \`kitn list\` \u2014 browse components
|
|
1560
|
+
`;
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1408
1564
|
// src/commands/init.ts
|
|
1409
1565
|
var init_exports = {};
|
|
1410
1566
|
__export(init_exports, {
|
|
@@ -1412,8 +1568,8 @@ __export(init_exports, {
|
|
|
1412
1568
|
});
|
|
1413
1569
|
import * as p3 from "@clack/prompts";
|
|
1414
1570
|
import pc4 from "picocolors";
|
|
1415
|
-
import { mkdir as
|
|
1416
|
-
import { join as
|
|
1571
|
+
import { mkdir as mkdir5, writeFile as writeFile8 } from "fs/promises";
|
|
1572
|
+
import { join as join9 } from "path";
|
|
1417
1573
|
function getPluginTemplate(framework) {
|
|
1418
1574
|
const adapterName = framework === "hono-openapi" ? "hono-openapi" : framework;
|
|
1419
1575
|
return `import { createAIPlugin } from "@kitn/adapters/${adapterName}";
|
|
@@ -1551,13 +1707,45 @@ async function initCommand(opts = {}) {
|
|
|
1551
1707
|
p3.log.info(`Patched tsconfig.json with path: ${pc4.bold("@kitn/*")}`);
|
|
1552
1708
|
p3.log.info("Installing core engine and adapter...");
|
|
1553
1709
|
await addCommand(["core", "routes"], { overwrite: true });
|
|
1554
|
-
const aiDir =
|
|
1555
|
-
await
|
|
1556
|
-
const barrelPath =
|
|
1557
|
-
await
|
|
1558
|
-
const pluginPath =
|
|
1559
|
-
await
|
|
1710
|
+
const aiDir = join9(cwd, baseDir);
|
|
1711
|
+
await mkdir5(aiDir, { recursive: true });
|
|
1712
|
+
const barrelPath = join9(aiDir, "index.ts");
|
|
1713
|
+
await writeFile8(barrelPath, createBarrelFile());
|
|
1714
|
+
const pluginPath = join9(aiDir, "plugin.ts");
|
|
1715
|
+
await writeFile8(pluginPath, getPluginTemplate(framework));
|
|
1560
1716
|
p3.log.success(`Created ${pc4.bold(baseDir + "/plugin.ts")} \u2014 configure your AI provider there`);
|
|
1717
|
+
try {
|
|
1718
|
+
const rulesConfig = await fetchRulesConfig(config.registries);
|
|
1719
|
+
let selectedToolIds;
|
|
1720
|
+
if (opts.yes) {
|
|
1721
|
+
selectedToolIds = rulesConfig.tools.map((t) => t.id);
|
|
1722
|
+
} else {
|
|
1723
|
+
const selected = await p3.multiselect({
|
|
1724
|
+
message: "Which AI coding tools do you use?",
|
|
1725
|
+
options: rulesConfig.tools.map((t) => ({
|
|
1726
|
+
value: t.id,
|
|
1727
|
+
label: t.name,
|
|
1728
|
+
hint: t.description
|
|
1729
|
+
})),
|
|
1730
|
+
required: false
|
|
1731
|
+
});
|
|
1732
|
+
if (p3.isCancel(selected)) {
|
|
1733
|
+
selectedToolIds = [];
|
|
1734
|
+
} else {
|
|
1735
|
+
selectedToolIds = selected;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
if (selectedToolIds.length > 0) {
|
|
1739
|
+
const updatedConfig = { ...config, aiTools: selectedToolIds };
|
|
1740
|
+
await writeConfig(cwd, updatedConfig);
|
|
1741
|
+
const written = await generateRulesFiles(cwd, updatedConfig, selectedToolIds);
|
|
1742
|
+
for (const filePath of written) {
|
|
1743
|
+
p3.log.success(`Created ${pc4.bold(filePath)}`);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
} catch {
|
|
1747
|
+
p3.log.warn("Could not generate AI coding tool rules (non-fatal).");
|
|
1748
|
+
}
|
|
1561
1749
|
const mountCode = framework === "elysia" ? `app.use(new Elysia({ prefix: "/api" }).use(ai.router));` : `app.route("/api", ai.router);`;
|
|
1562
1750
|
p3.note(
|
|
1563
1751
|
[
|
|
@@ -1585,6 +1773,7 @@ var init_init = __esm({
|
|
|
1585
1773
|
init_tsconfig_patcher();
|
|
1586
1774
|
init_barrel_manager();
|
|
1587
1775
|
init_add();
|
|
1776
|
+
init_rules_generator();
|
|
1588
1777
|
}
|
|
1589
1778
|
});
|
|
1590
1779
|
|
|
@@ -1720,7 +1909,7 @@ __export(diff_exports, {
|
|
|
1720
1909
|
diffCommand: () => diffCommand
|
|
1721
1910
|
});
|
|
1722
1911
|
import * as p5 from "@clack/prompts";
|
|
1723
|
-
import { join as
|
|
1912
|
+
import { join as join10 } from "path";
|
|
1724
1913
|
async function diffCommand(componentName) {
|
|
1725
1914
|
const cwd = process.cwd();
|
|
1726
1915
|
const config = await readConfig(cwd);
|
|
@@ -1751,8 +1940,8 @@ async function diffCommand(componentName) {
|
|
|
1751
1940
|
for (const file of registryItem.files) {
|
|
1752
1941
|
if (indexItem.type === "kitn:package") {
|
|
1753
1942
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1754
|
-
const localPath =
|
|
1755
|
-
const relativePath =
|
|
1943
|
+
const localPath = join10(cwd, baseDir, file.path);
|
|
1944
|
+
const relativePath = join10(baseDir, file.path);
|
|
1756
1945
|
const localContent = await readExistingFile(localPath);
|
|
1757
1946
|
if (localContent === null) {
|
|
1758
1947
|
p5.log.warn(`${relativePath}: file missing locally`);
|
|
@@ -1776,7 +1965,7 @@ async function diffCommand(componentName) {
|
|
|
1776
1965
|
return "storage";
|
|
1777
1966
|
}
|
|
1778
1967
|
})();
|
|
1779
|
-
const localPath =
|
|
1968
|
+
const localPath = join10(cwd, config.aliases[aliasKey], fileName);
|
|
1780
1969
|
const localContent = await readExistingFile(localPath);
|
|
1781
1970
|
if (localContent === null) {
|
|
1782
1971
|
p5.log.warn(`${fileName}: file missing locally`);
|
|
@@ -1810,8 +1999,8 @@ __export(remove_exports, {
|
|
|
1810
1999
|
});
|
|
1811
2000
|
import * as p6 from "@clack/prompts";
|
|
1812
2001
|
import pc6 from "picocolors";
|
|
1813
|
-
import { join as
|
|
1814
|
-
import { unlink as unlink3, readFile as readFile7, writeFile as
|
|
2002
|
+
import { join as join11, relative as relative3, dirname as dirname5 } from "path";
|
|
2003
|
+
import { unlink as unlink3, readFile as readFile7, writeFile as writeFile9 } from "fs/promises";
|
|
1815
2004
|
import { existsSync as existsSync2 } from "fs";
|
|
1816
2005
|
async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
1817
2006
|
const entry = lock[installedKey];
|
|
@@ -1819,15 +2008,15 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
1819
2008
|
const deleted = [];
|
|
1820
2009
|
for (const filePath of entry.files) {
|
|
1821
2010
|
try {
|
|
1822
|
-
await unlink3(
|
|
2011
|
+
await unlink3(join11(cwd, filePath));
|
|
1823
2012
|
deleted.push(filePath);
|
|
1824
2013
|
} catch {
|
|
1825
2014
|
p6.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
1826
2015
|
}
|
|
1827
2016
|
}
|
|
1828
2017
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1829
|
-
const barrelPath =
|
|
1830
|
-
const barrelDir =
|
|
2018
|
+
const barrelPath = join11(cwd, baseDir, "index.ts");
|
|
2019
|
+
const barrelDir = join11(cwd, baseDir);
|
|
1831
2020
|
const barrelEligibleDirs = /* @__PURE__ */ new Set([
|
|
1832
2021
|
config.aliases.agents,
|
|
1833
2022
|
config.aliases.tools,
|
|
@@ -1837,9 +2026,9 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
1837
2026
|
let barrelContent = await readFile7(barrelPath, "utf-8");
|
|
1838
2027
|
let barrelChanged = false;
|
|
1839
2028
|
for (const filePath of deleted) {
|
|
1840
|
-
const fileDir =
|
|
2029
|
+
const fileDir = dirname5(filePath);
|
|
1841
2030
|
if (!barrelEligibleDirs.has(fileDir)) continue;
|
|
1842
|
-
const importPath = "./" + relative3(barrelDir,
|
|
2031
|
+
const importPath = "./" + relative3(barrelDir, join11(cwd, filePath)).replace(/\\/g, "/");
|
|
1843
2032
|
const updated = removeImportFromBarrel(barrelContent, importPath);
|
|
1844
2033
|
if (updated !== barrelContent) {
|
|
1845
2034
|
barrelContent = updated;
|
|
@@ -1847,8 +2036,8 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
1847
2036
|
}
|
|
1848
2037
|
}
|
|
1849
2038
|
if (barrelChanged) {
|
|
1850
|
-
await
|
|
1851
|
-
p6.log.info(`Updated barrel file: ${
|
|
2039
|
+
await writeFile9(barrelPath, barrelContent);
|
|
2040
|
+
p6.log.info(`Updated barrel file: ${join11(baseDir, "index.ts")}`);
|
|
1852
2041
|
}
|
|
1853
2042
|
}
|
|
1854
2043
|
delete lock[installedKey];
|
|
@@ -1996,6 +2185,19 @@ var init_update = __esm({
|
|
|
1996
2185
|
}
|
|
1997
2186
|
});
|
|
1998
2187
|
|
|
2188
|
+
// src/utils/naming.ts
|
|
2189
|
+
function toCamelCase(str) {
|
|
2190
|
+
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
2191
|
+
}
|
|
2192
|
+
function toTitleCase(str) {
|
|
2193
|
+
return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
2194
|
+
}
|
|
2195
|
+
var init_naming = __esm({
|
|
2196
|
+
"src/utils/naming.ts"() {
|
|
2197
|
+
"use strict";
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
2200
|
+
|
|
1999
2201
|
// src/commands/create.ts
|
|
2000
2202
|
var create_exports = {};
|
|
2001
2203
|
__export(create_exports, {
|
|
@@ -2004,15 +2206,9 @@ __export(create_exports, {
|
|
|
2004
2206
|
});
|
|
2005
2207
|
import * as p8 from "@clack/prompts";
|
|
2006
2208
|
import pc7 from "picocolors";
|
|
2007
|
-
import { join as
|
|
2209
|
+
import { join as join12, relative as relative4 } from "path";
|
|
2008
2210
|
import { existsSync as existsSync3 } from "fs";
|
|
2009
|
-
import { readFile as readFile8, writeFile as
|
|
2010
|
-
function toCamelCase(str) {
|
|
2011
|
-
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
2012
|
-
}
|
|
2013
|
-
function toTitleCase(str) {
|
|
2014
|
-
return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
2015
|
-
}
|
|
2211
|
+
import { readFile as readFile8, writeFile as writeFile10, mkdir as mkdir6 } from "fs/promises";
|
|
2016
2212
|
function generateAgentSource(name) {
|
|
2017
2213
|
const camel = toCamelCase(name);
|
|
2018
2214
|
return `import { registerAgent } from "@kitn/core";
|
|
@@ -2119,7 +2315,7 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2119
2315
|
const validType = type;
|
|
2120
2316
|
const kitnType = typeToKitnType[validType];
|
|
2121
2317
|
const fileName = validType === "skill" ? `${name}.md` : `${name}.ts`;
|
|
2122
|
-
const filePath =
|
|
2318
|
+
const filePath = join12(cwd, getInstallPath(config, kitnType, fileName));
|
|
2123
2319
|
const dummyContent = "";
|
|
2124
2320
|
const status = await checkFileStatus(filePath, dummyContent);
|
|
2125
2321
|
if (status !== "new" /* New */) {
|
|
@@ -2147,18 +2343,18 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2147
2343
|
let barrelUpdated = false;
|
|
2148
2344
|
if (BARREL_TYPES.includes(validType)) {
|
|
2149
2345
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
2150
|
-
const barrelPath =
|
|
2346
|
+
const barrelPath = join12(cwd, baseDir, "index.ts");
|
|
2151
2347
|
let barrelContent;
|
|
2152
2348
|
if (existsSync3(barrelPath)) {
|
|
2153
2349
|
barrelContent = await readFile8(barrelPath, "utf-8");
|
|
2154
2350
|
} else {
|
|
2155
2351
|
barrelContent = createBarrelFile();
|
|
2156
|
-
await
|
|
2352
|
+
await mkdir6(join12(cwd, baseDir), { recursive: true });
|
|
2157
2353
|
}
|
|
2158
|
-
const importPath = "./" + relative4(
|
|
2354
|
+
const importPath = "./" + relative4(join12(cwd, baseDir), filePath).replace(/\.ts$/, ".js");
|
|
2159
2355
|
const updatedBarrel = addImportToBarrel(barrelContent, importPath);
|
|
2160
2356
|
if (updatedBarrel !== barrelContent) {
|
|
2161
|
-
await
|
|
2357
|
+
await writeFile10(barrelPath, updatedBarrel);
|
|
2162
2358
|
barrelUpdated = true;
|
|
2163
2359
|
}
|
|
2164
2360
|
}
|
|
@@ -2186,6 +2382,7 @@ var init_create = __esm({
|
|
|
2186
2382
|
"src/commands/create.ts"() {
|
|
2187
2383
|
"use strict";
|
|
2188
2384
|
init_config();
|
|
2385
|
+
init_naming();
|
|
2189
2386
|
init_file_writer();
|
|
2190
2387
|
init_barrel_manager();
|
|
2191
2388
|
VALID_TYPES = ["agent", "tool", "skill", "storage", "cron"];
|
|
@@ -2200,36 +2397,661 @@ var init_create = __esm({
|
|
|
2200
2397
|
}
|
|
2201
2398
|
});
|
|
2202
2399
|
|
|
2400
|
+
// src/utils/component-resolver.ts
|
|
2401
|
+
import { readdir, readFile as readFile9 } from "fs/promises";
|
|
2402
|
+
import { join as join13, relative as relative5 } from "path";
|
|
2403
|
+
function stripSuffix(name, suffix) {
|
|
2404
|
+
if (name.endsWith(`-${suffix}`)) {
|
|
2405
|
+
return name.slice(0, -(suffix.length + 1));
|
|
2406
|
+
}
|
|
2407
|
+
return name;
|
|
2408
|
+
}
|
|
2409
|
+
function toolsDir(config, cwd) {
|
|
2410
|
+
const baseAlias = config.aliases.base ?? "src/ai";
|
|
2411
|
+
const tools = config.aliases.tools ?? join13(baseAlias, "tools");
|
|
2412
|
+
return join13(cwd, tools);
|
|
2413
|
+
}
|
|
2414
|
+
function agentsDir(config, cwd) {
|
|
2415
|
+
const baseAlias = config.aliases.base ?? "src/ai";
|
|
2416
|
+
const agents = config.aliases.agents ?? join13(baseAlias, "agents");
|
|
2417
|
+
return join13(cwd, agents);
|
|
2418
|
+
}
|
|
2419
|
+
async function findFile(dir, candidates) {
|
|
2420
|
+
for (const name of candidates) {
|
|
2421
|
+
const filePath = join13(dir, `${name}.ts`);
|
|
2422
|
+
try {
|
|
2423
|
+
await readFile9(filePath);
|
|
2424
|
+
return filePath;
|
|
2425
|
+
} catch {
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
return null;
|
|
2429
|
+
}
|
|
2430
|
+
function parseExportName(source) {
|
|
2431
|
+
const match = source.match(/export\s+const\s+(\w+)/);
|
|
2432
|
+
return match ? match[1] : null;
|
|
2433
|
+
}
|
|
2434
|
+
function parseAgentName(source) {
|
|
2435
|
+
const match = source.match(/registerAgent\s*\(\s*\{[^}]*name:\s*"([^"]+)"/s);
|
|
2436
|
+
return match ? match[1] : null;
|
|
2437
|
+
}
|
|
2438
|
+
function computeImportPath(fromDir, toFile) {
|
|
2439
|
+
let rel = relative5(fromDir, toFile);
|
|
2440
|
+
if (!rel.startsWith(".")) {
|
|
2441
|
+
rel = `./${rel}`;
|
|
2442
|
+
}
|
|
2443
|
+
return rel.replace(/\.ts$/, ".js");
|
|
2444
|
+
}
|
|
2445
|
+
async function resolveToolByName(name, config, cwd) {
|
|
2446
|
+
const tDir = toolsDir(config, cwd);
|
|
2447
|
+
const aDir = agentsDir(config, cwd);
|
|
2448
|
+
const lock = await readLock(cwd);
|
|
2449
|
+
for (const [componentName, entry] of Object.entries(lock)) {
|
|
2450
|
+
if (componentName === name || componentName === `${name}-tool`) {
|
|
2451
|
+
const toolFile = entry.files.find((f) => {
|
|
2452
|
+
const toolsAlias = config.aliases.tools ?? join13(config.aliases.base ?? "src/ai", "tools");
|
|
2453
|
+
return f.startsWith(toolsAlias);
|
|
2454
|
+
});
|
|
2455
|
+
if (toolFile) {
|
|
2456
|
+
const filePath2 = join13(cwd, toolFile);
|
|
2457
|
+
try {
|
|
2458
|
+
const source2 = await readFile9(filePath2, "utf-8");
|
|
2459
|
+
const exportName2 = parseExportName(source2);
|
|
2460
|
+
if (exportName2) {
|
|
2461
|
+
return {
|
|
2462
|
+
filePath: filePath2,
|
|
2463
|
+
exportName: exportName2,
|
|
2464
|
+
importPath: computeImportPath(aDir, filePath2)
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
} catch {
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
const candidates = [name, stripSuffix(name, "tool")];
|
|
2473
|
+
const uniqueCandidates = [...new Set(candidates)];
|
|
2474
|
+
const filePath = await findFile(tDir, uniqueCandidates);
|
|
2475
|
+
if (!filePath) return null;
|
|
2476
|
+
const source = await readFile9(filePath, "utf-8");
|
|
2477
|
+
const exportName = parseExportName(source);
|
|
2478
|
+
if (!exportName) return null;
|
|
2479
|
+
return {
|
|
2480
|
+
filePath,
|
|
2481
|
+
exportName,
|
|
2482
|
+
importPath: computeImportPath(aDir, filePath)
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
async function resolveAgentByName(name, config, cwd) {
|
|
2486
|
+
const aDir = agentsDir(config, cwd);
|
|
2487
|
+
const lock = await readLock(cwd);
|
|
2488
|
+
for (const [componentName, entry] of Object.entries(lock)) {
|
|
2489
|
+
if (componentName === name || componentName === `${name}-agent`) {
|
|
2490
|
+
const agentFile = entry.files.find((f) => {
|
|
2491
|
+
const agentsAlias = config.aliases.agents ?? join13(config.aliases.base ?? "src/ai", "agents");
|
|
2492
|
+
return f.startsWith(agentsAlias);
|
|
2493
|
+
});
|
|
2494
|
+
if (agentFile) {
|
|
2495
|
+
const filePath2 = join13(cwd, agentFile);
|
|
2496
|
+
try {
|
|
2497
|
+
const source2 = await readFile9(filePath2, "utf-8");
|
|
2498
|
+
const agentName2 = parseAgentName(source2);
|
|
2499
|
+
return {
|
|
2500
|
+
filePath: filePath2,
|
|
2501
|
+
name: agentName2 ?? componentName
|
|
2502
|
+
};
|
|
2503
|
+
} catch {
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
const candidates = [name, stripSuffix(name, "agent")];
|
|
2509
|
+
const uniqueCandidates = [...new Set(candidates)];
|
|
2510
|
+
const filePath = await findFile(aDir, uniqueCandidates);
|
|
2511
|
+
if (!filePath) return null;
|
|
2512
|
+
const source = await readFile9(filePath, "utf-8");
|
|
2513
|
+
const agentName = parseAgentName(source);
|
|
2514
|
+
const fallbackName = filePath.split("/").pop().replace(/\.ts$/, "");
|
|
2515
|
+
return {
|
|
2516
|
+
filePath,
|
|
2517
|
+
name: agentName ?? fallbackName
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
async function listTools(config, cwd) {
|
|
2521
|
+
const dir = toolsDir(config, cwd);
|
|
2522
|
+
return listComponentsInDir(dir);
|
|
2523
|
+
}
|
|
2524
|
+
async function listAgents(config, cwd) {
|
|
2525
|
+
const dir = agentsDir(config, cwd);
|
|
2526
|
+
return listComponentsInDir(dir);
|
|
2527
|
+
}
|
|
2528
|
+
async function listComponentsInDir(dir) {
|
|
2529
|
+
try {
|
|
2530
|
+
const entries = await readdir(dir);
|
|
2531
|
+
return entries.filter((f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && !f.endsWith(".d.ts")).sort().map((f) => ({
|
|
2532
|
+
name: f.replace(/\.ts$/, ""),
|
|
2533
|
+
filePath: join13(dir, f)
|
|
2534
|
+
}));
|
|
2535
|
+
} catch {
|
|
2536
|
+
return [];
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
var init_component_resolver = __esm({
|
|
2540
|
+
"src/utils/component-resolver.ts"() {
|
|
2541
|
+
"use strict";
|
|
2542
|
+
init_config();
|
|
2543
|
+
}
|
|
2544
|
+
});
|
|
2545
|
+
|
|
2546
|
+
// src/installers/agent-linker.ts
|
|
2547
|
+
function linkToolToAgent(content, tool, toolKey) {
|
|
2548
|
+
const key = toolKey ?? tool.exportName;
|
|
2549
|
+
const { exportName, importPath } = tool;
|
|
2550
|
+
const toolEntry = key === exportName ? exportName : `${key}: ${exportName}`;
|
|
2551
|
+
if (hasToolEntry(content, key)) {
|
|
2552
|
+
return { content, changed: false };
|
|
2553
|
+
}
|
|
2554
|
+
const importLine = `import { ${exportName} } from "${importPath}";`;
|
|
2555
|
+
let result = content;
|
|
2556
|
+
if (!result.includes(importLine)) {
|
|
2557
|
+
const lines = result.split("\n");
|
|
2558
|
+
let lastImportIndex = -1;
|
|
2559
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2560
|
+
if (/^import\s+/.test(lines[i])) lastImportIndex = i;
|
|
2561
|
+
}
|
|
2562
|
+
if (lastImportIndex === -1) {
|
|
2563
|
+
result = `${importLine}
|
|
2564
|
+
${result}`;
|
|
2565
|
+
} else {
|
|
2566
|
+
lines.splice(lastImportIndex + 1, 0, importLine);
|
|
2567
|
+
result = lines.join("\n");
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
const insertResult = insertToolEntry(result, key, exportName);
|
|
2571
|
+
if (insertResult.error) {
|
|
2572
|
+
return {
|
|
2573
|
+
content,
|
|
2574
|
+
changed: false,
|
|
2575
|
+
error: `Could not auto-modify the agent file. Add manually:
|
|
2576
|
+
1. Import: ${importLine}
|
|
2577
|
+
2. Add to tools: { ${toolEntry} }`
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
return { content: insertResult.content, changed: true };
|
|
2581
|
+
}
|
|
2582
|
+
function unlinkToolFromAgent(content, tool, toolKey) {
|
|
2583
|
+
const key = toolKey ?? tool.exportName;
|
|
2584
|
+
const { exportName, importPath } = tool;
|
|
2585
|
+
if (!hasToolEntry(content, key)) {
|
|
2586
|
+
return { content, changed: false };
|
|
2587
|
+
}
|
|
2588
|
+
const removeResult = removeToolEntry(content, key, exportName);
|
|
2589
|
+
if (removeResult.error) {
|
|
2590
|
+
return {
|
|
2591
|
+
content,
|
|
2592
|
+
changed: false,
|
|
2593
|
+
error: `Could not auto-modify the agent file. Remove manually:
|
|
2594
|
+
1. Remove from tools: ${key === exportName ? key : `${key}: ${exportName}`}
|
|
2595
|
+
2. Remove import if unused: import { ${exportName} } from "${importPath}";`
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
let result = removeResult.content;
|
|
2599
|
+
if (!isExportNameReferenced(result, exportName)) {
|
|
2600
|
+
result = removeImportLine(result, exportName, importPath);
|
|
2601
|
+
}
|
|
2602
|
+
return { content: result, changed: true };
|
|
2603
|
+
}
|
|
2604
|
+
function hasToolEntry(content, key) {
|
|
2605
|
+
const toolsMatch = extractToolsBlock(content);
|
|
2606
|
+
if (!toolsMatch) return false;
|
|
2607
|
+
const toolsContent = toolsMatch.inner;
|
|
2608
|
+
const keyPattern = new RegExp(
|
|
2609
|
+
`(?:^|[,{\\s])${escapeRegex(key)}(?:\\s*[:,}\\s]|$)`
|
|
2610
|
+
);
|
|
2611
|
+
return keyPattern.test(toolsContent);
|
|
2612
|
+
}
|
|
2613
|
+
function extractToolsBlock(content) {
|
|
2614
|
+
const singleLine = /^([ \t]*)tools\s*:\s*\{([^}]*)\}/m;
|
|
2615
|
+
const singleMatch = singleLine.exec(content);
|
|
2616
|
+
if (singleMatch) {
|
|
2617
|
+
return {
|
|
2618
|
+
full: singleMatch[0],
|
|
2619
|
+
inner: singleMatch[2],
|
|
2620
|
+
startIndex: singleMatch.index,
|
|
2621
|
+
indent: singleMatch[1]
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
const multiStart = /^([ \t]*)tools\s*:\s*\{/m;
|
|
2625
|
+
const multiMatch = multiStart.exec(content);
|
|
2626
|
+
if (!multiMatch) return null;
|
|
2627
|
+
const braceStart = multiMatch.index + multiMatch[0].length;
|
|
2628
|
+
let depth = 1;
|
|
2629
|
+
let i = braceStart;
|
|
2630
|
+
while (i < content.length && depth > 0) {
|
|
2631
|
+
if (content[i] === "{") depth++;
|
|
2632
|
+
else if (content[i] === "}") depth--;
|
|
2633
|
+
i++;
|
|
2634
|
+
}
|
|
2635
|
+
if (depth !== 0) return null;
|
|
2636
|
+
const full = content.slice(multiMatch.index, i);
|
|
2637
|
+
const inner = content.slice(braceStart, i - 1);
|
|
2638
|
+
return {
|
|
2639
|
+
full,
|
|
2640
|
+
inner,
|
|
2641
|
+
startIndex: multiMatch.index,
|
|
2642
|
+
indent: multiMatch[1]
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2645
|
+
function insertToolEntry(content, key, exportName) {
|
|
2646
|
+
const block = extractToolsBlock(content);
|
|
2647
|
+
if (!block) return { content, error: "no tools block found" };
|
|
2648
|
+
const entry = key === exportName ? key : `${key}: ${exportName}`;
|
|
2649
|
+
const trimmedInner = block.inner.trim();
|
|
2650
|
+
let newToolsContent;
|
|
2651
|
+
if (trimmedInner === "") {
|
|
2652
|
+
newToolsContent = `tools: { ${entry} }`;
|
|
2653
|
+
} else if (!block.inner.includes("\n")) {
|
|
2654
|
+
const cleaned = trimmedInner.replace(/,?\s*$/, "");
|
|
2655
|
+
newToolsContent = `tools: { ${cleaned}, ${entry} }`;
|
|
2656
|
+
} else {
|
|
2657
|
+
const entryIndent = block.indent + " ";
|
|
2658
|
+
const existingTrimmed = block.inner.trimEnd();
|
|
2659
|
+
const withComma = existingTrimmed.endsWith(",") ? existingTrimmed : existingTrimmed + ",";
|
|
2660
|
+
newToolsContent = `tools: {
|
|
2661
|
+
${withComma}
|
|
2662
|
+
${entryIndent}${entry},
|
|
2663
|
+
${block.indent}}`;
|
|
2664
|
+
}
|
|
2665
|
+
const newContent = content.replace(block.full, newToolsContent);
|
|
2666
|
+
return { content: newContent };
|
|
2667
|
+
}
|
|
2668
|
+
function removeToolEntry(content, key, exportName) {
|
|
2669
|
+
const block = extractToolsBlock(content);
|
|
2670
|
+
if (!block) return { content, error: "no tools block found" };
|
|
2671
|
+
const trimmedInner = block.inner.trim();
|
|
2672
|
+
if (!block.inner.includes("\n")) {
|
|
2673
|
+
const entries = trimmedInner.split(",").map((e) => e.trim()).filter((e) => e !== "");
|
|
2674
|
+
const filtered = entries.filter((e) => {
|
|
2675
|
+
const colonIdx = e.indexOf(":");
|
|
2676
|
+
const entryKey = colonIdx >= 0 ? e.slice(0, colonIdx).trim() : e.trim();
|
|
2677
|
+
return entryKey !== key;
|
|
2678
|
+
});
|
|
2679
|
+
const newInner = filtered.length === 0 ? "" : ` ${filtered.join(", ")} `;
|
|
2680
|
+
const newBlock = `tools: {${newInner}}`;
|
|
2681
|
+
return { content: content.replace(block.full, newBlock) };
|
|
2682
|
+
} else {
|
|
2683
|
+
const lines = block.inner.split("\n");
|
|
2684
|
+
const keyPattern = new RegExp(
|
|
2685
|
+
`^\\s*${escapeRegex(key)}\\s*(?::|,|$)`
|
|
2686
|
+
);
|
|
2687
|
+
const filtered = lines.filter((line) => !keyPattern.test(line));
|
|
2688
|
+
const hasEntries = filtered.some((l) => l.trim() !== "");
|
|
2689
|
+
if (!hasEntries) {
|
|
2690
|
+
const newBlock2 = `tools: {}`;
|
|
2691
|
+
return { content: content.replace(block.full, newBlock2) };
|
|
2692
|
+
}
|
|
2693
|
+
const cleanedLines = filtered.slice();
|
|
2694
|
+
for (let i = cleanedLines.length - 1; i >= 0; i--) {
|
|
2695
|
+
if (cleanedLines[i].trim() !== "") {
|
|
2696
|
+
if (!cleanedLines[i].trimEnd().endsWith(",")) {
|
|
2697
|
+
cleanedLines[i] = cleanedLines[i].trimEnd() + ",";
|
|
2698
|
+
}
|
|
2699
|
+
break;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
const newBlock = `tools: {
|
|
2703
|
+
${cleanedLines.join("\n")}
|
|
2704
|
+
${block.indent}}`;
|
|
2705
|
+
return { content: content.replace(block.full, newBlock) };
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
function isExportNameReferenced(content, exportName) {
|
|
2709
|
+
const lines = content.split("\n");
|
|
2710
|
+
for (const line of lines) {
|
|
2711
|
+
if (/^import\s+/.test(line)) continue;
|
|
2712
|
+
const wordPattern = new RegExp(`\\b${escapeRegex(exportName)}\\b`);
|
|
2713
|
+
if (wordPattern.test(line)) return true;
|
|
2714
|
+
}
|
|
2715
|
+
return false;
|
|
2716
|
+
}
|
|
2717
|
+
function removeImportLine(content, exportName, importPath) {
|
|
2718
|
+
const lines = content.split("\n");
|
|
2719
|
+
const result = [];
|
|
2720
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2721
|
+
const line = lines[i];
|
|
2722
|
+
const singleImportMatch = line.match(
|
|
2723
|
+
/^import\s*\{([^}]+)\}\s*from\s*["'](.+?)["']\s*;?\s*$/
|
|
2724
|
+
);
|
|
2725
|
+
if (singleImportMatch && singleImportMatch[2] === importPath) {
|
|
2726
|
+
const imports = singleImportMatch[1].split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
2727
|
+
if (imports.length === 1 && imports[0] === exportName) {
|
|
2728
|
+
if (i + 1 < lines.length && lines[i + 1].trim() === "") {
|
|
2729
|
+
i++;
|
|
2730
|
+
}
|
|
2731
|
+
continue;
|
|
2732
|
+
}
|
|
2733
|
+
const remaining = imports.filter((s) => s !== exportName);
|
|
2734
|
+
result.push(
|
|
2735
|
+
`import { ${remaining.join(", ")} } from "${importPath}";`
|
|
2736
|
+
);
|
|
2737
|
+
continue;
|
|
2738
|
+
}
|
|
2739
|
+
if (/^import\s*\{/.test(line) && !line.includes("}") && content.includes(importPath)) {
|
|
2740
|
+
const importLines = [line];
|
|
2741
|
+
let j = i + 1;
|
|
2742
|
+
while (j < lines.length && !lines[j].includes("}")) {
|
|
2743
|
+
importLines.push(lines[j]);
|
|
2744
|
+
j++;
|
|
2745
|
+
}
|
|
2746
|
+
if (j < lines.length) {
|
|
2747
|
+
importLines.push(lines[j]);
|
|
2748
|
+
}
|
|
2749
|
+
const fullImport = importLines.join("\n");
|
|
2750
|
+
if (fullImport.includes(importPath)) {
|
|
2751
|
+
const namesMatch = fullImport.match(/\{([^}]+)\}/);
|
|
2752
|
+
if (namesMatch) {
|
|
2753
|
+
const imports = namesMatch[1].split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
2754
|
+
if (imports.length === 1 && imports[0] === exportName) {
|
|
2755
|
+
i = j;
|
|
2756
|
+
if (i + 1 < lines.length && lines[i + 1].trim() === "") {
|
|
2757
|
+
i++;
|
|
2758
|
+
}
|
|
2759
|
+
continue;
|
|
2760
|
+
}
|
|
2761
|
+
const remaining = imports.filter((s) => s !== exportName);
|
|
2762
|
+
if (remaining.length <= 2) {
|
|
2763
|
+
result.push(
|
|
2764
|
+
`import { ${remaining.join(", ")} } from "${importPath}";`
|
|
2765
|
+
);
|
|
2766
|
+
} else {
|
|
2767
|
+
result.push(`import {`);
|
|
2768
|
+
remaining.forEach((name, idx) => {
|
|
2769
|
+
result.push(
|
|
2770
|
+
` ${name}${idx < remaining.length - 1 ? "," : ""}`
|
|
2771
|
+
);
|
|
2772
|
+
});
|
|
2773
|
+
result.push(`} from "${importPath}";`);
|
|
2774
|
+
}
|
|
2775
|
+
i = j;
|
|
2776
|
+
continue;
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
result.push(line);
|
|
2781
|
+
}
|
|
2782
|
+
return result.join("\n");
|
|
2783
|
+
}
|
|
2784
|
+
function escapeRegex(s) {
|
|
2785
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2786
|
+
}
|
|
2787
|
+
var init_agent_linker = __esm({
|
|
2788
|
+
"src/installers/agent-linker.ts"() {
|
|
2789
|
+
"use strict";
|
|
2790
|
+
}
|
|
2791
|
+
});
|
|
2792
|
+
|
|
2793
|
+
// src/commands/link.ts
|
|
2794
|
+
var link_exports = {};
|
|
2795
|
+
__export(link_exports, {
|
|
2796
|
+
linkCommand: () => linkCommand
|
|
2797
|
+
});
|
|
2798
|
+
import * as p9 from "@clack/prompts";
|
|
2799
|
+
import pc8 from "picocolors";
|
|
2800
|
+
import { readFile as readFile10, writeFile as writeFile11 } from "fs/promises";
|
|
2801
|
+
import { basename } from "path";
|
|
2802
|
+
async function linkCommand(type, name, opts) {
|
|
2803
|
+
p9.intro(pc8.bgCyan(pc8.black(" kitn link ")));
|
|
2804
|
+
const cwd = process.cwd();
|
|
2805
|
+
const config = await readConfig(cwd);
|
|
2806
|
+
if (!config) {
|
|
2807
|
+
p9.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2808
|
+
process.exit(1);
|
|
2809
|
+
}
|
|
2810
|
+
if (type && type !== "tool") {
|
|
2811
|
+
p9.log.error(
|
|
2812
|
+
`Unsupported type "${type}". Only ${pc8.bold("tool")} is supported.`
|
|
2813
|
+
);
|
|
2814
|
+
process.exit(1);
|
|
2815
|
+
}
|
|
2816
|
+
let toolName = name;
|
|
2817
|
+
if (!toolName) {
|
|
2818
|
+
const tools = await listTools(config, cwd);
|
|
2819
|
+
if (tools.length === 0) {
|
|
2820
|
+
p9.log.error("No tools found in your project.");
|
|
2821
|
+
process.exit(1);
|
|
2822
|
+
}
|
|
2823
|
+
const selected = await p9.select({
|
|
2824
|
+
message: "Select a tool to link:",
|
|
2825
|
+
options: tools.map((t) => ({
|
|
2826
|
+
value: t.name,
|
|
2827
|
+
label: t.name
|
|
2828
|
+
}))
|
|
2829
|
+
});
|
|
2830
|
+
if (p9.isCancel(selected)) {
|
|
2831
|
+
p9.cancel("Cancelled.");
|
|
2832
|
+
process.exit(0);
|
|
2833
|
+
}
|
|
2834
|
+
toolName = selected;
|
|
2835
|
+
}
|
|
2836
|
+
const tool = await resolveToolByName(toolName, config, cwd);
|
|
2837
|
+
if (!tool) {
|
|
2838
|
+
p9.log.error(
|
|
2839
|
+
`Tool "${toolName}" not found. Check that the file exists in your tools directory.`
|
|
2840
|
+
);
|
|
2841
|
+
process.exit(1);
|
|
2842
|
+
}
|
|
2843
|
+
let agentName = opts?.to;
|
|
2844
|
+
if (!agentName) {
|
|
2845
|
+
const agents = await listAgents(config, cwd);
|
|
2846
|
+
if (agents.length === 0) {
|
|
2847
|
+
p9.log.error("No agents found in your project.");
|
|
2848
|
+
process.exit(1);
|
|
2849
|
+
}
|
|
2850
|
+
const selected = await p9.select({
|
|
2851
|
+
message: "Select an agent to link the tool to:",
|
|
2852
|
+
options: agents.map((a) => ({
|
|
2853
|
+
value: a.name,
|
|
2854
|
+
label: a.name
|
|
2855
|
+
}))
|
|
2856
|
+
});
|
|
2857
|
+
if (p9.isCancel(selected)) {
|
|
2858
|
+
p9.cancel("Cancelled.");
|
|
2859
|
+
process.exit(0);
|
|
2860
|
+
}
|
|
2861
|
+
agentName = selected;
|
|
2862
|
+
}
|
|
2863
|
+
const agent = await resolveAgentByName(agentName, config, cwd);
|
|
2864
|
+
if (!agent) {
|
|
2865
|
+
p9.log.error(
|
|
2866
|
+
`Agent "${agentName}" not found. Check that the file exists in your agents directory.`
|
|
2867
|
+
);
|
|
2868
|
+
process.exit(1);
|
|
2869
|
+
}
|
|
2870
|
+
const agentContent = await readFile10(agent.filePath, "utf-8");
|
|
2871
|
+
const toolRef = {
|
|
2872
|
+
exportName: tool.exportName,
|
|
2873
|
+
importPath: tool.importPath
|
|
2874
|
+
};
|
|
2875
|
+
const result = linkToolToAgent(agentContent, toolRef, opts?.as);
|
|
2876
|
+
if (result.error) {
|
|
2877
|
+
p9.log.warn(result.error);
|
|
2878
|
+
p9.outro("Could not auto-link. Follow the manual instructions above.");
|
|
2879
|
+
process.exit(1);
|
|
2880
|
+
}
|
|
2881
|
+
if (!result.changed) {
|
|
2882
|
+
p9.log.info(
|
|
2883
|
+
`${pc8.cyan(tool.exportName)} is already linked to ${pc8.cyan(basename(agent.filePath))}.`
|
|
2884
|
+
);
|
|
2885
|
+
p9.outro("Nothing to do.");
|
|
2886
|
+
return;
|
|
2887
|
+
}
|
|
2888
|
+
await writeFile11(agent.filePath, result.content);
|
|
2889
|
+
p9.log.success(
|
|
2890
|
+
`Linked ${pc8.cyan(tool.exportName)} to ${pc8.cyan(basename(agent.filePath))}`
|
|
2891
|
+
);
|
|
2892
|
+
p9.log.message(
|
|
2893
|
+
` ${pc8.green("+")} import { ${tool.exportName} } from "${tool.importPath}"`
|
|
2894
|
+
);
|
|
2895
|
+
p9.log.message(
|
|
2896
|
+
` ${pc8.green("+")} tools: { ${opts?.as ? `${opts.as}: ${tool.exportName}` : tool.exportName} }`
|
|
2897
|
+
);
|
|
2898
|
+
p9.outro("Done!");
|
|
2899
|
+
}
|
|
2900
|
+
var init_link = __esm({
|
|
2901
|
+
"src/commands/link.ts"() {
|
|
2902
|
+
"use strict";
|
|
2903
|
+
init_config();
|
|
2904
|
+
init_component_resolver();
|
|
2905
|
+
init_agent_linker();
|
|
2906
|
+
}
|
|
2907
|
+
});
|
|
2908
|
+
|
|
2909
|
+
// src/commands/unlink.ts
|
|
2910
|
+
var unlink_exports = {};
|
|
2911
|
+
__export(unlink_exports, {
|
|
2912
|
+
unlinkCommand: () => unlinkCommand
|
|
2913
|
+
});
|
|
2914
|
+
import * as p10 from "@clack/prompts";
|
|
2915
|
+
import pc9 from "picocolors";
|
|
2916
|
+
import { readFile as readFile11, writeFile as writeFile12 } from "fs/promises";
|
|
2917
|
+
import { basename as basename2 } from "path";
|
|
2918
|
+
async function unlinkCommand(type, name, opts) {
|
|
2919
|
+
p10.intro(pc9.bgCyan(pc9.black(" kitn unlink ")));
|
|
2920
|
+
const cwd = process.cwd();
|
|
2921
|
+
const config = await readConfig(cwd);
|
|
2922
|
+
if (!config) {
|
|
2923
|
+
p10.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2924
|
+
process.exit(1);
|
|
2925
|
+
}
|
|
2926
|
+
if (type && type !== "tool") {
|
|
2927
|
+
p10.log.error(
|
|
2928
|
+
`Unsupported type "${type}". Only ${pc9.bold("tool")} is supported.`
|
|
2929
|
+
);
|
|
2930
|
+
process.exit(1);
|
|
2931
|
+
}
|
|
2932
|
+
let toolName = name;
|
|
2933
|
+
if (!toolName) {
|
|
2934
|
+
const tools = await listTools(config, cwd);
|
|
2935
|
+
if (tools.length === 0) {
|
|
2936
|
+
p10.log.error("No tools found in your project.");
|
|
2937
|
+
process.exit(1);
|
|
2938
|
+
}
|
|
2939
|
+
const selected = await p10.select({
|
|
2940
|
+
message: "Select a tool to unlink:",
|
|
2941
|
+
options: tools.map((t) => ({
|
|
2942
|
+
value: t.name,
|
|
2943
|
+
label: t.name
|
|
2944
|
+
}))
|
|
2945
|
+
});
|
|
2946
|
+
if (p10.isCancel(selected)) {
|
|
2947
|
+
p10.cancel("Cancelled.");
|
|
2948
|
+
process.exit(0);
|
|
2949
|
+
}
|
|
2950
|
+
toolName = selected;
|
|
2951
|
+
}
|
|
2952
|
+
const tool = await resolveToolByName(toolName, config, cwd);
|
|
2953
|
+
if (!tool) {
|
|
2954
|
+
p10.log.error(
|
|
2955
|
+
`Tool "${toolName}" not found. Check that the file exists in your tools directory.`
|
|
2956
|
+
);
|
|
2957
|
+
process.exit(1);
|
|
2958
|
+
}
|
|
2959
|
+
let agentName = opts?.from;
|
|
2960
|
+
if (!agentName) {
|
|
2961
|
+
const agents = await listAgents(config, cwd);
|
|
2962
|
+
if (agents.length === 0) {
|
|
2963
|
+
p10.log.error("No agents found in your project.");
|
|
2964
|
+
process.exit(1);
|
|
2965
|
+
}
|
|
2966
|
+
const selected = await p10.select({
|
|
2967
|
+
message: "Select an agent to unlink the tool from:",
|
|
2968
|
+
options: agents.map((a) => ({
|
|
2969
|
+
value: a.name,
|
|
2970
|
+
label: a.name
|
|
2971
|
+
}))
|
|
2972
|
+
});
|
|
2973
|
+
if (p10.isCancel(selected)) {
|
|
2974
|
+
p10.cancel("Cancelled.");
|
|
2975
|
+
process.exit(0);
|
|
2976
|
+
}
|
|
2977
|
+
agentName = selected;
|
|
2978
|
+
}
|
|
2979
|
+
const agent = await resolveAgentByName(agentName, config, cwd);
|
|
2980
|
+
if (!agent) {
|
|
2981
|
+
p10.log.error(
|
|
2982
|
+
`Agent "${agentName}" not found. Check that the file exists in your agents directory.`
|
|
2983
|
+
);
|
|
2984
|
+
process.exit(1);
|
|
2985
|
+
}
|
|
2986
|
+
const agentContent = await readFile11(agent.filePath, "utf-8");
|
|
2987
|
+
const toolRef = {
|
|
2988
|
+
exportName: tool.exportName,
|
|
2989
|
+
importPath: tool.importPath
|
|
2990
|
+
};
|
|
2991
|
+
const result = unlinkToolFromAgent(agentContent, toolRef);
|
|
2992
|
+
if (result.error) {
|
|
2993
|
+
p10.log.warn(result.error);
|
|
2994
|
+
p10.outro("Could not auto-unlink. Follow the manual instructions above.");
|
|
2995
|
+
process.exit(1);
|
|
2996
|
+
}
|
|
2997
|
+
if (!result.changed) {
|
|
2998
|
+
p10.log.info(
|
|
2999
|
+
`${pc9.cyan(tool.exportName)} is not linked to ${pc9.cyan(basename2(agent.filePath))}.`
|
|
3000
|
+
);
|
|
3001
|
+
p10.outro("Nothing to do.");
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
await writeFile12(agent.filePath, result.content);
|
|
3005
|
+
p10.log.success(
|
|
3006
|
+
`Unlinked ${pc9.cyan(tool.exportName)} from ${pc9.cyan(basename2(agent.filePath))}`
|
|
3007
|
+
);
|
|
3008
|
+
p10.log.message(
|
|
3009
|
+
` ${pc9.red("-")} import { ${tool.exportName} } from "${tool.importPath}"`
|
|
3010
|
+
);
|
|
3011
|
+
p10.log.message(
|
|
3012
|
+
` ${pc9.red("-")} tools: { ${tool.exportName} }`
|
|
3013
|
+
);
|
|
3014
|
+
p10.outro("Done!");
|
|
3015
|
+
}
|
|
3016
|
+
var init_unlink = __esm({
|
|
3017
|
+
"src/commands/unlink.ts"() {
|
|
3018
|
+
"use strict";
|
|
3019
|
+
init_config();
|
|
3020
|
+
init_component_resolver();
|
|
3021
|
+
init_agent_linker();
|
|
3022
|
+
}
|
|
3023
|
+
});
|
|
3024
|
+
|
|
2203
3025
|
// src/commands/info.ts
|
|
2204
3026
|
var info_exports = {};
|
|
2205
3027
|
__export(info_exports, {
|
|
2206
3028
|
infoCommand: () => infoCommand
|
|
2207
3029
|
});
|
|
2208
|
-
import * as
|
|
2209
|
-
import
|
|
3030
|
+
import * as p11 from "@clack/prompts";
|
|
3031
|
+
import pc10 from "picocolors";
|
|
2210
3032
|
async function infoCommand(component) {
|
|
2211
3033
|
const cwd = process.cwd();
|
|
2212
3034
|
const config = await readConfig(cwd);
|
|
2213
3035
|
if (!config) {
|
|
2214
|
-
|
|
3036
|
+
p11.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2215
3037
|
process.exit(1);
|
|
2216
3038
|
}
|
|
2217
3039
|
const ref = parseComponentRef(component);
|
|
2218
3040
|
const fetcher = new RegistryFetcher(config.registries);
|
|
2219
|
-
const s =
|
|
3041
|
+
const s = p11.spinner();
|
|
2220
3042
|
s.start("Fetching component info...");
|
|
2221
3043
|
let index;
|
|
2222
3044
|
try {
|
|
2223
3045
|
index = await fetcher.fetchIndex(ref.namespace);
|
|
2224
3046
|
} catch (err) {
|
|
2225
|
-
s.stop(
|
|
2226
|
-
|
|
3047
|
+
s.stop(pc10.red("Failed to fetch registry"));
|
|
3048
|
+
p11.log.error(err.message);
|
|
2227
3049
|
process.exit(1);
|
|
2228
3050
|
}
|
|
2229
3051
|
const indexItem = index.items.find((i) => i.name === ref.name);
|
|
2230
3052
|
if (!indexItem) {
|
|
2231
|
-
s.stop(
|
|
2232
|
-
|
|
3053
|
+
s.stop(pc10.red("Component not found"));
|
|
3054
|
+
p11.log.error(`Component '${ref.name}' not found in registry.`);
|
|
2233
3055
|
process.exit(1);
|
|
2234
3056
|
}
|
|
2235
3057
|
const dir = typeToDir[indexItem.type];
|
|
@@ -2237,8 +3059,8 @@ async function infoCommand(component) {
|
|
|
2237
3059
|
try {
|
|
2238
3060
|
item = await fetcher.fetchItem(ref.name, dir, ref.namespace, ref.version);
|
|
2239
3061
|
} catch (err) {
|
|
2240
|
-
s.stop(
|
|
2241
|
-
|
|
3062
|
+
s.stop(pc10.red("Failed to fetch component"));
|
|
3063
|
+
p11.log.error(err.message);
|
|
2242
3064
|
process.exit(1);
|
|
2243
3065
|
}
|
|
2244
3066
|
s.stop("Component found");
|
|
@@ -2246,63 +3068,63 @@ async function infoCommand(component) {
|
|
|
2246
3068
|
const typeName = indexItem.type.replace("kitn:", "");
|
|
2247
3069
|
console.log();
|
|
2248
3070
|
console.log(
|
|
2249
|
-
` ${
|
|
3071
|
+
` ${pc10.bold(item.name)} ${pc10.cyan(`v${version}`)}${" ".repeat(Math.max(1, 40 - item.name.length - version.length - 2))}${pc10.dim(ref.namespace)}`
|
|
2250
3072
|
);
|
|
2251
|
-
console.log(` ${
|
|
3073
|
+
console.log(` ${pc10.dim(item.description)}`);
|
|
2252
3074
|
console.log();
|
|
2253
|
-
console.log(` ${
|
|
3075
|
+
console.log(` ${pc10.dim("Type:")} ${typeName}`);
|
|
2254
3076
|
if (item.dependencies?.length) {
|
|
2255
3077
|
console.log(
|
|
2256
|
-
` ${
|
|
3078
|
+
` ${pc10.dim("Dependencies:")} ${item.dependencies.join(", ")}`
|
|
2257
3079
|
);
|
|
2258
3080
|
}
|
|
2259
3081
|
if (item.registryDependencies?.length) {
|
|
2260
3082
|
console.log(
|
|
2261
|
-
` ${
|
|
3083
|
+
` ${pc10.dim("Registry deps:")} ${item.registryDependencies.join(", ")}`
|
|
2262
3084
|
);
|
|
2263
3085
|
}
|
|
2264
3086
|
if (item.categories?.length) {
|
|
2265
3087
|
console.log(
|
|
2266
|
-
` ${
|
|
3088
|
+
` ${pc10.dim("Categories:")} ${item.categories.join(", ")}`
|
|
2267
3089
|
);
|
|
2268
3090
|
}
|
|
2269
3091
|
if (item.updatedAt) {
|
|
2270
|
-
console.log(` ${
|
|
3092
|
+
console.log(` ${pc10.dim("Updated:")} ${item.updatedAt}`);
|
|
2271
3093
|
}
|
|
2272
3094
|
const versions = indexItem.versions;
|
|
2273
3095
|
if (versions?.length) {
|
|
2274
|
-
console.log(` ${
|
|
3096
|
+
console.log(` ${pc10.dim("Versions:")} ${versions.join(", ")}`);
|
|
2275
3097
|
}
|
|
2276
3098
|
if (item.changelog?.length) {
|
|
2277
3099
|
console.log();
|
|
2278
|
-
console.log(` ${
|
|
3100
|
+
console.log(` ${pc10.bold("Changelog:")}`);
|
|
2279
3101
|
for (const entry of item.changelog) {
|
|
2280
|
-
const tag = entry.type === "feature" ?
|
|
3102
|
+
const tag = entry.type === "feature" ? pc10.green(entry.type) : entry.type === "fix" ? pc10.yellow(entry.type) : entry.type === "breaking" ? pc10.red(entry.type) : pc10.dim(entry.type);
|
|
2281
3103
|
console.log(
|
|
2282
|
-
` ${
|
|
3104
|
+
` ${pc10.cyan(entry.version)} ${pc10.dim(entry.date)} ${tag} ${entry.note}`
|
|
2283
3105
|
);
|
|
2284
3106
|
}
|
|
2285
3107
|
}
|
|
2286
3108
|
console.log();
|
|
2287
3109
|
const fileCount = item.files.length;
|
|
2288
|
-
console.log(` ${
|
|
3110
|
+
console.log(` ${pc10.bold(`Files:`)} ${pc10.dim(`(${fileCount})`)}`);
|
|
2289
3111
|
const maxShown = 10;
|
|
2290
3112
|
for (const file of item.files.slice(0, maxShown)) {
|
|
2291
|
-
console.log(` ${
|
|
3113
|
+
console.log(` ${pc10.dim(file.path)}`);
|
|
2292
3114
|
}
|
|
2293
3115
|
if (fileCount > maxShown) {
|
|
2294
|
-
console.log(` ${
|
|
3116
|
+
console.log(` ${pc10.dim(`... and ${fileCount - maxShown} more`)}`);
|
|
2295
3117
|
}
|
|
2296
3118
|
const lock = await readLock(cwd);
|
|
2297
3119
|
const installed = lock[item.name];
|
|
2298
3120
|
if (installed) {
|
|
2299
3121
|
console.log();
|
|
2300
3122
|
console.log(
|
|
2301
|
-
` ${
|
|
3123
|
+
` ${pc10.green("Installed")} ${pc10.dim(`v${installed.version}`)}`
|
|
2302
3124
|
);
|
|
2303
3125
|
if (version !== installed.version) {
|
|
2304
3126
|
console.log(
|
|
2305
|
-
` ${
|
|
3127
|
+
` ${pc10.yellow("Update available:")} ${pc10.dim(`v${installed.version}`)} \u2192 ${pc10.cyan(`v${version}`)}`
|
|
2306
3128
|
);
|
|
2307
3129
|
}
|
|
2308
3130
|
}
|
|
@@ -2324,41 +3146,41 @@ __export(check_exports, {
|
|
|
2324
3146
|
checkCommand: () => checkCommand
|
|
2325
3147
|
});
|
|
2326
3148
|
import { execSync as execSync2 } from "child_process";
|
|
2327
|
-
import * as
|
|
2328
|
-
import
|
|
3149
|
+
import * as p12 from "@clack/prompts";
|
|
3150
|
+
import pc11 from "picocolors";
|
|
2329
3151
|
async function checkCommand(currentVersion) {
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
const s =
|
|
3152
|
+
p12.intro(pc11.bgCyan(pc11.black(" kitn check ")));
|
|
3153
|
+
p12.log.info(`kitn v${currentVersion}`);
|
|
3154
|
+
const s = p12.spinner();
|
|
2333
3155
|
s.start("Checking for updates...");
|
|
2334
3156
|
const latest = await fetchLatestVersion();
|
|
2335
3157
|
if (!latest) {
|
|
2336
|
-
s.stop(
|
|
2337
|
-
|
|
3158
|
+
s.stop(pc11.yellow("Could not reach the npm registry"));
|
|
3159
|
+
p12.outro("Try again later.");
|
|
2338
3160
|
return;
|
|
2339
3161
|
}
|
|
2340
3162
|
if (isNewer(latest, currentVersion)) {
|
|
2341
|
-
s.stop(
|
|
3163
|
+
s.stop(pc11.yellow(`Update available: ${currentVersion} \u2192 ${latest}`));
|
|
2342
3164
|
const pm = detectCliInstaller();
|
|
2343
3165
|
const installCmd = getGlobalInstallCommand(pm, "@kitnai/cli");
|
|
2344
|
-
const shouldUpdate = await
|
|
2345
|
-
if (
|
|
2346
|
-
|
|
3166
|
+
const shouldUpdate = await p12.confirm({ message: "Update now?" });
|
|
3167
|
+
if (p12.isCancel(shouldUpdate) || !shouldUpdate) {
|
|
3168
|
+
p12.log.message(` Run: ${pc11.cyan(installCmd)}`);
|
|
2347
3169
|
} else {
|
|
2348
|
-
const us =
|
|
3170
|
+
const us = p12.spinner();
|
|
2349
3171
|
us.start("Updating...");
|
|
2350
3172
|
try {
|
|
2351
3173
|
execSync2(installCmd, { stdio: "pipe" });
|
|
2352
|
-
us.stop(
|
|
3174
|
+
us.stop(pc11.green(`Updated to v${latest}`));
|
|
2353
3175
|
} catch {
|
|
2354
|
-
us.stop(
|
|
2355
|
-
|
|
3176
|
+
us.stop(pc11.red("Update failed"));
|
|
3177
|
+
p12.log.message(` Run manually: ${pc11.cyan(installCmd)}`);
|
|
2356
3178
|
}
|
|
2357
3179
|
}
|
|
2358
3180
|
} else {
|
|
2359
|
-
s.stop(
|
|
3181
|
+
s.stop(pc11.green("You're on the latest version"));
|
|
2360
3182
|
}
|
|
2361
|
-
|
|
3183
|
+
p12.outro("");
|
|
2362
3184
|
}
|
|
2363
3185
|
var init_check = __esm({
|
|
2364
3186
|
"src/commands/check.ts"() {
|
|
@@ -2368,6 +3190,64 @@ var init_check = __esm({
|
|
|
2368
3190
|
}
|
|
2369
3191
|
});
|
|
2370
3192
|
|
|
3193
|
+
// src/commands/rules.ts
|
|
3194
|
+
var rules_exports = {};
|
|
3195
|
+
__export(rules_exports, {
|
|
3196
|
+
rulesCommand: () => rulesCommand
|
|
3197
|
+
});
|
|
3198
|
+
import * as p13 from "@clack/prompts";
|
|
3199
|
+
import pc12 from "picocolors";
|
|
3200
|
+
async function rulesCommand() {
|
|
3201
|
+
p13.intro(pc12.bgCyan(pc12.black(" kitn rules ")));
|
|
3202
|
+
const cwd = process.cwd();
|
|
3203
|
+
const config = await readConfig(cwd);
|
|
3204
|
+
if (!config) {
|
|
3205
|
+
p13.log.error(`No kitn.json found. Run ${pc12.bold("kitn init")} first.`);
|
|
3206
|
+
process.exit(1);
|
|
3207
|
+
}
|
|
3208
|
+
let selectedIds = config.aiTools;
|
|
3209
|
+
if (!selectedIds || selectedIds.length === 0) {
|
|
3210
|
+
const rulesConfig = await fetchRulesConfig(config.registries);
|
|
3211
|
+
const selected = await p13.multiselect({
|
|
3212
|
+
message: "Which AI coding tools do you use?",
|
|
3213
|
+
options: rulesConfig.tools.map((t) => ({
|
|
3214
|
+
value: t.id,
|
|
3215
|
+
label: t.name,
|
|
3216
|
+
hint: t.description
|
|
3217
|
+
})),
|
|
3218
|
+
required: false
|
|
3219
|
+
});
|
|
3220
|
+
if (p13.isCancel(selected)) {
|
|
3221
|
+
p13.cancel("Cancelled.");
|
|
3222
|
+
process.exit(0);
|
|
3223
|
+
}
|
|
3224
|
+
selectedIds = selected;
|
|
3225
|
+
if (selectedIds.length === 0) {
|
|
3226
|
+
p13.log.warn("No tools selected. Nothing to generate.");
|
|
3227
|
+
p13.outro("Done.");
|
|
3228
|
+
return;
|
|
3229
|
+
}
|
|
3230
|
+
const updatedConfig = { ...config, aiTools: selectedIds };
|
|
3231
|
+
await writeConfig(cwd, updatedConfig);
|
|
3232
|
+
p13.log.info(`Saved tool selections to ${pc12.bold("kitn.json")}`);
|
|
3233
|
+
}
|
|
3234
|
+
const s = p13.spinner();
|
|
3235
|
+
s.start("Generating rules files");
|
|
3236
|
+
const written = await generateRulesFiles(cwd, config, selectedIds);
|
|
3237
|
+
s.stop("Rules files generated");
|
|
3238
|
+
for (const filePath of written) {
|
|
3239
|
+
p13.log.success(`${pc12.green("+")} ${filePath}`);
|
|
3240
|
+
}
|
|
3241
|
+
p13.outro(`Generated ${written.length} rules file${written.length === 1 ? "" : "s"}.`);
|
|
3242
|
+
}
|
|
3243
|
+
var init_rules = __esm({
|
|
3244
|
+
"src/commands/rules.ts"() {
|
|
3245
|
+
"use strict";
|
|
3246
|
+
init_config();
|
|
3247
|
+
init_rules_generator();
|
|
3248
|
+
}
|
|
3249
|
+
});
|
|
3250
|
+
|
|
2371
3251
|
// src/commands/registry.ts
|
|
2372
3252
|
var registry_exports = {};
|
|
2373
3253
|
__export(registry_exports, {
|
|
@@ -2375,8 +3255,8 @@ __export(registry_exports, {
|
|
|
2375
3255
|
registryListCommand: () => registryListCommand,
|
|
2376
3256
|
registryRemoveCommand: () => registryRemoveCommand
|
|
2377
3257
|
});
|
|
2378
|
-
import * as
|
|
2379
|
-
import
|
|
3258
|
+
import * as p14 from "@clack/prompts";
|
|
3259
|
+
import pc13 from "picocolors";
|
|
2380
3260
|
async function registryAddCommand(namespace, url, opts = {}) {
|
|
2381
3261
|
const cwd = opts.cwd ?? process.cwd();
|
|
2382
3262
|
const config = await readConfig(cwd);
|
|
@@ -2402,10 +3282,10 @@ async function registryAddCommand(namespace, url, opts = {}) {
|
|
|
2402
3282
|
config.registries[namespace] = url;
|
|
2403
3283
|
}
|
|
2404
3284
|
await writeConfig(cwd, config);
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
if (opts.homepage)
|
|
2408
|
-
if (opts.description)
|
|
3285
|
+
p14.log.success(`Added registry ${pc13.bold(namespace)}`);
|
|
3286
|
+
p14.log.message(pc13.dim(` ${url}`));
|
|
3287
|
+
if (opts.homepage) p14.log.message(pc13.dim(` Homepage: ${opts.homepage}`));
|
|
3288
|
+
if (opts.description) p14.log.message(pc13.dim(` ${opts.description}`));
|
|
2409
3289
|
}
|
|
2410
3290
|
async function registryRemoveCommand(namespace, opts = {}) {
|
|
2411
3291
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -2426,10 +3306,10 @@ async function registryRemoveCommand(namespace, opts = {}) {
|
|
|
2426
3306
|
}
|
|
2427
3307
|
delete config.registries[namespace];
|
|
2428
3308
|
await writeConfig(cwd, config);
|
|
2429
|
-
|
|
3309
|
+
p14.log.success(`Removed registry ${pc13.bold(namespace)}`);
|
|
2430
3310
|
if (affectedComponents.length > 0) {
|
|
2431
|
-
|
|
2432
|
-
` + affectedComponents.map((name) => ` ${
|
|
3311
|
+
p14.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:
|
|
3312
|
+
` + affectedComponents.map((name) => ` ${pc13.yellow("!")} ${name}`).join("\n"));
|
|
2433
3313
|
}
|
|
2434
3314
|
return { affectedComponents };
|
|
2435
3315
|
}
|
|
@@ -2444,15 +3324,15 @@ async function registryListCommand(opts = {}) {
|
|
|
2444
3324
|
return { namespace, url, homepage, description };
|
|
2445
3325
|
});
|
|
2446
3326
|
if (entries.length === 0) {
|
|
2447
|
-
|
|
3327
|
+
p14.log.message(pc13.dim(" No registries configured."));
|
|
2448
3328
|
} else {
|
|
2449
3329
|
const lines = [];
|
|
2450
3330
|
for (const { namespace, url, homepage, description } of entries) {
|
|
2451
|
-
lines.push(` ${
|
|
3331
|
+
lines.push(` ${pc13.bold(namespace.padEnd(16))} ${pc13.dim(url)}`);
|
|
2452
3332
|
if (description) lines.push(` ${" ".repeat(16)} ${description}`);
|
|
2453
|
-
if (homepage) lines.push(` ${" ".repeat(16)} ${
|
|
3333
|
+
if (homepage) lines.push(` ${" ".repeat(16)} ${pc13.dim(homepage)}`);
|
|
2454
3334
|
}
|
|
2455
|
-
|
|
3335
|
+
p14.log.message(lines.join("\n"));
|
|
2456
3336
|
}
|
|
2457
3337
|
return entries;
|
|
2458
3338
|
}
|
|
@@ -2466,14 +3346,14 @@ var init_registry = __esm({
|
|
|
2466
3346
|
// src/index.ts
|
|
2467
3347
|
init_update_check();
|
|
2468
3348
|
import { Command } from "commander";
|
|
2469
|
-
var VERSION = true ? "0.1.
|
|
3349
|
+
var VERSION = true ? "0.1.31" : "0.0.0-dev";
|
|
2470
3350
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
2471
3351
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
2472
3352
|
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) => {
|
|
2473
3353
|
const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
2474
3354
|
await initCommand2(opts);
|
|
2475
3355
|
});
|
|
2476
|
-
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) => {
|
|
3356
|
+
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").option("-y, --yes", "skip confirmation prompt").action(async (components, opts) => {
|
|
2477
3357
|
const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
2478
3358
|
await addCommand2(components, opts);
|
|
2479
3359
|
});
|
|
@@ -2497,6 +3377,14 @@ program.command("create").description("Scaffold a new kitn component").argument(
|
|
|
2497
3377
|
const { createCommand: createCommand2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
2498
3378
|
await createCommand2(type, name);
|
|
2499
3379
|
});
|
|
3380
|
+
program.command("link").description("Wire a tool to an agent").argument("[type]", "component type (tool)").argument("[name]", "component name").option("--to <agent>", "target agent name").option("--as <key>", "key name in the tools object").action(async (type, name, opts) => {
|
|
3381
|
+
const { linkCommand: linkCommand2 } = await Promise.resolve().then(() => (init_link(), link_exports));
|
|
3382
|
+
await linkCommand2(type, name, opts);
|
|
3383
|
+
});
|
|
3384
|
+
program.command("unlink").description("Unwire a tool from an agent").argument("[type]", "component type (tool)").argument("[name]", "component name").option("--from <agent>", "target agent name").action(async (type, name, opts) => {
|
|
3385
|
+
const { unlinkCommand: unlinkCommand2 } = await Promise.resolve().then(() => (init_unlink(), unlink_exports));
|
|
3386
|
+
await unlinkCommand2(type, name, opts);
|
|
3387
|
+
});
|
|
2500
3388
|
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) => {
|
|
2501
3389
|
const { infoCommand: infoCommand2 } = await Promise.resolve().then(() => (init_info(), info_exports));
|
|
2502
3390
|
await infoCommand2(component);
|
|
@@ -2505,6 +3393,10 @@ program.command("check").description("Check for CLI updates").action(async () =>
|
|
|
2505
3393
|
const { checkCommand: checkCommand2 } = await Promise.resolve().then(() => (init_check(), check_exports));
|
|
2506
3394
|
await checkCommand2(VERSION);
|
|
2507
3395
|
});
|
|
3396
|
+
program.command("rules").description("Regenerate AI coding tool rules files").action(async () => {
|
|
3397
|
+
const { rulesCommand: rulesCommand2 } = await Promise.resolve().then(() => (init_rules(), rules_exports));
|
|
3398
|
+
await rulesCommand2();
|
|
3399
|
+
});
|
|
2508
3400
|
var registry = program.command("registry").description("Manage component registries");
|
|
2509
3401
|
registry.command("add").description("Add a component registry").argument("<namespace>", "registry namespace (e.g. @myteam)").argument("<url>", "URL template with {type} and {name} placeholders").option("-o, --overwrite", "overwrite if namespace already exists").option("--homepage <url>", "registry homepage URL").option("--description <text>", "short description of the registry").action(async (namespace, url, opts) => {
|
|
2510
3402
|
const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|