@kitnai/cli 0.1.30 → 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 +968 -92
- 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,8 @@ __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, writeFile as writeFile8 } from "fs/promises";
|
|
1572
|
+
import { join as join9 } from "path";
|
|
1433
1573
|
function getPluginTemplate(framework) {
|
|
1434
1574
|
const adapterName = framework === "hono-openapi" ? "hono-openapi" : framework;
|
|
1435
1575
|
return `import { createAIPlugin } from "@kitn/adapters/${adapterName}";
|
|
@@ -1567,13 +1707,45 @@ async function initCommand(opts = {}) {
|
|
|
1567
1707
|
p3.log.info(`Patched tsconfig.json with path: ${pc4.bold("@kitn/*")}`);
|
|
1568
1708
|
p3.log.info("Installing core engine and adapter...");
|
|
1569
1709
|
await addCommand(["core", "routes"], { overwrite: true });
|
|
1570
|
-
const aiDir =
|
|
1571
|
-
await
|
|
1572
|
-
const barrelPath =
|
|
1573
|
-
await
|
|
1574
|
-
const pluginPath =
|
|
1575
|
-
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));
|
|
1576
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
|
+
}
|
|
1577
1749
|
const mountCode = framework === "elysia" ? `app.use(new Elysia({ prefix: "/api" }).use(ai.router));` : `app.route("/api", ai.router);`;
|
|
1578
1750
|
p3.note(
|
|
1579
1751
|
[
|
|
@@ -1601,6 +1773,7 @@ var init_init = __esm({
|
|
|
1601
1773
|
init_tsconfig_patcher();
|
|
1602
1774
|
init_barrel_manager();
|
|
1603
1775
|
init_add();
|
|
1776
|
+
init_rules_generator();
|
|
1604
1777
|
}
|
|
1605
1778
|
});
|
|
1606
1779
|
|
|
@@ -1736,7 +1909,7 @@ __export(diff_exports, {
|
|
|
1736
1909
|
diffCommand: () => diffCommand
|
|
1737
1910
|
});
|
|
1738
1911
|
import * as p5 from "@clack/prompts";
|
|
1739
|
-
import { join as
|
|
1912
|
+
import { join as join10 } from "path";
|
|
1740
1913
|
async function diffCommand(componentName) {
|
|
1741
1914
|
const cwd = process.cwd();
|
|
1742
1915
|
const config = await readConfig(cwd);
|
|
@@ -1767,8 +1940,8 @@ async function diffCommand(componentName) {
|
|
|
1767
1940
|
for (const file of registryItem.files) {
|
|
1768
1941
|
if (indexItem.type === "kitn:package") {
|
|
1769
1942
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1770
|
-
const localPath =
|
|
1771
|
-
const relativePath =
|
|
1943
|
+
const localPath = join10(cwd, baseDir, file.path);
|
|
1944
|
+
const relativePath = join10(baseDir, file.path);
|
|
1772
1945
|
const localContent = await readExistingFile(localPath);
|
|
1773
1946
|
if (localContent === null) {
|
|
1774
1947
|
p5.log.warn(`${relativePath}: file missing locally`);
|
|
@@ -1792,7 +1965,7 @@ async function diffCommand(componentName) {
|
|
|
1792
1965
|
return "storage";
|
|
1793
1966
|
}
|
|
1794
1967
|
})();
|
|
1795
|
-
const localPath =
|
|
1968
|
+
const localPath = join10(cwd, config.aliases[aliasKey], fileName);
|
|
1796
1969
|
const localContent = await readExistingFile(localPath);
|
|
1797
1970
|
if (localContent === null) {
|
|
1798
1971
|
p5.log.warn(`${fileName}: file missing locally`);
|
|
@@ -1826,8 +1999,8 @@ __export(remove_exports, {
|
|
|
1826
1999
|
});
|
|
1827
2000
|
import * as p6 from "@clack/prompts";
|
|
1828
2001
|
import pc6 from "picocolors";
|
|
1829
|
-
import { join as
|
|
1830
|
-
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";
|
|
1831
2004
|
import { existsSync as existsSync2 } from "fs";
|
|
1832
2005
|
async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
1833
2006
|
const entry = lock[installedKey];
|
|
@@ -1835,15 +2008,15 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
1835
2008
|
const deleted = [];
|
|
1836
2009
|
for (const filePath of entry.files) {
|
|
1837
2010
|
try {
|
|
1838
|
-
await unlink3(
|
|
2011
|
+
await unlink3(join11(cwd, filePath));
|
|
1839
2012
|
deleted.push(filePath);
|
|
1840
2013
|
} catch {
|
|
1841
2014
|
p6.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
1842
2015
|
}
|
|
1843
2016
|
}
|
|
1844
2017
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1845
|
-
const barrelPath =
|
|
1846
|
-
const barrelDir =
|
|
2018
|
+
const barrelPath = join11(cwd, baseDir, "index.ts");
|
|
2019
|
+
const barrelDir = join11(cwd, baseDir);
|
|
1847
2020
|
const barrelEligibleDirs = /* @__PURE__ */ new Set([
|
|
1848
2021
|
config.aliases.agents,
|
|
1849
2022
|
config.aliases.tools,
|
|
@@ -1853,9 +2026,9 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
1853
2026
|
let barrelContent = await readFile7(barrelPath, "utf-8");
|
|
1854
2027
|
let barrelChanged = false;
|
|
1855
2028
|
for (const filePath of deleted) {
|
|
1856
|
-
const fileDir =
|
|
2029
|
+
const fileDir = dirname5(filePath);
|
|
1857
2030
|
if (!barrelEligibleDirs.has(fileDir)) continue;
|
|
1858
|
-
const importPath = "./" + relative3(barrelDir,
|
|
2031
|
+
const importPath = "./" + relative3(barrelDir, join11(cwd, filePath)).replace(/\\/g, "/");
|
|
1859
2032
|
const updated = removeImportFromBarrel(barrelContent, importPath);
|
|
1860
2033
|
if (updated !== barrelContent) {
|
|
1861
2034
|
barrelContent = updated;
|
|
@@ -1863,8 +2036,8 @@ async function removeSingleComponent(installedKey, lock, config, cwd) {
|
|
|
1863
2036
|
}
|
|
1864
2037
|
}
|
|
1865
2038
|
if (barrelChanged) {
|
|
1866
|
-
await
|
|
1867
|
-
p6.log.info(`Updated barrel file: ${
|
|
2039
|
+
await writeFile9(barrelPath, barrelContent);
|
|
2040
|
+
p6.log.info(`Updated barrel file: ${join11(baseDir, "index.ts")}`);
|
|
1868
2041
|
}
|
|
1869
2042
|
}
|
|
1870
2043
|
delete lock[installedKey];
|
|
@@ -2012,6 +2185,19 @@ var init_update = __esm({
|
|
|
2012
2185
|
}
|
|
2013
2186
|
});
|
|
2014
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
|
+
|
|
2015
2201
|
// src/commands/create.ts
|
|
2016
2202
|
var create_exports = {};
|
|
2017
2203
|
__export(create_exports, {
|
|
@@ -2020,15 +2206,9 @@ __export(create_exports, {
|
|
|
2020
2206
|
});
|
|
2021
2207
|
import * as p8 from "@clack/prompts";
|
|
2022
2208
|
import pc7 from "picocolors";
|
|
2023
|
-
import { join as
|
|
2209
|
+
import { join as join12, relative as relative4 } from "path";
|
|
2024
2210
|
import { existsSync as existsSync3 } from "fs";
|
|
2025
|
-
import { readFile as readFile8, writeFile 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
|
-
}
|
|
2211
|
+
import { readFile as readFile8, writeFile as writeFile10, mkdir as mkdir6 } from "fs/promises";
|
|
2032
2212
|
function generateAgentSource(name) {
|
|
2033
2213
|
const camel = toCamelCase(name);
|
|
2034
2214
|
return `import { registerAgent } from "@kitn/core";
|
|
@@ -2135,7 +2315,7 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2135
2315
|
const validType = type;
|
|
2136
2316
|
const kitnType = typeToKitnType[validType];
|
|
2137
2317
|
const fileName = validType === "skill" ? `${name}.md` : `${name}.ts`;
|
|
2138
|
-
const filePath =
|
|
2318
|
+
const filePath = join12(cwd, getInstallPath(config, kitnType, fileName));
|
|
2139
2319
|
const dummyContent = "";
|
|
2140
2320
|
const status = await checkFileStatus(filePath, dummyContent);
|
|
2141
2321
|
if (status !== "new" /* New */) {
|
|
@@ -2163,18 +2343,18 @@ async function createComponentInProject(type, name, opts) {
|
|
|
2163
2343
|
let barrelUpdated = false;
|
|
2164
2344
|
if (BARREL_TYPES.includes(validType)) {
|
|
2165
2345
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
2166
|
-
const barrelPath =
|
|
2346
|
+
const barrelPath = join12(cwd, baseDir, "index.ts");
|
|
2167
2347
|
let barrelContent;
|
|
2168
2348
|
if (existsSync3(barrelPath)) {
|
|
2169
2349
|
barrelContent = await readFile8(barrelPath, "utf-8");
|
|
2170
2350
|
} else {
|
|
2171
2351
|
barrelContent = createBarrelFile();
|
|
2172
|
-
await
|
|
2352
|
+
await mkdir6(join12(cwd, baseDir), { recursive: true });
|
|
2173
2353
|
}
|
|
2174
|
-
const importPath = "./" + relative4(
|
|
2354
|
+
const importPath = "./" + relative4(join12(cwd, baseDir), filePath).replace(/\.ts$/, ".js");
|
|
2175
2355
|
const updatedBarrel = addImportToBarrel(barrelContent, importPath);
|
|
2176
2356
|
if (updatedBarrel !== barrelContent) {
|
|
2177
|
-
await
|
|
2357
|
+
await writeFile10(barrelPath, updatedBarrel);
|
|
2178
2358
|
barrelUpdated = true;
|
|
2179
2359
|
}
|
|
2180
2360
|
}
|
|
@@ -2202,6 +2382,7 @@ var init_create = __esm({
|
|
|
2202
2382
|
"src/commands/create.ts"() {
|
|
2203
2383
|
"use strict";
|
|
2204
2384
|
init_config();
|
|
2385
|
+
init_naming();
|
|
2205
2386
|
init_file_writer();
|
|
2206
2387
|
init_barrel_manager();
|
|
2207
2388
|
VALID_TYPES = ["agent", "tool", "skill", "storage", "cron"];
|
|
@@ -2216,36 +2397,661 @@ var init_create = __esm({
|
|
|
2216
2397
|
}
|
|
2217
2398
|
});
|
|
2218
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
|
+
|
|
2219
3025
|
// src/commands/info.ts
|
|
2220
3026
|
var info_exports = {};
|
|
2221
3027
|
__export(info_exports, {
|
|
2222
3028
|
infoCommand: () => infoCommand
|
|
2223
3029
|
});
|
|
2224
|
-
import * as
|
|
2225
|
-
import
|
|
3030
|
+
import * as p11 from "@clack/prompts";
|
|
3031
|
+
import pc10 from "picocolors";
|
|
2226
3032
|
async function infoCommand(component) {
|
|
2227
3033
|
const cwd = process.cwd();
|
|
2228
3034
|
const config = await readConfig(cwd);
|
|
2229
3035
|
if (!config) {
|
|
2230
|
-
|
|
3036
|
+
p11.log.error("No kitn.json found. Run `kitn init` first.");
|
|
2231
3037
|
process.exit(1);
|
|
2232
3038
|
}
|
|
2233
3039
|
const ref = parseComponentRef(component);
|
|
2234
3040
|
const fetcher = new RegistryFetcher(config.registries);
|
|
2235
|
-
const s =
|
|
3041
|
+
const s = p11.spinner();
|
|
2236
3042
|
s.start("Fetching component info...");
|
|
2237
3043
|
let index;
|
|
2238
3044
|
try {
|
|
2239
3045
|
index = await fetcher.fetchIndex(ref.namespace);
|
|
2240
3046
|
} catch (err) {
|
|
2241
|
-
s.stop(
|
|
2242
|
-
|
|
3047
|
+
s.stop(pc10.red("Failed to fetch registry"));
|
|
3048
|
+
p11.log.error(err.message);
|
|
2243
3049
|
process.exit(1);
|
|
2244
3050
|
}
|
|
2245
3051
|
const indexItem = index.items.find((i) => i.name === ref.name);
|
|
2246
3052
|
if (!indexItem) {
|
|
2247
|
-
s.stop(
|
|
2248
|
-
|
|
3053
|
+
s.stop(pc10.red("Component not found"));
|
|
3054
|
+
p11.log.error(`Component '${ref.name}' not found in registry.`);
|
|
2249
3055
|
process.exit(1);
|
|
2250
3056
|
}
|
|
2251
3057
|
const dir = typeToDir[indexItem.type];
|
|
@@ -2253,8 +3059,8 @@ async function infoCommand(component) {
|
|
|
2253
3059
|
try {
|
|
2254
3060
|
item = await fetcher.fetchItem(ref.name, dir, ref.namespace, ref.version);
|
|
2255
3061
|
} catch (err) {
|
|
2256
|
-
s.stop(
|
|
2257
|
-
|
|
3062
|
+
s.stop(pc10.red("Failed to fetch component"));
|
|
3063
|
+
p11.log.error(err.message);
|
|
2258
3064
|
process.exit(1);
|
|
2259
3065
|
}
|
|
2260
3066
|
s.stop("Component found");
|
|
@@ -2262,63 +3068,63 @@ async function infoCommand(component) {
|
|
|
2262
3068
|
const typeName = indexItem.type.replace("kitn:", "");
|
|
2263
3069
|
console.log();
|
|
2264
3070
|
console.log(
|
|
2265
|
-
` ${
|
|
3071
|
+
` ${pc10.bold(item.name)} ${pc10.cyan(`v${version}`)}${" ".repeat(Math.max(1, 40 - item.name.length - version.length - 2))}${pc10.dim(ref.namespace)}`
|
|
2266
3072
|
);
|
|
2267
|
-
console.log(` ${
|
|
3073
|
+
console.log(` ${pc10.dim(item.description)}`);
|
|
2268
3074
|
console.log();
|
|
2269
|
-
console.log(` ${
|
|
3075
|
+
console.log(` ${pc10.dim("Type:")} ${typeName}`);
|
|
2270
3076
|
if (item.dependencies?.length) {
|
|
2271
3077
|
console.log(
|
|
2272
|
-
` ${
|
|
3078
|
+
` ${pc10.dim("Dependencies:")} ${item.dependencies.join(", ")}`
|
|
2273
3079
|
);
|
|
2274
3080
|
}
|
|
2275
3081
|
if (item.registryDependencies?.length) {
|
|
2276
3082
|
console.log(
|
|
2277
|
-
` ${
|
|
3083
|
+
` ${pc10.dim("Registry deps:")} ${item.registryDependencies.join(", ")}`
|
|
2278
3084
|
);
|
|
2279
3085
|
}
|
|
2280
3086
|
if (item.categories?.length) {
|
|
2281
3087
|
console.log(
|
|
2282
|
-
` ${
|
|
3088
|
+
` ${pc10.dim("Categories:")} ${item.categories.join(", ")}`
|
|
2283
3089
|
);
|
|
2284
3090
|
}
|
|
2285
3091
|
if (item.updatedAt) {
|
|
2286
|
-
console.log(` ${
|
|
3092
|
+
console.log(` ${pc10.dim("Updated:")} ${item.updatedAt}`);
|
|
2287
3093
|
}
|
|
2288
3094
|
const versions = indexItem.versions;
|
|
2289
3095
|
if (versions?.length) {
|
|
2290
|
-
console.log(` ${
|
|
3096
|
+
console.log(` ${pc10.dim("Versions:")} ${versions.join(", ")}`);
|
|
2291
3097
|
}
|
|
2292
3098
|
if (item.changelog?.length) {
|
|
2293
3099
|
console.log();
|
|
2294
|
-
console.log(` ${
|
|
3100
|
+
console.log(` ${pc10.bold("Changelog:")}`);
|
|
2295
3101
|
for (const entry of item.changelog) {
|
|
2296
|
-
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);
|
|
2297
3103
|
console.log(
|
|
2298
|
-
` ${
|
|
3104
|
+
` ${pc10.cyan(entry.version)} ${pc10.dim(entry.date)} ${tag} ${entry.note}`
|
|
2299
3105
|
);
|
|
2300
3106
|
}
|
|
2301
3107
|
}
|
|
2302
3108
|
console.log();
|
|
2303
3109
|
const fileCount = item.files.length;
|
|
2304
|
-
console.log(` ${
|
|
3110
|
+
console.log(` ${pc10.bold(`Files:`)} ${pc10.dim(`(${fileCount})`)}`);
|
|
2305
3111
|
const maxShown = 10;
|
|
2306
3112
|
for (const file of item.files.slice(0, maxShown)) {
|
|
2307
|
-
console.log(` ${
|
|
3113
|
+
console.log(` ${pc10.dim(file.path)}`);
|
|
2308
3114
|
}
|
|
2309
3115
|
if (fileCount > maxShown) {
|
|
2310
|
-
console.log(` ${
|
|
3116
|
+
console.log(` ${pc10.dim(`... and ${fileCount - maxShown} more`)}`);
|
|
2311
3117
|
}
|
|
2312
3118
|
const lock = await readLock(cwd);
|
|
2313
3119
|
const installed = lock[item.name];
|
|
2314
3120
|
if (installed) {
|
|
2315
3121
|
console.log();
|
|
2316
3122
|
console.log(
|
|
2317
|
-
` ${
|
|
3123
|
+
` ${pc10.green("Installed")} ${pc10.dim(`v${installed.version}`)}`
|
|
2318
3124
|
);
|
|
2319
3125
|
if (version !== installed.version) {
|
|
2320
3126
|
console.log(
|
|
2321
|
-
` ${
|
|
3127
|
+
` ${pc10.yellow("Update available:")} ${pc10.dim(`v${installed.version}`)} \u2192 ${pc10.cyan(`v${version}`)}`
|
|
2322
3128
|
);
|
|
2323
3129
|
}
|
|
2324
3130
|
}
|
|
@@ -2340,41 +3146,41 @@ __export(check_exports, {
|
|
|
2340
3146
|
checkCommand: () => checkCommand
|
|
2341
3147
|
});
|
|
2342
3148
|
import { execSync as execSync2 } from "child_process";
|
|
2343
|
-
import * as
|
|
2344
|
-
import
|
|
3149
|
+
import * as p12 from "@clack/prompts";
|
|
3150
|
+
import pc11 from "picocolors";
|
|
2345
3151
|
async function checkCommand(currentVersion) {
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
const s =
|
|
3152
|
+
p12.intro(pc11.bgCyan(pc11.black(" kitn check ")));
|
|
3153
|
+
p12.log.info(`kitn v${currentVersion}`);
|
|
3154
|
+
const s = p12.spinner();
|
|
2349
3155
|
s.start("Checking for updates...");
|
|
2350
3156
|
const latest = await fetchLatestVersion();
|
|
2351
3157
|
if (!latest) {
|
|
2352
|
-
s.stop(
|
|
2353
|
-
|
|
3158
|
+
s.stop(pc11.yellow("Could not reach the npm registry"));
|
|
3159
|
+
p12.outro("Try again later.");
|
|
2354
3160
|
return;
|
|
2355
3161
|
}
|
|
2356
3162
|
if (isNewer(latest, currentVersion)) {
|
|
2357
|
-
s.stop(
|
|
3163
|
+
s.stop(pc11.yellow(`Update available: ${currentVersion} \u2192 ${latest}`));
|
|
2358
3164
|
const pm = detectCliInstaller();
|
|
2359
3165
|
const installCmd = getGlobalInstallCommand(pm, "@kitnai/cli");
|
|
2360
|
-
const shouldUpdate = await
|
|
2361
|
-
if (
|
|
2362
|
-
|
|
3166
|
+
const shouldUpdate = await p12.confirm({ message: "Update now?" });
|
|
3167
|
+
if (p12.isCancel(shouldUpdate) || !shouldUpdate) {
|
|
3168
|
+
p12.log.message(` Run: ${pc11.cyan(installCmd)}`);
|
|
2363
3169
|
} else {
|
|
2364
|
-
const us =
|
|
3170
|
+
const us = p12.spinner();
|
|
2365
3171
|
us.start("Updating...");
|
|
2366
3172
|
try {
|
|
2367
3173
|
execSync2(installCmd, { stdio: "pipe" });
|
|
2368
|
-
us.stop(
|
|
3174
|
+
us.stop(pc11.green(`Updated to v${latest}`));
|
|
2369
3175
|
} catch {
|
|
2370
|
-
us.stop(
|
|
2371
|
-
|
|
3176
|
+
us.stop(pc11.red("Update failed"));
|
|
3177
|
+
p12.log.message(` Run manually: ${pc11.cyan(installCmd)}`);
|
|
2372
3178
|
}
|
|
2373
3179
|
}
|
|
2374
3180
|
} else {
|
|
2375
|
-
s.stop(
|
|
3181
|
+
s.stop(pc11.green("You're on the latest version"));
|
|
2376
3182
|
}
|
|
2377
|
-
|
|
3183
|
+
p12.outro("");
|
|
2378
3184
|
}
|
|
2379
3185
|
var init_check = __esm({
|
|
2380
3186
|
"src/commands/check.ts"() {
|
|
@@ -2384,6 +3190,64 @@ var init_check = __esm({
|
|
|
2384
3190
|
}
|
|
2385
3191
|
});
|
|
2386
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
|
+
|
|
2387
3251
|
// src/commands/registry.ts
|
|
2388
3252
|
var registry_exports = {};
|
|
2389
3253
|
__export(registry_exports, {
|
|
@@ -2391,8 +3255,8 @@ __export(registry_exports, {
|
|
|
2391
3255
|
registryListCommand: () => registryListCommand,
|
|
2392
3256
|
registryRemoveCommand: () => registryRemoveCommand
|
|
2393
3257
|
});
|
|
2394
|
-
import * as
|
|
2395
|
-
import
|
|
3258
|
+
import * as p14 from "@clack/prompts";
|
|
3259
|
+
import pc13 from "picocolors";
|
|
2396
3260
|
async function registryAddCommand(namespace, url, opts = {}) {
|
|
2397
3261
|
const cwd = opts.cwd ?? process.cwd();
|
|
2398
3262
|
const config = await readConfig(cwd);
|
|
@@ -2418,10 +3282,10 @@ async function registryAddCommand(namespace, url, opts = {}) {
|
|
|
2418
3282
|
config.registries[namespace] = url;
|
|
2419
3283
|
}
|
|
2420
3284
|
await writeConfig(cwd, config);
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
if (opts.homepage)
|
|
2424
|
-
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}`));
|
|
2425
3289
|
}
|
|
2426
3290
|
async function registryRemoveCommand(namespace, opts = {}) {
|
|
2427
3291
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -2442,10 +3306,10 @@ async function registryRemoveCommand(namespace, opts = {}) {
|
|
|
2442
3306
|
}
|
|
2443
3307
|
delete config.registries[namespace];
|
|
2444
3308
|
await writeConfig(cwd, config);
|
|
2445
|
-
|
|
3309
|
+
p14.log.success(`Removed registry ${pc13.bold(namespace)}`);
|
|
2446
3310
|
if (affectedComponents.length > 0) {
|
|
2447
|
-
|
|
2448
|
-
` + affectedComponents.map((name) => ` ${
|
|
3311
|
+
p14.log.warn(`${affectedComponents.length} installed component(s) referenced this registry:
|
|
3312
|
+
` + affectedComponents.map((name) => ` ${pc13.yellow("!")} ${name}`).join("\n"));
|
|
2449
3313
|
}
|
|
2450
3314
|
return { affectedComponents };
|
|
2451
3315
|
}
|
|
@@ -2460,15 +3324,15 @@ async function registryListCommand(opts = {}) {
|
|
|
2460
3324
|
return { namespace, url, homepage, description };
|
|
2461
3325
|
});
|
|
2462
3326
|
if (entries.length === 0) {
|
|
2463
|
-
|
|
3327
|
+
p14.log.message(pc13.dim(" No registries configured."));
|
|
2464
3328
|
} else {
|
|
2465
3329
|
const lines = [];
|
|
2466
3330
|
for (const { namespace, url, homepage, description } of entries) {
|
|
2467
|
-
lines.push(` ${
|
|
3331
|
+
lines.push(` ${pc13.bold(namespace.padEnd(16))} ${pc13.dim(url)}`);
|
|
2468
3332
|
if (description) lines.push(` ${" ".repeat(16)} ${description}`);
|
|
2469
|
-
if (homepage) lines.push(` ${" ".repeat(16)} ${
|
|
3333
|
+
if (homepage) lines.push(` ${" ".repeat(16)} ${pc13.dim(homepage)}`);
|
|
2470
3334
|
}
|
|
2471
|
-
|
|
3335
|
+
p14.log.message(lines.join("\n"));
|
|
2472
3336
|
}
|
|
2473
3337
|
return entries;
|
|
2474
3338
|
}
|
|
@@ -2482,7 +3346,7 @@ var init_registry = __esm({
|
|
|
2482
3346
|
// src/index.ts
|
|
2483
3347
|
init_update_check();
|
|
2484
3348
|
import { Command } from "commander";
|
|
2485
|
-
var VERSION = true ? "0.1.
|
|
3349
|
+
var VERSION = true ? "0.1.31" : "0.0.0-dev";
|
|
2486
3350
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
2487
3351
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
2488
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) => {
|
|
@@ -2513,6 +3377,14 @@ program.command("create").description("Scaffold a new kitn component").argument(
|
|
|
2513
3377
|
const { createCommand: createCommand2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
2514
3378
|
await createCommand2(type, name);
|
|
2515
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
|
+
});
|
|
2516
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) => {
|
|
2517
3389
|
const { infoCommand: infoCommand2 } = await Promise.resolve().then(() => (init_info(), info_exports));
|
|
2518
3390
|
await infoCommand2(component);
|
|
@@ -2521,6 +3393,10 @@ program.command("check").description("Check for CLI updates").action(async () =>
|
|
|
2521
3393
|
const { checkCommand: checkCommand2 } = await Promise.resolve().then(() => (init_check(), check_exports));
|
|
2522
3394
|
await checkCommand2(VERSION);
|
|
2523
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
|
+
});
|
|
2524
3400
|
var registry = program.command("registry").description("Manage component registries");
|
|
2525
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) => {
|
|
2526
3402
|
const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|