@kitnai/cli 0.1.30 → 0.1.32
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 +1011 -94
- 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",
|
|
@@ -1421,6 +1422,145 @@ var init_add = __esm({
|
|
|
1421
1422
|
}
|
|
1422
1423
|
});
|
|
1423
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
|
+
|
|
1424
1564
|
// src/commands/init.ts
|
|
1425
1565
|
var init_exports = {};
|
|
1426
1566
|
__export(init_exports, {
|
|
@@ -1428,8 +1568,20 @@ __export(init_exports, {
|
|
|
1428
1568
|
});
|
|
1429
1569
|
import * as p3 from "@clack/prompts";
|
|
1430
1570
|
import pc4 from "picocolors";
|
|
1431
|
-
import { mkdir as
|
|
1432
|
-
import { join as
|
|
1571
|
+
import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
1572
|
+
import { join as join9 } from "path";
|
|
1573
|
+
async function detectFramework(cwd) {
|
|
1574
|
+
try {
|
|
1575
|
+
const pkg = JSON.parse(await readFile7(join9(cwd, "package.json"), "utf-8"));
|
|
1576
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1577
|
+
if (deps["elysia"]) return "elysia";
|
|
1578
|
+
if (deps["@hono/zod-openapi"]) return "hono-openapi";
|
|
1579
|
+
if (deps["hono"]) return "hono";
|
|
1580
|
+
return null;
|
|
1581
|
+
} catch {
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1433
1585
|
function getPluginTemplate(framework) {
|
|
1434
1586
|
const adapterName = framework === "hono-openapi" ? "hono-openapi" : framework;
|
|
1435
1587
|
return `import { createAIPlugin } from "@kitn/adapters/${adapterName}";
|
|
@@ -1497,12 +1649,41 @@ async function initCommand(opts = {}) {
|
|
|
1497
1649
|
}
|
|
1498
1650
|
const validFrameworks = ["hono", "hono-openapi", "elysia"];
|
|
1499
1651
|
let framework;
|
|
1652
|
+
const detected = await detectFramework(cwd);
|
|
1500
1653
|
if (opts.framework) {
|
|
1501
1654
|
if (!validFrameworks.includes(opts.framework)) {
|
|
1502
1655
|
p3.log.error(`Invalid framework: ${opts.framework}. Must be one of: ${validFrameworks.join(", ")}`);
|
|
1503
1656
|
process.exit(1);
|
|
1504
1657
|
}
|
|
1505
1658
|
framework = opts.framework;
|
|
1659
|
+
} else if (detected) {
|
|
1660
|
+
framework = detected;
|
|
1661
|
+
p3.log.info(`Detected ${pc4.bold(detected)} from package.json`);
|
|
1662
|
+
if (!opts.yes) {
|
|
1663
|
+
const confirm6 = await p3.confirm({
|
|
1664
|
+
message: `Use ${detected}?`,
|
|
1665
|
+
initialValue: true
|
|
1666
|
+
});
|
|
1667
|
+
if (p3.isCancel(confirm6)) {
|
|
1668
|
+
p3.cancel("Init cancelled.");
|
|
1669
|
+
process.exit(0);
|
|
1670
|
+
}
|
|
1671
|
+
if (!confirm6) {
|
|
1672
|
+
const selected = await p3.select({
|
|
1673
|
+
message: "Which HTTP framework do you use?",
|
|
1674
|
+
options: [
|
|
1675
|
+
{ value: "hono", label: "Hono" },
|
|
1676
|
+
{ value: "hono-openapi", label: "Hono + OpenAPI", hint: "zod-openapi routes with /doc endpoint" },
|
|
1677
|
+
{ value: "elysia", label: "Elysia", hint: "Bun-native framework" }
|
|
1678
|
+
]
|
|
1679
|
+
});
|
|
1680
|
+
if (p3.isCancel(selected)) {
|
|
1681
|
+
p3.cancel("Init cancelled.");
|
|
1682
|
+
process.exit(0);
|
|
1683
|
+
}
|
|
1684
|
+
framework = selected;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1506
1687
|
} else if (opts.yes) {
|
|
1507
1688
|
framework = "hono";
|
|
1508
1689
|
} else {
|
|
@@ -1567,13 +1748,45 @@ async function initCommand(opts = {}) {
|
|
|
1567
1748
|
p3.log.info(`Patched tsconfig.json with path: ${pc4.bold("@kitn/*")}`);
|
|
1568
1749
|
p3.log.info("Installing core engine and adapter...");
|
|
1569
1750
|
await addCommand(["core", "routes"], { overwrite: true });
|
|
1570
|
-
const aiDir =
|
|
1571
|
-
await
|
|
1572
|
-
const barrelPath =
|
|
1573
|
-
await
|
|
1574
|
-
const pluginPath =
|
|
1575
|
-
await
|
|
1751
|
+
const aiDir = join9(cwd, baseDir);
|
|
1752
|
+
await mkdir5(aiDir, { recursive: true });
|
|
1753
|
+
const barrelPath = join9(aiDir, "index.ts");
|
|
1754
|
+
await writeFile8(barrelPath, createBarrelFile());
|
|
1755
|
+
const pluginPath = join9(aiDir, "plugin.ts");
|
|
1756
|
+
await writeFile8(pluginPath, getPluginTemplate(framework));
|
|
1576
1757
|
p3.log.success(`Created ${pc4.bold(baseDir + "/plugin.ts")} \u2014 configure your AI provider there`);
|
|
1758
|
+
try {
|
|
1759
|
+
const rulesConfig = await fetchRulesConfig(config.registries);
|
|
1760
|
+
let selectedToolIds;
|
|
1761
|
+
if (opts.yes) {
|
|
1762
|
+
selectedToolIds = rulesConfig.tools.map((t) => t.id);
|
|
1763
|
+
} else {
|
|
1764
|
+
const selected = await p3.multiselect({
|
|
1765
|
+
message: "Which AI coding tools do you use?",
|
|
1766
|
+
options: rulesConfig.tools.map((t) => ({
|
|
1767
|
+
value: t.id,
|
|
1768
|
+
label: t.name,
|
|
1769
|
+
hint: t.description
|
|
1770
|
+
})),
|
|
1771
|
+
required: false
|
|
1772
|
+
});
|
|
1773
|
+
if (p3.isCancel(selected)) {
|
|
1774
|
+
selectedToolIds = [];
|
|
1775
|
+
} else {
|
|
1776
|
+
selectedToolIds = selected;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
if (selectedToolIds.length > 0) {
|
|
1780
|
+
const updatedConfig = { ...config, aiTools: selectedToolIds };
|
|
1781
|
+
await writeConfig(cwd, updatedConfig);
|
|
1782
|
+
const written = await generateRulesFiles(cwd, updatedConfig, selectedToolIds);
|
|
1783
|
+
for (const filePath of written) {
|
|
1784
|
+
p3.log.success(`Created ${pc4.bold(filePath)}`);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
} catch {
|
|
1788
|
+
p3.log.warn("Could not generate AI coding tool rules (non-fatal).");
|
|
1789
|
+
}
|
|
1577
1790
|
const mountCode = framework === "elysia" ? `app.use(new Elysia({ prefix: "/api" }).use(ai.router));` : `app.route("/api", ai.router);`;
|
|
1578
1791
|
p3.note(
|
|
1579
1792
|
[
|
|
@@ -1601,6 +1814,7 @@ var init_init = __esm({
|
|
|
1601
1814
|
init_tsconfig_patcher();
|
|
1602
1815
|
init_barrel_manager();
|
|
1603
1816
|
init_add();
|
|
1817
|
+
init_rules_generator();
|
|
1604
1818
|
}
|
|
1605
1819
|
});
|
|
1606
1820
|
|
|
@@ -1736,7 +1950,7 @@ __export(diff_exports, {
|
|
|
1736
1950
|
diffCommand: () => diffCommand
|
|
1737
1951
|
});
|
|
1738
1952
|
import * as p5 from "@clack/prompts";
|
|
1739
|
-
import { join as
|
|
1953
|
+
import { join as join10 } from "path";
|
|
1740
1954
|
async function diffCommand(componentName) {
|
|
1741
1955
|
const cwd = process.cwd();
|
|
1742
1956
|
const config = await readConfig(cwd);
|
|
@@ -1767,8 +1981,8 @@ async function diffCommand(componentName) {
|
|
|
1767
1981
|
for (const file of registryItem.files) {
|
|
1768
1982
|
if (indexItem.type === "kitn:package") {
|
|
1769
1983
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1770
|
-
const localPath =
|
|
1771
|
-
const relativePath =
|
|
1984
|
+
const localPath = join10(cwd, baseDir, file.path);
|
|
1985
|
+
const relativePath = join10(baseDir, file.path);
|
|
1772
1986
|
const localContent = await readExistingFile(localPath);
|
|
1773
1987
|
if (localContent === null) {
|
|
1774
1988
|
p5.log.warn(`${relativePath}: file missing locally`);
|
|
@@ -1792,7 +2006,7 @@ async function diffCommand(componentName) {
|
|
|
1792
2006
|
return "storage";
|
|
1793
2007
|
}
|
|
1794
2008
|
})();
|
|
1795
|
-
const localPath =
|
|
2009
|
+
const localPath = join10(cwd, config.aliases[aliasKey], fileName);
|
|
1796
2010
|
const localContent = await readExistingFile(localPath);
|
|
1797
2011
|
if (localContent === null) {
|
|
1798
2012
|
p5.log.warn(`${fileName}: file missing locally`);
|
|
@@ -1826,8 +2040,8 @@ __export(remove_exports, {
|
|
|
1826
2040
|
});
|
|
1827
2041
|
import * as p6 from "@clack/prompts";
|
|
1828
2042
|
import pc6 from "picocolors";
|
|
1829
|
-
import { join as
|
|
1830
|
-
import { unlink as unlink3, readFile as
|
|
2043
|
+
import { join as join11, relative as relative3, dirname as dirname5 } from "path";
|
|
2044
|
+
import { unlink as unlink3, readFile as readFile8, writeFile as writeFile9 } from "fs/promises";
|
|
1831
2045
|
import { existsSync as existsSync2 } from "fs";
|
|
1832
2046
|
async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
1833
2047
|
const entry = lock[installedKey];
|
|
@@ -1835,27 +2049,27 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
1835
2049
|
const deleted = [];
|
|
1836
2050
|
for (const filePath of entry.files) {
|
|
1837
2051
|
try {
|
|
1838
|
-
await unlink3(
|
|
2052
|
+
await unlink3(join11(cwd, filePath));
|
|
1839
2053
|
deleted.push(filePath);
|
|
1840
2054
|
} catch {
|
|
1841
2055
|
p6.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
1842
2056
|
}
|
|
1843
2057
|
}
|
|
1844
2058
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1845
|
-
const barrelPath =
|
|
1846
|
-
const barrelDir =
|
|
2059
|
+
const barrelPath = join11(cwd, baseDir, "index.ts");
|
|
2060
|
+
const barrelDir = join11(cwd, baseDir);
|
|
1847
2061
|
const barrelEligibleDirs = /* @__PURE__ */ new Set([
|
|
1848
2062
|
config.aliases.agents,
|
|
1849
2063
|
config.aliases.tools,
|
|
1850
2064
|
config.aliases.skills
|
|
1851
2065
|
]);
|
|
1852
2066
|
if (existsSync2(barrelPath) && deleted.length > 0) {
|
|
1853
|
-
let barrelContent = await
|
|
2067
|
+
let barrelContent = await readFile8(barrelPath, "utf-8");
|
|
1854
2068
|
let barrelChanged = false;
|
|
1855
2069
|
for (const filePath of deleted) {
|
|
1856
|
-
const fileDir =
|
|
2070
|
+
const fileDir = dirname5(filePath);
|
|
1857
2071
|
if (!barrelEligibleDirs.has(fileDir)) continue;
|
|
1858
|
-
const importPath = "./" + relative3(barrelDir,
|
|
2072
|
+
const importPath = "./" + relative3(barrelDir, join11(cwd, filePath)).replace(/\\/g, "/");
|
|
1859
2073
|
const updated = removeImportFromBarrel(barrelContent, importPath);
|
|
1860
2074
|
if (updated !== barrelContent) {
|
|
1861
2075
|
barrelContent = updated;
|
|
@@ -1863,8 +2077,8 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
1863
2077
|
}
|
|
1864
2078
|
}
|
|
1865
2079
|
if (barrelChanged) {
|
|
1866
|
-
await
|
|
1867
|
-
p6.log.info(`Updated barrel file: ${
|
|
2080
|
+
await writeFile9(barrelPath, barrelContent);
|
|
2081
|
+
p6.log.info(`Updated barrel file: ${join11(baseDir, "index.ts")}`);
|
|
1868
2082
|
}
|
|
1869
2083
|
}
|
|
1870
2084
|
delete lock[installedKey];
|
|
@@ -2012,6 +2226,19 @@ var init_update = __esm({
|
|
|
2012
2226
|
}
|
|
2013
2227
|
});
|
|
2014
2228
|
|
|
2229
|
+
// src/utils/naming.ts
|
|
2230
|
+
function toCamelCase(str) {
|
|
2231
|
+
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
2232
|
+
}
|
|
2233
|
+
function toTitleCase(str) {
|
|
2234
|
+
return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
2235
|
+
}
|
|
2236
|
+
var init_naming = __esm({
|
|
2237
|
+
"src/utils/naming.ts"() {
|
|
2238
|
+
"use strict";
|
|
2239
|
+
}
|
|
2240
|
+
});
|
|
2241
|
+
|
|
2015
2242
|
// src/commands/create.ts
|
|
2016
2243
|
var create_exports = {};
|
|
2017
2244
|
__export(create_exports, {
|
|
@@ -2020,15 +2247,9 @@ __export(create_exports, {
|
|
|
2020
2247
|
});
|
|
2021
2248
|
import * as p8 from "@clack/prompts";
|
|
2022
2249
|
import pc7 from "picocolors";
|
|
2023
|
-
import { join as
|
|
2250
|
+
import { join as join12, relative as relative4 } from "path";
|
|
2024
2251
|
import { existsSync as existsSync3 } from "fs";
|
|
2025
|
-
import { readFile as
|
|
2026
|
-
function toCamelCase(str) {
|
|
2027
|
-
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
2028
|
-
}
|
|
2029
|
-
function toTitleCase(str) {
|
|
2030
|
-
return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
2031
|
-
}
|
|
2252
|
+
import { readFile as readFile9, writeFile as writeFile10, mkdir as mkdir6 } from "fs/promises";
|
|
2032
2253
|
function generateAgentSource(name) {
|
|
2033
2254
|
const camel = toCamelCase(name);
|
|
2034
2255
|
return `import { registerAgent } from "@kitn/core";
|
|
@@ -2135,7 +2356,7 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2135
2356
|
const validType = type;
|
|
2136
2357
|
const kitnType = typeToKitnType[validType];
|
|
2137
2358
|
const fileName = validType === "skill" ? `${name}.md` : `${name}.ts`;
|
|
2138
|
-
const filePath =
|
|
2359
|
+
const filePath = join12(cwd, getInstallPath(config, kitnType, fileName));
|
|
2139
2360
|
const dummyContent = "";
|
|
2140
2361
|
const status = await checkFileStatus(filePath, dummyContent);
|
|
2141
2362
|
if (status !== "new" /* New */) {
|
|
@@ -2163,18 +2384,18 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2163
2384
|
let barrelUpdated = false;
|
|
2164
2385
|
if (BARREL_TYPES.includes(validType)) {
|
|
2165
2386
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
2166
|
-
const barrelPath =
|
|
2387
|
+
const barrelPath = join12(cwd, baseDir, "index.ts");
|
|
2167
2388
|
let barrelContent;
|
|
2168
2389
|
if (existsSync3(barrelPath)) {
|
|
2169
|
-
barrelContent = await
|
|
2390
|
+
barrelContent = await readFile9(barrelPath, "utf-8");
|
|
2170
2391
|
} else {
|
|
2171
2392
|
barrelContent = createBarrelFile();
|
|
2172
|
-
await
|
|
2393
|
+
await mkdir6(join12(cwd, baseDir), { recursive: true });
|
|
2173
2394
|
}
|
|
2174
|
-
const importPath = "./" + relative4(
|
|
2395
|
+
const importPath = "./" + relative4(join12(cwd, baseDir), filePath).replace(/\.ts$/, ".js");
|
|
2175
2396
|
const updatedBarrel = addImportToBarrel(barrelContent, importPath);
|
|
2176
2397
|
if (updatedBarrel !== barrelContent) {
|
|
2177
|
-
await
|
|
2398
|
+
await writeFile10(barrelPath, updatedBarrel);
|
|
2178
2399
|
barrelUpdated = true;
|
|
2179
2400
|
}
|
|
2180
2401
|
}
|
|
@@ -2202,6 +2423,7 @@ var init_create = __esm({
|
|
|
2202
2423
|
"src/commands/create.ts"() {
|
|
2203
2424
|
"use strict";
|
|
2204
2425
|
init_config();
|
|
2426
|
+
init_naming();
|
|
2205
2427
|
init_file_writer();
|
|
2206
2428
|
init_barrel_manager();
|
|
2207
2429
|
VALID_TYPES = ["agent", "tool", "skill", "storage", "cron"];
|
|
@@ -2216,36 +2438,661 @@ var init_create = __esm({
|
|
|
2216
2438
|
}
|
|
2217
2439
|
});
|
|
2218
2440
|
|
|
2441
|
+
// src/utils/component-resolver.ts
|
|
2442
|
+
import { readdir, readFile as readFile10 } from "fs/promises";
|
|
2443
|
+
import { join as join13, relative as relative5 } from "path";
|
|
2444
|
+
function stripSuffix(name, suffix) {
|
|
2445
|
+
if (name.endsWith(`-${suffix}`)) {
|
|
2446
|
+
return name.slice(0, -(suffix.length + 1));
|
|
2447
|
+
}
|
|
2448
|
+
return name;
|
|
2449
|
+
}
|
|
2450
|
+
function toolsDir(config, cwd) {
|
|
2451
|
+
const baseAlias = config.aliases.base ?? "src/ai";
|
|
2452
|
+
const tools = config.aliases.tools ?? join13(baseAlias, "tools");
|
|
2453
|
+
return join13(cwd, tools);
|
|
2454
|
+
}
|
|
2455
|
+
function agentsDir(config, cwd) {
|
|
2456
|
+
const baseAlias = config.aliases.base ?? "src/ai";
|
|
2457
|
+
const agents = config.aliases.agents ?? join13(baseAlias, "agents");
|
|
2458
|
+
return join13(cwd, agents);
|
|
2459
|
+
}
|
|
2460
|
+
async function findFile(dir, candidates) {
|
|
2461
|
+
for (const name of candidates) {
|
|
2462
|
+
const filePath = join13(dir, `${name}.ts`);
|
|
2463
|
+
try {
|
|
2464
|
+
await readFile10(filePath);
|
|
2465
|
+
return filePath;
|
|
2466
|
+
} catch {
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
return null;
|
|
2470
|
+
}
|
|
2471
|
+
function parseExportName(source) {
|
|
2472
|
+
const match = source.match(/export\s+const\s+(\w+)/);
|
|
2473
|
+
return match ? match[1] : null;
|
|
2474
|
+
}
|
|
2475
|
+
function parseAgentName(source) {
|
|
2476
|
+
const match = source.match(/registerAgent\s*\(\s*\{[^}]*name:\s*"([^"]+)"/s);
|
|
2477
|
+
return match ? match[1] : null;
|
|
2478
|
+
}
|
|
2479
|
+
function computeImportPath(fromDir, toFile) {
|
|
2480
|
+
let rel = relative5(fromDir, toFile);
|
|
2481
|
+
if (!rel.startsWith(".")) {
|
|
2482
|
+
rel = `./${rel}`;
|
|
2483
|
+
}
|
|
2484
|
+
return rel.replace(/\.ts$/, ".js");
|
|
2485
|
+
}
|
|
2486
|
+
async function resolveToolByName(name, config, cwd) {
|
|
2487
|
+
const tDir = toolsDir(config, cwd);
|
|
2488
|
+
const aDir = agentsDir(config, cwd);
|
|
2489
|
+
const lock = await readLock(cwd);
|
|
2490
|
+
for (const [componentName, entry] of Object.entries(lock)) {
|
|
2491
|
+
if (componentName === name || componentName === `${name}-tool`) {
|
|
2492
|
+
const toolFile = entry.files.find((f) => {
|
|
2493
|
+
const toolsAlias = config.aliases.tools ?? join13(config.aliases.base ?? "src/ai", "tools");
|
|
2494
|
+
return f.startsWith(toolsAlias);
|
|
2495
|
+
});
|
|
2496
|
+
if (toolFile) {
|
|
2497
|
+
const filePath2 = join13(cwd, toolFile);
|
|
2498
|
+
try {
|
|
2499
|
+
const source2 = await readFile10(filePath2, "utf-8");
|
|
2500
|
+
const exportName2 = parseExportName(source2);
|
|
2501
|
+
if (exportName2) {
|
|
2502
|
+
return {
|
|
2503
|
+
filePath: filePath2,
|
|
2504
|
+
exportName: exportName2,
|
|
2505
|
+
importPath: computeImportPath(aDir, filePath2)
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
} catch {
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
const candidates = [name, stripSuffix(name, "tool")];
|
|
2514
|
+
const uniqueCandidates = [...new Set(candidates)];
|
|
2515
|
+
const filePath = await findFile(tDir, uniqueCandidates);
|
|
2516
|
+
if (!filePath) return null;
|
|
2517
|
+
const source = await readFile10(filePath, "utf-8");
|
|
2518
|
+
const exportName = parseExportName(source);
|
|
2519
|
+
if (!exportName) return null;
|
|
2520
|
+
return {
|
|
2521
|
+
filePath,
|
|
2522
|
+
exportName,
|
|
2523
|
+
importPath: computeImportPath(aDir, filePath)
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
async function resolveAgentByName(name, config, cwd) {
|
|
2527
|
+
const aDir = agentsDir(config, cwd);
|
|
2528
|
+
const lock = await readLock(cwd);
|
|
2529
|
+
for (const [componentName, entry] of Object.entries(lock)) {
|
|
2530
|
+
if (componentName === name || componentName === `${name}-agent`) {
|
|
2531
|
+
const agentFile = entry.files.find((f) => {
|
|
2532
|
+
const agentsAlias = config.aliases.agents ?? join13(config.aliases.base ?? "src/ai", "agents");
|
|
2533
|
+
return f.startsWith(agentsAlias);
|
|
2534
|
+
});
|
|
2535
|
+
if (agentFile) {
|
|
2536
|
+
const filePath2 = join13(cwd, agentFile);
|
|
2537
|
+
try {
|
|
2538
|
+
const source2 = await readFile10(filePath2, "utf-8");
|
|
2539
|
+
const agentName2 = parseAgentName(source2);
|
|
2540
|
+
return {
|
|
2541
|
+
filePath: filePath2,
|
|
2542
|
+
name: agentName2 ?? componentName
|
|
2543
|
+
};
|
|
2544
|
+
} catch {
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
const candidates = [name, stripSuffix(name, "agent")];
|
|
2550
|
+
const uniqueCandidates = [...new Set(candidates)];
|
|
2551
|
+
const filePath = await findFile(aDir, uniqueCandidates);
|
|
2552
|
+
if (!filePath) return null;
|
|
2553
|
+
const source = await readFile10(filePath, "utf-8");
|
|
2554
|
+
const agentName = parseAgentName(source);
|
|
2555
|
+
const fallbackName = filePath.split("/").pop().replace(/\.ts$/, "");
|
|
2556
|
+
return {
|
|
2557
|
+
filePath,
|
|
2558
|
+
name: agentName ?? fallbackName
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2561
|
+
async function listTools(config, cwd) {
|
|
2562
|
+
const dir = toolsDir(config, cwd);
|
|
2563
|
+
return listComponentsInDir(dir);
|
|
2564
|
+
}
|
|
2565
|
+
async function listAgents(config, cwd) {
|
|
2566
|
+
const dir = agentsDir(config, cwd);
|
|
2567
|
+
return listComponentsInDir(dir);
|
|
2568
|
+
}
|
|
2569
|
+
async function listComponentsInDir(dir) {
|
|
2570
|
+
try {
|
|
2571
|
+
const entries = await readdir(dir);
|
|
2572
|
+
return entries.filter((f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && !f.endsWith(".d.ts")).sort().map((f) => ({
|
|
2573
|
+
name: f.replace(/\.ts$/, ""),
|
|
2574
|
+
filePath: join13(dir, f)
|
|
2575
|
+
}));
|
|
2576
|
+
} catch {
|
|
2577
|
+
return [];
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
var init_component_resolver = __esm({
|
|
2581
|
+
"src/utils/component-resolver.ts"() {
|
|
2582
|
+
"use strict";
|
|
2583
|
+
init_config();
|
|
2584
|
+
}
|
|
2585
|
+
});
|
|
2586
|
+
|
|
2587
|
+
// src/installers/agent-linker.ts
|
|
2588
|
+
function linkToolToAgent(content, tool, toolKey) {
|
|
2589
|
+
const key = toolKey ?? tool.exportName;
|
|
2590
|
+
const { exportName, importPath } = tool;
|
|
2591
|
+
const toolEntry = key === exportName ? exportName : `${key}: ${exportName}`;
|
|
2592
|
+
if (hasToolEntry(content, key)) {
|
|
2593
|
+
return { content, changed: false };
|
|
2594
|
+
}
|
|
2595
|
+
const importLine = `import { ${exportName} } from "${importPath}";`;
|
|
2596
|
+
let result = content;
|
|
2597
|
+
if (!result.includes(importLine)) {
|
|
2598
|
+
const lines = result.split("\n");
|
|
2599
|
+
let lastImportIndex = -1;
|
|
2600
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2601
|
+
if (/^import\s+/.test(lines[i])) lastImportIndex = i;
|
|
2602
|
+
}
|
|
2603
|
+
if (lastImportIndex === -1) {
|
|
2604
|
+
result = `${importLine}
|
|
2605
|
+
${result}`;
|
|
2606
|
+
} else {
|
|
2607
|
+
lines.splice(lastImportIndex + 1, 0, importLine);
|
|
2608
|
+
result = lines.join("\n");
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
const insertResult = insertToolEntry(result, key, exportName);
|
|
2612
|
+
if (insertResult.error) {
|
|
2613
|
+
return {
|
|
2614
|
+
content,
|
|
2615
|
+
changed: false,
|
|
2616
|
+
error: `Could not auto-modify the agent file. Add manually:
|
|
2617
|
+
1. Import: ${importLine}
|
|
2618
|
+
2. Add to tools: { ${toolEntry} }`
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2621
|
+
return { content: insertResult.content, changed: true };
|
|
2622
|
+
}
|
|
2623
|
+
function unlinkToolFromAgent(content, tool, toolKey) {
|
|
2624
|
+
const key = toolKey ?? tool.exportName;
|
|
2625
|
+
const { exportName, importPath } = tool;
|
|
2626
|
+
if (!hasToolEntry(content, key)) {
|
|
2627
|
+
return { content, changed: false };
|
|
2628
|
+
}
|
|
2629
|
+
const removeResult = removeToolEntry(content, key, exportName);
|
|
2630
|
+
if (removeResult.error) {
|
|
2631
|
+
return {
|
|
2632
|
+
content,
|
|
2633
|
+
changed: false,
|
|
2634
|
+
error: `Could not auto-modify the agent file. Remove manually:
|
|
2635
|
+
1. Remove from tools: ${key === exportName ? key : `${key}: ${exportName}`}
|
|
2636
|
+
2. Remove import if unused: import { ${exportName} } from "${importPath}";`
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
let result = removeResult.content;
|
|
2640
|
+
if (!isExportNameReferenced(result, exportName)) {
|
|
2641
|
+
result = removeImportLine(result, exportName, importPath);
|
|
2642
|
+
}
|
|
2643
|
+
return { content: result, changed: true };
|
|
2644
|
+
}
|
|
2645
|
+
function hasToolEntry(content, key) {
|
|
2646
|
+
const toolsMatch = extractToolsBlock(content);
|
|
2647
|
+
if (!toolsMatch) return false;
|
|
2648
|
+
const toolsContent = toolsMatch.inner;
|
|
2649
|
+
const keyPattern = new RegExp(
|
|
2650
|
+
`(?:^|[,{\\s])${escapeRegex(key)}(?:\\s*[:,}\\s]|$)`
|
|
2651
|
+
);
|
|
2652
|
+
return keyPattern.test(toolsContent);
|
|
2653
|
+
}
|
|
2654
|
+
function extractToolsBlock(content) {
|
|
2655
|
+
const singleLine = /^([ \t]*)tools\s*:\s*\{([^}]*)\}/m;
|
|
2656
|
+
const singleMatch = singleLine.exec(content);
|
|
2657
|
+
if (singleMatch) {
|
|
2658
|
+
return {
|
|
2659
|
+
full: singleMatch[0],
|
|
2660
|
+
inner: singleMatch[2],
|
|
2661
|
+
startIndex: singleMatch.index,
|
|
2662
|
+
indent: singleMatch[1]
|
|
2663
|
+
};
|
|
2664
|
+
}
|
|
2665
|
+
const multiStart = /^([ \t]*)tools\s*:\s*\{/m;
|
|
2666
|
+
const multiMatch = multiStart.exec(content);
|
|
2667
|
+
if (!multiMatch) return null;
|
|
2668
|
+
const braceStart = multiMatch.index + multiMatch[0].length;
|
|
2669
|
+
let depth = 1;
|
|
2670
|
+
let i = braceStart;
|
|
2671
|
+
while (i < content.length && depth > 0) {
|
|
2672
|
+
if (content[i] === "{") depth++;
|
|
2673
|
+
else if (content[i] === "}") depth--;
|
|
2674
|
+
i++;
|
|
2675
|
+
}
|
|
2676
|
+
if (depth !== 0) return null;
|
|
2677
|
+
const full = content.slice(multiMatch.index, i);
|
|
2678
|
+
const inner = content.slice(braceStart, i - 1);
|
|
2679
|
+
return {
|
|
2680
|
+
full,
|
|
2681
|
+
inner,
|
|
2682
|
+
startIndex: multiMatch.index,
|
|
2683
|
+
indent: multiMatch[1]
|
|
2684
|
+
};
|
|
2685
|
+
}
|
|
2686
|
+
function insertToolEntry(content, key, exportName) {
|
|
2687
|
+
const block = extractToolsBlock(content);
|
|
2688
|
+
if (!block) return { content, error: "no tools block found" };
|
|
2689
|
+
const entry = key === exportName ? key : `${key}: ${exportName}`;
|
|
2690
|
+
const trimmedInner = block.inner.trim();
|
|
2691
|
+
let newToolsContent;
|
|
2692
|
+
if (trimmedInner === "") {
|
|
2693
|
+
newToolsContent = `tools: { ${entry} }`;
|
|
2694
|
+
} else if (!block.inner.includes("\n")) {
|
|
2695
|
+
const cleaned = trimmedInner.replace(/,?\s*$/, "");
|
|
2696
|
+
newToolsContent = `tools: { ${cleaned}, ${entry} }`;
|
|
2697
|
+
} else {
|
|
2698
|
+
const entryIndent = block.indent + " ";
|
|
2699
|
+
const existingTrimmed = block.inner.trimEnd();
|
|
2700
|
+
const withComma = existingTrimmed.endsWith(",") ? existingTrimmed : existingTrimmed + ",";
|
|
2701
|
+
newToolsContent = `tools: {
|
|
2702
|
+
${withComma}
|
|
2703
|
+
${entryIndent}${entry},
|
|
2704
|
+
${block.indent}}`;
|
|
2705
|
+
}
|
|
2706
|
+
const newContent = content.replace(block.full, newToolsContent);
|
|
2707
|
+
return { content: newContent };
|
|
2708
|
+
}
|
|
2709
|
+
function removeToolEntry(content, key, exportName) {
|
|
2710
|
+
const block = extractToolsBlock(content);
|
|
2711
|
+
if (!block) return { content, error: "no tools block found" };
|
|
2712
|
+
const trimmedInner = block.inner.trim();
|
|
2713
|
+
if (!block.inner.includes("\n")) {
|
|
2714
|
+
const entries = trimmedInner.split(",").map((e) => e.trim()).filter((e) => e !== "");
|
|
2715
|
+
const filtered = entries.filter((e) => {
|
|
2716
|
+
const colonIdx = e.indexOf(":");
|
|
2717
|
+
const entryKey = colonIdx >= 0 ? e.slice(0, colonIdx).trim() : e.trim();
|
|
2718
|
+
return entryKey !== key;
|
|
2719
|
+
});
|
|
2720
|
+
const newInner = filtered.length === 0 ? "" : ` ${filtered.join(", ")} `;
|
|
2721
|
+
const newBlock = `tools: {${newInner}}`;
|
|
2722
|
+
return { content: content.replace(block.full, newBlock) };
|
|
2723
|
+
} else {
|
|
2724
|
+
const lines = block.inner.split("\n");
|
|
2725
|
+
const keyPattern = new RegExp(
|
|
2726
|
+
`^\\s*${escapeRegex(key)}\\s*(?::|,|$)`
|
|
2727
|
+
);
|
|
2728
|
+
const filtered = lines.filter((line) => !keyPattern.test(line));
|
|
2729
|
+
const hasEntries = filtered.some((l) => l.trim() !== "");
|
|
2730
|
+
if (!hasEntries) {
|
|
2731
|
+
const newBlock2 = `tools: {}`;
|
|
2732
|
+
return { content: content.replace(block.full, newBlock2) };
|
|
2733
|
+
}
|
|
2734
|
+
const cleanedLines = filtered.slice();
|
|
2735
|
+
for (let i = cleanedLines.length - 1; i >= 0; i--) {
|
|
2736
|
+
if (cleanedLines[i].trim() !== "") {
|
|
2737
|
+
if (!cleanedLines[i].trimEnd().endsWith(",")) {
|
|
2738
|
+
cleanedLines[i] = cleanedLines[i].trimEnd() + ",";
|
|
2739
|
+
}
|
|
2740
|
+
break;
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
const newBlock = `tools: {
|
|
2744
|
+
${cleanedLines.join("\n")}
|
|
2745
|
+
${block.indent}}`;
|
|
2746
|
+
return { content: content.replace(block.full, newBlock) };
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
function isExportNameReferenced(content, exportName) {
|
|
2750
|
+
const lines = content.split("\n");
|
|
2751
|
+
for (const line of lines) {
|
|
2752
|
+
if (/^import\s+/.test(line)) continue;
|
|
2753
|
+
const wordPattern = new RegExp(`\\b${escapeRegex(exportName)}\\b`);
|
|
2754
|
+
if (wordPattern.test(line)) return true;
|
|
2755
|
+
}
|
|
2756
|
+
return false;
|
|
2757
|
+
}
|
|
2758
|
+
function removeImportLine(content, exportName, importPath) {
|
|
2759
|
+
const lines = content.split("\n");
|
|
2760
|
+
const result = [];
|
|
2761
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2762
|
+
const line = lines[i];
|
|
2763
|
+
const singleImportMatch = line.match(
|
|
2764
|
+
/^import\s*\{([^}]+)\}\s*from\s*["'](.+?)["']\s*;?\s*$/
|
|
2765
|
+
);
|
|
2766
|
+
if (singleImportMatch && singleImportMatch[2] === importPath) {
|
|
2767
|
+
const imports = singleImportMatch[1].split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
2768
|
+
if (imports.length === 1 && imports[0] === exportName) {
|
|
2769
|
+
if (i + 1 < lines.length && lines[i + 1].trim() === "") {
|
|
2770
|
+
i++;
|
|
2771
|
+
}
|
|
2772
|
+
continue;
|
|
2773
|
+
}
|
|
2774
|
+
const remaining = imports.filter((s) => s !== exportName);
|
|
2775
|
+
result.push(
|
|
2776
|
+
`import { ${remaining.join(", ")} } from "${importPath}";`
|
|
2777
|
+
);
|
|
2778
|
+
continue;
|
|
2779
|
+
}
|
|
2780
|
+
if (/^import\s*\{/.test(line) && !line.includes("}") && content.includes(importPath)) {
|
|
2781
|
+
const importLines = [line];
|
|
2782
|
+
let j = i + 1;
|
|
2783
|
+
while (j < lines.length && !lines[j].includes("}")) {
|
|
2784
|
+
importLines.push(lines[j]);
|
|
2785
|
+
j++;
|
|
2786
|
+
}
|
|
2787
|
+
if (j < lines.length) {
|
|
2788
|
+
importLines.push(lines[j]);
|
|
2789
|
+
}
|
|
2790
|
+
const fullImport = importLines.join("\n");
|
|
2791
|
+
if (fullImport.includes(importPath)) {
|
|
2792
|
+
const namesMatch = fullImport.match(/\{([^}]+)\}/);
|
|
2793
|
+
if (namesMatch) {
|
|
2794
|
+
const imports = namesMatch[1].split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
2795
|
+
if (imports.length === 1 && imports[0] === exportName) {
|
|
2796
|
+
i = j;
|
|
2797
|
+
if (i + 1 < lines.length && lines[i + 1].trim() === "") {
|
|
2798
|
+
i++;
|
|
2799
|
+
}
|
|
2800
|
+
continue;
|
|
2801
|
+
}
|
|
2802
|
+
const remaining = imports.filter((s) => s !== exportName);
|
|
2803
|
+
if (remaining.length <= 2) {
|
|
2804
|
+
result.push(
|
|
2805
|
+
`import { ${remaining.join(", ")} } from "${importPath}";`
|
|
2806
|
+
);
|
|
2807
|
+
} else {
|
|
2808
|
+
result.push(`import {`);
|
|
2809
|
+
remaining.forEach((name, idx) => {
|
|
2810
|
+
result.push(
|
|
2811
|
+
` ${name}${idx < remaining.length - 1 ? "," : ""}`
|
|
2812
|
+
);
|
|
2813
|
+
});
|
|
2814
|
+
result.push(`} from "${importPath}";`);
|
|
2815
|
+
}
|
|
2816
|
+
i = j;
|
|
2817
|
+
continue;
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
result.push(line);
|
|
2822
|
+
}
|
|
2823
|
+
return result.join("\n");
|
|
2824
|
+
}
|
|
2825
|
+
function escapeRegex(s) {
|
|
2826
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2827
|
+
}
|
|
2828
|
+
var init_agent_linker = __esm({
|
|
2829
|
+
"src/installers/agent-linker.ts"() {
|
|
2830
|
+
"use strict";
|
|
2831
|
+
}
|
|
2832
|
+
});
|
|
2833
|
+
|
|
2834
|
+
// src/commands/link.ts
|
|
2835
|
+
var link_exports = {};
|
|
2836
|
+
__export(link_exports, {
|
|
2837
|
+
linkCommand: () => linkCommand
|
|
2838
|
+
});
|
|
2839
|
+
import * as p9 from "@clack/prompts";
|
|
2840
|
+
import pc8 from "picocolors";
|
|
2841
|
+
import { readFile as readFile11, writeFile as writeFile11 } from "fs/promises";
|
|
2842
|
+
import { basename } from "path";
|
|
2843
|
+
async function linkCommand(type, name, opts) {
|
|
2844
|
+
p9.intro(pc8.bgCyan(pc8.black(" kitn link ")));
|
|
2845
|
+
const cwd = process.cwd();
|
|
2846
|
+
const config = await readConfig(cwd);
|
|
2847
|
+
if (!config) {
|
|
2848
|
+
p9.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2849
|
+
process.exit(1);
|
|
2850
|
+
}
|
|
2851
|
+
if (type && type !== "tool") {
|
|
2852
|
+
p9.log.error(
|
|
2853
|
+
`Unsupported type "${type}". Only ${pc8.bold("tool")} is supported.`
|
|
2854
|
+
);
|
|
2855
|
+
process.exit(1);
|
|
2856
|
+
}
|
|
2857
|
+
let toolName = name;
|
|
2858
|
+
if (!toolName) {
|
|
2859
|
+
const tools = await listTools(config, cwd);
|
|
2860
|
+
if (tools.length === 0) {
|
|
2861
|
+
p9.log.error("No tools found in your project.");
|
|
2862
|
+
process.exit(1);
|
|
2863
|
+
}
|
|
2864
|
+
const selected = await p9.select({
|
|
2865
|
+
message: "Select a tool to link:",
|
|
2866
|
+
options: tools.map((t) => ({
|
|
2867
|
+
value: t.name,
|
|
2868
|
+
label: t.name
|
|
2869
|
+
}))
|
|
2870
|
+
});
|
|
2871
|
+
if (p9.isCancel(selected)) {
|
|
2872
|
+
p9.cancel("Cancelled.");
|
|
2873
|
+
process.exit(0);
|
|
2874
|
+
}
|
|
2875
|
+
toolName = selected;
|
|
2876
|
+
}
|
|
2877
|
+
const tool = await resolveToolByName(toolName, config, cwd);
|
|
2878
|
+
if (!tool) {
|
|
2879
|
+
p9.log.error(
|
|
2880
|
+
`Tool "${toolName}" not found. Check that the file exists in your tools directory.`
|
|
2881
|
+
);
|
|
2882
|
+
process.exit(1);
|
|
2883
|
+
}
|
|
2884
|
+
let agentName = opts?.to;
|
|
2885
|
+
if (!agentName) {
|
|
2886
|
+
const agents = await listAgents(config, cwd);
|
|
2887
|
+
if (agents.length === 0) {
|
|
2888
|
+
p9.log.error("No agents found in your project.");
|
|
2889
|
+
process.exit(1);
|
|
2890
|
+
}
|
|
2891
|
+
const selected = await p9.select({
|
|
2892
|
+
message: "Select an agent to link the tool to:",
|
|
2893
|
+
options: agents.map((a) => ({
|
|
2894
|
+
value: a.name,
|
|
2895
|
+
label: a.name
|
|
2896
|
+
}))
|
|
2897
|
+
});
|
|
2898
|
+
if (p9.isCancel(selected)) {
|
|
2899
|
+
p9.cancel("Cancelled.");
|
|
2900
|
+
process.exit(0);
|
|
2901
|
+
}
|
|
2902
|
+
agentName = selected;
|
|
2903
|
+
}
|
|
2904
|
+
const agent = await resolveAgentByName(agentName, config, cwd);
|
|
2905
|
+
if (!agent) {
|
|
2906
|
+
p9.log.error(
|
|
2907
|
+
`Agent "${agentName}" not found. Check that the file exists in your agents directory.`
|
|
2908
|
+
);
|
|
2909
|
+
process.exit(1);
|
|
2910
|
+
}
|
|
2911
|
+
const agentContent = await readFile11(agent.filePath, "utf-8");
|
|
2912
|
+
const toolRef = {
|
|
2913
|
+
exportName: tool.exportName,
|
|
2914
|
+
importPath: tool.importPath
|
|
2915
|
+
};
|
|
2916
|
+
const result = linkToolToAgent(agentContent, toolRef, opts?.as);
|
|
2917
|
+
if (result.error) {
|
|
2918
|
+
p9.log.warn(result.error);
|
|
2919
|
+
p9.outro("Could not auto-link. Follow the manual instructions above.");
|
|
2920
|
+
process.exit(1);
|
|
2921
|
+
}
|
|
2922
|
+
if (!result.changed) {
|
|
2923
|
+
p9.log.info(
|
|
2924
|
+
`${pc8.cyan(tool.exportName)} is already linked to ${pc8.cyan(basename(agent.filePath))}.`
|
|
2925
|
+
);
|
|
2926
|
+
p9.outro("Nothing to do.");
|
|
2927
|
+
return;
|
|
2928
|
+
}
|
|
2929
|
+
await writeFile11(agent.filePath, result.content);
|
|
2930
|
+
p9.log.success(
|
|
2931
|
+
`Linked ${pc8.cyan(tool.exportName)} to ${pc8.cyan(basename(agent.filePath))}`
|
|
2932
|
+
);
|
|
2933
|
+
p9.log.message(
|
|
2934
|
+
` ${pc8.green("+")} import { ${tool.exportName} } from "${tool.importPath}"`
|
|
2935
|
+
);
|
|
2936
|
+
p9.log.message(
|
|
2937
|
+
` ${pc8.green("+")} tools: { ${opts?.as ? `${opts.as}: ${tool.exportName}` : tool.exportName} }`
|
|
2938
|
+
);
|
|
2939
|
+
p9.outro("Done!");
|
|
2940
|
+
}
|
|
2941
|
+
var init_link = __esm({
|
|
2942
|
+
"src/commands/link.ts"() {
|
|
2943
|
+
"use strict";
|
|
2944
|
+
init_config();
|
|
2945
|
+
init_component_resolver();
|
|
2946
|
+
init_agent_linker();
|
|
2947
|
+
}
|
|
2948
|
+
});
|
|
2949
|
+
|
|
2950
|
+
// src/commands/unlink.ts
|
|
2951
|
+
var unlink_exports = {};
|
|
2952
|
+
__export(unlink_exports, {
|
|
2953
|
+
unlinkCommand: () => unlinkCommand
|
|
2954
|
+
});
|
|
2955
|
+
import * as p10 from "@clack/prompts";
|
|
2956
|
+
import pc9 from "picocolors";
|
|
2957
|
+
import { readFile as readFile12, writeFile as writeFile12 } from "fs/promises";
|
|
2958
|
+
import { basename as basename2 } from "path";
|
|
2959
|
+
async function unlinkCommand(type, name, opts) {
|
|
2960
|
+
p10.intro(pc9.bgCyan(pc9.black(" kitn unlink ")));
|
|
2961
|
+
const cwd = process.cwd();
|
|
2962
|
+
const config = await readConfig(cwd);
|
|
2963
|
+
if (!config) {
|
|
2964
|
+
p10.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2965
|
+
process.exit(1);
|
|
2966
|
+
}
|
|
2967
|
+
if (type && type !== "tool") {
|
|
2968
|
+
p10.log.error(
|
|
2969
|
+
`Unsupported type "${type}". Only ${pc9.bold("tool")} is supported.`
|
|
2970
|
+
);
|
|
2971
|
+
process.exit(1);
|
|
2972
|
+
}
|
|
2973
|
+
let toolName = name;
|
|
2974
|
+
if (!toolName) {
|
|
2975
|
+
const tools = await listTools(config, cwd);
|
|
2976
|
+
if (tools.length === 0) {
|
|
2977
|
+
p10.log.error("No tools found in your project.");
|
|
2978
|
+
process.exit(1);
|
|
2979
|
+
}
|
|
2980
|
+
const selected = await p10.select({
|
|
2981
|
+
message: "Select a tool to unlink:",
|
|
2982
|
+
options: tools.map((t) => ({
|
|
2983
|
+
value: t.name,
|
|
2984
|
+
label: t.name
|
|
2985
|
+
}))
|
|
2986
|
+
});
|
|
2987
|
+
if (p10.isCancel(selected)) {
|
|
2988
|
+
p10.cancel("Cancelled.");
|
|
2989
|
+
process.exit(0);
|
|
2990
|
+
}
|
|
2991
|
+
toolName = selected;
|
|
2992
|
+
}
|
|
2993
|
+
const tool = await resolveToolByName(toolName, config, cwd);
|
|
2994
|
+
if (!tool) {
|
|
2995
|
+
p10.log.error(
|
|
2996
|
+
`Tool "${toolName}" not found. Check that the file exists in your tools directory.`
|
|
2997
|
+
);
|
|
2998
|
+
process.exit(1);
|
|
2999
|
+
}
|
|
3000
|
+
let agentName = opts?.from;
|
|
3001
|
+
if (!agentName) {
|
|
3002
|
+
const agents = await listAgents(config, cwd);
|
|
3003
|
+
if (agents.length === 0) {
|
|
3004
|
+
p10.log.error("No agents found in your project.");
|
|
3005
|
+
process.exit(1);
|
|
3006
|
+
}
|
|
3007
|
+
const selected = await p10.select({
|
|
3008
|
+
message: "Select an agent to unlink the tool from:",
|
|
3009
|
+
options: agents.map((a) => ({
|
|
3010
|
+
value: a.name,
|
|
3011
|
+
label: a.name
|
|
3012
|
+
}))
|
|
3013
|
+
});
|
|
3014
|
+
if (p10.isCancel(selected)) {
|
|
3015
|
+
p10.cancel("Cancelled.");
|
|
3016
|
+
process.exit(0);
|
|
3017
|
+
}
|
|
3018
|
+
agentName = selected;
|
|
3019
|
+
}
|
|
3020
|
+
const agent = await resolveAgentByName(agentName, config, cwd);
|
|
3021
|
+
if (!agent) {
|
|
3022
|
+
p10.log.error(
|
|
3023
|
+
`Agent "${agentName}" not found. Check that the file exists in your agents directory.`
|
|
3024
|
+
);
|
|
3025
|
+
process.exit(1);
|
|
3026
|
+
}
|
|
3027
|
+
const agentContent = await readFile12(agent.filePath, "utf-8");
|
|
3028
|
+
const toolRef = {
|
|
3029
|
+
exportName: tool.exportName,
|
|
3030
|
+
importPath: tool.importPath
|
|
3031
|
+
};
|
|
3032
|
+
const result = unlinkToolFromAgent(agentContent, toolRef);
|
|
3033
|
+
if (result.error) {
|
|
3034
|
+
p10.log.warn(result.error);
|
|
3035
|
+
p10.outro("Could not auto-unlink. Follow the manual instructions above.");
|
|
3036
|
+
process.exit(1);
|
|
3037
|
+
}
|
|
3038
|
+
if (!result.changed) {
|
|
3039
|
+
p10.log.info(
|
|
3040
|
+
`${pc9.cyan(tool.exportName)} is not linked to ${pc9.cyan(basename2(agent.filePath))}.`
|
|
3041
|
+
);
|
|
3042
|
+
p10.outro("Nothing to do.");
|
|
3043
|
+
return;
|
|
3044
|
+
}
|
|
3045
|
+
await writeFile12(agent.filePath, result.content);
|
|
3046
|
+
p10.log.success(
|
|
3047
|
+
`Unlinked ${pc9.cyan(tool.exportName)} from ${pc9.cyan(basename2(agent.filePath))}`
|
|
3048
|
+
);
|
|
3049
|
+
p10.log.message(
|
|
3050
|
+
` ${pc9.red("-")} import { ${tool.exportName} } from "${tool.importPath}"`
|
|
3051
|
+
);
|
|
3052
|
+
p10.log.message(
|
|
3053
|
+
` ${pc9.red("-")} tools: { ${tool.exportName} }`
|
|
3054
|
+
);
|
|
3055
|
+
p10.outro("Done!");
|
|
3056
|
+
}
|
|
3057
|
+
var init_unlink = __esm({
|
|
3058
|
+
"src/commands/unlink.ts"() {
|
|
3059
|
+
"use strict";
|
|
3060
|
+
init_config();
|
|
3061
|
+
init_component_resolver();
|
|
3062
|
+
init_agent_linker();
|
|
3063
|
+
}
|
|
3064
|
+
});
|
|
3065
|
+
|
|
2219
3066
|
// src/commands/info.ts
|
|
2220
3067
|
var info_exports = {};
|
|
2221
3068
|
__export(info_exports, {
|
|
2222
3069
|
infoCommand: () => infoCommand
|
|
2223
3070
|
});
|
|
2224
|
-
import * as
|
|
2225
|
-
import
|
|
3071
|
+
import * as p11 from "@clack/prompts";
|
|
3072
|
+
import pc10 from "picocolors";
|
|
2226
3073
|
async function infoCommand(component) {
|
|
2227
3074
|
const cwd = process.cwd();
|
|
2228
3075
|
const config = await readConfig(cwd);
|
|
2229
3076
|
if (!config) {
|
|
2230
|
-
|
|
3077
|
+
p11.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2231
3078
|
process.exit(1);
|
|
2232
3079
|
}
|
|
2233
3080
|
const ref = parseComponentRef(component);
|
|
2234
3081
|
const fetcher = new RegistryFetcher(config.registries);
|
|
2235
|
-
const s =
|
|
3082
|
+
const s = p11.spinner();
|
|
2236
3083
|
s.start("Fetching component info...");
|
|
2237
3084
|
let index;
|
|
2238
3085
|
try {
|
|
2239
3086
|
index = await fetcher.fetchIndex(ref.namespace);
|
|
2240
3087
|
} catch (err) {
|
|
2241
|
-
s.stop(
|
|
2242
|
-
|
|
3088
|
+
s.stop(pc10.red("Failed to fetch registry"));
|
|
3089
|
+
p11.log.error(err.message);
|
|
2243
3090
|
process.exit(1);
|
|
2244
3091
|
}
|
|
2245
3092
|
const indexItem = index.items.find((i) => i.name === ref.name);
|
|
2246
3093
|
if (!indexItem) {
|
|
2247
|
-
s.stop(
|
|
2248
|
-
|
|
3094
|
+
s.stop(pc10.red("Component not found"));
|
|
3095
|
+
p11.log.error(`Component '${ref.name}' not found in registry.`);
|
|
2249
3096
|
process.exit(1);
|
|
2250
3097
|
}
|
|
2251
3098
|
const dir = typeToDir[indexItem.type];
|
|
@@ -2253,8 +3100,8 @@ async function infoCommand(component) {
|
|
|
2253
3100
|
try {
|
|
2254
3101
|
item = await fetcher.fetchItem(ref.name, dir, ref.namespace, ref.version);
|
|
2255
3102
|
} catch (err) {
|
|
2256
|
-
s.stop(
|
|
2257
|
-
|
|
3103
|
+
s.stop(pc10.red("Failed to fetch component"));
|
|
3104
|
+
p11.log.error(err.message);
|
|
2258
3105
|
process.exit(1);
|
|
2259
3106
|
}
|
|
2260
3107
|
s.stop("Component found");
|
|
@@ -2262,63 +3109,63 @@ async function infoCommand(component) {
|
|
|
2262
3109
|
const typeName = indexItem.type.replace("kitn:", "");
|
|
2263
3110
|
console.log();
|
|
2264
3111
|
console.log(
|
|
2265
|
-
` ${
|
|
3112
|
+
` ${pc10.bold(item.name)} ${pc10.cyan(`v${version}`)}${" ".repeat(Math.max(1, 40 - item.name.length - version.length - 2))}${pc10.dim(ref.namespace)}`
|
|
2266
3113
|
);
|
|
2267
|
-
console.log(` ${
|
|
3114
|
+
console.log(` ${pc10.dim(item.description)}`);
|
|
2268
3115
|
console.log();
|
|
2269
|
-
console.log(` ${
|
|
3116
|
+
console.log(` ${pc10.dim("Type:")} ${typeName}`);
|
|
2270
3117
|
if (item.dependencies?.length) {
|
|
2271
3118
|
console.log(
|
|
2272
|
-
` ${
|
|
3119
|
+
` ${pc10.dim("Dependencies:")} ${item.dependencies.join(", ")}`
|
|
2273
3120
|
);
|
|
2274
3121
|
}
|
|
2275
3122
|
if (item.registryDependencies?.length) {
|
|
2276
3123
|
console.log(
|
|
2277
|
-
` ${
|
|
3124
|
+
` ${pc10.dim("Registry deps:")} ${item.registryDependencies.join(", ")}`
|
|
2278
3125
|
);
|
|
2279
3126
|
}
|
|
2280
3127
|
if (item.categories?.length) {
|
|
2281
3128
|
console.log(
|
|
2282
|
-
` ${
|
|
3129
|
+
` ${pc10.dim("Categories:")} ${item.categories.join(", ")}`
|
|
2283
3130
|
);
|
|
2284
3131
|
}
|
|
2285
3132
|
if (item.updatedAt) {
|
|
2286
|
-
console.log(` ${
|
|
3133
|
+
console.log(` ${pc10.dim("Updated:")} ${item.updatedAt}`);
|
|
2287
3134
|
}
|
|
2288
3135
|
const versions = indexItem.versions;
|
|
2289
3136
|
if (versions?.length) {
|
|
2290
|
-
console.log(` ${
|
|
3137
|
+
console.log(` ${pc10.dim("Versions:")} ${versions.join(", ")}`);
|
|
2291
3138
|
}
|
|
2292
3139
|
if (item.changelog?.length) {
|
|
2293
3140
|
console.log();
|
|
2294
|
-
console.log(` ${
|
|
3141
|
+
console.log(` ${pc10.bold("Changelog:")}`);
|
|
2295
3142
|
for (const entry of item.changelog) {
|
|
2296
|
-
const tag = entry.type === "feature" ?
|
|
3143
|
+
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);
|
|
2297
3144
|
console.log(
|
|
2298
|
-
` ${
|
|
3145
|
+
` ${pc10.cyan(entry.version)} ${pc10.dim(entry.date)} ${tag} ${entry.note}`
|
|
2299
3146
|
);
|
|
2300
3147
|
}
|
|
2301
3148
|
}
|
|
2302
3149
|
console.log();
|
|
2303
3150
|
const fileCount = item.files.length;
|
|
2304
|
-
console.log(` ${
|
|
3151
|
+
console.log(` ${pc10.bold(`Files:`)} ${pc10.dim(`(${fileCount})`)}`);
|
|
2305
3152
|
const maxShown = 10;
|
|
2306
3153
|
for (const file of item.files.slice(0, maxShown)) {
|
|
2307
|
-
console.log(` ${
|
|
3154
|
+
console.log(` ${pc10.dim(file.path)}`);
|
|
2308
3155
|
}
|
|
2309
3156
|
if (fileCount > maxShown) {
|
|
2310
|
-
console.log(` ${
|
|
3157
|
+
console.log(` ${pc10.dim(`... and ${fileCount - maxShown} more`)}`);
|
|
2311
3158
|
}
|
|
2312
3159
|
const lock = await readLock(cwd);
|
|
2313
3160
|
const installed = lock[item.name];
|
|
2314
3161
|
if (installed) {
|
|
2315
3162
|
console.log();
|
|
2316
3163
|
console.log(
|
|
2317
|
-
` ${
|
|
3164
|
+
` ${pc10.green("Installed")} ${pc10.dim(`v${installed.version}`)}`
|
|
2318
3165
|
);
|
|
2319
3166
|
if (version !== installed.version) {
|
|
2320
3167
|
console.log(
|
|
2321
|
-
` ${
|
|
3168
|
+
` ${pc10.yellow("Update available:")} ${pc10.dim(`v${installed.version}`)} \u2192 ${pc10.cyan(`v${version}`)}`
|
|
2322
3169
|
);
|
|
2323
3170
|
}
|
|
2324
3171
|
}
|
|
@@ -2340,41 +3187,41 @@ __export(check_exports, {
|
|
|
2340
3187
|
checkCommand: () => checkCommand
|
|
2341
3188
|
});
|
|
2342
3189
|
import { execSync as execSync2 } from "child_process";
|
|
2343
|
-
import * as
|
|
2344
|
-
import
|
|
3190
|
+
import * as p12 from "@clack/prompts";
|
|
3191
|
+
import pc11 from "picocolors";
|
|
2345
3192
|
async function checkCommand(currentVersion) {
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
const s =
|
|
3193
|
+
p12.intro(pc11.bgCyan(pc11.black(" kitn check ")));
|
|
3194
|
+
p12.log.info(`kitn v${currentVersion}`);
|
|
3195
|
+
const s = p12.spinner();
|
|
2349
3196
|
s.start("Checking for updates...");
|
|
2350
3197
|
const latest = await fetchLatestVersion();
|
|
2351
3198
|
if (!latest) {
|
|
2352
|
-
s.stop(
|
|
2353
|
-
|
|
3199
|
+
s.stop(pc11.yellow("Could not reach the npm registry"));
|
|
3200
|
+
p12.outro("Try again later.");
|
|
2354
3201
|
return;
|
|
2355
3202
|
}
|
|
2356
3203
|
if (isNewer(latest, currentVersion)) {
|
|
2357
|
-
s.stop(
|
|
3204
|
+
s.stop(pc11.yellow(`Update available: ${currentVersion} \u2192 ${latest}`));
|
|
2358
3205
|
const pm = detectCliInstaller();
|
|
2359
3206
|
const installCmd = getGlobalInstallCommand(pm, "@kitnai/cli");
|
|
2360
|
-
const shouldUpdate = await
|
|
2361
|
-
if (
|
|
2362
|
-
|
|
3207
|
+
const shouldUpdate = await p12.confirm({ message: "Update now?" });
|
|
3208
|
+
if (p12.isCancel(shouldUpdate) || !shouldUpdate) {
|
|
3209
|
+
p12.log.message(` Run: ${pc11.cyan(installCmd)}`);
|
|
2363
3210
|
} else {
|
|
2364
|
-
const us =
|
|
3211
|
+
const us = p12.spinner();
|
|
2365
3212
|
us.start("Updating...");
|
|
2366
3213
|
try {
|
|
2367
3214
|
execSync2(installCmd, { stdio: "pipe" });
|
|
2368
|
-
us.stop(
|
|
3215
|
+
us.stop(pc11.green(`Updated to v${latest}`));
|
|
2369
3216
|
} catch {
|
|
2370
|
-
us.stop(
|
|
2371
|
-
|
|
3217
|
+
us.stop(pc11.red("Update failed"));
|
|
3218
|
+
p12.log.message(` Run manually: ${pc11.cyan(installCmd)}`);
|
|
2372
3219
|
}
|
|
2373
3220
|
}
|
|
2374
3221
|
} else {
|
|
2375
|
-
s.stop(
|
|
3222
|
+
s.stop(pc11.green("You're on the latest version"));
|
|
2376
3223
|
}
|
|
2377
|
-
|
|
3224
|
+
p12.outro("");
|
|
2378
3225
|
}
|
|
2379
3226
|
var init_check = __esm({
|
|
2380
3227
|
"src/commands/check.ts"() {
|
|
@@ -2384,6 +3231,64 @@ var init_check = __esm({
|
|
|
2384
3231
|
}
|
|
2385
3232
|
});
|
|
2386
3233
|
|
|
3234
|
+
// src/commands/rules.ts
|
|
3235
|
+
var rules_exports = {};
|
|
3236
|
+
__export(rules_exports, {
|
|
3237
|
+
rulesCommand: () => rulesCommand
|
|
3238
|
+
});
|
|
3239
|
+
import * as p13 from "@clack/prompts";
|
|
3240
|
+
import pc12 from "picocolors";
|
|
3241
|
+
async function rulesCommand() {
|
|
3242
|
+
p13.intro(pc12.bgCyan(pc12.black(" kitn rules ")));
|
|
3243
|
+
const cwd = process.cwd();
|
|
3244
|
+
const config = await readConfig(cwd);
|
|
3245
|
+
if (!config) {
|
|
3246
|
+
p13.log.error(`No kitn.json found. Run ${pc12.bold("kitn init")} first.`);
|
|
3247
|
+
process.exit(1);
|
|
3248
|
+
}
|
|
3249
|
+
let selectedIds = config.aiTools;
|
|
3250
|
+
if (!selectedIds || selectedIds.length === 0) {
|
|
3251
|
+
const rulesConfig = await fetchRulesConfig(config.registries);
|
|
3252
|
+
const selected = await p13.multiselect({
|
|
3253
|
+
message: "Which AI coding tools do you use?",
|
|
3254
|
+
options: rulesConfig.tools.map((t) => ({
|
|
3255
|
+
value: t.id,
|
|
3256
|
+
label: t.name,
|
|
3257
|
+
hint: t.description
|
|
3258
|
+
})),
|
|
3259
|
+
required: false
|
|
3260
|
+
});
|
|
3261
|
+
if (p13.isCancel(selected)) {
|
|
3262
|
+
p13.cancel("Cancelled.");
|
|
3263
|
+
process.exit(0);
|
|
3264
|
+
}
|
|
3265
|
+
selectedIds = selected;
|
|
3266
|
+
if (selectedIds.length === 0) {
|
|
3267
|
+
p13.log.warn("No tools selected. Nothing to generate.");
|
|
3268
|
+
p13.outro("Done.");
|
|
3269
|
+
return;
|
|
3270
|
+
}
|
|
3271
|
+
const updatedConfig = { ...config, aiTools: selectedIds };
|
|
3272
|
+
await writeConfig(cwd, updatedConfig);
|
|
3273
|
+
p13.log.info(`Saved tool selections to ${pc12.bold("kitn.json")}`);
|
|
3274
|
+
}
|
|
3275
|
+
const s = p13.spinner();
|
|
3276
|
+
s.start("Generating rules files");
|
|
3277
|
+
const written = await generateRulesFiles(cwd, config, selectedIds);
|
|
3278
|
+
s.stop("Rules files generated");
|
|
3279
|
+
for (const filePath of written) {
|
|
3280
|
+
p13.log.success(`${pc12.green("+")} ${filePath}`);
|
|
3281
|
+
}
|
|
3282
|
+
p13.outro(`Generated ${written.length} rules file${written.length === 1 ? "" : "s"}.`);
|
|
3283
|
+
}
|
|
3284
|
+
var init_rules = __esm({
|
|
3285
|
+
"src/commands/rules.ts"() {
|
|
3286
|
+
"use strict";
|
|
3287
|
+
init_config();
|
|
3288
|
+
init_rules_generator();
|
|
3289
|
+
}
|
|
3290
|
+
});
|
|
3291
|
+
|
|
2387
3292
|
// src/commands/registry.ts
|
|
2388
3293
|
var registry_exports = {};
|
|
2389
3294
|
__export(registry_exports, {
|
|
@@ -2391,8 +3296,8 @@ __export(registry_exports, {
|
|
|
2391
3296
|
registryListCommand: () => registryListCommand,
|
|
2392
3297
|
registryRemoveCommand: () => registryRemoveCommand
|
|
2393
3298
|
});
|
|
2394
|
-
import * as
|
|
2395
|
-
import
|
|
3299
|
+
import * as p14 from "@clack/prompts";
|
|
3300
|
+
import pc13 from "picocolors";
|
|
2396
3301
|
async function registryAddCommand(namespace, url, opts = {}) {
|
|
2397
3302
|
const cwd = opts.cwd ?? process.cwd();
|
|
2398
3303
|
const config = await readConfig(cwd);
|
|
@@ -2418,10 +3323,10 @@ async function registryAddCommand(namespace, url, opts = {}) {
|
|
|
2418
3323
|
config.registries[namespace] = url;
|
|
2419
3324
|
}
|
|
2420
3325
|
await writeConfig(cwd, config);
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
if (opts.homepage)
|
|
2424
|
-
if (opts.description)
|
|
3326
|
+
p14.log.success(`Added registry ${pc13.bold(namespace)}`);
|
|
3327
|
+
p14.log.message(pc13.dim(` ${url}`));
|
|
3328
|
+
if (opts.homepage) p14.log.message(pc13.dim(` Homepage: ${opts.homepage}`));
|
|
3329
|
+
if (opts.description) p14.log.message(pc13.dim(` ${opts.description}`));
|
|
2425
3330
|
}
|
|
2426
3331
|
async function registryRemoveCommand(namespace, opts = {}) {
|
|
2427
3332
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -2442,10 +3347,10 @@ async function registryRemoveCommand(namespace, opts = {}) {
|
|
|
2442
3347
|
}
|
|
2443
3348
|
delete config.registries[namespace];
|
|
2444
3349
|
await writeConfig(cwd, config);
|
|
2445
|
-
|
|
3350
|
+
p14.log.success(`Removed registry ${pc13.bold(namespace)}`);
|
|
2446
3351
|
if (affectedComponents.length > 0) {
|
|
2447
|
-
|
|
2448
|
-
` + affectedComponents.map((name) => ` ${
|
|
3352
|
+
p14.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:
|
|
3353
|
+
` + affectedComponents.map((name) => ` ${pc13.yellow("!")} ${name}`).join("\n"));
|
|
2449
3354
|
}
|
|
2450
3355
|
return { affectedComponents };
|
|
2451
3356
|
}
|
|
@@ -2460,15 +3365,15 @@ async function registryListCommand(opts = {}) {
|
|
|
2460
3365
|
return { namespace, url, homepage, description };
|
|
2461
3366
|
});
|
|
2462
3367
|
if (entries.length === 0) {
|
|
2463
|
-
|
|
3368
|
+
p14.log.message(pc13.dim(" No registries configured."));
|
|
2464
3369
|
} else {
|
|
2465
3370
|
const lines = [];
|
|
2466
3371
|
for (const { namespace, url, homepage, description } of entries) {
|
|
2467
|
-
lines.push(` ${
|
|
3372
|
+
lines.push(` ${pc13.bold(namespace.padEnd(16))} ${pc13.dim(url)}`);
|
|
2468
3373
|
if (description) lines.push(` ${" ".repeat(16)} ${description}`);
|
|
2469
|
-
if (homepage) lines.push(` ${" ".repeat(16)} ${
|
|
3374
|
+
if (homepage) lines.push(` ${" ".repeat(16)} ${pc13.dim(homepage)}`);
|
|
2470
3375
|
}
|
|
2471
|
-
|
|
3376
|
+
p14.log.message(lines.join("\n"));
|
|
2472
3377
|
}
|
|
2473
3378
|
return entries;
|
|
2474
3379
|
}
|
|
@@ -2482,7 +3387,7 @@ var init_registry = __esm({
|
|
|
2482
3387
|
// src/index.ts
|
|
2483
3388
|
init_update_check();
|
|
2484
3389
|
import { Command } from "commander";
|
|
2485
|
-
var VERSION = true ? "0.1.
|
|
3390
|
+
var VERSION = true ? "0.1.32" : "0.0.0-dev";
|
|
2486
3391
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
2487
3392
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
2488
3393
|
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) => {
|
|
@@ -2513,6 +3418,14 @@ program.command("create").description("Scaffold a new kitn component").argument(
|
|
|
2513
3418
|
const { createCommand: createCommand2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
2514
3419
|
await createCommand2(type, name);
|
|
2515
3420
|
});
|
|
3421
|
+
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) => {
|
|
3422
|
+
const { linkCommand: linkCommand2 } = await Promise.resolve().then(() => (init_link(), link_exports));
|
|
3423
|
+
await linkCommand2(type, name, opts);
|
|
3424
|
+
});
|
|
3425
|
+
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) => {
|
|
3426
|
+
const { unlinkCommand: unlinkCommand2 } = await Promise.resolve().then(() => (init_unlink(), unlink_exports));
|
|
3427
|
+
await unlinkCommand2(type, name, opts);
|
|
3428
|
+
});
|
|
2516
3429
|
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) => {
|
|
2517
3430
|
const { infoCommand: infoCommand2 } = await Promise.resolve().then(() => (init_info(), info_exports));
|
|
2518
3431
|
await infoCommand2(component);
|
|
@@ -2521,6 +3434,10 @@ program.command("check").description("Check for CLI updates").action(async () =>
|
|
|
2521
3434
|
const { checkCommand: checkCommand2 } = await Promise.resolve().then(() => (init_check(), check_exports));
|
|
2522
3435
|
await checkCommand2(VERSION);
|
|
2523
3436
|
});
|
|
3437
|
+
program.command("rules").description("Regenerate AI coding tool rules files").action(async () => {
|
|
3438
|
+
const { rulesCommand: rulesCommand2 } = await Promise.resolve().then(() => (init_rules(), rules_exports));
|
|
3439
|
+
await rulesCommand2();
|
|
3440
|
+
});
|
|
2524
3441
|
var registry = program.command("registry").description("Manage component registries");
|
|
2525
3442
|
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) => {
|
|
2526
3443
|
const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|