skillops-cli 1.2.1 → 1.3.0
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 +92 -65
- package/dist/cli.js +2424 -301
- package/dist/index.d.ts +349 -93
- package/dist/index.js +2709 -803
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import fs10 from "fs";
|
|
5
|
+
import path10 from "path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
7
|
-
import
|
|
7
|
+
import YAML6 from "yaml";
|
|
8
8
|
|
|
9
9
|
// src/auditor/parser.ts
|
|
10
10
|
import fs2 from "fs";
|
|
@@ -14,7 +14,7 @@ import path from "path";
|
|
|
14
14
|
import fs from "fs";
|
|
15
15
|
import YAML from "yaml";
|
|
16
16
|
import { z } from "zod";
|
|
17
|
-
var SKILLOPS_VERSION = "1.
|
|
17
|
+
var SKILLOPS_VERSION = "1.3.0";
|
|
18
18
|
var SKILL_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
19
19
|
var SEMVER_REGEX = /^\d+\.\d+\.\d+$/;
|
|
20
20
|
var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
@@ -1389,6 +1389,342 @@ function auditCatalog(catalogDir, options = {}) {
|
|
|
1389
1389
|
import fs3 from "fs";
|
|
1390
1390
|
import path2 from "path";
|
|
1391
1391
|
import YAML2 from "yaml";
|
|
1392
|
+
|
|
1393
|
+
// src/ui/terminal.ts
|
|
1394
|
+
var ESC = String.fromCharCode(27);
|
|
1395
|
+
var ANSI_ESCAPE_REGEX = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
|
|
1396
|
+
function isColorEnabled(options) {
|
|
1397
|
+
const env = options?.env ?? process.env;
|
|
1398
|
+
const isTTY = options?.isTTY ?? Boolean(process.stdout?.isTTY);
|
|
1399
|
+
if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") {
|
|
1400
|
+
return false;
|
|
1401
|
+
}
|
|
1402
|
+
if (env.FORCE_COLOR !== void 0) {
|
|
1403
|
+
const val = env.FORCE_COLOR.trim().toLowerCase();
|
|
1404
|
+
if (val === "0" || val === "false") {
|
|
1405
|
+
return false;
|
|
1406
|
+
}
|
|
1407
|
+
if (val === "1" || val === "2" || val === "3" || val === "true") {
|
|
1408
|
+
return true;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
if (env.TERM === "dumb") {
|
|
1412
|
+
return false;
|
|
1413
|
+
}
|
|
1414
|
+
return isTTY;
|
|
1415
|
+
}
|
|
1416
|
+
function wrapAnsi(openCode, closeCode, text, enabled) {
|
|
1417
|
+
const useColor = enabled ?? isColorEnabled();
|
|
1418
|
+
if (!useColor || text.length === 0) {
|
|
1419
|
+
return text;
|
|
1420
|
+
}
|
|
1421
|
+
return `${ESC}[${openCode}m${text}${ESC}[${closeCode}m`;
|
|
1422
|
+
}
|
|
1423
|
+
var style = {
|
|
1424
|
+
bold: (text, enabled) => wrapAnsi("1", "22", text, enabled),
|
|
1425
|
+
dim: (text, enabled) => wrapAnsi("2", "22", text, enabled),
|
|
1426
|
+
italic: (text, enabled) => wrapAnsi("3", "23", text, enabled),
|
|
1427
|
+
underline: (text, enabled) => wrapAnsi("4", "24", text, enabled),
|
|
1428
|
+
cyan: (text, enabled) => wrapAnsi("36", "39", text, enabled),
|
|
1429
|
+
green: (text, enabled) => wrapAnsi("32", "39", text, enabled),
|
|
1430
|
+
yellow: (text, enabled) => wrapAnsi("33", "39", text, enabled),
|
|
1431
|
+
red: (text, enabled) => wrapAnsi("31", "39", text, enabled),
|
|
1432
|
+
magenta: (text, enabled) => wrapAnsi("35", "39", text, enabled),
|
|
1433
|
+
blue: (text, enabled) => wrapAnsi("34", "39", text, enabled),
|
|
1434
|
+
gray: (text, enabled) => wrapAnsi("90", "39", text, enabled),
|
|
1435
|
+
white: (text, enabled) => wrapAnsi("37", "39", text, enabled)
|
|
1436
|
+
};
|
|
1437
|
+
function stripAnsi(text) {
|
|
1438
|
+
return text.replace(ANSI_ESCAPE_REGEX, "");
|
|
1439
|
+
}
|
|
1440
|
+
function visibleLength(text) {
|
|
1441
|
+
return Array.from(stripAnsi(text)).length;
|
|
1442
|
+
}
|
|
1443
|
+
function padVisible(text, width, align = "left") {
|
|
1444
|
+
const len = visibleLength(text);
|
|
1445
|
+
if (len >= width) {
|
|
1446
|
+
return text;
|
|
1447
|
+
}
|
|
1448
|
+
const diff = width - len;
|
|
1449
|
+
if (align === "right") {
|
|
1450
|
+
return `${" ".repeat(diff)}${text}`;
|
|
1451
|
+
}
|
|
1452
|
+
if (align === "center") {
|
|
1453
|
+
const left = Math.floor(diff / 2);
|
|
1454
|
+
const right = diff - left;
|
|
1455
|
+
return `${" ".repeat(left)}${text}${" ".repeat(right)}`;
|
|
1456
|
+
}
|
|
1457
|
+
return `${text}${" ".repeat(diff)}`;
|
|
1458
|
+
}
|
|
1459
|
+
function renderBox(lines, options) {
|
|
1460
|
+
const paddingX = options?.paddingX ?? 2;
|
|
1461
|
+
const minWidth = options?.minWidth ?? 64;
|
|
1462
|
+
const colorEnabled = options?.colorEnabled;
|
|
1463
|
+
const borderFn = options?.borderColor ?? style.cyan;
|
|
1464
|
+
const titleText = options?.title ? ` ${options.title.trim()} ` : "";
|
|
1465
|
+
const titleVisibleLen = visibleLength(titleText);
|
|
1466
|
+
const maxLineLen = lines.reduce(
|
|
1467
|
+
(max, line) => Math.max(max, visibleLength(line)),
|
|
1468
|
+
0
|
|
1469
|
+
);
|
|
1470
|
+
const innerWidth = Math.max(
|
|
1471
|
+
minWidth,
|
|
1472
|
+
maxLineLen + paddingX * 2,
|
|
1473
|
+
titleVisibleLen + 4
|
|
1474
|
+
);
|
|
1475
|
+
const contentWidth = innerWidth - paddingX * 2;
|
|
1476
|
+
const padStr = " ".repeat(paddingX);
|
|
1477
|
+
const topBorder = titleVisibleLen > 0 ? `${borderFn("\u256D\u2500", colorEnabled)}${style.bold(titleText, colorEnabled)}${borderFn(`${"\u2500".repeat(Math.max(1, innerWidth - titleVisibleLen - 1))}\u256E`, colorEnabled)}` : borderFn(`\u256D${"\u2500".repeat(innerWidth)}\u256E`, colorEnabled);
|
|
1478
|
+
const bodyLines = lines.map((line) => {
|
|
1479
|
+
const padded = padVisible(line, contentWidth, "left");
|
|
1480
|
+
return `${borderFn("\u2502", colorEnabled)}${padStr}${padded}${padStr}${borderFn("\u2502", colorEnabled)}`;
|
|
1481
|
+
});
|
|
1482
|
+
const bottomBorder = borderFn(`\u2570${"\u2500".repeat(innerWidth)}\u256F`, colorEnabled);
|
|
1483
|
+
return [topBorder, ...bodyLines, bottomBorder].join("\n");
|
|
1484
|
+
}
|
|
1485
|
+
function renderHeaderBanner(subtitle = "Framework & Library for High-End AI Coding Skills & Subagents", options) {
|
|
1486
|
+
const ver = options?.version ?? SKILLOPS_VERSION;
|
|
1487
|
+
const c = options?.colorEnabled;
|
|
1488
|
+
const headline = `${style.bold(style.cyan(`\u26A1 SkillOps v${ver}`, c), c)} ${style.dim("\u2014", c)} ${subtitle}`;
|
|
1489
|
+
return renderBox([headline], {
|
|
1490
|
+
borderColor: style.cyan,
|
|
1491
|
+
paddingX: 2,
|
|
1492
|
+
minWidth: 72,
|
|
1493
|
+
colorEnabled: c
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
function renderTable(headers, rows, options) {
|
|
1497
|
+
const indent = " ".repeat(options?.indent ?? 2);
|
|
1498
|
+
const c = options?.colorEnabled;
|
|
1499
|
+
const colCount = headers.length;
|
|
1500
|
+
const widths = headers.map((h) => visibleLength(h));
|
|
1501
|
+
for (const row of rows) {
|
|
1502
|
+
for (let i = 0; i < colCount; i++) {
|
|
1503
|
+
const cell = row[i] ?? "";
|
|
1504
|
+
widths[i] = Math.max(widths[i] ?? 0, visibleLength(cell));
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
const aligns = options?.align ?? headers.map(() => "left");
|
|
1508
|
+
const headerLine = headers.map(
|
|
1509
|
+
(h, i) => style.bold(
|
|
1510
|
+
style.cyan(padVisible(h, widths[i] ?? 0, aligns[i] ?? "left"), c),
|
|
1511
|
+
c
|
|
1512
|
+
)
|
|
1513
|
+
).join(" ");
|
|
1514
|
+
const separatorLine = widths.map((w) => style.dim("\u2500".repeat(Math.max(1, w)), c)).join(" ");
|
|
1515
|
+
const bodyLines = rows.map(
|
|
1516
|
+
(row) => headers.map(
|
|
1517
|
+
(_, i) => padVisible(row[i] ?? "", widths[i] ?? 0, aligns[i] ?? "left")
|
|
1518
|
+
).join(" ")
|
|
1519
|
+
);
|
|
1520
|
+
return [
|
|
1521
|
+
`${indent}${headerLine}`,
|
|
1522
|
+
`${indent}${separatorLine}`,
|
|
1523
|
+
...bodyLines.map((line) => `${indent}${line}`)
|
|
1524
|
+
].join("\n");
|
|
1525
|
+
}
|
|
1526
|
+
function renderTokenBar(current, max, options) {
|
|
1527
|
+
const width = options?.width ?? 18;
|
|
1528
|
+
const unit = options?.unit ?? "tokens";
|
|
1529
|
+
const c = options?.colorEnabled;
|
|
1530
|
+
const safeMax = max > 0 ? max : 1;
|
|
1531
|
+
const ratio = Math.max(0, current / safeMax);
|
|
1532
|
+
const clampedRatio = Math.min(1, ratio);
|
|
1533
|
+
const filledCount = Math.round(clampedRatio * width);
|
|
1534
|
+
const emptyCount = Math.max(0, width - filledCount);
|
|
1535
|
+
const colorFn = ratio > 1 ? style.red : ratio > 0.85 ? style.yellow : style.green;
|
|
1536
|
+
const filledStr = colorFn("\u2588".repeat(filledCount), c);
|
|
1537
|
+
const emptyStr = style.dim("\u2591".repeat(emptyCount), c);
|
|
1538
|
+
const pctStr = `${(ratio * 100).toFixed(1)}%`;
|
|
1539
|
+
const prefix = options?.label ? `${style.bold(options.label, c)} ` : "";
|
|
1540
|
+
return `${prefix}[${filledStr}${emptyStr}] ${style.bold(pctStr, c)} (${current} / ${max} ${unit})`;
|
|
1541
|
+
}
|
|
1542
|
+
var renderProgressBar = renderTokenBar;
|
|
1543
|
+
function renderSectionHeader(title, colorEnabled) {
|
|
1544
|
+
const c = colorEnabled;
|
|
1545
|
+
return `
|
|
1546
|
+
${style.bold(style.cyan(title, c), c)}
|
|
1547
|
+
${style.dim("\u2500".repeat(Math.max(24, visibleLength(title))), c)}`;
|
|
1548
|
+
}
|
|
1549
|
+
function formatBadge(type, customLabel, colorEnabled) {
|
|
1550
|
+
switch (type) {
|
|
1551
|
+
case "pass":
|
|
1552
|
+
return style.bold(
|
|
1553
|
+
style.green(`\u2714 ${customLabel ?? "PASS"}`, colorEnabled),
|
|
1554
|
+
colorEnabled
|
|
1555
|
+
);
|
|
1556
|
+
case "warn":
|
|
1557
|
+
return style.bold(
|
|
1558
|
+
style.yellow(`\u26A0 ${customLabel ?? "WARN"}`, colorEnabled),
|
|
1559
|
+
colorEnabled
|
|
1560
|
+
);
|
|
1561
|
+
case "fail":
|
|
1562
|
+
return style.bold(
|
|
1563
|
+
style.red(`\u2716 ${customLabel ?? "FAIL"}`, colorEnabled),
|
|
1564
|
+
colorEnabled
|
|
1565
|
+
);
|
|
1566
|
+
case "info":
|
|
1567
|
+
return style.bold(
|
|
1568
|
+
style.cyan(`\u2139 ${customLabel ?? "INFO"}`, colorEnabled),
|
|
1569
|
+
colorEnabled
|
|
1570
|
+
);
|
|
1571
|
+
case "skill":
|
|
1572
|
+
return style.bold(
|
|
1573
|
+
style.cyan(`[${customLabel ?? "SKILL"}]`, colorEnabled),
|
|
1574
|
+
colorEnabled
|
|
1575
|
+
);
|
|
1576
|
+
case "subagent":
|
|
1577
|
+
return style.bold(
|
|
1578
|
+
style.magenta(`[${customLabel ?? "SUBAGENT"}]`, colorEnabled),
|
|
1579
|
+
colorEnabled
|
|
1580
|
+
);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
function renderStep(step, total, title, detail, status = "done", colorEnabled) {
|
|
1584
|
+
const icon = status === "done" ? style.green("\u2714", colorEnabled) : status === "warn" ? style.yellow("\u26A0", colorEnabled) : style.dim("\u25CB", colorEnabled);
|
|
1585
|
+
const stepBadge = style.dim(`[${step}/${total}]`, colorEnabled);
|
|
1586
|
+
const titleStyled = style.bold(padVisible(title, 28, "left"), colorEnabled);
|
|
1587
|
+
const detailStyled = detail ? ` ${style.dim("\u2022", colorEnabled)} ${detail}` : "";
|
|
1588
|
+
return ` ${icon} ${stepBadge} ${titleStyled}${detailStyled}`;
|
|
1589
|
+
}
|
|
1590
|
+
function renderNextSteps(hints, colorEnabled) {
|
|
1591
|
+
if (hints.length === 0) {
|
|
1592
|
+
return "";
|
|
1593
|
+
}
|
|
1594
|
+
const c = colorEnabled;
|
|
1595
|
+
const maxCmdLen = hints.reduce(
|
|
1596
|
+
(m, h) => Math.max(m, visibleLength(h.command)),
|
|
1597
|
+
0
|
|
1598
|
+
);
|
|
1599
|
+
const lines = [
|
|
1600
|
+
"",
|
|
1601
|
+
` ${style.bold(style.cyan("\u{1F4A1} Next Steps:", c), c)}`,
|
|
1602
|
+
...hints.map(
|
|
1603
|
+
(h) => ` ${style.green(padVisible(h.command, maxCmdLen + 2, "left"), c)} ${style.dim(`# ${h.description}`, c)}`
|
|
1604
|
+
),
|
|
1605
|
+
""
|
|
1606
|
+
];
|
|
1607
|
+
return lines.join("\n");
|
|
1608
|
+
}
|
|
1609
|
+
function renderAiUsageGuide(targets, skills, subagents, colorEnabled) {
|
|
1610
|
+
const c = colorEnabled;
|
|
1611
|
+
const sampleSkill = skills[0] ?? "systematic-debugging";
|
|
1612
|
+
const sampleAgent = subagents[0] ?? "context-scout";
|
|
1613
|
+
const lines = [
|
|
1614
|
+
`${style.bold("How to use your installed Skills & Subagents inside your AI:", c)}`,
|
|
1615
|
+
""
|
|
1616
|
+
];
|
|
1617
|
+
const targetSet = new Set(targets);
|
|
1618
|
+
if (targetSet.has("claude") || targetSet.size === 0) {
|
|
1619
|
+
lines.push(
|
|
1620
|
+
`\u2022 ${style.bold("Claude Code:", c)} Ask naturally (auto-routed via .claude/skills/${sampleSkill}/SKILL.md) or delegate to @${sampleAgent}`
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
if (targetSet.has("cursor")) {
|
|
1624
|
+
lines.push(
|
|
1625
|
+
`\u2022 ${style.bold("Cursor IDE:", c)} Type ${style.cyan(`@${sampleSkill}`, c)} in Cursor Chat/Composer or let .cursor/rules/${sampleSkill}.mdc auto-attach`
|
|
1626
|
+
);
|
|
1627
|
+
}
|
|
1628
|
+
if (targetSet.has("antigravity")) {
|
|
1629
|
+
lines.push(
|
|
1630
|
+
`\u2022 ${style.bold("Antigravity / Gemini:", c)} Auto-activated from .agent/skills/${sampleSkill}/SKILL.md when your prompt matches its domain`
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
if (targetSet.has("copilot")) {
|
|
1634
|
+
lines.push(
|
|
1635
|
+
`\u2022 ${style.bold("GitHub Copilot:", c)} Use ${style.cyan(`/${sampleSkill}`, c)} prompt in Copilot Chat or reference .github/instructions/${sampleSkill}.instructions.md`
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
if (targetSet.has("windsurf") || targetSet.has("cline") || targetSet.has("roocode") || targetSet.has("kilocode")) {
|
|
1639
|
+
lines.push(
|
|
1640
|
+
`\u2022 ${style.bold("Windsurf / Cline / Roo / Kilo:", c)} Rules in .windsurf/rules/, .clinerules/, .roo/rules/, .kilocode/rules/ activate automatically`
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
lines.push(
|
|
1644
|
+
`\u2022 ${style.bold("Inspect any Skill/Agent:", c)} Run ${style.green(`npx skillops-cli info ${sampleSkill}`, c)} to view triggers, L3 scripts & prompts`
|
|
1645
|
+
);
|
|
1646
|
+
return renderBox(lines, {
|
|
1647
|
+
title: "\u{1F680} Using Your Skills & Subagents",
|
|
1648
|
+
borderColor: style.green,
|
|
1649
|
+
paddingX: 2,
|
|
1650
|
+
minWidth: 72,
|
|
1651
|
+
colorEnabled: c
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
function levenshteinDistance(a, b) {
|
|
1655
|
+
const s1 = a.toLowerCase();
|
|
1656
|
+
const s2 = b.toLowerCase();
|
|
1657
|
+
const m = s1.length;
|
|
1658
|
+
const n = s2.length;
|
|
1659
|
+
let prevRow = Array.from({ length: n + 1 }, (_, j) => j);
|
|
1660
|
+
for (let i = 1; i <= m; i++) {
|
|
1661
|
+
const currRow = [i];
|
|
1662
|
+
for (let j = 1; j <= n; j++) {
|
|
1663
|
+
const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
|
|
1664
|
+
const deletion = (prevRow[j] ?? 0) + 1;
|
|
1665
|
+
const insertion = (currRow[j - 1] ?? 0) + 1;
|
|
1666
|
+
const substitution = (prevRow[j - 1] ?? 0) + cost;
|
|
1667
|
+
currRow[j] = Math.min(deletion, insertion, substitution);
|
|
1668
|
+
}
|
|
1669
|
+
prevRow = currRow;
|
|
1670
|
+
}
|
|
1671
|
+
return prevRow[n] ?? 0;
|
|
1672
|
+
}
|
|
1673
|
+
var DEFAULT_VALID_COMMANDS = [
|
|
1674
|
+
"quickstart",
|
|
1675
|
+
"setup",
|
|
1676
|
+
"init",
|
|
1677
|
+
"list",
|
|
1678
|
+
"search",
|
|
1679
|
+
"find",
|
|
1680
|
+
"info",
|
|
1681
|
+
"show",
|
|
1682
|
+
"inspect",
|
|
1683
|
+
"add",
|
|
1684
|
+
"install",
|
|
1685
|
+
"remove",
|
|
1686
|
+
"uninstall",
|
|
1687
|
+
"rm",
|
|
1688
|
+
"clean",
|
|
1689
|
+
"create",
|
|
1690
|
+
"new",
|
|
1691
|
+
"harnesses",
|
|
1692
|
+
"doctor",
|
|
1693
|
+
"status",
|
|
1694
|
+
"tokens",
|
|
1695
|
+
"budget",
|
|
1696
|
+
"audit",
|
|
1697
|
+
"route",
|
|
1698
|
+
"recommend",
|
|
1699
|
+
"sync",
|
|
1700
|
+
"eval",
|
|
1701
|
+
"mcp",
|
|
1702
|
+
"mcp-setup"
|
|
1703
|
+
];
|
|
1704
|
+
function suggestCommand(unknownCommand, validCommands = DEFAULT_VALID_COMMANDS) {
|
|
1705
|
+
const needle = unknownCommand.trim().toLowerCase();
|
|
1706
|
+
if (!needle) {
|
|
1707
|
+
return null;
|
|
1708
|
+
}
|
|
1709
|
+
for (const candidate of validCommands) {
|
|
1710
|
+
if (candidate.startsWith(needle) || needle.startsWith(candidate)) {
|
|
1711
|
+
return candidate;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
let bestMatch = null;
|
|
1715
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
1716
|
+
for (const candidate of validCommands) {
|
|
1717
|
+
const dist = levenshteinDistance(needle, candidate);
|
|
1718
|
+
if (dist < bestDistance) {
|
|
1719
|
+
bestDistance = dist;
|
|
1720
|
+
bestMatch = candidate;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
const maxAllowedDistance = Math.max(2, Math.floor(needle.length * 0.45));
|
|
1724
|
+
return bestDistance <= maxAllowedDistance ? bestMatch : null;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// src/auditor/token_economy.ts
|
|
1392
1728
|
var ALWAYS_ON_INSTRUCTION_CANDIDATES = [
|
|
1393
1729
|
"CLAUDE.md",
|
|
1394
1730
|
".claude/CLAUDE.md",
|
|
@@ -1533,7 +1869,9 @@ function analyzeRepoTokenEconomy(repoPath, options = {}) {
|
|
|
1533
1869
|
];
|
|
1534
1870
|
for (const ruleGroup of scopedRuleDirs) {
|
|
1535
1871
|
if (fs3.existsSync(ruleGroup.dirPath) && fs3.statSync(ruleGroup.dirPath).isDirectory()) {
|
|
1536
|
-
const entries = fs3.readdirSync(ruleGroup.dirPath, { withFileTypes: true }).filter(
|
|
1872
|
+
const entries = fs3.readdirSync(ruleGroup.dirPath, { withFileTypes: true }).filter(
|
|
1873
|
+
(e) => e.isFile() && (e.name.endsWith(".mdc") || e.name.endsWith(".md"))
|
|
1874
|
+
).sort((a, b) => a.name.localeCompare(b.name));
|
|
1537
1875
|
for (const entry of entries) {
|
|
1538
1876
|
const fullPath = path2.join(ruleGroup.dirPath, entry.name);
|
|
1539
1877
|
const relPath = `${ruleGroup.relPrefix}/${entry.name}`;
|
|
@@ -1670,7 +2008,11 @@ function analyzeRepoTokenEconomy(repoPath, options = {}) {
|
|
|
1670
2008
|
0,
|
|
1671
2009
|
monolithicEquivalentTokens - progressiveDisclosureL1Tokens
|
|
1672
2010
|
);
|
|
1673
|
-
const tokenSavingsPct = monolithicEquivalentTokens > 0 ? Number(
|
|
2011
|
+
const tokenSavingsPct = monolithicEquivalentTokens > 0 ? Number(
|
|
2012
|
+
(estimatedTokensSaved / monolithicEquivalentTokens * 100).toFixed(
|
|
2013
|
+
2
|
|
2014
|
+
)
|
|
2015
|
+
) : 0;
|
|
1674
2016
|
return TokenEconomyReportSchema.parse({
|
|
1675
2017
|
repo_path: resolvedRepo.replace(/\\/g, "/"),
|
|
1676
2018
|
catalog_path: resolvedCatalog.replace(/\\/g, "/"),
|
|
@@ -1742,9 +2084,15 @@ function formatTokenEconomyReport(report, format = "table") {
|
|
|
1742
2084
|
return `${lines2.join("\n")}
|
|
1743
2085
|
`;
|
|
1744
2086
|
}
|
|
2087
|
+
const startupBar = renderProgressBar(
|
|
2088
|
+
report.startup_token_tax,
|
|
2089
|
+
report.startup_tax_budget,
|
|
2090
|
+
{ width: 18, unit: "tokens" }
|
|
2091
|
+
);
|
|
1745
2092
|
const lines = [
|
|
1746
2093
|
"=== SkillOps Token Economy & Startup Context Tax Analyzer ===",
|
|
1747
2094
|
`Startup Token Tax : ${report.startup_token_tax} / ${report.startup_tax_budget} tokens [${report.passed_budget ? "PASS" : "OVER BUDGET"}]`,
|
|
2095
|
+
`Startup Budget Meter : ${startupBar}`,
|
|
1748
2096
|
`Monolithic Equivalent : ${report.monolithic_equivalent_tokens} tokens`,
|
|
1749
2097
|
`Progressive L1 Footprint : ${report.progressive_disclosure_l1_tokens} tokens`,
|
|
1750
2098
|
`Token Savings : ${report.estimated_tokens_saved} tokens (${report.token_savings_pct.toFixed(2)}% saved)`,
|
|
@@ -1760,6 +2108,18 @@ function formatTokenEconomyReport(report, format = "table") {
|
|
|
1760
2108
|
);
|
|
1761
2109
|
}
|
|
1762
2110
|
}
|
|
2111
|
+
lines.push(
|
|
2112
|
+
renderNextSteps([
|
|
2113
|
+
{
|
|
2114
|
+
command: "npx skillops-cli quickstart",
|
|
2115
|
+
description: "Sync Progressive Disclosure Skills & Context-Isolated Subagents"
|
|
2116
|
+
},
|
|
2117
|
+
{
|
|
2118
|
+
command: "npx skillops-cli doctor",
|
|
2119
|
+
description: "Inspect repository lockfile & startup tax diagnostics"
|
|
2120
|
+
}
|
|
2121
|
+
]).trimEnd()
|
|
2122
|
+
);
|
|
1763
2123
|
return `${lines.join("\n")}
|
|
1764
2124
|
`;
|
|
1765
2125
|
}
|
|
@@ -2368,6 +2728,11 @@ ${rows}
|
|
|
2368
2728
|
].join("\n");
|
|
2369
2729
|
}
|
|
2370
2730
|
|
|
2731
|
+
// src/framework/index.ts
|
|
2732
|
+
import fs9 from "fs";
|
|
2733
|
+
import path9 from "path";
|
|
2734
|
+
import YAML5 from "yaml";
|
|
2735
|
+
|
|
2371
2736
|
// src/mcp/index.ts
|
|
2372
2737
|
import fs7 from "fs";
|
|
2373
2738
|
import path7 from "path";
|
|
@@ -2639,7 +3004,10 @@ function scoreSkillAgainstProfile(parsedSkill, profile, taskPrompt = "") {
|
|
|
2639
3004
|
matchedSignals.push(`lang:${lang}`);
|
|
2640
3005
|
}
|
|
2641
3006
|
}
|
|
2642
|
-
for (const dep of [
|
|
3007
|
+
for (const dep of [
|
|
3008
|
+
...manifest.routing_signals.dependencies_any,
|
|
3009
|
+
...manifest.tags
|
|
3010
|
+
]) {
|
|
2643
3011
|
const lowerDep = dep.toLowerCase();
|
|
2644
3012
|
const depTokens = tokenizeRoutingText(lowerDep);
|
|
2645
3013
|
if (repoSignalSet.has(lowerDep) || depTokens.some((t) => repoSignalSet.has(t))) {
|
|
@@ -2676,7 +3044,9 @@ function compileSkillBundle(profile, catalogDir, options = {}) {
|
|
|
2676
3044
|
const taskPrompt = options.taskPrompt ?? "";
|
|
2677
3045
|
const resolvedCatalog = path6.resolve(catalogDir);
|
|
2678
3046
|
const skillDirs = discoverSkillDirectories(resolvedCatalog);
|
|
2679
|
-
const parsedSkills = skillDirs.map(
|
|
3047
|
+
const parsedSkills = skillDirs.map(
|
|
3048
|
+
(dir) => parseSkillDirectory(dir, { requireLayout: false })
|
|
3049
|
+
);
|
|
2680
3050
|
const featureVectors = /* @__PURE__ */ new Map();
|
|
2681
3051
|
for (const p of parsedSkills) {
|
|
2682
3052
|
featureVectors.set(p.manifest.name, extractSkillFeatures(p.manifest));
|
|
@@ -2792,7 +3162,7 @@ function routeRepository(repoPath, catalogDir, options = {}) {
|
|
|
2792
3162
|
const profile = fingerprintRepository(repoPath);
|
|
2793
3163
|
return compileSkillBundle(profile, catalogDir, options);
|
|
2794
3164
|
}
|
|
2795
|
-
function formatRoutedBundle(bundle, format = "table") {
|
|
3165
|
+
function formatRoutedBundle(bundle, format = "table", colorEnabled) {
|
|
2796
3166
|
if (format === "json") {
|
|
2797
3167
|
return `${JSON.stringify(bundle, null, 2)}
|
|
2798
3168
|
`;
|
|
@@ -2815,16 +3185,40 @@ function formatRoutedBundle(bundle, format = "table") {
|
|
|
2815
3185
|
""
|
|
2816
3186
|
].join("\n");
|
|
2817
3187
|
}
|
|
3188
|
+
const topSkill = bundle.selected_skills[0]?.name ?? "security-sast-gate";
|
|
3189
|
+
const l1Bar = renderProgressBar(
|
|
3190
|
+
bundle.total_l1_tokens,
|
|
3191
|
+
bundle.max_l1_tokens,
|
|
3192
|
+
{
|
|
3193
|
+
width: 18,
|
|
3194
|
+
unit: "tokens",
|
|
3195
|
+
colorEnabled
|
|
3196
|
+
}
|
|
3197
|
+
);
|
|
3198
|
+
const nextSteps = renderNextSteps(
|
|
3199
|
+
[
|
|
3200
|
+
{
|
|
3201
|
+
command: "npx skillops-cli quickstart",
|
|
3202
|
+
description: "Install this recommended bundle & wire MCP in 1 step"
|
|
3203
|
+
},
|
|
3204
|
+
{
|
|
3205
|
+
command: `npx skillops-cli info ${topSkill}`,
|
|
3206
|
+
description: `Inspect ${topSkill} triggers, L3 scripts & AI invocation guide`
|
|
3207
|
+
}
|
|
3208
|
+
],
|
|
3209
|
+
colorEnabled
|
|
3210
|
+
);
|
|
2818
3211
|
return [
|
|
2819
3212
|
"=== SkillOps Context-Aware Router ===",
|
|
2820
3213
|
`Repo : ${bundle.repo_profile.repo_path}`,
|
|
2821
3214
|
`Stack : languages=[${bundle.repo_profile.languages.join(", ")}] frameworks=[${bundle.repo_profile.frameworks.join(", ")}] db=[${bundle.repo_profile.databases.join(", ")}]`,
|
|
2822
3215
|
`Selected : ${bundle.selected_skills.length}/${bundle.max_skills} skills (${bundle.total_l1_tokens}/${bundle.max_l1_tokens} L1 tokens, max cosine=${bundle.max_pairwise_similarity.toFixed(4)})`,
|
|
3216
|
+
`L1 Budget : ${l1Bar}`,
|
|
2823
3217
|
`Subagents : ${bundle.recommended_subagents.join(", ") || "none"}`,
|
|
2824
3218
|
...bundle.selected_skills.map(
|
|
2825
3219
|
(s) => ` + ${s.name} [${s.track}] score=${s.relevance_score} (${s.l1_tokens} L1 tokens) signals=[${s.matched_signals.join(", ")}]`
|
|
2826
3220
|
),
|
|
2827
|
-
|
|
3221
|
+
nextSteps
|
|
2828
3222
|
].join("\n");
|
|
2829
3223
|
}
|
|
2830
3224
|
|
|
@@ -3701,8 +4095,12 @@ function applyPlannedFiles(resolvedRepo, resolvedCatalog, targets, routedBundle,
|
|
|
3701
4095
|
total_l1_tokens: routedBundle.total_l1_tokens,
|
|
3702
4096
|
artifacts: newLockArtifacts
|
|
3703
4097
|
});
|
|
3704
|
-
fs8.writeFileSync(
|
|
3705
|
-
|
|
4098
|
+
fs8.writeFileSync(
|
|
4099
|
+
lockfilePath,
|
|
4100
|
+
`${JSON.stringify(lockData, null, 2)}
|
|
4101
|
+
`,
|
|
4102
|
+
"utf-8"
|
|
4103
|
+
);
|
|
3706
4104
|
}
|
|
3707
4105
|
return SyncReportSchema.parse({
|
|
3708
4106
|
repo_path: resolvedRepo.replace(/\\/g, "/"),
|
|
@@ -3753,15 +4151,26 @@ function syncRepository(repoPath, catalogDir, options = {}) {
|
|
|
3753
4151
|
target: "codex",
|
|
3754
4152
|
itemName: "codex-agents-index",
|
|
3755
4153
|
relativePath: "AGENTS.md",
|
|
3756
|
-
content: renderCodexAgentsIndex(
|
|
4154
|
+
content: renderCodexAgentsIndex(
|
|
4155
|
+
existingAgentsMd,
|
|
4156
|
+
selectedSkills,
|
|
4157
|
+
selectedSubagents
|
|
4158
|
+
)
|
|
3757
4159
|
});
|
|
3758
4160
|
}
|
|
3759
4161
|
}
|
|
3760
|
-
return applyPlannedFiles(
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
4162
|
+
return applyPlannedFiles(
|
|
4163
|
+
resolvedRepo,
|
|
4164
|
+
resolvedCatalog,
|
|
4165
|
+
targets,
|
|
4166
|
+
routedBundle,
|
|
4167
|
+
plannedFiles,
|
|
4168
|
+
{
|
|
4169
|
+
dryRun,
|
|
4170
|
+
force,
|
|
4171
|
+
preserveUnlistedLockEntries: false
|
|
4172
|
+
}
|
|
4173
|
+
);
|
|
3765
4174
|
}
|
|
3766
4175
|
function addSkillOrSubagent(repoPath, catalogDir, itemName, options = {}) {
|
|
3767
4176
|
const resolvedRepo = path8.resolve(repoPath);
|
|
@@ -3805,15 +4214,24 @@ function addSkillOrSubagent(repoPath, catalogDir, itemName, options = {}) {
|
|
|
3805
4214
|
plannedFiles.push(...renderSkillFilesForRuntime(matchedSkill, target));
|
|
3806
4215
|
}
|
|
3807
4216
|
if (matchedSubagent) {
|
|
3808
|
-
plannedFiles.push(
|
|
4217
|
+
plannedFiles.push(
|
|
4218
|
+
...renderSubagentFilesForRuntime(matchedSubagent, target)
|
|
4219
|
+
);
|
|
3809
4220
|
}
|
|
3810
4221
|
}
|
|
3811
4222
|
const routedBundle = routeRepository(resolvedRepo, resolvedCatalog);
|
|
3812
|
-
return applyPlannedFiles(
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
4223
|
+
return applyPlannedFiles(
|
|
4224
|
+
resolvedRepo,
|
|
4225
|
+
resolvedCatalog,
|
|
4226
|
+
targets,
|
|
4227
|
+
routedBundle,
|
|
4228
|
+
plannedFiles,
|
|
4229
|
+
{
|
|
4230
|
+
dryRun,
|
|
4231
|
+
force,
|
|
4232
|
+
preserveUnlistedLockEntries: true
|
|
4233
|
+
}
|
|
4234
|
+
);
|
|
3817
4235
|
}
|
|
3818
4236
|
function pruneEmptyParentDirs(filePath, stopAtRepoRoot) {
|
|
3819
4237
|
const normalizedStop = path8.resolve(stopAtRepoRoot);
|
|
@@ -3867,7 +4285,9 @@ function removeSkillOrSubagent(repoPath, catalogDir, itemName, options = {}) {
|
|
|
3867
4285
|
`No synced artifacts found for '${itemName}' in .skillops-lock.json.`
|
|
3868
4286
|
);
|
|
3869
4287
|
}
|
|
3870
|
-
const newLockArtifacts = {
|
|
4288
|
+
const newLockArtifacts = {
|
|
4289
|
+
...existingLock.artifacts
|
|
4290
|
+
};
|
|
3871
4291
|
const artifacts = [];
|
|
3872
4292
|
const driftConflicts = [];
|
|
3873
4293
|
for (const [relPath, entry] of matchingEntries) {
|
|
@@ -3913,8 +4333,12 @@ function removeSkillOrSubagent(repoPath, catalogDir, itemName, options = {}) {
|
|
|
3913
4333
|
selected_subagents: updatedSubagents,
|
|
3914
4334
|
artifacts: newLockArtifacts
|
|
3915
4335
|
});
|
|
3916
|
-
fs8.writeFileSync(
|
|
3917
|
-
|
|
4336
|
+
fs8.writeFileSync(
|
|
4337
|
+
lockfilePath,
|
|
4338
|
+
`${JSON.stringify(lockData, null, 2)}
|
|
4339
|
+
`,
|
|
4340
|
+
"utf-8"
|
|
4341
|
+
);
|
|
3918
4342
|
}
|
|
3919
4343
|
const routedBundle = routeRepository(resolvedRepo, resolvedCatalog);
|
|
3920
4344
|
return SyncReportSchema.parse({
|
|
@@ -4009,7 +4433,7 @@ function cleanSyncedArtifacts(repoPath, catalogDir, options = {}) {
|
|
|
4009
4433
|
passed: driftConflicts.length === 0
|
|
4010
4434
|
});
|
|
4011
4435
|
}
|
|
4012
|
-
function formatSyncReport(report, format = "table") {
|
|
4436
|
+
function formatSyncReport(report, format = "table", colorEnabled) {
|
|
4013
4437
|
if (format === "json") {
|
|
4014
4438
|
return `${JSON.stringify(report, null, 2)}
|
|
4015
4439
|
`;
|
|
@@ -4031,6 +4455,38 @@ function formatSyncReport(report, format = "table") {
|
|
|
4031
4455
|
""
|
|
4032
4456
|
].join("\n");
|
|
4033
4457
|
}
|
|
4458
|
+
const c = colorEnabled;
|
|
4459
|
+
const activeArtifacts = report.artifacts.filter(
|
|
4460
|
+
(a) => a.action === "created" || a.action === "updated" || a.action === "unchanged"
|
|
4461
|
+
);
|
|
4462
|
+
const firstSkill = activeArtifacts.find((a) => a.skill_name !== "codex-agents-index")?.skill_name ?? report.routed_bundle.selected_skills[0]?.name ?? "security-sast-gate";
|
|
4463
|
+
const usageBox = activeArtifacts.length > 0 ? renderBox(
|
|
4464
|
+
[
|
|
4465
|
+
`${style.bold("How to use your installed Skills & Subagents inside your AI:", c)}`,
|
|
4466
|
+
` \u2022 ${style.cyan("Claude Code / Antigravity:", c)} Ask naturally (auto-routed via SKILL.md) or delegate to a Subagent`,
|
|
4467
|
+
` \u2022 ${style.cyan("Cursor / Windsurf / Cline / Copilot:", c)} Reference ${style.green(`@${firstSkill}`, c)} in Chat or let rules auto-attach`,
|
|
4468
|
+
` \u2022 ${style.cyan("Deep-Inspect:", c)} Run ${style.green(`npx skillops-cli info ${firstSkill}`, c)} to view triggers & Level 3 scripts`
|
|
4469
|
+
],
|
|
4470
|
+
{
|
|
4471
|
+
title: "\u{1F680} Using Your Skills & Subagents",
|
|
4472
|
+
borderColor: style.cyan,
|
|
4473
|
+
minWidth: 74,
|
|
4474
|
+
colorEnabled: c
|
|
4475
|
+
}
|
|
4476
|
+
) : "";
|
|
4477
|
+
const nextSteps = renderNextSteps(
|
|
4478
|
+
[
|
|
4479
|
+
{
|
|
4480
|
+
command: `npx skillops-cli info ${firstSkill}`,
|
|
4481
|
+
description: "Inspect triggers, token budget & Level 3 scripts"
|
|
4482
|
+
},
|
|
4483
|
+
{
|
|
4484
|
+
command: "npx skillops-cli doctor",
|
|
4485
|
+
description: "Verify lockfile SHA-256 integrity & Startup Token Tax"
|
|
4486
|
+
}
|
|
4487
|
+
],
|
|
4488
|
+
c
|
|
4489
|
+
);
|
|
4034
4490
|
return [
|
|
4035
4491
|
"=== SkillOps Multi-Runtime Transpiler & Sync Engine ===",
|
|
4036
4492
|
`Repo : ${report.repo_path}`,
|
|
@@ -4040,180 +4496,1810 @@ function formatSyncReport(report, format = "table") {
|
|
|
4040
4496
|
(a) => ` [${a.action.toUpperCase()}] (${a.target}) ${a.relative_path}`
|
|
4041
4497
|
),
|
|
4042
4498
|
...report.drift_conflicts.map(
|
|
4043
|
-
(
|
|
4499
|
+
(cItem) => ` [DRIFT CONFLICT] ${cItem} modified locally. Re-run with --force to overwrite.`
|
|
4044
4500
|
),
|
|
4045
|
-
|
|
4046
|
-
|
|
4501
|
+
usageBox,
|
|
4502
|
+
nextSteps
|
|
4503
|
+
].filter(Boolean).join("\n");
|
|
4047
4504
|
}
|
|
4048
4505
|
|
|
4049
|
-
// src/
|
|
4050
|
-
var
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
}
|
|
4062
|
-
}
|
|
4063
|
-
if (repoPath) {
|
|
4064
|
-
const repoCatalog = path9.join(path9.resolve(repoPath), "catalog");
|
|
4065
|
-
if (fs9.existsSync(repoCatalog)) {
|
|
4066
|
-
return repoCatalog;
|
|
4067
|
-
}
|
|
4068
|
-
}
|
|
4069
|
-
const cwdCatalog = path9.resolve(process.cwd(), "catalog");
|
|
4070
|
-
if (fs9.existsSync(cwdCatalog)) {
|
|
4071
|
-
return cwdCatalog;
|
|
4072
|
-
}
|
|
4073
|
-
return path9.join(PACKAGE_ROOT, "catalog");
|
|
4074
|
-
}
|
|
4075
|
-
function runCli(argv) {
|
|
4076
|
-
const args = argv.slice(2);
|
|
4077
|
-
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
4078
|
-
process.stdout.write(
|
|
4079
|
-
[
|
|
4080
|
-
`SkillOps v${SKILLOPS_VERSION} \u2014 The Zero-API NPX Engine for High-End AI Skills & Subagents`,
|
|
4081
|
-
"",
|
|
4082
|
-
"Usage: npx skillops@latest <command> [options]",
|
|
4083
|
-
"",
|
|
4084
|
-
"Commands:",
|
|
4085
|
-
" init Scaffold .skillops.yml and detect repository agent harnesses",
|
|
4086
|
-
" list Browse bundled high-end Skills & Subagents with token budgets",
|
|
4087
|
-
" add Install a specific high-end Skill or Subagent into active runtimes",
|
|
4088
|
-
" remove Surgically uninstall a synced Skill or Subagent from active runtimes",
|
|
4089
|
-
" clean Remove all SkillOps-managed runtime artifacts tracked in .skillops-lock.json",
|
|
4090
|
-
" harnesses List all 20 supported AI coding agents, target paths, MCP support & repo detection",
|
|
4091
|
-
" doctor Diagnose Node/Python environment, .skillops.yml config & lockfile SHA-256 drift",
|
|
4092
|
-
" status Alias for 'doctor' \u2014 inspect repository lockfile drift and token tax status",
|
|
4093
|
-
" tokens Analyze repository Startup Token Tax and Progressive Disclosure savings",
|
|
4094
|
-
" audit Run L1/L2/L3 token budget linter, Subagent check & collision matrix",
|
|
4095
|
-
" route Fingerprint repository stack and compile optimal <=12 skill bundle",
|
|
4096
|
-
" sync Transpile Skills & Subagents across all 20 AI coding agents (Claude, Cursor, Windsurf, etc.)",
|
|
4097
|
-
" eval Execute zero-API deterministic TDD benchmarks (with_skill vs without_skill)",
|
|
4098
|
-
" mcp Start native JSON-RPC 2.0 stdio Model Context Protocol server",
|
|
4099
|
-
" mcp-setup Register SkillOps MCP server across all MCP-compatible AI coding IDEs & agents",
|
|
4100
|
-
""
|
|
4101
|
-
].join("\n")
|
|
4102
|
-
);
|
|
4103
|
-
return ExitCode.SUCCESS;
|
|
4104
|
-
}
|
|
4105
|
-
if (args.includes("--version") || args.includes("-V") || args[0] === "version") {
|
|
4106
|
-
process.stdout.write(`skillops ${SKILLOPS_VERSION}
|
|
4107
|
-
`);
|
|
4108
|
-
return ExitCode.SUCCESS;
|
|
4109
|
-
}
|
|
4110
|
-
const command = args[0];
|
|
4111
|
-
if (command === "init") {
|
|
4112
|
-
const repoIdx = args.indexOf("--repo");
|
|
4113
|
-
const repoPath = path9.resolve(
|
|
4114
|
-
repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd()
|
|
4115
|
-
);
|
|
4116
|
-
const catalogDir = resolveCatalogDir(args, repoPath);
|
|
4117
|
-
const profile = fingerprintRepository(repoPath);
|
|
4118
|
-
const targetsIdx = args.indexOf("--targets");
|
|
4119
|
-
const initTargets = targetsIdx !== -1 && args[targetsIdx + 1] ? resolveRuntimeTargets(args[targetsIdx + 1], repoPath) : profile.detected_harnesses.length > 0 ? profile.detected_harnesses : [...CORE_RUNTIME_TARGETS];
|
|
4120
|
-
const configPayload = SkillOpsConfigSchema.parse({
|
|
4121
|
-
version: "1.0",
|
|
4122
|
-
catalog_dir: path9.relative(repoPath, catalogDir).replace(/\\/g, "/") || "catalog",
|
|
4123
|
-
token_economy: {
|
|
4124
|
-
startup_tax_budget: 2500,
|
|
4125
|
-
max_active_skills: 12,
|
|
4126
|
-
max_l1_tokens: 1500,
|
|
4127
|
-
max_similarity: 0.78
|
|
4506
|
+
// src/ui/help.ts
|
|
4507
|
+
var COMMAND_HELP_SPECS = [
|
|
4508
|
+
{
|
|
4509
|
+
name: "quickstart",
|
|
4510
|
+
aliases: ["setup"],
|
|
4511
|
+
pillar: "library",
|
|
4512
|
+
summary: "1-step onboarding: detect project stack, route optimal Skills & Subagents, sync to AI harnesses & wire MCP",
|
|
4513
|
+
usage: "npx skillops-cli quickstart [options]",
|
|
4514
|
+
options: [
|
|
4515
|
+
{
|
|
4516
|
+
flags: "--project, --repo, -p <dir>",
|
|
4517
|
+
description: "Target repository directory (default: current directory)"
|
|
4128
4518
|
},
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4519
|
+
{
|
|
4520
|
+
flags: "--harness, --targets, -t <list|auto|all>",
|
|
4521
|
+
description: "Target AI coding harnesses (e.g. claude,cursor,windsurf or auto/all)"
|
|
4132
4522
|
},
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
"",
|
|
4178
|
-
"
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
""
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4523
|
+
{
|
|
4524
|
+
flags: "--top, --max-skills <n>",
|
|
4525
|
+
description: "Maximum number of Skills to activate in the bundle (default: 12)"
|
|
4526
|
+
},
|
|
4527
|
+
{
|
|
4528
|
+
flags: "--skip-mcp, --no-mcp",
|
|
4529
|
+
description: "Skip automatic MCP server registration"
|
|
4530
|
+
},
|
|
4531
|
+
{
|
|
4532
|
+
flags: "--dry-run",
|
|
4533
|
+
description: "Preview planned changes without writing files to disk"
|
|
4534
|
+
},
|
|
4535
|
+
{
|
|
4536
|
+
flags: "--force",
|
|
4537
|
+
description: "Overwrite locally modified artifacts if drift is detected"
|
|
4538
|
+
},
|
|
4539
|
+
{
|
|
4540
|
+
flags: "--format, -f <table|json|markdown>",
|
|
4541
|
+
description: "Output report format (default: table)"
|
|
4542
|
+
}
|
|
4543
|
+
],
|
|
4544
|
+
examples: [
|
|
4545
|
+
{
|
|
4546
|
+
command: "npx skillops-cli quickstart",
|
|
4547
|
+
description: "Auto-detect stack & harnesses, install optimal Skills/Subagents, and configure MCP"
|
|
4548
|
+
},
|
|
4549
|
+
{
|
|
4550
|
+
command: "npx skillops-cli quickstart --harness cursor,claude --top 6",
|
|
4551
|
+
description: "Quickstart specifically for Cursor and Claude Code with top 6 Skills"
|
|
4552
|
+
}
|
|
4553
|
+
]
|
|
4554
|
+
},
|
|
4555
|
+
{
|
|
4556
|
+
name: "list",
|
|
4557
|
+
aliases: [],
|
|
4558
|
+
pillar: "library",
|
|
4559
|
+
summary: "Browse all bundled Hybrid Executable Skills & Context-Isolated Subagents with token budgets",
|
|
4560
|
+
usage: "npx skillops-cli list [options]",
|
|
4561
|
+
options: [
|
|
4562
|
+
{
|
|
4563
|
+
flags: "--catalog <dir>",
|
|
4564
|
+
description: "Custom catalog directory to list"
|
|
4565
|
+
},
|
|
4566
|
+
{
|
|
4567
|
+
flags: "--format, -f <table|json|markdown>",
|
|
4568
|
+
description: "Output format (default: table)"
|
|
4569
|
+
}
|
|
4570
|
+
],
|
|
4571
|
+
examples: [
|
|
4572
|
+
{
|
|
4573
|
+
command: "npx skillops-cli list",
|
|
4574
|
+
description: "Display all 10 Skills and 9 Subagents with L1/L2 token footprints"
|
|
4575
|
+
},
|
|
4576
|
+
{
|
|
4577
|
+
command: "npx skillops-cli info security-sast-gate",
|
|
4578
|
+
description: "Deep-inspect a specific Skill from the Library after listing"
|
|
4579
|
+
}
|
|
4580
|
+
]
|
|
4581
|
+
},
|
|
4582
|
+
{
|
|
4583
|
+
name: "search",
|
|
4584
|
+
aliases: ["find"],
|
|
4585
|
+
pillar: "library",
|
|
4586
|
+
summary: "Search the Library of Skills & Subagents by keyword, domain, framework, or routing signal",
|
|
4587
|
+
usage: "npx skillops-cli search <query> [options]",
|
|
4588
|
+
options: [
|
|
4589
|
+
{
|
|
4590
|
+
flags: "--catalog <dir>",
|
|
4591
|
+
description: "Custom catalog directory to search"
|
|
4592
|
+
},
|
|
4593
|
+
{
|
|
4594
|
+
flags: "--format, -f <table|json|markdown>",
|
|
4595
|
+
description: "Output format (default: table)"
|
|
4596
|
+
}
|
|
4597
|
+
],
|
|
4598
|
+
examples: [
|
|
4599
|
+
{
|
|
4600
|
+
command: "npx skillops-cli search security",
|
|
4601
|
+
description: "Find Skills and Subagents matching security/SAST/OWASP signals"
|
|
4602
|
+
},
|
|
4603
|
+
{
|
|
4604
|
+
command: "npx skillops-cli find postgres",
|
|
4605
|
+
description: "Find database & PostgreSQL optimization Skills in the Library"
|
|
4606
|
+
}
|
|
4607
|
+
]
|
|
4608
|
+
},
|
|
4609
|
+
{
|
|
4610
|
+
name: "info",
|
|
4611
|
+
aliases: ["show", "inspect"],
|
|
4612
|
+
pillar: "library",
|
|
4613
|
+
summary: "Inspect a Skill or Subagent's L1 routing triggers, L2 token budget, L3 scripts & how to use it in your AI",
|
|
4614
|
+
usage: "npx skillops-cli info <skill-or-subagent-name> [options]",
|
|
4615
|
+
options: [
|
|
4616
|
+
{
|
|
4617
|
+
flags: "--catalog <dir>",
|
|
4618
|
+
description: "Custom catalog directory"
|
|
4619
|
+
},
|
|
4620
|
+
{
|
|
4621
|
+
flags: "--format, -f <table|json|markdown>",
|
|
4622
|
+
description: "Output format (default: table)"
|
|
4623
|
+
}
|
|
4624
|
+
],
|
|
4625
|
+
examples: [
|
|
4626
|
+
{
|
|
4627
|
+
command: "npx skillops-cli info bug-reproducer-tdd",
|
|
4628
|
+
description: "Inspect routing triggers, L3 scripts & IDE invocation guide for bug-reproducer-tdd"
|
|
4629
|
+
},
|
|
4630
|
+
{
|
|
4631
|
+
command: "npx skillops-cli show red-team-breaker",
|
|
4632
|
+
description: "Inspect the red-team-breaker Context-Isolated Subagent card"
|
|
4633
|
+
}
|
|
4634
|
+
]
|
|
4635
|
+
},
|
|
4636
|
+
{
|
|
4637
|
+
name: "add",
|
|
4638
|
+
aliases: ["install", "i"],
|
|
4639
|
+
pillar: "library",
|
|
4640
|
+
summary: "Install one or more Skills or Subagents into your active AI coding harnesses",
|
|
4641
|
+
usage: "npx skillops-cli add <name...> [options]",
|
|
4642
|
+
options: [
|
|
4643
|
+
{
|
|
4644
|
+
flags: "--project, --repo, -p <dir>",
|
|
4645
|
+
description: "Target repository directory"
|
|
4646
|
+
},
|
|
4647
|
+
{
|
|
4648
|
+
flags: "--harness, --targets, -t <list|auto|all>",
|
|
4649
|
+
description: "Target AI coding harnesses (e.g. claude,cursor,windsurf or all)"
|
|
4650
|
+
},
|
|
4651
|
+
{
|
|
4652
|
+
flags: "--dry-run",
|
|
4653
|
+
description: "Preview planned file writes without modifying disk"
|
|
4654
|
+
},
|
|
4655
|
+
{
|
|
4656
|
+
flags: "--force",
|
|
4657
|
+
description: "Overwrite locally drifted files"
|
|
4658
|
+
},
|
|
4659
|
+
{
|
|
4660
|
+
flags: "--format, -f <table|json|markdown>",
|
|
4661
|
+
description: "Output format (default: table)"
|
|
4662
|
+
}
|
|
4663
|
+
],
|
|
4664
|
+
examples: [
|
|
4665
|
+
{
|
|
4666
|
+
command: "npx skillops-cli install security-sast-gate",
|
|
4667
|
+
description: "Install the security-sast-gate Skill into configured/default harnesses"
|
|
4668
|
+
},
|
|
4669
|
+
{
|
|
4670
|
+
command: "npx skillops-cli add mcp-architect context-scout --harness claude,cursor",
|
|
4671
|
+
description: "Install both a Skill and a Subagent into Claude & Cursor"
|
|
4672
|
+
}
|
|
4673
|
+
]
|
|
4674
|
+
},
|
|
4675
|
+
{
|
|
4676
|
+
name: "remove",
|
|
4677
|
+
aliases: ["uninstall", "rm"],
|
|
4678
|
+
pillar: "library",
|
|
4679
|
+
summary: "Surgically uninstall a synced Skill or Subagent from active AI harnesses",
|
|
4680
|
+
usage: "npx skillops-cli remove <name...> [options]",
|
|
4681
|
+
options: [
|
|
4682
|
+
{
|
|
4683
|
+
flags: "--project, --repo, -p <dir>",
|
|
4684
|
+
description: "Target repository directory"
|
|
4685
|
+
},
|
|
4686
|
+
{
|
|
4687
|
+
flags: "--harness, --targets, -t <list>",
|
|
4688
|
+
description: "Restrict removal to specific harnesses"
|
|
4689
|
+
},
|
|
4690
|
+
{
|
|
4691
|
+
flags: "--force",
|
|
4692
|
+
description: "Remove even if local SHA-256 drift is detected"
|
|
4693
|
+
}
|
|
4694
|
+
],
|
|
4695
|
+
examples: [
|
|
4696
|
+
{
|
|
4697
|
+
command: "npx skillops-cli uninstall mcp-architect",
|
|
4698
|
+
description: "Remove all transpiled runtime files for mcp-architect and update .skillops-lock.json"
|
|
4699
|
+
}
|
|
4700
|
+
]
|
|
4701
|
+
},
|
|
4702
|
+
{
|
|
4703
|
+
name: "route",
|
|
4704
|
+
aliases: ["recommend"],
|
|
4705
|
+
pillar: "library",
|
|
4706
|
+
summary: "Fingerprint repository stack and recommend the optimal collision-free <=12 Skill bundle",
|
|
4707
|
+
usage: "npx skillops-cli recommend [options]",
|
|
4708
|
+
options: [
|
|
4709
|
+
{
|
|
4710
|
+
flags: "--project, --repo, -p <dir>",
|
|
4711
|
+
description: "Repository directory to fingerprint"
|
|
4712
|
+
},
|
|
4713
|
+
{
|
|
4714
|
+
flags: "--task <prompt>",
|
|
4715
|
+
description: "Optional natural-language task prompt to boost task-specific Skills"
|
|
4716
|
+
},
|
|
4717
|
+
{
|
|
4718
|
+
flags: "--top, --max-skills <n>",
|
|
4719
|
+
description: "Maximum Skills to select (default: 12)"
|
|
4720
|
+
},
|
|
4721
|
+
{
|
|
4722
|
+
flags: "--max-l1-tokens <n>",
|
|
4723
|
+
description: "Maximum total L1 routing token budget (default: 1500)"
|
|
4724
|
+
},
|
|
4725
|
+
{
|
|
4726
|
+
flags: "--format, -f <table|json|markdown>",
|
|
4727
|
+
description: "Output format (default: table)"
|
|
4728
|
+
}
|
|
4729
|
+
],
|
|
4730
|
+
examples: [
|
|
4731
|
+
{
|
|
4732
|
+
command: "npx skillops-cli recommend",
|
|
4733
|
+
description: "Recommend the best Skills & Subagents for the current repository stack"
|
|
4734
|
+
},
|
|
4735
|
+
{
|
|
4736
|
+
command: 'npx skillops-cli route --task "optimize slow postgres queries in FastAPI" --top 4',
|
|
4737
|
+
description: "Route top 4 Skills tailored to a specific engineering task"
|
|
4738
|
+
}
|
|
4739
|
+
]
|
|
4740
|
+
},
|
|
4741
|
+
{
|
|
4742
|
+
name: "create",
|
|
4743
|
+
aliases: ["new"],
|
|
4744
|
+
pillar: "framework",
|
|
4745
|
+
summary: "Scaffold a custom Skill (SKILL.md + L3 script + evals.json) or Subagent (SUBAGENT.md) using the SkillOps Framework",
|
|
4746
|
+
usage: "npx skillops-cli create <skill|subagent> <kebab-case-name> [options]",
|
|
4747
|
+
options: [
|
|
4748
|
+
{
|
|
4749
|
+
flags: "--description, -d <text>",
|
|
4750
|
+
description: "Custom trigger description (automatically normalized to INV-SKILL-01..04 contracts)"
|
|
4751
|
+
},
|
|
4752
|
+
{
|
|
4753
|
+
flags: "--catalog <dir>",
|
|
4754
|
+
description: "Target catalog directory (default: ./catalog)"
|
|
4755
|
+
},
|
|
4756
|
+
{
|
|
4757
|
+
flags: "--force",
|
|
4758
|
+
description: "Overwrite existing Skill or Subagent template"
|
|
4759
|
+
},
|
|
4760
|
+
{
|
|
4761
|
+
flags: "--format, -f <table|json|markdown>",
|
|
4762
|
+
description: "Output format (default: table)"
|
|
4763
|
+
}
|
|
4764
|
+
],
|
|
4765
|
+
examples: [
|
|
4766
|
+
{
|
|
4767
|
+
command: "npx skillops-cli create skill graphql-schema-guard",
|
|
4768
|
+
description: "Scaffold a new INV-SKILL-01..03 compliant Skill with Level 3 script & eval suite"
|
|
4769
|
+
},
|
|
4770
|
+
{
|
|
4771
|
+
command: "npx skillops-cli create subagent migration-reviewer",
|
|
4772
|
+
description: "Scaffold a new INV-SKILL-04 compliant Context-Isolated Subagent"
|
|
4773
|
+
}
|
|
4774
|
+
]
|
|
4775
|
+
},
|
|
4776
|
+
{
|
|
4777
|
+
name: "audit",
|
|
4778
|
+
aliases: [],
|
|
4779
|
+
pillar: "framework",
|
|
4780
|
+
summary: "Run L1/L2/L3 token budget linter, Subagent contract verifier & TF-IDF cosine collision matrix",
|
|
4781
|
+
usage: "npx skillops-cli audit [options]",
|
|
4782
|
+
options: [
|
|
4783
|
+
{
|
|
4784
|
+
flags: "--catalog <dir>",
|
|
4785
|
+
description: "Catalog directory to audit"
|
|
4786
|
+
},
|
|
4787
|
+
{
|
|
4788
|
+
flags: "--require-layout",
|
|
4789
|
+
description: "Enforce full directory layout including evals/evals.json and executable L3 scripts"
|
|
4790
|
+
},
|
|
4791
|
+
{
|
|
4792
|
+
flags: "--strict",
|
|
4793
|
+
description: "Fail with non-zero exit code on HIGH_RISK collisions as well as BLOCKING collisions"
|
|
4794
|
+
},
|
|
4795
|
+
{
|
|
4796
|
+
flags: "--format, -f <table|json|github>",
|
|
4797
|
+
description: "Output format (default: table)"
|
|
4798
|
+
}
|
|
4799
|
+
],
|
|
4800
|
+
examples: [
|
|
4801
|
+
{
|
|
4802
|
+
command: "npx skillops-cli audit --require-layout --strict",
|
|
4803
|
+
description: "Certify that all Skills & Subagents pass token budgets and zero-collision invariants"
|
|
4804
|
+
}
|
|
4805
|
+
]
|
|
4806
|
+
},
|
|
4807
|
+
{
|
|
4808
|
+
name: "eval",
|
|
4809
|
+
aliases: [],
|
|
4810
|
+
pillar: "framework",
|
|
4811
|
+
summary: "Execute zero-API deterministic TDD benchmarks (with_skill vs without_skill) and export dashboards",
|
|
4812
|
+
usage: "npx skillops-cli eval [options]",
|
|
4813
|
+
options: [
|
|
4814
|
+
{
|
|
4815
|
+
flags: "--catalog <dir>",
|
|
4816
|
+
description: "Catalog directory to evaluate"
|
|
4817
|
+
},
|
|
4818
|
+
{
|
|
4819
|
+
flags: "--skill <name>",
|
|
4820
|
+
description: "Evaluate a single Skill by name"
|
|
4821
|
+
},
|
|
4822
|
+
{
|
|
4823
|
+
flags: "--out <file>",
|
|
4824
|
+
description: "Write formatted benchmark report (e.g. HTML/Markdown) to disk"
|
|
4825
|
+
},
|
|
4826
|
+
{
|
|
4827
|
+
flags: "--strict",
|
|
4828
|
+
description: "Exit with code 4 if any Skill fails CERTIFIED_IMPROVEMENT"
|
|
4829
|
+
},
|
|
4830
|
+
{
|
|
4831
|
+
flags: "--format, -f <table|json|markdown|html|github>",
|
|
4832
|
+
description: "Output format (default: table)"
|
|
4833
|
+
}
|
|
4834
|
+
],
|
|
4835
|
+
examples: [
|
|
4836
|
+
{
|
|
4837
|
+
command: "npx skillops-cli eval --strict",
|
|
4838
|
+
description: "Run deterministic A/B evals across all Skills in the catalog"
|
|
4839
|
+
},
|
|
4840
|
+
{
|
|
4841
|
+
command: "npx skillops-cli eval --format html --out benchmark.html",
|
|
4842
|
+
description: "Export a standalone HTML benchmark dashboard"
|
|
4843
|
+
}
|
|
4844
|
+
]
|
|
4845
|
+
},
|
|
4846
|
+
{
|
|
4847
|
+
name: "init",
|
|
4848
|
+
aliases: [],
|
|
4849
|
+
pillar: "runtime",
|
|
4850
|
+
summary: "Scaffold declarative .skillops.yml and detect repository AI coding harnesses",
|
|
4851
|
+
usage: "npx skillops-cli init [options]",
|
|
4852
|
+
options: [
|
|
4853
|
+
{
|
|
4854
|
+
flags: "--project, --repo, -p <dir>",
|
|
4855
|
+
description: "Target repository directory"
|
|
4856
|
+
},
|
|
4857
|
+
{
|
|
4858
|
+
flags: "--harness, --targets, -t <list|auto|all>",
|
|
4859
|
+
description: "Initial target harnesses to record in .skillops.yml"
|
|
4860
|
+
}
|
|
4861
|
+
],
|
|
4862
|
+
examples: [
|
|
4863
|
+
{
|
|
4864
|
+
command: "npx skillops-cli init",
|
|
4865
|
+
description: "Create .skillops.yml with auto-detected stack and harnesses"
|
|
4866
|
+
}
|
|
4867
|
+
]
|
|
4868
|
+
},
|
|
4869
|
+
{
|
|
4870
|
+
name: "sync",
|
|
4871
|
+
aliases: [],
|
|
4872
|
+
pillar: "runtime",
|
|
4873
|
+
summary: "Transpile routed Skills & Subagents across up to 20 AI coding agents with SHA-256 lockfile protection",
|
|
4874
|
+
usage: "npx skillops-cli sync [options]",
|
|
4875
|
+
options: [
|
|
4876
|
+
{
|
|
4877
|
+
flags: "--project, --repo, -p <dir>",
|
|
4878
|
+
description: "Target repository directory"
|
|
4879
|
+
},
|
|
4880
|
+
{
|
|
4881
|
+
flags: "--harness, --targets, -t <list|auto|all>",
|
|
4882
|
+
description: "Target AI coding harnesses (default: .skillops.yml targets or core 5)"
|
|
4883
|
+
},
|
|
4884
|
+
{
|
|
4885
|
+
flags: "--top, --max-skills <n>",
|
|
4886
|
+
description: "Maximum Skills to sync"
|
|
4887
|
+
},
|
|
4888
|
+
{
|
|
4889
|
+
flags: "--dry-run",
|
|
4890
|
+
description: "Preview planned artifacts without writing files"
|
|
4891
|
+
},
|
|
4892
|
+
{
|
|
4893
|
+
flags: "--force",
|
|
4894
|
+
description: "Overwrite locally drifted files"
|
|
4895
|
+
}
|
|
4896
|
+
],
|
|
4897
|
+
examples: [
|
|
4898
|
+
{
|
|
4899
|
+
command: "npx skillops-cli sync --auto",
|
|
4900
|
+
description: "Sync optimal Skills & Subagents into all AI harnesses detected in the repo"
|
|
4901
|
+
},
|
|
4902
|
+
{
|
|
4903
|
+
command: "npx skillops-cli sync --harness all",
|
|
4904
|
+
description: "Transpile across all 20 supported AI coding agents"
|
|
4905
|
+
}
|
|
4906
|
+
]
|
|
4907
|
+
},
|
|
4908
|
+
{
|
|
4909
|
+
name: "clean",
|
|
4910
|
+
aliases: [],
|
|
4911
|
+
pillar: "runtime",
|
|
4912
|
+
summary: "Remove all SkillOps-managed runtime artifacts tracked in .skillops-lock.json",
|
|
4913
|
+
usage: "npx skillops-cli clean [options]",
|
|
4914
|
+
options: [
|
|
4915
|
+
{
|
|
4916
|
+
flags: "--project, --repo, -p <dir>",
|
|
4917
|
+
description: "Target repository directory"
|
|
4918
|
+
},
|
|
4919
|
+
{
|
|
4920
|
+
flags: "--dry-run",
|
|
4921
|
+
description: "Preview files that would be removed"
|
|
4922
|
+
},
|
|
4923
|
+
{
|
|
4924
|
+
flags: "--force",
|
|
4925
|
+
description: "Remove even if local edits are detected"
|
|
4926
|
+
}
|
|
4927
|
+
],
|
|
4928
|
+
examples: [
|
|
4929
|
+
{
|
|
4930
|
+
command: "npx skillops-cli clean",
|
|
4931
|
+
description: "Cleanly remove all synced artifacts and .skillops-lock.json"
|
|
4932
|
+
}
|
|
4933
|
+
]
|
|
4934
|
+
},
|
|
4935
|
+
{
|
|
4936
|
+
name: "tokens",
|
|
4937
|
+
aliases: ["budget"],
|
|
4938
|
+
pillar: "runtime",
|
|
4939
|
+
summary: "Analyze repository Startup Token Tax, Progressive Disclosure savings & context bloat hotspots",
|
|
4940
|
+
usage: "npx skillops-cli budget [options]",
|
|
4941
|
+
options: [
|
|
4942
|
+
{
|
|
4943
|
+
flags: "--project, --repo, -p <dir>",
|
|
4944
|
+
description: "Repository directory to inspect"
|
|
4945
|
+
},
|
|
4946
|
+
{
|
|
4947
|
+
flags: "--budget <tokens>",
|
|
4948
|
+
description: "Startup Token Tax ceiling (default: 2500)"
|
|
4949
|
+
},
|
|
4950
|
+
{
|
|
4951
|
+
flags: "--strict",
|
|
4952
|
+
description: "Fail with exit code 3 if Startup Token Tax exceeds budget"
|
|
4953
|
+
},
|
|
4954
|
+
{
|
|
4955
|
+
flags: "--format, -f <table|json|markdown|github>",
|
|
4956
|
+
description: "Output format (default: table)"
|
|
4957
|
+
}
|
|
4958
|
+
],
|
|
4959
|
+
examples: [
|
|
4960
|
+
{
|
|
4961
|
+
command: "npx skillops-cli budget",
|
|
4962
|
+
description: "Inspect repository Startup Token Tax and Progressive Disclosure savings"
|
|
4963
|
+
}
|
|
4964
|
+
]
|
|
4965
|
+
},
|
|
4966
|
+
{
|
|
4967
|
+
name: "doctor",
|
|
4968
|
+
aliases: ["status"],
|
|
4969
|
+
pillar: "runtime",
|
|
4970
|
+
summary: "Diagnose Node/Python environment, .skillops.yml validity, lockfile SHA-256 drift & Startup Token Tax",
|
|
4971
|
+
usage: "npx skillops-cli doctor [options]",
|
|
4972
|
+
options: [
|
|
4973
|
+
{
|
|
4974
|
+
flags: "--project, --repo, -p <dir>",
|
|
4975
|
+
description: "Repository directory to diagnose"
|
|
4976
|
+
},
|
|
4977
|
+
{
|
|
4978
|
+
flags: "--strict",
|
|
4979
|
+
description: "Fail on warnings as well as errors"
|
|
4980
|
+
},
|
|
4981
|
+
{
|
|
4982
|
+
flags: "--format, -f <table|json|markdown|github>",
|
|
4983
|
+
description: "Output format (default: table)"
|
|
4984
|
+
}
|
|
4985
|
+
],
|
|
4986
|
+
examples: [
|
|
4987
|
+
{
|
|
4988
|
+
command: "npx skillops-cli doctor",
|
|
4989
|
+
description: "Run full repository & lockfile health check"
|
|
4990
|
+
}
|
|
4991
|
+
]
|
|
4992
|
+
},
|
|
4993
|
+
{
|
|
4994
|
+
name: "harnesses",
|
|
4995
|
+
aliases: [],
|
|
4996
|
+
pillar: "runtime",
|
|
4997
|
+
summary: "List all 20 supported AI coding agents, target paths, MCP support & repository detection status",
|
|
4998
|
+
usage: "npx skillops-cli harnesses [options]",
|
|
4999
|
+
options: [
|
|
5000
|
+
{
|
|
5001
|
+
flags: "--project, --repo, -p <dir>",
|
|
5002
|
+
description: "Repository directory to scan for AI harnesses"
|
|
5003
|
+
},
|
|
5004
|
+
{
|
|
5005
|
+
flags: "--format, -f <table|json|markdown>",
|
|
5006
|
+
description: "Output format (default: table)"
|
|
5007
|
+
}
|
|
5008
|
+
],
|
|
5009
|
+
examples: [
|
|
5010
|
+
{
|
|
5011
|
+
command: "npx skillops-cli harnesses",
|
|
5012
|
+
description: "View all 20 supported AI coding IDEs/CLI agents and which ones are active in your repo"
|
|
5013
|
+
}
|
|
5014
|
+
]
|
|
5015
|
+
},
|
|
5016
|
+
{
|
|
5017
|
+
name: "mcp-setup",
|
|
5018
|
+
aliases: [],
|
|
5019
|
+
pillar: "runtime",
|
|
5020
|
+
summary: "Register SkillOps MCP server across all 13 MCP-compatible AI coding IDEs & agents",
|
|
5021
|
+
usage: "npx skillops-cli mcp-setup [options]",
|
|
5022
|
+
options: [
|
|
5023
|
+
{
|
|
5024
|
+
flags: "--project, --repo, -p <dir>",
|
|
5025
|
+
description: "Target repository directory"
|
|
5026
|
+
},
|
|
5027
|
+
{
|
|
5028
|
+
flags: "--harness, --targets, -t <list|auto|all>",
|
|
5029
|
+
description: "Target harnesses to configure MCP for"
|
|
5030
|
+
},
|
|
5031
|
+
{
|
|
5032
|
+
flags: "--dry-run",
|
|
5033
|
+
description: "Preview MCP config files without writing"
|
|
5034
|
+
}
|
|
5035
|
+
],
|
|
5036
|
+
examples: [
|
|
5037
|
+
{
|
|
5038
|
+
command: "npx skillops-cli mcp-setup --auto",
|
|
5039
|
+
description: "Automatically wire SkillOps MCP server into detected IDEs (.mcp.json, .cursor/mcp.json, etc.)"
|
|
5040
|
+
}
|
|
5041
|
+
]
|
|
5042
|
+
},
|
|
5043
|
+
{
|
|
5044
|
+
name: "mcp",
|
|
5045
|
+
aliases: [],
|
|
5046
|
+
pillar: "runtime",
|
|
5047
|
+
summary: "Start native JSON-RPC 2.0 stdio Model Context Protocol server",
|
|
5048
|
+
usage: "npx skillops-cli mcp [options]",
|
|
5049
|
+
options: [
|
|
5050
|
+
{
|
|
5051
|
+
flags: "--project, --repo, -p <dir>",
|
|
5052
|
+
description: "Repository root served over MCP"
|
|
5053
|
+
}
|
|
5054
|
+
],
|
|
5055
|
+
examples: [
|
|
5056
|
+
{
|
|
5057
|
+
command: "npx skillops-cli mcp",
|
|
5058
|
+
description: "Start stdio MCP server (invoked automatically by AI IDEs)"
|
|
5059
|
+
}
|
|
5060
|
+
]
|
|
5061
|
+
}
|
|
5062
|
+
];
|
|
5063
|
+
function findCommandHelpSpec(commandOrAlias) {
|
|
5064
|
+
const normalized = commandOrAlias.trim().toLowerCase();
|
|
5065
|
+
return COMMAND_HELP_SPECS.find(
|
|
5066
|
+
(spec) => spec.name === normalized || spec.aliases.includes(normalized)
|
|
5067
|
+
);
|
|
5068
|
+
}
|
|
5069
|
+
function renderGlobalHelp(version, colorEnabled) {
|
|
5070
|
+
const c = colorEnabled;
|
|
5071
|
+
const banner = renderHeaderBanner(
|
|
5072
|
+
"The Zero-API Framework & Library for High-End AI Coding Skills & Context-Isolated Subagents",
|
|
5073
|
+
{
|
|
5074
|
+
version,
|
|
5075
|
+
colorEnabled: c
|
|
5076
|
+
}
|
|
5077
|
+
);
|
|
5078
|
+
const quickstartBox = renderBox(
|
|
5079
|
+
[
|
|
5080
|
+
`${style.bold(style.green("npx skillops-cli quickstart", c), c)} ${style.dim("# 1-step setup: detect stack, install Skills/Subagents & wire MCP", c)}`,
|
|
5081
|
+
`${style.bold(style.cyan("npx skillops-cli info <name>", c), c)} ${style.dim("# Inspect any Skill/Subagent & see how to invoke it in your AI", c)}`,
|
|
5082
|
+
`${style.bold(style.magenta("npx skillops-cli create skill <name>", c), c)} ${style.dim("# Scaffold your own custom Skill using the SkillOps Framework", c)}`
|
|
5083
|
+
],
|
|
5084
|
+
{
|
|
5085
|
+
title: "\u{1F680} 10-Second Quickstart (Library + Framework)",
|
|
5086
|
+
borderColor: style.cyan,
|
|
5087
|
+
minWidth: 88,
|
|
5088
|
+
colorEnabled: c
|
|
5089
|
+
}
|
|
5090
|
+
);
|
|
5091
|
+
const formatCmdSection = (pillar) => {
|
|
5092
|
+
const specs = COMMAND_HELP_SPECS.filter((s) => s.pillar === pillar);
|
|
5093
|
+
return renderTable(
|
|
5094
|
+
["Command", "Aliases", "Description"],
|
|
5095
|
+
specs.map((s) => [
|
|
5096
|
+
style.bold(style.cyan(s.name, c), c),
|
|
5097
|
+
s.aliases.length > 0 ? style.dim(s.aliases.join(", "), c) : style.dim("\u2014", c),
|
|
5098
|
+
s.summary
|
|
5099
|
+
]),
|
|
5100
|
+
{ colorEnabled: c, indent: 2 }
|
|
5101
|
+
);
|
|
5102
|
+
};
|
|
5103
|
+
const globalFlagsTable = renderTable(
|
|
5104
|
+
["Global Flag", "Description"],
|
|
5105
|
+
[
|
|
5106
|
+
[
|
|
5107
|
+
style.cyan("--project, --repo, -p <dir>", c),
|
|
5108
|
+
"Target project/repository directory (default: current working directory)"
|
|
5109
|
+
],
|
|
5110
|
+
[
|
|
5111
|
+
style.cyan("--harness, --targets, -t <list|auto|all>", c),
|
|
5112
|
+
"Target AI coding harnesses (e.g. claude,cursor,windsurf,copilot or auto/all)"
|
|
5113
|
+
],
|
|
5114
|
+
[
|
|
5115
|
+
style.cyan("--format, -f <table|json|markdown|github>", c),
|
|
5116
|
+
"Output format for reports and CI pipelines (default: table)"
|
|
5117
|
+
],
|
|
5118
|
+
[
|
|
5119
|
+
style.cyan("--help, -h / --version, -V", c),
|
|
5120
|
+
"Show global or per-command help (`npx skillops-cli <cmd> --help`) / CLI version"
|
|
5121
|
+
]
|
|
5122
|
+
],
|
|
5123
|
+
{ colorEnabled: c, indent: 2 }
|
|
5124
|
+
);
|
|
5125
|
+
return [
|
|
5126
|
+
`SkillOps v${version} \u2014 The Zero-API NPX Engine for High-End AI Skills & Subagents`,
|
|
5127
|
+
"Usage: npx skillops-cli <command> [options]",
|
|
5128
|
+
"",
|
|
5129
|
+
banner,
|
|
5130
|
+
quickstartBox,
|
|
5131
|
+
renderSectionHeader(
|
|
5132
|
+
"\u{1F4DA} 1. Library of High-End Skills & Subagents (Explore, Search & Install)",
|
|
5133
|
+
c
|
|
5134
|
+
),
|
|
5135
|
+
formatCmdSection("library"),
|
|
5136
|
+
renderSectionHeader(
|
|
5137
|
+
"\u{1F6E0}\uFE0F 2. Framework for Building Custom Skills & Subagents (Scaffold, Audit & Certify)",
|
|
5138
|
+
c
|
|
5139
|
+
),
|
|
5140
|
+
formatCmdSection("framework"),
|
|
5141
|
+
renderSectionHeader(
|
|
5142
|
+
"\u2699\uFE0F 3. Universal Runtime Sync, Token Economy & Diagnostics (Top 20 AI Harnesses)",
|
|
5143
|
+
c
|
|
5144
|
+
),
|
|
5145
|
+
formatCmdSection("runtime"),
|
|
5146
|
+
renderSectionHeader("\u{1F310} Global Options", c),
|
|
5147
|
+
globalFlagsTable,
|
|
5148
|
+
"",
|
|
5149
|
+
` ${style.dim("\u{1F4A1} Tip: Run", c)} ${style.bold(style.cyan("npx skillops-cli <command> --help", c), c)} ${style.dim("for detailed flags and copy-paste examples.", c)}`,
|
|
5150
|
+
""
|
|
5151
|
+
].join("\n");
|
|
5152
|
+
}
|
|
5153
|
+
function renderCommandHelp(spec, version, colorEnabled) {
|
|
5154
|
+
const c = colorEnabled;
|
|
5155
|
+
const pillarBadge = spec.pillar === "library" ? formatBadge("info", "LIBRARY", c) : spec.pillar === "framework" ? formatBadge("pass", "FRAMEWORK", c) : formatBadge("subagent", "RUNTIME", c);
|
|
5156
|
+
const aliasText = spec.aliases.length > 0 ? ` (aliases: ${spec.aliases.map((a) => style.cyan(a, c)).join(", ")})` : "";
|
|
5157
|
+
const headerBox = renderBox(
|
|
5158
|
+
[
|
|
5159
|
+
`${pillarBadge} ${style.bold(style.cyan(`skillops ${spec.name}`, c), c)}${aliasText}`,
|
|
5160
|
+
"",
|
|
5161
|
+
spec.summary,
|
|
5162
|
+
"",
|
|
5163
|
+
`${style.bold("Usage:", c)} ${style.green(spec.usage, c)}`
|
|
5164
|
+
],
|
|
5165
|
+
{
|
|
5166
|
+
title: `SkillOps v${version} \u2014 Command Reference`,
|
|
5167
|
+
borderColor: style.cyan,
|
|
5168
|
+
minWidth: 78,
|
|
5169
|
+
colorEnabled: c
|
|
5170
|
+
}
|
|
5171
|
+
);
|
|
5172
|
+
const optionsSection = spec.options.length > 0 ? [
|
|
5173
|
+
renderSectionHeader("Options & Flags", c),
|
|
5174
|
+
renderTable(
|
|
5175
|
+
["Flag", "Description"],
|
|
5176
|
+
spec.options.map((o) => [style.cyan(o.flags, c), o.description]),
|
|
5177
|
+
{ colorEnabled: c, indent: 2 }
|
|
5178
|
+
)
|
|
5179
|
+
].join("\n") : "";
|
|
5180
|
+
const examplesSection = renderNextSteps(spec.examples, c);
|
|
5181
|
+
return [headerBox, optionsSection, examplesSection].filter(Boolean).join("\n");
|
|
5182
|
+
}
|
|
5183
|
+
|
|
5184
|
+
// src/framework/index.ts
|
|
5185
|
+
function extractMarkdownHeadings(markdownBody) {
|
|
5186
|
+
const headings = [];
|
|
5187
|
+
for (const rawLine of markdownBody.split(/\r?\n/)) {
|
|
5188
|
+
const trimmed = rawLine.trim();
|
|
5189
|
+
if (trimmed.startsWith("## ")) {
|
|
5190
|
+
headings.push(trimmed.slice(3).trim());
|
|
5191
|
+
}
|
|
5192
|
+
}
|
|
5193
|
+
return headings;
|
|
5194
|
+
}
|
|
5195
|
+
function inspectCatalogItem(catalogDir, itemName) {
|
|
5196
|
+
const needle = itemName.trim().toLowerCase();
|
|
5197
|
+
for (const skillDir of discoverSkillDirectories(catalogDir)) {
|
|
5198
|
+
const parsed = parseSkillDirectory(skillDir, { requireLayout: false });
|
|
5199
|
+
if (parsed.manifest.name.toLowerCase() === needle) {
|
|
5200
|
+
const l1Tokens = parsed.manifest.description_tokens;
|
|
5201
|
+
const l2Lines = parsed.manifest.body_lines;
|
|
5202
|
+
const l2Tokens = parsed.manifest.body_tokens;
|
|
5203
|
+
const totalRaw = l1Tokens + l2Tokens;
|
|
5204
|
+
const savingsPct = totalRaw > 0 ? Number(((totalRaw - l1Tokens) / totalRaw * 100).toFixed(1)) : 0;
|
|
5205
|
+
let evalCasesCount = 0;
|
|
5206
|
+
if (parsed.evalsPath && fs9.existsSync(parsed.evalsPath)) {
|
|
5207
|
+
try {
|
|
5208
|
+
const rawEvals = JSON.parse(
|
|
5209
|
+
fs9.readFileSync(parsed.evalsPath, "utf-8")
|
|
5210
|
+
);
|
|
5211
|
+
evalCasesCount = Array.isArray(rawEvals.evals) ? rawEvals.evals.length : 0;
|
|
5212
|
+
} catch {
|
|
5213
|
+
evalCasesCount = 0;
|
|
5214
|
+
}
|
|
5215
|
+
}
|
|
5216
|
+
return {
|
|
5217
|
+
kind: "skill",
|
|
5218
|
+
name: parsed.manifest.name,
|
|
5219
|
+
description: parsed.manifest.description,
|
|
5220
|
+
path: parsed.skillMdPath.replace(/\\/g, "/"),
|
|
5221
|
+
l1Tokens,
|
|
5222
|
+
l2Lines,
|
|
5223
|
+
l2Tokens,
|
|
5224
|
+
savingsPct,
|
|
5225
|
+
scripts: parsed.manifest.entrypoints.scripts.map((s) => s.path),
|
|
5226
|
+
evalCasesCount,
|
|
5227
|
+
tools: [],
|
|
5228
|
+
maxOutputTokens: null,
|
|
5229
|
+
isolation: null,
|
|
5230
|
+
headings: extractMarkdownHeadings(parsed.markdownBody)
|
|
5231
|
+
};
|
|
5232
|
+
}
|
|
5233
|
+
}
|
|
5234
|
+
for (const subPath of discoverSubagentFiles(catalogDir)) {
|
|
5235
|
+
const parsed = parseSubagentFile(subPath);
|
|
5236
|
+
if (parsed.manifest.name.toLowerCase() === needle) {
|
|
5237
|
+
return {
|
|
5238
|
+
kind: "subagent",
|
|
5239
|
+
name: parsed.manifest.name,
|
|
5240
|
+
description: parsed.manifest.description,
|
|
5241
|
+
path: parsed.filePath.replace(/\\/g, "/"),
|
|
5242
|
+
l1Tokens: parsed.manifest.system_prompt_tokens,
|
|
5243
|
+
l2Lines: parsed.manifest.system_prompt_lines,
|
|
5244
|
+
l2Tokens: parsed.manifest.system_prompt_tokens,
|
|
5245
|
+
savingsPct: 0,
|
|
5246
|
+
scripts: [],
|
|
5247
|
+
evalCasesCount: 0,
|
|
5248
|
+
tools: parsed.manifest.tools,
|
|
5249
|
+
maxOutputTokens: parsed.manifest.max_output_tokens,
|
|
5250
|
+
isolation: parsed.manifest.context_isolation ? "context-isolated" : "shared",
|
|
5251
|
+
headings: extractMarkdownHeadings(parsed.systemPromptBody)
|
|
5252
|
+
};
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
5255
|
+
throw new Error(
|
|
5256
|
+
`Skill or Subagent '${itemName}' not found in catalog '${catalogDir.replace(/\\/g, "/")}'. Run 'npx skillops-cli list' to see all available items.`
|
|
5257
|
+
);
|
|
5258
|
+
}
|
|
5259
|
+
function formatCatalogItemInfo(info, format = "table", colorEnabled) {
|
|
5260
|
+
if (format === "json") {
|
|
5261
|
+
return `${JSON.stringify(info, null, 2)}
|
|
5262
|
+
`;
|
|
5263
|
+
}
|
|
5264
|
+
if (format === "markdown") {
|
|
5265
|
+
const lines = [
|
|
5266
|
+
`## ${info.kind === "skill" ? "Skill" : "Subagent"}: \`${info.name}\``,
|
|
5267
|
+
"",
|
|
5268
|
+
`- **Type:** ${info.kind.toUpperCase()}`,
|
|
5269
|
+
`- **Description:** ${info.description}`,
|
|
5270
|
+
info.kind === "skill" ? `- **Token Economy:** L1=${info.l1Tokens}t | L2=${info.l2Lines} lines (${info.l2Tokens}t) | Savings=${info.savingsPct}%` : `- **Isolation Contract:** Prompt=${info.l1Tokens}t | Max Output <= ${info.maxOutputTokens}t | Isolation=${info.isolation} | Tools=${info.tools.join(", ")}`,
|
|
5271
|
+
...info.scripts.length > 0 ? [`- **Executable L3 Scripts:** \`${info.scripts.join("`, `")}\``] : [],
|
|
5272
|
+
...info.headings.length > 0 ? [`- **Workflow Sections:** ${info.headings.join(" \u2022 ")}`] : [],
|
|
5273
|
+
"",
|
|
5274
|
+
"### Quick Install",
|
|
5275
|
+
"```bash",
|
|
5276
|
+
`npx skillops-cli add ${info.name}`,
|
|
5277
|
+
"```",
|
|
5278
|
+
""
|
|
5279
|
+
];
|
|
5280
|
+
return lines.join("\n");
|
|
5281
|
+
}
|
|
5282
|
+
const c = colorEnabled;
|
|
5283
|
+
const badge = info.kind === "skill" ? formatBadge("skill", "HYBRID EXECUTABLE SKILL", c) : formatBadge("subagent", "CONTEXT-ISOLATED SUBAGENT", c);
|
|
5284
|
+
const cardLines = [
|
|
5285
|
+
`${badge} ${style.bold(style.cyan(info.name, c), c)}`,
|
|
5286
|
+
"",
|
|
5287
|
+
`${style.bold("Description:", c)}`,
|
|
5288
|
+
` ${info.description}`,
|
|
5289
|
+
""
|
|
5290
|
+
];
|
|
5291
|
+
if (info.kind === "skill") {
|
|
5292
|
+
cardLines.push(
|
|
5293
|
+
`${style.bold("Token Economy (INV-SKILL-01..03):", c)}`,
|
|
5294
|
+
` \u2022 L1 Routing Footprint : ${style.green(`${info.l1Tokens} tokens`, c)} (<= 160 budget)`,
|
|
5295
|
+
` \u2022 L2 Playbook Body : ${info.l2Lines} lines (${info.l2Tokens} tokens, <= 250 lines)`,
|
|
5296
|
+
` \u2022 Progressive Savings : ${style.bold(style.green(`${info.savingsPct}%`, c), c)} Startup Token Tax saved until triggered`,
|
|
5297
|
+
` \u2022 L3 Scripts (${info.scripts.length}) : ${info.scripts.length > 0 ? style.cyan(info.scripts.join(", "), c) : "none"}`,
|
|
5298
|
+
` \u2022 TDD Eval Cases : ${info.evalCasesCount} deterministic benchmark cases`
|
|
5299
|
+
);
|
|
5300
|
+
} else {
|
|
5301
|
+
cardLines.push(
|
|
5302
|
+
`${style.bold("Context Isolation Contract (INV-SKILL-04):", c)}`,
|
|
5303
|
+
` \u2022 System Prompt Tokens : ${style.green(`${info.l1Tokens} tokens`, c)}`,
|
|
5304
|
+
` \u2022 Max Output Cap : <= ${style.bold(`${info.maxOutputTokens} tokens`, c)}`,
|
|
5305
|
+
` \u2022 Isolation Mode : ${style.cyan(info.isolation ?? "context-isolated", c)}`,
|
|
5306
|
+
` \u2022 Allowed Tools (${info.tools.length}) : ${info.tools.join(", ")}`
|
|
5307
|
+
);
|
|
5308
|
+
}
|
|
5309
|
+
if (info.headings.length > 0) {
|
|
5310
|
+
cardLines.push(
|
|
5311
|
+
"",
|
|
5312
|
+
`${style.bold("Workflow Playbook Sections:", c)}`,
|
|
5313
|
+
...info.headings.slice(0, 6).map((h) => ` \u2022 ${h}`)
|
|
5314
|
+
);
|
|
5315
|
+
}
|
|
5316
|
+
cardLines.push(
|
|
5317
|
+
"",
|
|
5318
|
+
`${style.bold("How to Invoke in Your AI Agent:", c)}`,
|
|
5319
|
+
` \u2022 ${style.bold("Install into repo:", c)} ${style.green(`npx skillops-cli add ${info.name}`, c)}`,
|
|
5320
|
+
info.kind === "skill" ? ` \u2022 ${style.bold("Cursor / Copilot / Claude:", c)} Reference ${style.cyan(`@${info.name}`, c)} or ${style.cyan(`/${info.name}`, c)} in chat` : ` \u2022 ${style.bold("Delegate Subagent:", c)} Ask your main agent to delegate heavy exploration to ${style.magenta(`@${info.name}`, c)}`
|
|
5321
|
+
);
|
|
5322
|
+
const box = renderBox(cardLines, {
|
|
5323
|
+
title: `\u{1F4E6} SkillOps Library Inspector \u2014 ${info.name}`,
|
|
5324
|
+
borderColor: info.kind === "skill" ? style.cyan : style.magenta,
|
|
5325
|
+
minWidth: 74,
|
|
5326
|
+
colorEnabled: c
|
|
5327
|
+
});
|
|
5328
|
+
const nextSteps = renderNextSteps(
|
|
5329
|
+
[
|
|
5330
|
+
{
|
|
5331
|
+
command: `npx skillops-cli add ${info.name}`,
|
|
5332
|
+
description: `Install '${info.name}' into your active AI coding agents`
|
|
5333
|
+
},
|
|
5334
|
+
...info.kind === "skill" ? [
|
|
5335
|
+
{
|
|
5336
|
+
command: `npx skillops-cli eval --skill ${info.name}`,
|
|
5337
|
+
description: `Run deterministic TDD benchmark for '${info.name}'`
|
|
5338
|
+
}
|
|
5339
|
+
] : [],
|
|
5340
|
+
{
|
|
5341
|
+
command: "npx skillops-cli list",
|
|
5342
|
+
description: "Browse all 10 Skills & 9 Subagents in the Library"
|
|
5343
|
+
}
|
|
5344
|
+
],
|
|
5345
|
+
c
|
|
5346
|
+
);
|
|
5347
|
+
return `${box}
|
|
5348
|
+
${nextSteps}`;
|
|
5349
|
+
}
|
|
5350
|
+
function searchCatalog(catalogDir, query) {
|
|
5351
|
+
const rawTerms = query.trim().toLowerCase().split(/[^a-z0-9_-]+/).filter((t) => t.length > 0);
|
|
5352
|
+
const matches = [];
|
|
5353
|
+
for (const skillDir of discoverSkillDirectories(catalogDir)) {
|
|
5354
|
+
const parsed = parseSkillDirectory(skillDir, { requireLayout: false });
|
|
5355
|
+
const haystackName = parsed.manifest.name.toLowerCase();
|
|
5356
|
+
const haystackDesc = parsed.manifest.description.toLowerCase();
|
|
5357
|
+
const haystackBody = parsed.markdownBody.toLowerCase();
|
|
5358
|
+
let score = rawTerms.length === 0 ? 1 : 0;
|
|
5359
|
+
for (const term of rawTerms) {
|
|
5360
|
+
if (haystackName.includes(term)) {
|
|
5361
|
+
score += 5;
|
|
5362
|
+
}
|
|
5363
|
+
if (haystackDesc.includes(term)) {
|
|
5364
|
+
score += 3;
|
|
5365
|
+
}
|
|
5366
|
+
if (haystackBody.includes(term)) {
|
|
5367
|
+
score += 1;
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
5370
|
+
if (score > 0) {
|
|
5371
|
+
matches.push({
|
|
5372
|
+
kind: "skill",
|
|
5373
|
+
name: parsed.manifest.name,
|
|
5374
|
+
score,
|
|
5375
|
+
tokens: parsed.manifest.description_tokens,
|
|
5376
|
+
summary: parsed.manifest.description.length > 78 ? `${parsed.manifest.description.slice(0, 75)}...` : parsed.manifest.description
|
|
5377
|
+
});
|
|
5378
|
+
}
|
|
5379
|
+
}
|
|
5380
|
+
for (const subPath of discoverSubagentFiles(catalogDir)) {
|
|
5381
|
+
const parsed = parseSubagentFile(subPath);
|
|
5382
|
+
const haystackName = parsed.manifest.name.toLowerCase();
|
|
5383
|
+
const haystackDesc = parsed.manifest.description.toLowerCase();
|
|
5384
|
+
const haystackTools = parsed.manifest.tools.join(" ").toLowerCase();
|
|
5385
|
+
let score = rawTerms.length === 0 ? 1 : 0;
|
|
5386
|
+
for (const term of rawTerms) {
|
|
5387
|
+
if (haystackName.includes(term)) {
|
|
5388
|
+
score += 5;
|
|
5389
|
+
}
|
|
5390
|
+
if (haystackDesc.includes(term)) {
|
|
5391
|
+
score += 3;
|
|
5392
|
+
}
|
|
5393
|
+
if (haystackTools.includes(term)) {
|
|
5394
|
+
score += 2;
|
|
5395
|
+
}
|
|
5396
|
+
}
|
|
5397
|
+
if (score > 0) {
|
|
5398
|
+
matches.push({
|
|
5399
|
+
kind: "subagent",
|
|
5400
|
+
name: parsed.manifest.name,
|
|
5401
|
+
score,
|
|
5402
|
+
tokens: parsed.manifest.system_prompt_tokens,
|
|
5403
|
+
summary: parsed.manifest.description.length > 78 ? `${parsed.manifest.description.slice(0, 75)}...` : parsed.manifest.description
|
|
5404
|
+
});
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5407
|
+
matches.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
|
|
5408
|
+
return {
|
|
5409
|
+
query,
|
|
5410
|
+
total_matches: matches.length,
|
|
5411
|
+
matches
|
|
5412
|
+
};
|
|
5413
|
+
}
|
|
5414
|
+
function formatCatalogSearchReport(report, format = "table", colorEnabled) {
|
|
5415
|
+
if (format === "json") {
|
|
5416
|
+
return `${JSON.stringify(report, null, 2)}
|
|
5417
|
+
`;
|
|
5418
|
+
}
|
|
5419
|
+
if (format === "markdown") {
|
|
5420
|
+
const lines = [
|
|
5421
|
+
`## SkillOps Library Search \u2014 \`${report.query || "*"}\` (${report.total_matches} matches)`,
|
|
5422
|
+
"",
|
|
5423
|
+
"| Type | Name | Tokens | Summary |",
|
|
5424
|
+
"| :--- | :--- | ---: | :--- |",
|
|
5425
|
+
...report.matches.map(
|
|
5426
|
+
(m) => `| ${m.kind.toUpperCase()} | \`${m.name}\` | ${m.tokens}t | ${m.summary} |`
|
|
5427
|
+
),
|
|
5428
|
+
""
|
|
5429
|
+
];
|
|
5430
|
+
return lines.join("\n");
|
|
5431
|
+
}
|
|
5432
|
+
const c = colorEnabled;
|
|
5433
|
+
const header = renderBox(
|
|
5434
|
+
[
|
|
5435
|
+
`${style.bold(style.cyan("\u{1F50D} SkillOps Library Search", c), c)} \u2014 Query: ${style.bold(`"${report.query || "*"}"`, c)} (${report.total_matches} matches)`
|
|
5436
|
+
],
|
|
5437
|
+
{ minWidth: 74, colorEnabled: c }
|
|
5438
|
+
);
|
|
5439
|
+
if (report.matches.length === 0) {
|
|
5440
|
+
return `${header}
|
|
5441
|
+
${formatBadge("warn", "No matching Skills or Subagents found.", c)}
|
|
5442
|
+
${renderNextSteps([{ command: "npx skillops-cli list", description: "View all 10 Skills & 9 Subagents in the Library" }], c)}`;
|
|
5443
|
+
}
|
|
5444
|
+
const table = renderTable(
|
|
5445
|
+
["Type", "Name", "Tokens", "When to Use / Summary"],
|
|
5446
|
+
report.matches.map((m) => [
|
|
5447
|
+
m.kind === "skill" ? formatBadge("skill", "SKILL", c) : formatBadge("subagent", "SUBAGENT", c),
|
|
5448
|
+
style.bold(m.name, c),
|
|
5449
|
+
`${m.tokens}t`,
|
|
5450
|
+
m.summary
|
|
5451
|
+
]),
|
|
5452
|
+
{ align: ["left", "left", "right", "left"], colorEnabled: c }
|
|
5453
|
+
);
|
|
5454
|
+
const topItem = report.matches[0]?.name ?? "bug-reproducer-tdd";
|
|
5455
|
+
const nextSteps = renderNextSteps(
|
|
5456
|
+
[
|
|
5457
|
+
{
|
|
5458
|
+
command: `npx skillops-cli info ${topItem}`,
|
|
5459
|
+
description: `Inspect '${topItem}' in detail`
|
|
5460
|
+
},
|
|
5461
|
+
{
|
|
5462
|
+
command: `npx skillops-cli add ${topItem}`,
|
|
5463
|
+
description: `Install '${topItem}' into your project's AI agents`
|
|
5464
|
+
}
|
|
5465
|
+
],
|
|
5466
|
+
c
|
|
5467
|
+
);
|
|
5468
|
+
return `${header}
|
|
5469
|
+
|
|
5470
|
+
${table}
|
|
5471
|
+
${nextSteps}`;
|
|
5472
|
+
}
|
|
5473
|
+
function buildValidSkillDescription(name, humanTitle, customDesc) {
|
|
5474
|
+
const domainPhrase = name.replace(/-/g, " ");
|
|
5475
|
+
const defaultDescription = `Use when implementing, auditing, or troubleshooting ${domainPhrase} workflows across production codebases to enforce deterministic ${humanTitle} invariants, structural contracts, boundary-value assertions, and automated Level 3 verification scripts before applying surgical changes. Do not trigger for pure documentation edits, static asset bundling, or unrelated formatting changes.`;
|
|
5476
|
+
if (!customDesc?.trim()) {
|
|
5477
|
+
return defaultDescription;
|
|
5478
|
+
}
|
|
5479
|
+
let candidate = customDesc.trim().replace(/\s+/g, " ");
|
|
5480
|
+
if (!candidate.startsWith("Use when")) {
|
|
5481
|
+
candidate = `Use when working on ${domainPhrase}: ${candidate}`;
|
|
5482
|
+
}
|
|
5483
|
+
const words = countWords(candidate);
|
|
5484
|
+
if (words < 40) {
|
|
5485
|
+
candidate = `${candidate} Enforces deterministic ${humanTitle} invariants, automated Level 3 script verification, architectural boundary checks, and zero-regression guardrails across production repositories. Do not trigger for pure documentation edits, static asset bundling, or unrelated formatting changes.`;
|
|
5486
|
+
}
|
|
5487
|
+
return candidate;
|
|
5488
|
+
}
|
|
5489
|
+
function scaffoldFrameworkItem(catalogDir, kind, rawName, options = {}) {
|
|
5490
|
+
const name = rawName.trim().toLowerCase();
|
|
5491
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name)) {
|
|
5492
|
+
throw new Error(
|
|
5493
|
+
`Invalid ${kind} name '${rawName}': must be kebab-case ('^[a-z0-9]+(-[a-z0-9]+)*$').`
|
|
5494
|
+
);
|
|
5495
|
+
}
|
|
5496
|
+
const createdFiles = [];
|
|
5497
|
+
if (kind === "skill") {
|
|
5498
|
+
const skillDir = path9.join(catalogDir, "skills", name);
|
|
5499
|
+
const skillMdPath = path9.join(skillDir, "SKILL.md");
|
|
5500
|
+
const snakeName = name.replace(/-/g, "_");
|
|
5501
|
+
const scriptRelPath = `scripts/verify_${snakeName}.py`;
|
|
5502
|
+
const scriptAbsPath = path9.join(
|
|
5503
|
+
skillDir,
|
|
5504
|
+
"scripts",
|
|
5505
|
+
`verify_${snakeName}.py`
|
|
5506
|
+
);
|
|
5507
|
+
const evalsAbsPath = path9.join(skillDir, "evals", "evals.json");
|
|
5508
|
+
if (fs9.existsSync(skillMdPath) && !options.force) {
|
|
5509
|
+
throw new Error(
|
|
5510
|
+
`Skill '${name}' already exists at '${skillMdPath.replace(/\\/g, "/")}'. Pass --force to overwrite.`
|
|
5511
|
+
);
|
|
5512
|
+
}
|
|
5513
|
+
const humanTitle2 = name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
5514
|
+
const description = buildValidSkillDescription(
|
|
5515
|
+
name,
|
|
5516
|
+
humanTitle2,
|
|
5517
|
+
options.description
|
|
5518
|
+
);
|
|
5519
|
+
const skillMdContent = [
|
|
5520
|
+
"---",
|
|
5521
|
+
`name: "${name}"`,
|
|
5522
|
+
`description: "${description.replace(/"/g, '\\"')}"`,
|
|
5523
|
+
'version: "1.0.0"',
|
|
5524
|
+
'track: "arch-quality"',
|
|
5525
|
+
"tags:",
|
|
5526
|
+
` - "${name}"`,
|
|
5527
|
+
' - "verification"',
|
|
5528
|
+
' - "deterministic-validation"',
|
|
5529
|
+
"routing_signals:",
|
|
5530
|
+
' languages: ["typescript", "python"]',
|
|
5531
|
+
' files_any: ["package.json", "pyproject.toml"]',
|
|
5532
|
+
" dependencies_any: []",
|
|
5533
|
+
" negative_triggers:",
|
|
5534
|
+
' - "Do not trigger for pure documentation edits, static asset bundling, or unrelated formatting changes."',
|
|
5535
|
+
"entrypoints:",
|
|
5536
|
+
" scripts:",
|
|
5537
|
+
` - path: "${scriptRelPath}"`,
|
|
5538
|
+
` description: "Executes deterministic structural verification and invariant checks for ${humanTitle2}."`,
|
|
5539
|
+
"---",
|
|
5540
|
+
"",
|
|
5541
|
+
`# ${humanTitle2} (\`${name}\`)`,
|
|
5542
|
+
"",
|
|
5543
|
+
"## When to Use",
|
|
5544
|
+
`- Activate this skill when working on **${humanTitle2}** tasks requiring deterministic validation.`,
|
|
5545
|
+
"- Delegate exploratory repository scans to a Context-Isolated Subagent before modifying production files.",
|
|
5546
|
+
"",
|
|
5547
|
+
"## Step-by-Step Playbook",
|
|
5548
|
+
"1. **Baseline Discovery:** Inspect target modules and identify architectural constraints.",
|
|
5549
|
+
"2. **Deterministic Verification (Level 3 Script):** Run the bundled verification script before and after changes:",
|
|
5550
|
+
" ```bash",
|
|
5551
|
+
` python ${scriptRelPath} --target .`,
|
|
5552
|
+
" ```",
|
|
5553
|
+
"3. **Minimal Surgical Implementation:** Apply focused changes and verify zero regressions.",
|
|
5554
|
+
"",
|
|
5555
|
+
"## Anti-Patterns to Avoid",
|
|
5556
|
+
"- Never skip Level 3 deterministic verification before declaring completion.",
|
|
5557
|
+
"- Avoid loading large raw logs into the primary context window.",
|
|
5558
|
+
""
|
|
5559
|
+
].join("\n");
|
|
5560
|
+
const scriptContent = [
|
|
5561
|
+
'"""Deterministic Level 3 verification script generated by SkillOps Framework."""',
|
|
5562
|
+
"",
|
|
5563
|
+
"from __future__ import annotations",
|
|
5564
|
+
"",
|
|
5565
|
+
"import argparse",
|
|
5566
|
+
"import json",
|
|
5567
|
+
"import sys",
|
|
5568
|
+
"",
|
|
5569
|
+
"",
|
|
5570
|
+
"def main() -> int:",
|
|
5571
|
+
` parser = argparse.ArgumentParser(description="Verify ${humanTitle2} invariants.")`,
|
|
5572
|
+
' parser.add_argument("--target", default=".", help="Target directory or file path to verify.")',
|
|
5573
|
+
" args = parser.parse_args()",
|
|
5574
|
+
` payload = {"skill": "${name}", "target": args.target, "status": "pass"}`,
|
|
5575
|
+
" print(json.dumps(payload, indent=2))",
|
|
5576
|
+
" return 0",
|
|
5577
|
+
"",
|
|
5578
|
+
"",
|
|
5579
|
+
'if __name__ == "__main__":',
|
|
5580
|
+
" sys.exit(main())",
|
|
5581
|
+
""
|
|
5582
|
+
].join("\n");
|
|
5583
|
+
const evalsContent = JSON.stringify(
|
|
5584
|
+
{
|
|
5585
|
+
skill_name: name,
|
|
5586
|
+
schema_version: "1.0",
|
|
5587
|
+
evals: [
|
|
5588
|
+
{
|
|
5589
|
+
id: `eval-01-verify-${name}`,
|
|
5590
|
+
name: `Verify ${humanTitle2} deterministic workflow and Level 3 script execution`,
|
|
5591
|
+
prompt: `Audit and enforce ${humanTitle2} invariants using the deterministic verification script.`,
|
|
5592
|
+
fixtures: [],
|
|
5593
|
+
assertions: [
|
|
5594
|
+
{
|
|
5595
|
+
id: "assert-ran-verification-script",
|
|
5596
|
+
type: "tool_call_contains",
|
|
5597
|
+
target: `verify_${snakeName}.py`,
|
|
5598
|
+
description: `Agent executed verify_${snakeName}.py before finalizing changes.`
|
|
5599
|
+
}
|
|
5600
|
+
]
|
|
5601
|
+
}
|
|
5602
|
+
]
|
|
5603
|
+
},
|
|
5604
|
+
null,
|
|
5605
|
+
2
|
|
5606
|
+
);
|
|
5607
|
+
fs9.mkdirSync(path9.dirname(scriptAbsPath), { recursive: true });
|
|
5608
|
+
fs9.mkdirSync(path9.dirname(evalsAbsPath), { recursive: true });
|
|
5609
|
+
fs9.writeFileSync(skillMdPath, skillMdContent, "utf-8");
|
|
5610
|
+
fs9.writeFileSync(scriptAbsPath, scriptContent, "utf-8");
|
|
5611
|
+
fs9.writeFileSync(evalsAbsPath, `${evalsContent}
|
|
5612
|
+
`, "utf-8");
|
|
5613
|
+
createdFiles.push(
|
|
5614
|
+
skillMdPath.replace(/\\/g, "/"),
|
|
5615
|
+
scriptAbsPath.replace(/\\/g, "/"),
|
|
5616
|
+
evalsAbsPath.replace(/\\/g, "/")
|
|
5617
|
+
);
|
|
5618
|
+
return {
|
|
5619
|
+
kind: "skill",
|
|
5620
|
+
name,
|
|
5621
|
+
target_dir: skillDir.replace(/\\/g, "/"),
|
|
5622
|
+
created_files: createdFiles
|
|
5623
|
+
};
|
|
5624
|
+
}
|
|
5625
|
+
const subagentDir = path9.join(catalogDir, "subagents", name);
|
|
5626
|
+
const subagentMdPath = path9.join(subagentDir, "SUBAGENT.md");
|
|
5627
|
+
if (fs9.existsSync(subagentMdPath) && !options.force) {
|
|
5628
|
+
throw new Error(
|
|
5629
|
+
`Subagent '${name}' already exists at '${subagentMdPath.replace(/\\/g, "/")}'. Pass --force to overwrite.`
|
|
5630
|
+
);
|
|
5631
|
+
}
|
|
5632
|
+
const humanTitle = name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
5633
|
+
const rawSubDesc = options.description?.trim();
|
|
5634
|
+
const defaultSubDescription = rawSubDesc && countWords(rawSubDesc) >= 10 ? rawSubDesc : `Context-isolated specialist subagent for ${humanTitle}. Use when delegating high-volume repository analysis, structural inspection, or verification without polluting the main agent context window.`;
|
|
5635
|
+
const subagentMdContent = [
|
|
5636
|
+
"---",
|
|
5637
|
+
`name: "${name}"`,
|
|
5638
|
+
`description: "${defaultSubDescription.replace(/"/g, '\\"')}"`,
|
|
5639
|
+
"tools:",
|
|
5640
|
+
' - "Read"',
|
|
5641
|
+
' - "Grep"',
|
|
5642
|
+
' - "Glob"',
|
|
5643
|
+
"user_invocable: true",
|
|
5644
|
+
"context_isolation: true",
|
|
5645
|
+
"max_output_tokens: 350",
|
|
5646
|
+
'model_tier: "fast"',
|
|
5647
|
+
"tags:",
|
|
5648
|
+
` - "${name}"`,
|
|
5649
|
+
' - "context-isolated"',
|
|
5650
|
+
"---",
|
|
5651
|
+
"",
|
|
5652
|
+
`# ${humanTitle} Subagent`,
|
|
5653
|
+
"",
|
|
5654
|
+
"## Mission & Context Isolation Contract (`INV-SKILL-04`)",
|
|
5655
|
+
`You are the **${humanTitle}** context-isolated subagent. Execute deep inspection in your isolated context window and return a concise structured summary (\`<= 350\` tokens) to the parent orchestrator.`,
|
|
5656
|
+
"",
|
|
5657
|
+
"## Output Format",
|
|
5658
|
+
"1. **Findings Summary:** High-signal bullet points with exact file paths and line numbers.",
|
|
5659
|
+
"2. **Recommended Actions:** Concrete next steps for the parent agent.",
|
|
5660
|
+
""
|
|
5661
|
+
].join("\n");
|
|
5662
|
+
fs9.mkdirSync(subagentDir, { recursive: true });
|
|
5663
|
+
fs9.writeFileSync(subagentMdPath, subagentMdContent, "utf-8");
|
|
5664
|
+
createdFiles.push(subagentMdPath.replace(/\\/g, "/"));
|
|
5665
|
+
return {
|
|
5666
|
+
kind: "subagent",
|
|
5667
|
+
name,
|
|
5668
|
+
target_dir: subagentDir.replace(/\\/g, "/"),
|
|
5669
|
+
created_files: createdFiles
|
|
5670
|
+
};
|
|
5671
|
+
}
|
|
5672
|
+
function formatScaffoldReport(result, format = "table", colorEnabled) {
|
|
5673
|
+
if (format === "json") {
|
|
5674
|
+
return `${JSON.stringify(result, null, 2)}
|
|
5675
|
+
`;
|
|
5676
|
+
}
|
|
5677
|
+
if (format === "markdown") {
|
|
5678
|
+
return [
|
|
5679
|
+
`## SkillOps Framework Scaffolder \u2014 Created ${result.kind.toUpperCase()} \`${result.name}\``,
|
|
5680
|
+
"",
|
|
5681
|
+
`- **Target Directory:** \`${result.target_dir}\``,
|
|
5682
|
+
...result.created_files.map((f) => `- **Created:** \`${f}\``),
|
|
5683
|
+
""
|
|
5684
|
+
].join("\n");
|
|
5685
|
+
}
|
|
5686
|
+
const c = colorEnabled;
|
|
5687
|
+
const box = renderBox(
|
|
5688
|
+
[
|
|
5689
|
+
`${formatBadge("pass", "CREATED", c)} Scaffolded new SkillOps Framework ${style.bold(result.kind.toUpperCase(), c)}: ${style.bold(style.cyan(result.name, c), c)}`,
|
|
5690
|
+
"",
|
|
5691
|
+
`${style.bold("Generated Files:", c)}`,
|
|
5692
|
+
...result.created_files.map((f) => ` \u2022 ${style.green(f, c)}`)
|
|
5693
|
+
],
|
|
5694
|
+
{
|
|
5695
|
+
title: "\u{1F6E0}\uFE0F SkillOps Framework Scaffolder",
|
|
5696
|
+
borderColor: style.green,
|
|
5697
|
+
minWidth: 74,
|
|
5698
|
+
colorEnabled: c
|
|
5699
|
+
}
|
|
5700
|
+
);
|
|
5701
|
+
const nextSteps = renderNextSteps(
|
|
5702
|
+
[
|
|
5703
|
+
{
|
|
5704
|
+
command: `npx skillops-cli info ${result.name}`,
|
|
5705
|
+
description: `Inspect your new ${result.kind} metadata & token budget`
|
|
5706
|
+
},
|
|
5707
|
+
{
|
|
5708
|
+
command: "npx skillops-cli audit --require-layout",
|
|
5709
|
+
description: "Verify INV-SKILL-01..04 contract compliance & cosine collisions"
|
|
5710
|
+
},
|
|
5711
|
+
{
|
|
5712
|
+
command: `npx skillops-cli add ${result.name}`,
|
|
5713
|
+
description: `Transpile & install '${result.name}' into your AI coding agents`
|
|
5714
|
+
}
|
|
5715
|
+
],
|
|
5716
|
+
c
|
|
5717
|
+
);
|
|
5718
|
+
return `${box}
|
|
5719
|
+
${nextSteps}`;
|
|
5720
|
+
}
|
|
5721
|
+
function runQuickstart(repoPath, catalogDir, options = {}) {
|
|
5722
|
+
const absRepo = path9.resolve(repoPath);
|
|
5723
|
+
const dryRun = options.dryRun ?? false;
|
|
5724
|
+
const force = options.force ?? false;
|
|
5725
|
+
const skipMcp = options.skipMcp ?? false;
|
|
5726
|
+
const profile = fingerprintRepository(absRepo);
|
|
5727
|
+
const existingCfg = loadSkillOpsConfig(absRepo);
|
|
5728
|
+
let activeTargets;
|
|
5729
|
+
if (options.targetsArg) {
|
|
5730
|
+
activeTargets = resolveRuntimeTargets(options.targetsArg, absRepo);
|
|
5731
|
+
} else if (existingCfg.config?.targets && existingCfg.config.targets.length > 0) {
|
|
5732
|
+
activeTargets = [...existingCfg.config.targets];
|
|
5733
|
+
} else if (profile.detected_harnesses.length > 0) {
|
|
5734
|
+
activeTargets = [...profile.detected_harnesses];
|
|
5735
|
+
} else {
|
|
5736
|
+
activeTargets = [...CORE_RUNTIME_TARGETS];
|
|
5737
|
+
}
|
|
5738
|
+
const maxSkills = options.maxSkills ?? existingCfg.config?.token_economy.max_active_skills ?? 12;
|
|
5739
|
+
const maxL1Tokens = existingCfg.config?.token_economy.max_l1_tokens ?? 1500;
|
|
5740
|
+
const maxSimilarity = existingCfg.config?.token_economy.max_similarity ?? 0.78;
|
|
5741
|
+
const configPath = path9.join(absRepo, ".skillops.yml");
|
|
5742
|
+
if (!dryRun) {
|
|
5743
|
+
const configPayload = SkillOpsConfigSchema.parse({
|
|
5744
|
+
version: "1.0",
|
|
5745
|
+
catalog_dir: path9.relative(absRepo, catalogDir).replace(/\\/g, "/") || "catalog",
|
|
5746
|
+
token_economy: {
|
|
5747
|
+
startup_tax_budget: existingCfg.config?.token_economy.startup_tax_budget ?? 2500,
|
|
5748
|
+
max_active_skills: maxSkills,
|
|
5749
|
+
max_l1_tokens: maxL1Tokens,
|
|
5750
|
+
max_similarity: maxSimilarity
|
|
5751
|
+
},
|
|
5752
|
+
detected_stack: {
|
|
5753
|
+
languages: profile.languages,
|
|
5754
|
+
frameworks: profile.frameworks
|
|
5755
|
+
},
|
|
5756
|
+
targets: activeTargets
|
|
5757
|
+
});
|
|
5758
|
+
fs9.mkdirSync(absRepo, { recursive: true });
|
|
5759
|
+
fs9.writeFileSync(configPath, YAML5.stringify(configPayload), "utf-8");
|
|
5760
|
+
}
|
|
5761
|
+
const bundle = routeRepository(absRepo, catalogDir, {
|
|
5762
|
+
maxSkills,
|
|
5763
|
+
maxL1Tokens,
|
|
5764
|
+
maxSimilarity
|
|
5765
|
+
});
|
|
5766
|
+
const syncReport = syncRepository(absRepo, catalogDir, {
|
|
5767
|
+
targets: activeTargets,
|
|
5768
|
+
dryRun,
|
|
5769
|
+
force,
|
|
5770
|
+
maxSkills,
|
|
5771
|
+
maxL1Tokens,
|
|
5772
|
+
maxSimilarity
|
|
5773
|
+
});
|
|
5774
|
+
const mcpReport = skipMcp ? null : setupMcpForHarnesses(absRepo, {
|
|
5775
|
+
targets: activeTargets,
|
|
5776
|
+
dryRun
|
|
5777
|
+
});
|
|
5778
|
+
return {
|
|
5779
|
+
repo_path: absRepo.replace(/\\/g, "/"),
|
|
5780
|
+
config_path: configPath.replace(/\\/g, "/"),
|
|
5781
|
+
dry_run: dryRun,
|
|
5782
|
+
detected_languages: profile.languages,
|
|
5783
|
+
detected_frameworks: profile.frameworks,
|
|
5784
|
+
detected_harnesses: profile.detected_harnesses,
|
|
5785
|
+
active_targets: activeTargets,
|
|
5786
|
+
selected_skills: bundle.selected_skills.map((s) => s.name),
|
|
5787
|
+
selected_subagents: bundle.recommended_subagents,
|
|
5788
|
+
total_l1_tokens: bundle.total_l1_tokens,
|
|
5789
|
+
max_l1_budget: maxL1Tokens,
|
|
5790
|
+
synced_files_count: syncReport.artifacts.filter(
|
|
5791
|
+
(a) => a.action === "created" || a.action === "updated" || a.action === "unchanged" || a.action === "planned"
|
|
5792
|
+
).length,
|
|
5793
|
+
mcp_configured_count: mcpReport ? mcpReport.configured_count : 0,
|
|
5794
|
+
passed: bundle.passed_invariants && syncReport.passed
|
|
5795
|
+
};
|
|
5796
|
+
}
|
|
5797
|
+
function formatQuickstartReport(report, format = "table", colorEnabled) {
|
|
5798
|
+
if (format === "json") {
|
|
5799
|
+
return `${JSON.stringify(report, null, 2)}
|
|
5800
|
+
`;
|
|
5801
|
+
}
|
|
5802
|
+
if (format === "markdown") {
|
|
5803
|
+
return [
|
|
5804
|
+
`## SkillOps Quickstart Report (${report.dry_run ? "DRY-RUN" : "INSTALLED"})`,
|
|
5805
|
+
"",
|
|
5806
|
+
`- **Repository:** \`${report.repo_path}\``,
|
|
5807
|
+
`- **Stack Detected:** ${[...report.detected_languages, ...report.detected_frameworks].join(", ") || "generic"}`,
|
|
5808
|
+
`- **Target AI Agents (${report.active_targets.length}):** \`${report.active_targets.join("`, `")}\``,
|
|
5809
|
+
`- **Routed Skills (${report.selected_skills.length}):** \`${report.selected_skills.join("`, `")}\``,
|
|
5810
|
+
`- **Routed Subagents (${report.selected_subagents.length}):** \`${report.selected_subagents.join("`, `")}\``,
|
|
5811
|
+
`- **Token Budget:** \`${report.total_l1_tokens} / ${report.max_l1_budget}\` L1 tokens`,
|
|
5812
|
+
`- **Synced Artifacts:** ${report.synced_files_count} files | **MCP Configs:** ${report.mcp_configured_count}`,
|
|
5813
|
+
""
|
|
5814
|
+
].join("\n");
|
|
5815
|
+
}
|
|
5816
|
+
const c = colorEnabled;
|
|
5817
|
+
const stackLabel = [...report.detected_languages, ...report.detected_frameworks].join(", ") || "general-purpose";
|
|
5818
|
+
const targetsLabel = report.active_targets.length > 5 ? `${report.active_targets.slice(0, 5).join(", ")} (+${report.active_targets.length - 5} more)` : report.active_targets.join(", ");
|
|
5819
|
+
const banner = renderHeaderBanner(
|
|
5820
|
+
`1-Step Quickstart & AI Agent Library Setup ${report.dry_run ? "[DRY-RUN]" : "[READY]"}`,
|
|
5821
|
+
{ colorEnabled: c }
|
|
5822
|
+
);
|
|
5823
|
+
const stepsBlock = [
|
|
5824
|
+
renderStep(
|
|
5825
|
+
1,
|
|
5826
|
+
4,
|
|
5827
|
+
"Detect Stack & AI Agents",
|
|
5828
|
+
`Stack: ${style.cyan(stackLabel, c)} \u2022 Targets: ${style.bold(targetsLabel, c)}`,
|
|
5829
|
+
"done",
|
|
5830
|
+
c
|
|
5831
|
+
),
|
|
5832
|
+
renderStep(
|
|
5833
|
+
2,
|
|
5834
|
+
4,
|
|
5835
|
+
"Route Optimal Library Bundle",
|
|
5836
|
+
`${style.green(`${report.selected_skills.length} Skills`, c)} + ${style.magenta(`${report.selected_subagents.length} Subagents`, c)} selected`,
|
|
5837
|
+
"done",
|
|
5838
|
+
c
|
|
5839
|
+
),
|
|
5840
|
+
renderStep(
|
|
5841
|
+
3,
|
|
5842
|
+
4,
|
|
5843
|
+
"Transpile & Sync Artifacts",
|
|
5844
|
+
`${report.synced_files_count} files synced (${report.dry_run ? "dry-run" : ".skillops.yml + .skillops-lock.json"})`,
|
|
5845
|
+
"done",
|
|
5846
|
+
c
|
|
5847
|
+
),
|
|
5848
|
+
renderStep(
|
|
5849
|
+
4,
|
|
5850
|
+
4,
|
|
5851
|
+
"Register MCP Server",
|
|
5852
|
+
`${report.mcp_configured_count} MCP-compatible agent config(s) updated`,
|
|
5853
|
+
"done",
|
|
5854
|
+
c
|
|
5855
|
+
)
|
|
5856
|
+
].join("\n");
|
|
5857
|
+
const tokenBar = ` ${renderTokenBar(
|
|
5858
|
+
report.total_l1_tokens,
|
|
5859
|
+
report.max_l1_budget,
|
|
5860
|
+
{
|
|
5861
|
+
label: "L1 Routing Budget:",
|
|
5862
|
+
colorEnabled: c
|
|
5863
|
+
}
|
|
5864
|
+
)}`;
|
|
5865
|
+
const bundleBox = renderBox(
|
|
5866
|
+
[
|
|
5867
|
+
`${style.bold("Installed Hybrid Executable Skills:", c)}`,
|
|
5868
|
+
` ${report.selected_skills.map((s) => style.cyan(s, c)).join(", ")}`,
|
|
5869
|
+
"",
|
|
5870
|
+
`${style.bold("Installed Context-Isolated Subagents:", c)}`,
|
|
5871
|
+
` ${report.selected_subagents.map((a) => style.magenta(a, c)).join(", ")}`
|
|
5872
|
+
],
|
|
5873
|
+
{
|
|
5874
|
+
title: "\u{1F4E6} Active Skills & Subagents Bundle",
|
|
5875
|
+
borderColor: style.cyan,
|
|
5876
|
+
minWidth: 72,
|
|
5877
|
+
colorEnabled: c
|
|
5878
|
+
}
|
|
5879
|
+
);
|
|
5880
|
+
const usageGuide = renderAiUsageGuide(
|
|
5881
|
+
report.active_targets,
|
|
5882
|
+
report.selected_skills,
|
|
5883
|
+
report.selected_subagents,
|
|
5884
|
+
c
|
|
5885
|
+
);
|
|
5886
|
+
const nextSteps = renderNextSteps(
|
|
5887
|
+
[
|
|
5888
|
+
{
|
|
5889
|
+
command: `npx skillops-cli info ${report.selected_skills[0] ?? "bug-reproducer-tdd"}`,
|
|
5890
|
+
description: "Inspect a Skill's triggers, L3 scripts & AI invocation syntax"
|
|
5891
|
+
},
|
|
5892
|
+
{
|
|
5893
|
+
command: "npx skillops-cli list",
|
|
5894
|
+
description: "Browse all 10 Skills & 9 Subagents in the Library"
|
|
5895
|
+
},
|
|
5896
|
+
{
|
|
5897
|
+
command: "npx skillops-cli create skill my-custom-skill",
|
|
5898
|
+
description: "Create your own custom Skill using the SkillOps Framework"
|
|
5899
|
+
},
|
|
5900
|
+
{
|
|
5901
|
+
command: "npx skillops-cli doctor",
|
|
5902
|
+
description: "Verify lockfile integrity & Startup Token Tax savings"
|
|
5903
|
+
}
|
|
5904
|
+
],
|
|
5905
|
+
c
|
|
5906
|
+
);
|
|
5907
|
+
return [
|
|
5908
|
+
banner,
|
|
5909
|
+
"",
|
|
5910
|
+
stepsBlock,
|
|
5911
|
+
"",
|
|
5912
|
+
tokenBar,
|
|
5913
|
+
"",
|
|
5914
|
+
bundleBox,
|
|
5915
|
+
"",
|
|
5916
|
+
usageGuide,
|
|
5917
|
+
nextSteps
|
|
5918
|
+
].join("\n");
|
|
5919
|
+
}
|
|
5920
|
+
|
|
5921
|
+
// src/cli.ts
|
|
5922
|
+
var PACKAGE_ROOT = path10.resolve(
|
|
5923
|
+
path10.dirname(fileURLToPath(import.meta.url)),
|
|
5924
|
+
".."
|
|
5925
|
+
);
|
|
5926
|
+
function getFlagValue(args, ...flagNames) {
|
|
5927
|
+
for (const flag of flagNames) {
|
|
5928
|
+
const idx = args.indexOf(flag);
|
|
5929
|
+
const val = idx !== -1 ? args[idx + 1] : void 0;
|
|
5930
|
+
if (val && !val.startsWith("-")) {
|
|
5931
|
+
return val;
|
|
5932
|
+
}
|
|
5933
|
+
}
|
|
5934
|
+
return void 0;
|
|
5935
|
+
}
|
|
5936
|
+
function getPositionalArgs(args) {
|
|
5937
|
+
const valueTakingFlags = /* @__PURE__ */ new Set([
|
|
5938
|
+
"--repo",
|
|
5939
|
+
"--project",
|
|
5940
|
+
"-p",
|
|
5941
|
+
"--targets",
|
|
5942
|
+
"--harness",
|
|
5943
|
+
"--harnesses",
|
|
5944
|
+
"--target",
|
|
5945
|
+
"-t",
|
|
5946
|
+
"--catalog",
|
|
5947
|
+
"--format",
|
|
5948
|
+
"-f",
|
|
5949
|
+
"--top",
|
|
5950
|
+
"--max-skills",
|
|
5951
|
+
"--max-l1-tokens",
|
|
5952
|
+
"--max-similarity",
|
|
5953
|
+
"--budget",
|
|
5954
|
+
"--task",
|
|
5955
|
+
"--skill",
|
|
5956
|
+
"--out",
|
|
5957
|
+
"--description",
|
|
5958
|
+
"-d"
|
|
5959
|
+
]);
|
|
5960
|
+
const positionals = [];
|
|
5961
|
+
for (let i = 1; i < args.length; i++) {
|
|
5962
|
+
const arg = args[i];
|
|
5963
|
+
if (!arg) {
|
|
5964
|
+
continue;
|
|
5965
|
+
}
|
|
5966
|
+
if (arg.startsWith("-")) {
|
|
5967
|
+
if (valueTakingFlags.has(arg)) {
|
|
5968
|
+
i += 1;
|
|
5969
|
+
}
|
|
5970
|
+
continue;
|
|
5971
|
+
}
|
|
5972
|
+
positionals.push(arg);
|
|
5973
|
+
}
|
|
5974
|
+
return positionals;
|
|
5975
|
+
}
|
|
5976
|
+
function resolveTargetsArg(args) {
|
|
5977
|
+
const explicit = getFlagValue(
|
|
5978
|
+
args,
|
|
5979
|
+
"--targets",
|
|
5980
|
+
"--harness",
|
|
5981
|
+
"--harnesses",
|
|
5982
|
+
"--target",
|
|
5983
|
+
"-t"
|
|
5984
|
+
);
|
|
5985
|
+
if (explicit) {
|
|
5986
|
+
return explicit;
|
|
5987
|
+
}
|
|
5988
|
+
if (args.includes("--auto")) {
|
|
5989
|
+
return "auto";
|
|
5990
|
+
}
|
|
5991
|
+
return void 0;
|
|
5992
|
+
}
|
|
5993
|
+
function resolveCatalogDir(args, repoPath, loadedConfig) {
|
|
5994
|
+
const catalogFlag = getFlagValue(args, "--catalog");
|
|
5995
|
+
if (catalogFlag) {
|
|
5996
|
+
return path10.resolve(catalogFlag);
|
|
5997
|
+
}
|
|
5998
|
+
const baseRepo = repoPath ? path10.resolve(repoPath) : process.cwd();
|
|
5999
|
+
if (loadedConfig?.catalog_dir) {
|
|
6000
|
+
const cfgCatalog = path10.resolve(baseRepo, loadedConfig.catalog_dir);
|
|
6001
|
+
if (fs10.existsSync(cfgCatalog)) {
|
|
6002
|
+
return cfgCatalog;
|
|
6003
|
+
}
|
|
6004
|
+
}
|
|
6005
|
+
if (repoPath) {
|
|
6006
|
+
const repoCatalog = path10.join(path10.resolve(repoPath), "catalog");
|
|
6007
|
+
if (fs10.existsSync(repoCatalog)) {
|
|
6008
|
+
return repoCatalog;
|
|
6009
|
+
}
|
|
6010
|
+
}
|
|
6011
|
+
const cwdCatalog = path10.resolve(process.cwd(), "catalog");
|
|
6012
|
+
if (fs10.existsSync(cwdCatalog)) {
|
|
6013
|
+
return cwdCatalog;
|
|
6014
|
+
}
|
|
6015
|
+
return path10.join(PACKAGE_ROOT, "catalog");
|
|
6016
|
+
}
|
|
6017
|
+
function runCli(argv) {
|
|
6018
|
+
const args = argv.slice(2);
|
|
6019
|
+
if (args.includes("--version") || args.includes("-V") || args[0] === "version") {
|
|
6020
|
+
process.stdout.write(`skillops ${SKILLOPS_VERSION}
|
|
6021
|
+
`);
|
|
6022
|
+
return ExitCode.SUCCESS;
|
|
6023
|
+
}
|
|
6024
|
+
if (args[0] === "help") {
|
|
6025
|
+
const targetCmd = args[1];
|
|
6026
|
+
if (targetCmd && !targetCmd.startsWith("-")) {
|
|
6027
|
+
const spec = findCommandHelpSpec(targetCmd);
|
|
6028
|
+
if (spec) {
|
|
6029
|
+
process.stdout.write(renderCommandHelp(spec, SKILLOPS_VERSION));
|
|
6030
|
+
return ExitCode.SUCCESS;
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
process.stdout.write(renderGlobalHelp(SKILLOPS_VERSION));
|
|
6034
|
+
return ExitCode.SUCCESS;
|
|
6035
|
+
}
|
|
6036
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
6037
|
+
process.stdout.write(renderGlobalHelp(SKILLOPS_VERSION));
|
|
6038
|
+
return ExitCode.SUCCESS;
|
|
6039
|
+
}
|
|
6040
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
6041
|
+
const firstCmd = args[0];
|
|
6042
|
+
if (firstCmd) {
|
|
6043
|
+
const spec = findCommandHelpSpec(firstCmd);
|
|
6044
|
+
if (spec) {
|
|
6045
|
+
process.stdout.write(renderCommandHelp(spec, SKILLOPS_VERSION));
|
|
6046
|
+
return ExitCode.SUCCESS;
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
process.stdout.write(renderGlobalHelp(SKILLOPS_VERSION));
|
|
6050
|
+
return ExitCode.SUCCESS;
|
|
6051
|
+
}
|
|
6052
|
+
const command = args[0] ?? "";
|
|
6053
|
+
if (command === "quickstart" || command === "setup") {
|
|
6054
|
+
const repoPath = path10.resolve(
|
|
6055
|
+
getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd()
|
|
6056
|
+
);
|
|
6057
|
+
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
6058
|
+
if (cfgRes.error) {
|
|
6059
|
+
process.stderr.write(
|
|
6060
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6061
|
+
`
|
|
6062
|
+
);
|
|
6063
|
+
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
6064
|
+
}
|
|
6065
|
+
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
6066
|
+
const targetsArg = resolveTargetsArg(args);
|
|
6067
|
+
const maxSkillsRaw = getFlagValue(args, "--max-skills", "--top");
|
|
6068
|
+
const maxSkills = maxSkillsRaw ? Number.parseInt(maxSkillsRaw, 10) : void 0;
|
|
6069
|
+
const dryRun = args.includes("--dry-run");
|
|
6070
|
+
const force = args.includes("--force");
|
|
6071
|
+
const skipMcp = args.includes("--skip-mcp") || args.includes("--no-mcp");
|
|
6072
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
6073
|
+
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
6074
|
+
try {
|
|
6075
|
+
const report = runQuickstart(repoPath, catalogDir, {
|
|
6076
|
+
targetsArg,
|
|
6077
|
+
maxSkills,
|
|
6078
|
+
dryRun,
|
|
6079
|
+
force,
|
|
6080
|
+
skipMcp
|
|
6081
|
+
});
|
|
6082
|
+
process.stdout.write(formatQuickstartReport(report, format));
|
|
6083
|
+
return report.passed ? ExitCode.SUCCESS : ExitCode.ERROR;
|
|
6084
|
+
} catch (err) {
|
|
6085
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6086
|
+
process.stderr.write(`Invalid --targets: ${msg}
|
|
6087
|
+
`);
|
|
6088
|
+
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
if (command === "info" || command === "show" || command === "inspect") {
|
|
6092
|
+
const positionals = getPositionalArgs(args);
|
|
6093
|
+
const itemName = positionals[0];
|
|
6094
|
+
if (!itemName) {
|
|
6095
|
+
process.stderr.write(
|
|
6096
|
+
"Usage: npx skillops-cli info <skill-or-subagent-name> [options]\n"
|
|
6097
|
+
);
|
|
6098
|
+
return ExitCode.ERROR;
|
|
6099
|
+
}
|
|
6100
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
6101
|
+
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
6102
|
+
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
6103
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
6104
|
+
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
6105
|
+
try {
|
|
6106
|
+
const info = inspectCatalogItem(catalogDir, itemName);
|
|
6107
|
+
process.stdout.write(formatCatalogItemInfo(info, format));
|
|
6108
|
+
return ExitCode.SUCCESS;
|
|
6109
|
+
} catch (err) {
|
|
6110
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6111
|
+
process.stderr.write(`Error: ${msg}
|
|
6112
|
+
`);
|
|
6113
|
+
return ExitCode.ERROR;
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
if (command === "search" || command === "find") {
|
|
6117
|
+
const positionals = getPositionalArgs(args);
|
|
6118
|
+
const query = positionals.join(" ");
|
|
6119
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
6120
|
+
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
6121
|
+
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
6122
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
6123
|
+
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
6124
|
+
const report = searchCatalog(catalogDir, query);
|
|
6125
|
+
process.stdout.write(formatCatalogSearchReport(report, format));
|
|
6126
|
+
return ExitCode.SUCCESS;
|
|
6127
|
+
}
|
|
6128
|
+
if (command === "create" || command === "new") {
|
|
6129
|
+
const positionals = getPositionalArgs(args);
|
|
6130
|
+
let kind = "skill";
|
|
6131
|
+
let rawName;
|
|
6132
|
+
const firstPos = positionals[0]?.toLowerCase();
|
|
6133
|
+
if (firstPos === "skill" || firstPos === "skills") {
|
|
6134
|
+
kind = "skill";
|
|
6135
|
+
rawName = positionals[1];
|
|
6136
|
+
} else if (firstPos === "subagent" || firstPos === "subagents" || firstPos === "agent" || firstPos === "agents") {
|
|
6137
|
+
kind = "subagent";
|
|
6138
|
+
rawName = positionals[1];
|
|
6139
|
+
} else {
|
|
6140
|
+
rawName = positionals[0];
|
|
6141
|
+
}
|
|
6142
|
+
if (!rawName) {
|
|
6143
|
+
process.stderr.write(
|
|
6144
|
+
"Usage: npx skillops-cli create <skill|subagent> <kebab-case-name> [--description <text>] [--force]\n"
|
|
6145
|
+
);
|
|
6146
|
+
return ExitCode.ERROR;
|
|
6147
|
+
}
|
|
6148
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
6149
|
+
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
6150
|
+
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
6151
|
+
const description = getFlagValue(args, "--description", "-d");
|
|
6152
|
+
const force = args.includes("--force");
|
|
6153
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
6154
|
+
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
6155
|
+
try {
|
|
6156
|
+
const result = scaffoldFrameworkItem(catalogDir, kind, rawName, {
|
|
6157
|
+
description,
|
|
6158
|
+
force
|
|
6159
|
+
});
|
|
6160
|
+
process.stdout.write(formatScaffoldReport(result, format));
|
|
6161
|
+
return ExitCode.SUCCESS;
|
|
6162
|
+
} catch (err) {
|
|
6163
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6164
|
+
process.stderr.write(`Error: ${msg}
|
|
6165
|
+
`);
|
|
6166
|
+
return ExitCode.ERROR;
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
6169
|
+
if (command === "init") {
|
|
6170
|
+
const repoPath = path10.resolve(
|
|
6171
|
+
getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd()
|
|
6172
|
+
);
|
|
6173
|
+
const catalogDir = resolveCatalogDir(args, repoPath);
|
|
6174
|
+
const profile = fingerprintRepository(repoPath);
|
|
6175
|
+
const targetsArg = resolveTargetsArg(args);
|
|
6176
|
+
let initTargets;
|
|
6177
|
+
try {
|
|
6178
|
+
initTargets = targetsArg ? resolveRuntimeTargets(targetsArg, repoPath) : profile.detected_harnesses.length > 0 ? profile.detected_harnesses : [...CORE_RUNTIME_TARGETS];
|
|
6179
|
+
} catch (err) {
|
|
6180
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6181
|
+
process.stderr.write(`Invalid --targets: ${msg}
|
|
6182
|
+
`);
|
|
6183
|
+
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
6184
|
+
}
|
|
6185
|
+
const configPayload = SkillOpsConfigSchema.parse({
|
|
6186
|
+
version: "1.0",
|
|
6187
|
+
catalog_dir: path10.relative(repoPath, catalogDir).replace(/\\/g, "/") || "catalog",
|
|
6188
|
+
token_economy: {
|
|
6189
|
+
startup_tax_budget: 2500,
|
|
6190
|
+
max_active_skills: 12,
|
|
6191
|
+
max_l1_tokens: 1500,
|
|
6192
|
+
max_similarity: 0.78
|
|
6193
|
+
},
|
|
6194
|
+
detected_stack: {
|
|
6195
|
+
languages: profile.languages,
|
|
6196
|
+
frameworks: profile.frameworks
|
|
6197
|
+
},
|
|
6198
|
+
targets: initTargets
|
|
6199
|
+
});
|
|
6200
|
+
const configPath = path10.join(repoPath, ".skillops.yml");
|
|
6201
|
+
fs10.mkdirSync(repoPath, { recursive: true });
|
|
6202
|
+
fs10.writeFileSync(configPath, YAML6.stringify(configPayload), "utf-8");
|
|
6203
|
+
process.stdout.write(
|
|
6204
|
+
`Initialized SkillOps configuration at ${configPath.replace(/\\/g, "/")}
|
|
6205
|
+
`
|
|
6206
|
+
);
|
|
6207
|
+
return ExitCode.SUCCESS;
|
|
6208
|
+
}
|
|
6209
|
+
if (command === "list") {
|
|
6210
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
6211
|
+
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
4207
6212
|
if (cfgRes.error) {
|
|
4208
|
-
process.stderr.write(
|
|
6213
|
+
process.stderr.write(
|
|
6214
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6215
|
+
`
|
|
6216
|
+
);
|
|
6217
|
+
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
6218
|
+
}
|
|
6219
|
+
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
6220
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
6221
|
+
const summary = auditCatalogTokenBudgets(catalogDir, {
|
|
6222
|
+
requireLayout: false
|
|
6223
|
+
});
|
|
6224
|
+
if (rawFormat === "json") {
|
|
6225
|
+
process.stdout.write(`${JSON.stringify(summary, null, 2)}
|
|
4209
6226
|
`);
|
|
6227
|
+
return ExitCode.SUCCESS;
|
|
6228
|
+
}
|
|
6229
|
+
if (rawFormat === "markdown") {
|
|
6230
|
+
const lines2 = [
|
|
6231
|
+
"## SkillOps Catalog \u2014 High-End Skills & Context-Isolated Subagents",
|
|
6232
|
+
"",
|
|
6233
|
+
"### Hybrid Executable Skills (`INV-SKILL-01` .. `03`)",
|
|
6234
|
+
"",
|
|
6235
|
+
"| Skill | L1 Tokens | L2 Lines | L2 Tokens | Scripts |Savings |",
|
|
6236
|
+
"| :--- | ---: | ---: | ---: | ---: | ---: |",
|
|
6237
|
+
...summary.skills.map((s) => {
|
|
6238
|
+
const totalRaw = s.l1Tokens + s.l2Tokens;
|
|
6239
|
+
const savingsPct = totalRaw > 0 ? ((totalRaw - s.l1Tokens) / totalRaw * 100).toFixed(1) : "0.0";
|
|
6240
|
+
return `| \`${s.skillName}\` | ${s.l1Tokens} | ${s.l2Lines} | ${s.l2Tokens} | ${s.scriptsCount} | ${savingsPct}% |`;
|
|
6241
|
+
}),
|
|
6242
|
+
"",
|
|
6243
|
+
"### Context-Isolated Subagents (`INV-SKILL-04`)",
|
|
6244
|
+
"",
|
|
6245
|
+
"| Subagent | Prompt Tokens | Max Output Tokens | Tools |",
|
|
6246
|
+
"| :--- | ---: | ---: | ---: |",
|
|
6247
|
+
...summary.subagents.map(
|
|
6248
|
+
(a) => `| \`${a.subagentName}\` | ${a.systemPromptTokens} | <= ${a.maxOutputTokens} | ${a.tools.length} |`
|
|
6249
|
+
),
|
|
6250
|
+
""
|
|
6251
|
+
];
|
|
6252
|
+
process.stdout.write(lines2.join("\n"));
|
|
6253
|
+
return ExitCode.SUCCESS;
|
|
6254
|
+
}
|
|
6255
|
+
const l1Bar = renderProgressBar(summary.totalL1Tokens, 1500, {
|
|
6256
|
+
width: 18,
|
|
6257
|
+
unit: "tokens"
|
|
6258
|
+
});
|
|
6259
|
+
const firstSkill = summary.skills[0]?.skillName ?? "security-sast-gate";
|
|
6260
|
+
const lines = [
|
|
6261
|
+
`=== SkillOps Catalog (v${SKILLOPS_VERSION}) ===`,
|
|
6262
|
+
`Skills (${summary.skills.length}) \u2014 Total L1 Routing Tax: ${summary.totalL1Tokens} tokens`,
|
|
6263
|
+
`L1 Budget Meter: ${l1Bar}`,
|
|
6264
|
+
...summary.skills.map(
|
|
6265
|
+
(s) => ` [SKILL] ${s.skillName} \u2014 L1: ${s.l1Tokens}t | L2: ${s.l2Lines} lines (${s.l2Tokens}t) | L3 Scripts: ${s.scriptsCount}`
|
|
6266
|
+
),
|
|
6267
|
+
`Subagents (${summary.subagents.length}) \u2014 Context-Isolated Specialist Agents:`,
|
|
6268
|
+
...summary.subagents.map(
|
|
6269
|
+
(a) => ` [SUBAGENT] ${a.subagentName} (prompt=${a.systemPromptTokens}t, max_out<=${a.maxOutputTokens}t, tools=${a.tools.length})`
|
|
6270
|
+
),
|
|
6271
|
+
renderNextSteps([
|
|
6272
|
+
{
|
|
6273
|
+
command: `npx skillops-cli info ${firstSkill}`,
|
|
6274
|
+
description: "Inspect triggers, L3 scripts & how to invoke in your AI"
|
|
6275
|
+
},
|
|
6276
|
+
{
|
|
6277
|
+
command: "npx skillops-cli search <keyword>",
|
|
6278
|
+
description: "Search Skills & Subagents by domain or signal"
|
|
6279
|
+
},
|
|
6280
|
+
{
|
|
6281
|
+
command: "npx skillops-cli quickstart",
|
|
6282
|
+
description: "Auto-detect project stack & install optimal bundle"
|
|
6283
|
+
}
|
|
6284
|
+
])
|
|
6285
|
+
];
|
|
6286
|
+
process.stdout.write(lines.join("\n"));
|
|
6287
|
+
return ExitCode.SUCCESS;
|
|
6288
|
+
}
|
|
6289
|
+
if (command === "audit") {
|
|
6290
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
6291
|
+
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
6292
|
+
if (cfgRes.error) {
|
|
6293
|
+
process.stderr.write(
|
|
6294
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6295
|
+
`
|
|
6296
|
+
);
|
|
4210
6297
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4211
6298
|
}
|
|
4212
6299
|
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
4213
6300
|
const strict = args.includes("--strict");
|
|
4214
6301
|
const requireLayout = args.includes("--require-layout");
|
|
4215
|
-
const
|
|
4216
|
-
const format = formatIdx !== -1 && args[formatIdx + 1] ? args[formatIdx + 1] : "table";
|
|
6302
|
+
const format = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4217
6303
|
const report = auditCatalog(catalogDir, { requireLayout });
|
|
4218
6304
|
if (format === "json") {
|
|
4219
6305
|
process.stdout.write(`${JSON.stringify(report, null, 2)}
|
|
@@ -4262,20 +6348,20 @@ function runCli(argv) {
|
|
|
4262
6348
|
}
|
|
4263
6349
|
return ExitCode.SUCCESS;
|
|
4264
6350
|
}
|
|
4265
|
-
if (command === "tokens") {
|
|
4266
|
-
const
|
|
4267
|
-
const repoPath = repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd();
|
|
6351
|
+
if (command === "tokens" || command === "budget") {
|
|
6352
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
4268
6353
|
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
4269
6354
|
if (cfgRes.error) {
|
|
4270
|
-
process.stderr.write(
|
|
4271
|
-
`
|
|
6355
|
+
process.stderr.write(
|
|
6356
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6357
|
+
`
|
|
6358
|
+
);
|
|
4272
6359
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4273
6360
|
}
|
|
4274
6361
|
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
4275
|
-
const
|
|
4276
|
-
const startupTaxBudget =
|
|
4277
|
-
const
|
|
4278
|
-
const rawFormat = formatIdx !== -1 && args[formatIdx + 1] ? args[formatIdx + 1] : "table";
|
|
6362
|
+
const budgetRaw = getFlagValue(args, "--budget");
|
|
6363
|
+
const startupTaxBudget = budgetRaw ? Number.parseInt(budgetRaw, 10) : cfgRes.config?.token_economy.startup_tax_budget ?? 2500;
|
|
6364
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4279
6365
|
const format = rawFormat === "json" || rawFormat === "markdown" || rawFormat === "github" ? rawFormat : "table";
|
|
4280
6366
|
const strict = args.includes("--strict");
|
|
4281
6367
|
const report = analyzeRepoTokenEconomy(repoPath, {
|
|
@@ -4288,26 +6374,25 @@ function runCli(argv) {
|
|
|
4288
6374
|
}
|
|
4289
6375
|
return ExitCode.SUCCESS;
|
|
4290
6376
|
}
|
|
4291
|
-
if (command === "route") {
|
|
4292
|
-
const
|
|
4293
|
-
const repoPath = repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd();
|
|
6377
|
+
if (command === "route" || command === "recommend") {
|
|
6378
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
4294
6379
|
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
4295
6380
|
if (cfgRes.error) {
|
|
4296
|
-
process.stderr.write(
|
|
4297
|
-
`
|
|
6381
|
+
process.stderr.write(
|
|
6382
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6383
|
+
`
|
|
6384
|
+
);
|
|
4298
6385
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4299
6386
|
}
|
|
4300
6387
|
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
4301
|
-
const
|
|
4302
|
-
const
|
|
4303
|
-
const
|
|
4304
|
-
const
|
|
4305
|
-
const
|
|
4306
|
-
const
|
|
4307
|
-
const
|
|
4308
|
-
const
|
|
4309
|
-
const formatIdx = args.indexOf("--format");
|
|
4310
|
-
const rawFormat = formatIdx !== -1 && args[formatIdx + 1] ? args[formatIdx + 1] : "table";
|
|
6388
|
+
const taskPrompt = getFlagValue(args, "--task") ?? "";
|
|
6389
|
+
const maxSkillsRaw = getFlagValue(args, "--max-skills", "--top");
|
|
6390
|
+
const maxSkills = maxSkillsRaw ? Number.parseInt(maxSkillsRaw, 10) : cfgRes.config?.token_economy.max_active_skills ?? 12;
|
|
6391
|
+
const maxL1Raw = getFlagValue(args, "--max-l1-tokens");
|
|
6392
|
+
const maxL1Tokens = maxL1Raw ? Number.parseInt(maxL1Raw, 10) : cfgRes.config?.token_economy.max_l1_tokens ?? 1500;
|
|
6393
|
+
const maxSimRaw = getFlagValue(args, "--max-similarity");
|
|
6394
|
+
const maxSimilarity = maxSimRaw ? Number.parseFloat(maxSimRaw) : cfgRes.config?.token_economy.max_similarity ?? 0.78;
|
|
6395
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4311
6396
|
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
4312
6397
|
const bundle = routeRepository(repoPath, catalogDir, {
|
|
4313
6398
|
taskPrompt,
|
|
@@ -4319,60 +6404,59 @@ function runCli(argv) {
|
|
|
4319
6404
|
return bundle.passed_invariants ? ExitCode.SUCCESS : ExitCode.ERROR;
|
|
4320
6405
|
}
|
|
4321
6406
|
if (command === "sync") {
|
|
4322
|
-
const
|
|
4323
|
-
const repoPath = repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd();
|
|
6407
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
4324
6408
|
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
4325
6409
|
if (cfgRes.error) {
|
|
4326
|
-
process.stderr.write(
|
|
4327
|
-
`
|
|
6410
|
+
process.stderr.write(
|
|
6411
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6412
|
+
`
|
|
6413
|
+
);
|
|
4328
6414
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4329
6415
|
}
|
|
4330
6416
|
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
4331
|
-
const
|
|
6417
|
+
const targetsArg = resolveTargetsArg(args);
|
|
4332
6418
|
let targets;
|
|
4333
6419
|
try {
|
|
4334
|
-
targets =
|
|
6420
|
+
targets = targetsArg ? resolveRuntimeTargets(targetsArg, repoPath) : cfgRes.config?.targets ?? [...CORE_RUNTIME_TARGETS];
|
|
4335
6421
|
} catch (err) {
|
|
4336
6422
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4337
6423
|
process.stderr.write(`Invalid --targets: ${msg}
|
|
4338
6424
|
`);
|
|
4339
6425
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4340
6426
|
}
|
|
6427
|
+
const maxSkillsRaw = getFlagValue(args, "--max-skills", "--top");
|
|
6428
|
+
const maxSkills = maxSkillsRaw ? Number.parseInt(maxSkillsRaw, 10) : cfgRes.config?.token_economy.max_active_skills;
|
|
4341
6429
|
const dryRun = args.includes("--dry-run");
|
|
4342
6430
|
const force = args.includes("--force");
|
|
4343
|
-
const
|
|
4344
|
-
const rawFormat = formatIdx !== -1 && args[formatIdx + 1] ? args[formatIdx + 1] : "table";
|
|
6431
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4345
6432
|
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
4346
6433
|
const report = syncRepository(repoPath, catalogDir, {
|
|
4347
6434
|
targets,
|
|
4348
6435
|
dryRun,
|
|
4349
6436
|
force,
|
|
4350
|
-
maxSkills
|
|
6437
|
+
maxSkills,
|
|
4351
6438
|
maxL1Tokens: cfgRes.config?.token_economy.max_l1_tokens,
|
|
4352
6439
|
maxSimilarity: cfgRes.config?.token_economy.max_similarity
|
|
4353
6440
|
});
|
|
4354
6441
|
process.stdout.write(formatSyncReport(report, format));
|
|
4355
6442
|
return report.passed ? ExitCode.SUCCESS : ExitCode.ERROR;
|
|
4356
6443
|
}
|
|
4357
|
-
if (command === "add") {
|
|
4358
|
-
const
|
|
4359
|
-
|
|
4360
|
-
process.stderr.write("Usage: npx skillops@latest add <skill-or-subagent-name> [options]\n");
|
|
4361
|
-
return ExitCode.ERROR;
|
|
4362
|
-
}
|
|
4363
|
-
const repoIdx = args.indexOf("--repo");
|
|
4364
|
-
const repoPath = repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd();
|
|
6444
|
+
if (command === "add" || command === "install" || command === "i") {
|
|
6445
|
+
const positionals = getPositionalArgs(args);
|
|
6446
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
4365
6447
|
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
4366
6448
|
if (cfgRes.error) {
|
|
4367
|
-
process.stderr.write(
|
|
4368
|
-
`
|
|
6449
|
+
process.stderr.write(
|
|
6450
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6451
|
+
`
|
|
6452
|
+
);
|
|
4369
6453
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4370
6454
|
}
|
|
4371
6455
|
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
4372
|
-
const
|
|
6456
|
+
const targetsArg = resolveTargetsArg(args);
|
|
4373
6457
|
let targets;
|
|
4374
6458
|
try {
|
|
4375
|
-
targets =
|
|
6459
|
+
targets = targetsArg ? resolveRuntimeTargets(targetsArg, repoPath) : cfgRes.config?.targets ?? [...CORE_RUNTIME_TARGETS];
|
|
4376
6460
|
} catch (err) {
|
|
4377
6461
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4378
6462
|
process.stderr.write(`Invalid --targets: ${msg}
|
|
@@ -4381,14 +6465,42 @@ function runCli(argv) {
|
|
|
4381
6465
|
}
|
|
4382
6466
|
const dryRun = args.includes("--dry-run");
|
|
4383
6467
|
const force = args.includes("--force");
|
|
4384
|
-
|
|
4385
|
-
|
|
6468
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
6469
|
+
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
6470
|
+
if (positionals.length === 0) {
|
|
6471
|
+
if (command === "add") {
|
|
6472
|
+
process.stderr.write(
|
|
6473
|
+
"Usage: npx skillops-cli add <skill-or-subagent-name> [more-names...] [options]\n"
|
|
6474
|
+
);
|
|
6475
|
+
return ExitCode.ERROR;
|
|
6476
|
+
}
|
|
6477
|
+
const maxSkillsRaw = getFlagValue(args, "--max-skills", "--top");
|
|
6478
|
+
const maxSkills = maxSkillsRaw ? Number.parseInt(maxSkillsRaw, 10) : cfgRes.config?.token_economy.max_active_skills;
|
|
6479
|
+
const report = syncRepository(repoPath, catalogDir, {
|
|
4386
6480
|
targets,
|
|
4387
6481
|
dryRun,
|
|
4388
|
-
force
|
|
6482
|
+
force,
|
|
6483
|
+
maxSkills,
|
|
6484
|
+
maxL1Tokens: cfgRes.config?.token_economy.max_l1_tokens,
|
|
6485
|
+
maxSimilarity: cfgRes.config?.token_economy.max_similarity
|
|
4389
6486
|
});
|
|
4390
|
-
process.stdout.write(formatSyncReport(report,
|
|
6487
|
+
process.stdout.write(formatSyncReport(report, format));
|
|
4391
6488
|
return report.passed ? ExitCode.SUCCESS : ExitCode.ERROR;
|
|
6489
|
+
}
|
|
6490
|
+
try {
|
|
6491
|
+
let allPassed = true;
|
|
6492
|
+
for (const itemName of positionals) {
|
|
6493
|
+
const report = addSkillOrSubagent(repoPath, catalogDir, itemName, {
|
|
6494
|
+
targets,
|
|
6495
|
+
dryRun,
|
|
6496
|
+
force
|
|
6497
|
+
});
|
|
6498
|
+
process.stdout.write(formatSyncReport(report, format));
|
|
6499
|
+
if (!report.passed) {
|
|
6500
|
+
allPassed = false;
|
|
6501
|
+
}
|
|
6502
|
+
}
|
|
6503
|
+
return allPassed ? ExitCode.SUCCESS : ExitCode.ERROR;
|
|
4392
6504
|
} catch (err) {
|
|
4393
6505
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4394
6506
|
process.stderr.write(`Error: ${msg}
|
|
@@ -4396,37 +6508,44 @@ function runCli(argv) {
|
|
|
4396
6508
|
return ExitCode.ERROR;
|
|
4397
6509
|
}
|
|
4398
6510
|
}
|
|
4399
|
-
if (command === "remove") {
|
|
4400
|
-
const
|
|
4401
|
-
if (
|
|
4402
|
-
process.stderr.write(
|
|
6511
|
+
if (command === "remove" || command === "uninstall" || command === "rm") {
|
|
6512
|
+
const positionals = getPositionalArgs(args);
|
|
6513
|
+
if (positionals.length === 0) {
|
|
6514
|
+
process.stderr.write(
|
|
6515
|
+
"Usage: npx skillops-cli remove <skill-or-subagent-name> [options]\n"
|
|
6516
|
+
);
|
|
4403
6517
|
return ExitCode.ERROR;
|
|
4404
6518
|
}
|
|
4405
|
-
const
|
|
4406
|
-
const repoPath = repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd();
|
|
6519
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
4407
6520
|
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
4408
6521
|
if (cfgRes.error) {
|
|
4409
|
-
process.stderr.write(
|
|
4410
|
-
`
|
|
6522
|
+
process.stderr.write(
|
|
6523
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6524
|
+
`
|
|
6525
|
+
);
|
|
4411
6526
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4412
6527
|
}
|
|
4413
6528
|
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
4414
|
-
const
|
|
4415
|
-
const rawTargetsArg = targetsIdx !== -1 && args[targetsIdx + 1] ? args[targetsIdx + 1] : void 0;
|
|
6529
|
+
const rawTargetsArg = resolveTargetsArg(args);
|
|
4416
6530
|
const dryRun = args.includes("--dry-run");
|
|
4417
6531
|
const force = args.includes("--force");
|
|
4418
|
-
const
|
|
4419
|
-
const rawFormat = formatIdx !== -1 && args[formatIdx + 1] ? args[formatIdx + 1] : "table";
|
|
6532
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4420
6533
|
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
4421
6534
|
try {
|
|
4422
6535
|
const targets = rawTargetsArg ? resolveRuntimeTargets(rawTargetsArg, repoPath) : void 0;
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
6536
|
+
let allPassed = true;
|
|
6537
|
+
for (const itemName of positionals) {
|
|
6538
|
+
const report = removeSkillOrSubagent(repoPath, catalogDir, itemName, {
|
|
6539
|
+
targets,
|
|
6540
|
+
dryRun,
|
|
6541
|
+
force
|
|
6542
|
+
});
|
|
6543
|
+
process.stdout.write(formatSyncReport(report, format));
|
|
6544
|
+
if (!report.passed) {
|
|
6545
|
+
allPassed = false;
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
return allPassed ? ExitCode.SUCCESS : ExitCode.ERROR;
|
|
4430
6549
|
} catch (err) {
|
|
4431
6550
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4432
6551
|
process.stderr.write(`Error: ${msg}
|
|
@@ -4435,39 +6554,40 @@ function runCli(argv) {
|
|
|
4435
6554
|
}
|
|
4436
6555
|
}
|
|
4437
6556
|
if (command === "clean") {
|
|
4438
|
-
const
|
|
4439
|
-
const repoPath = repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd();
|
|
6557
|
+
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
4440
6558
|
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
4441
6559
|
if (cfgRes.error) {
|
|
4442
|
-
process.stderr.write(
|
|
4443
|
-
`
|
|
6560
|
+
process.stderr.write(
|
|
6561
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6562
|
+
`
|
|
6563
|
+
);
|
|
4444
6564
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4445
6565
|
}
|
|
4446
6566
|
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
4447
6567
|
const dryRun = args.includes("--dry-run");
|
|
4448
6568
|
const force = args.includes("--force");
|
|
4449
|
-
const
|
|
4450
|
-
const rawFormat = formatIdx !== -1 && args[formatIdx + 1] ? args[formatIdx + 1] : "table";
|
|
6569
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4451
6570
|
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
4452
|
-
const report = cleanSyncedArtifacts(repoPath, catalogDir, {
|
|
6571
|
+
const report = cleanSyncedArtifacts(repoPath, catalogDir, {
|
|
6572
|
+
dryRun,
|
|
6573
|
+
force
|
|
6574
|
+
});
|
|
4453
6575
|
process.stdout.write(formatSyncReport(report, format));
|
|
4454
6576
|
return report.passed ? ExitCode.SUCCESS : ExitCode.ERROR;
|
|
4455
6577
|
}
|
|
4456
6578
|
if (command === "eval") {
|
|
4457
6579
|
const catalogDir = resolveCatalogDir(args);
|
|
4458
|
-
const
|
|
4459
|
-
const
|
|
4460
|
-
const formatIdx = args.indexOf("--format");
|
|
4461
|
-
const rawFormat = formatIdx !== -1 && args[formatIdx + 1] ? args[formatIdx + 1] : "table";
|
|
6580
|
+
const skillFilter = getFlagValue(args, "--skill");
|
|
6581
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4462
6582
|
const format = rawFormat === "json" || rawFormat === "markdown" || rawFormat === "html" || rawFormat === "github" ? rawFormat : "table";
|
|
4463
|
-
const
|
|
4464
|
-
const outPath =
|
|
6583
|
+
const outRaw = getFlagValue(args, "--out");
|
|
6584
|
+
const outPath = outRaw ? path10.resolve(outRaw) : null;
|
|
4465
6585
|
const strict = args.includes("--strict");
|
|
4466
6586
|
const summary = evaluateCatalog(catalogDir, { skillFilter });
|
|
4467
6587
|
const rendered = formatEvalSummary(summary, format);
|
|
4468
6588
|
if (outPath) {
|
|
4469
|
-
|
|
4470
|
-
|
|
6589
|
+
fs10.mkdirSync(path10.dirname(outPath), { recursive: true });
|
|
6590
|
+
fs10.writeFileSync(outPath, rendered, "utf-8");
|
|
4471
6591
|
}
|
|
4472
6592
|
process.stdout.write(rendered);
|
|
4473
6593
|
if (strict && !summary.passed) {
|
|
@@ -4476,26 +6596,26 @@ function runCli(argv) {
|
|
|
4476
6596
|
return ExitCode.SUCCESS;
|
|
4477
6597
|
}
|
|
4478
6598
|
if (command === "harnesses") {
|
|
4479
|
-
const
|
|
4480
|
-
|
|
4481
|
-
repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd()
|
|
6599
|
+
const repoPath = path10.resolve(
|
|
6600
|
+
getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd()
|
|
4482
6601
|
);
|
|
4483
|
-
const
|
|
4484
|
-
const rawFormat = formatIdx !== -1 && args[formatIdx + 1] ? args[formatIdx + 1] : "table";
|
|
6602
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4485
6603
|
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
4486
6604
|
const report = inspectHarnesses(repoPath);
|
|
4487
6605
|
process.stdout.write(formatHarnessInspectionReport(report, format));
|
|
4488
6606
|
return ExitCode.SUCCESS;
|
|
4489
6607
|
}
|
|
4490
6608
|
if (command === "doctor" || command === "status") {
|
|
4491
|
-
const
|
|
4492
|
-
|
|
4493
|
-
repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd()
|
|
6609
|
+
const repoPath = path10.resolve(
|
|
6610
|
+
getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd()
|
|
4494
6611
|
);
|
|
4495
6612
|
const loadedConfigRes = loadSkillOpsConfig(repoPath);
|
|
4496
|
-
const catalogDir = resolveCatalogDir(
|
|
4497
|
-
|
|
4498
|
-
|
|
6613
|
+
const catalogDir = resolveCatalogDir(
|
|
6614
|
+
args,
|
|
6615
|
+
repoPath,
|
|
6616
|
+
loadedConfigRes.config
|
|
6617
|
+
);
|
|
6618
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4499
6619
|
const format = rawFormat === "json" || rawFormat === "markdown" || rawFormat === "github" ? rawFormat : "table";
|
|
4500
6620
|
const strict = args.includes("--strict");
|
|
4501
6621
|
const report = diagnoseRepository(repoPath, { catalogDir });
|
|
@@ -4513,29 +6633,29 @@ function runCli(argv) {
|
|
|
4513
6633
|
return ExitCode.SUCCESS;
|
|
4514
6634
|
}
|
|
4515
6635
|
if (command === "mcp") {
|
|
4516
|
-
const
|
|
4517
|
-
|
|
4518
|
-
repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd()
|
|
6636
|
+
const repoPath = path10.resolve(
|
|
6637
|
+
getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd()
|
|
4519
6638
|
);
|
|
4520
6639
|
const catalogDir = resolveCatalogDir(args, repoPath);
|
|
4521
6640
|
startMcpStdioServer({ repoPath, catalogDir });
|
|
4522
6641
|
return ExitCode.SUCCESS;
|
|
4523
6642
|
}
|
|
4524
6643
|
if (command === "mcp-setup") {
|
|
4525
|
-
const
|
|
4526
|
-
|
|
4527
|
-
repoIdx !== -1 && args[repoIdx + 1] ? args[repoIdx + 1] : process.cwd()
|
|
6644
|
+
const repoPath = path10.resolve(
|
|
6645
|
+
getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd()
|
|
4528
6646
|
);
|
|
4529
6647
|
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
4530
6648
|
if (cfgRes.error) {
|
|
4531
|
-
process.stderr.write(
|
|
4532
|
-
`
|
|
6649
|
+
process.stderr.write(
|
|
6650
|
+
`Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
|
|
6651
|
+
`
|
|
6652
|
+
);
|
|
4533
6653
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4534
6654
|
}
|
|
4535
|
-
const
|
|
6655
|
+
const targetsArg = resolveTargetsArg(args);
|
|
4536
6656
|
let targets;
|
|
4537
6657
|
try {
|
|
4538
|
-
targets =
|
|
6658
|
+
targets = targetsArg ? resolveRuntimeTargets(targetsArg, repoPath) : cfgRes.config?.targets ?? [...CORE_RUNTIME_TARGETS];
|
|
4539
6659
|
} catch (err) {
|
|
4540
6660
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4541
6661
|
process.stderr.write(`Invalid --targets: ${msg}
|
|
@@ -4543,15 +6663,18 @@ function runCli(argv) {
|
|
|
4543
6663
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
4544
6664
|
}
|
|
4545
6665
|
const dryRun = args.includes("--dry-run");
|
|
4546
|
-
const
|
|
4547
|
-
const rawFormat = formatIdx !== -1 && args[formatIdx + 1] ? args[formatIdx + 1] : "table";
|
|
6666
|
+
const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
|
|
4548
6667
|
const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
|
|
4549
6668
|
const report = setupMcpForHarnesses(repoPath, { targets, dryRun });
|
|
4550
6669
|
process.stdout.write(formatMcpSetupReport(report, format));
|
|
4551
6670
|
return ExitCode.SUCCESS;
|
|
4552
6671
|
}
|
|
4553
|
-
|
|
4554
|
-
`
|
|
6672
|
+
const suggestion = suggestCommand(command);
|
|
6673
|
+
const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
|
|
6674
|
+
process.stderr.write(
|
|
6675
|
+
`Unknown command '${command}'.${hint} Run 'npx skillops-cli --help'.
|
|
6676
|
+
`
|
|
6677
|
+
);
|
|
4555
6678
|
return ExitCode.ERROR;
|
|
4556
6679
|
}
|
|
4557
6680
|
function isDirectCliInvocation() {
|
|
@@ -4560,10 +6683,10 @@ function isDirectCliInvocation() {
|
|
|
4560
6683
|
return false;
|
|
4561
6684
|
}
|
|
4562
6685
|
try {
|
|
4563
|
-
const resolvedArg =
|
|
6686
|
+
const resolvedArg = fs10.existsSync(entryArg) ? fs10.realpathSync(entryArg) : path10.resolve(entryArg);
|
|
4564
6687
|
const selfPath = fileURLToPath(import.meta.url);
|
|
4565
|
-
const resolvedSelf =
|
|
4566
|
-
const base =
|
|
6688
|
+
const resolvedSelf = fs10.existsSync(selfPath) ? fs10.realpathSync(selfPath) : selfPath;
|
|
6689
|
+
const base = path10.basename(resolvedArg, path10.extname(resolvedArg));
|
|
4567
6690
|
return pathToFileURL(resolvedArg).href === pathToFileURL(resolvedSelf).href || base === "skillops" || base === "skillops-cli";
|
|
4568
6691
|
} catch {
|
|
4569
6692
|
return false;
|