skillio 0.1.6 → 0.1.8
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 +4 -4
- package/dist/cli.js +30 -96
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,14 +66,16 @@ audits both Claude Code and Codex over all time).
|
|
|
66
66
|
## Usage
|
|
67
67
|
|
|
68
68
|
```sh
|
|
69
|
-
# bare command —
|
|
69
|
+
# bare command — per-skill ambient token cost, sorted desc, with verdict
|
|
70
70
|
skl
|
|
71
71
|
skillio # equivalent
|
|
72
72
|
|
|
73
73
|
# subcommands
|
|
74
74
|
skl ls # list skills per source with diffs
|
|
75
75
|
skl cost # ambient ballast cost (frontmatter tokens) per skill
|
|
76
|
+
skl cst # alias for cost
|
|
76
77
|
skl usage # consumption: usage count × frontmatter tokens
|
|
78
|
+
skl usg # alias for usage
|
|
77
79
|
skl rm brainstorming # remove from lock + delete on-disk dir (with Y/n prompt)
|
|
78
80
|
skl rm brainstorming writing-plans # remove multiple
|
|
79
81
|
skl rm --yes brainstorming # skip confirmation
|
|
@@ -97,11 +99,9 @@ skl usage -a claude -a codex # equivalent: repeated --agent flag
|
|
|
97
99
|
| anywhere with `-g` / `--global` | global override |
|
|
98
100
|
| with `--root <dir>` | that exact dir, treated as global |
|
|
99
101
|
|
|
100
|
-
> Bare `skl` (no subcommand) ignores `-g` — it always shows both Global and Local sections plus a grand Total.
|
|
101
|
-
|
|
102
102
|
## What it does
|
|
103
103
|
|
|
104
|
-
- **
|
|
104
|
+
- **Cost** (`skl`) — per-skill ambient token cost sorted descending, with a cleanup verdict. Bare `skl` = `skl cost` in local scope; `skl -g` = global scope.
|
|
105
105
|
- **Audit skill usage** (`skl usage`) — parse agent session logs and count which skills were invoked, when, and how often.
|
|
106
106
|
- **Manage a skills lock** (`skl ls`, `skl rm`) — inspect and remove skills from a local or global lock file.
|
|
107
107
|
|
package/dist/cli.js
CHANGED
|
@@ -596,6 +596,9 @@ function yellow(s) {
|
|
|
596
596
|
function red(s) {
|
|
597
597
|
return enabled ? `\x1B[31m${s}\x1B[0m` : s;
|
|
598
598
|
}
|
|
599
|
+
function cyan2(s) {
|
|
600
|
+
return enabled ? `\x1B[36m${s}\x1B[0m` : s;
|
|
601
|
+
}
|
|
599
602
|
|
|
600
603
|
// src/utils/discover-skills.ts
|
|
601
604
|
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
@@ -717,7 +720,8 @@ var costCommand = defineCommand({
|
|
|
717
720
|
cell = "missing";
|
|
718
721
|
else
|
|
719
722
|
cell = "(no frontmatter)";
|
|
720
|
-
|
|
723
|
+
const pad = " ".repeat(nameWidth - r.name.length);
|
|
724
|
+
console.log(`${cyan2(r.name)}${pad} ${cell}`);
|
|
721
725
|
}
|
|
722
726
|
console.log("");
|
|
723
727
|
console.log(`Total: ~${total} tok across ${rows.length} skills ${paint(message)}`);
|
|
@@ -788,19 +792,19 @@ var listCommand = defineCommand({
|
|
|
788
792
|
if (!row)
|
|
789
793
|
continue;
|
|
790
794
|
const countCell = countCells[i] ?? "";
|
|
791
|
-
const namesText = row.names.length ? row.names.join(" ") : "";
|
|
795
|
+
const namesText = row.names.length ? row.names.map(cyan2).join(" ") : "";
|
|
792
796
|
const line = `${row.label.padEnd(labelWidth)} : ${countCell.padEnd(countWidth)}${namesText ? ` : ${namesText}` : ""}`;
|
|
793
797
|
console.log(line.trimEnd());
|
|
794
798
|
}
|
|
795
799
|
const diffs = [];
|
|
796
800
|
if (lockOnly.length) {
|
|
797
|
-
diffs.push(`skills-lock.json has ${lockOnly.length} skill${lockOnly.length === 1 ? "" : "s"} missing on disk: ${lockOnly.join(", ")}`);
|
|
801
|
+
diffs.push(`skills-lock.json has ${lockOnly.length} skill${lockOnly.length === 1 ? "" : "s"} missing on disk: ${lockOnly.map(cyan2).join(", ")}`);
|
|
798
802
|
}
|
|
799
803
|
if (claudeNotInLock.length) {
|
|
800
|
-
diffs.push(`.claude/skills has ${claudeNotInLock.length} skill${claudeNotInLock.length === 1 ? "" : "s"} not in lock: ${claudeNotInLock.join(", ")}`);
|
|
804
|
+
diffs.push(`.claude/skills has ${claudeNotInLock.length} skill${claudeNotInLock.length === 1 ? "" : "s"} not in lock: ${claudeNotInLock.map(cyan2).join(", ")}`);
|
|
801
805
|
}
|
|
802
806
|
if (agentsNotInLock.length) {
|
|
803
|
-
diffs.push(`.agents/skills has ${agentsNotInLock.length} skill${agentsNotInLock.length === 1 ? "" : "s"} not in lock: ${agentsNotInLock.join(", ")}`);
|
|
807
|
+
diffs.push(`.agents/skills has ${agentsNotInLock.length} skill${agentsNotInLock.length === 1 ? "" : "s"} not in lock: ${agentsNotInLock.map(cyan2).join(", ")}`);
|
|
804
808
|
}
|
|
805
809
|
if (diffs.length) {
|
|
806
810
|
console.log("");
|
|
@@ -976,89 +980,6 @@ var removeCommand = defineCommand({
|
|
|
976
980
|
}
|
|
977
981
|
});
|
|
978
982
|
|
|
979
|
-
// src/commands/summary.ts
|
|
980
|
-
function classify2(total) {
|
|
981
|
-
if (total < 1000)
|
|
982
|
-
return { verdict: "ok", message: "OK — keep it lean", paint: green };
|
|
983
|
-
if (total <= 1500)
|
|
984
|
-
return { verdict: "plan", message: "time to plan some cleanup", paint: yellow };
|
|
985
|
-
return { verdict: "cleanup", message: "ballast — clean it up", paint: red };
|
|
986
|
-
}
|
|
987
|
-
function bucketTokens(records, source) {
|
|
988
|
-
return records.filter((r) => r.sources.includes(source)).reduce((acc, r) => acc + (r.frontmatterTokens ?? 0), 0);
|
|
989
|
-
}
|
|
990
|
-
function bucketCount(records, source) {
|
|
991
|
-
return records.filter((r) => r.sources.includes(source)).length;
|
|
992
|
-
}
|
|
993
|
-
function buildSection(opts) {
|
|
994
|
-
const lockPath = getLockPath(opts.isGlobal);
|
|
995
|
-
const records = [
|
|
996
|
-
...discoverSkills({ isGlobal: opts.isGlobal, cwd: opts.cwd, lockPath }).values()
|
|
997
|
-
];
|
|
998
|
-
const rows = [
|
|
999
|
-
{
|
|
1000
|
-
label: `${opts.prefix}.claude/skills`,
|
|
1001
|
-
count: bucketCount(records, ".claude"),
|
|
1002
|
-
tokens: bucketTokens(records, ".claude")
|
|
1003
|
-
},
|
|
1004
|
-
{
|
|
1005
|
-
label: `${opts.prefix}.agents/skills`,
|
|
1006
|
-
count: bucketCount(records, ".agents"),
|
|
1007
|
-
tokens: bucketTokens(records, ".agents")
|
|
1008
|
-
},
|
|
1009
|
-
{
|
|
1010
|
-
label: `${opts.prefix}skills-lock.json`,
|
|
1011
|
-
count: bucketCount(records, "lock"),
|
|
1012
|
-
tokens: bucketTokens(records, "lock")
|
|
1013
|
-
}
|
|
1014
|
-
];
|
|
1015
|
-
const totalTokens = records.reduce((acc, r) => acc + (r.frontmatterTokens ?? 0), 0);
|
|
1016
|
-
const totalCount = records.length;
|
|
1017
|
-
return {
|
|
1018
|
-
title: opts.isGlobal ? "Global" : "Local",
|
|
1019
|
-
rows,
|
|
1020
|
-
totalCount,
|
|
1021
|
-
totalTokens
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
1024
|
-
function formatRow(row, labelW, countW, tokenW) {
|
|
1025
|
-
const countCell = row.count === 0 ? "(empty)" : `${row.count} skill${row.count === 1 ? "" : "s"}`;
|
|
1026
|
-
const tokensCell = `~${row.tokens} tok`;
|
|
1027
|
-
return `${row.label.padEnd(labelW)} : ${countCell.padEnd(countW)} ${tokensCell.padStart(tokenW)}`;
|
|
1028
|
-
}
|
|
1029
|
-
function renderSection(section) {
|
|
1030
|
-
const labelW = Math.max(...section.rows.map((r) => r.label.length));
|
|
1031
|
-
const countCells = section.rows.map((r) => r.count === 0 ? "(empty)" : `${r.count} skill${r.count === 1 ? "" : "s"}`);
|
|
1032
|
-
const countW = Math.max(...countCells.map((c) => c.length));
|
|
1033
|
-
const tokenW = Math.max(...section.rows.map((r) => `~${r.tokens} tok`.length));
|
|
1034
|
-
return [section.title, ...section.rows.map((r) => formatRow(r, labelW, countW, tokenW))];
|
|
1035
|
-
}
|
|
1036
|
-
function runSummary(args) {
|
|
1037
|
-
const cwd = process.cwd();
|
|
1038
|
-
const global = buildSection({ isGlobal: true, cwd, prefix: "~/" });
|
|
1039
|
-
const local = buildSection({ isGlobal: false, cwd, prefix: "" });
|
|
1040
|
-
const lines = [];
|
|
1041
|
-
lines.push(...renderSection(global));
|
|
1042
|
-
lines.push("");
|
|
1043
|
-
lines.push(...renderSection(local));
|
|
1044
|
-
lines.push("");
|
|
1045
|
-
const grandTokens = global.totalTokens + local.totalTokens;
|
|
1046
|
-
const grandCount = global.totalCount + local.totalCount;
|
|
1047
|
-
const { message, paint } = classify2(grandTokens);
|
|
1048
|
-
lines.push(`Total: ${grandCount} skills ~${grandTokens} tok ${paint(message)}`);
|
|
1049
|
-
console.log(lines.join(`
|
|
1050
|
-
`));
|
|
1051
|
-
}
|
|
1052
|
-
var summaryCommand = defineCommand({
|
|
1053
|
-
meta: { description: "Show skill counts and tokens across global + local sources" },
|
|
1054
|
-
args: {
|
|
1055
|
-
global: { type: "boolean", alias: "g", default: false, description: "Use global scope" }
|
|
1056
|
-
},
|
|
1057
|
-
run({ args }) {
|
|
1058
|
-
runSummary({ global: args.global });
|
|
1059
|
-
}
|
|
1060
|
-
});
|
|
1061
|
-
|
|
1062
983
|
// src/commands/usage.ts
|
|
1063
984
|
import { existsSync as existsSync9 } from "node:fs";
|
|
1064
985
|
import { join as join10 } from "node:path";
|
|
@@ -1397,7 +1318,7 @@ function pad(n, width) {
|
|
|
1397
1318
|
return String(n).padStart(width);
|
|
1398
1319
|
}
|
|
1399
1320
|
function formatUsageRow(row) {
|
|
1400
|
-
return `${pad(row.count, row.countWidth)} ${row.name}`;
|
|
1321
|
+
return `${pad(row.count, row.countWidth)} ${cyan2(row.name)}`;
|
|
1401
1322
|
}
|
|
1402
1323
|
function parseAgents(agent) {
|
|
1403
1324
|
if (!agent)
|
|
@@ -1670,7 +1591,18 @@ function mergeAgentArgs(argv) {
|
|
|
1670
1591
|
out.splice(slotIdx, 0, "--agent", values.join("\x1F"));
|
|
1671
1592
|
return out;
|
|
1672
1593
|
}
|
|
1673
|
-
var SUBCOMMAND_NAMES = new Set([
|
|
1594
|
+
var SUBCOMMAND_NAMES = new Set([
|
|
1595
|
+
"list",
|
|
1596
|
+
"ls",
|
|
1597
|
+
"remove",
|
|
1598
|
+
"rm",
|
|
1599
|
+
"cost",
|
|
1600
|
+
"co",
|
|
1601
|
+
"cst",
|
|
1602
|
+
"usage",
|
|
1603
|
+
"us",
|
|
1604
|
+
"usg"
|
|
1605
|
+
]);
|
|
1674
1606
|
function reorderRootFlagsToSubcommand(argv) {
|
|
1675
1607
|
const tail = argv.slice(2);
|
|
1676
1608
|
const subIdx = tail.findIndex((t) => !!t && SUBCOMMAND_NAMES.has(t));
|
|
@@ -1702,8 +1634,8 @@ function printRootHelp() {
|
|
|
1702
1634
|
"",
|
|
1703
1635
|
" list, ls List skills per source with totals and lock-vs-disk diff",
|
|
1704
1636
|
" remove, rm Remove skills from lock and delete their on-disk dirs",
|
|
1705
|
-
" cost, co
|
|
1706
|
-
" usage, us
|
|
1637
|
+
" cost, co, cst Show ambient ballast cost (per-skill frontmatter tokens) sorted desc",
|
|
1638
|
+
" usage, us, usg Show skill usage × cost (consumption) with missed rows"
|
|
1707
1639
|
];
|
|
1708
1640
|
console.log(lines.join(`
|
|
1709
1641
|
`));
|
|
@@ -1754,13 +1686,13 @@ var main = defineCommand({
|
|
|
1754
1686
|
version,
|
|
1755
1687
|
description: "Audit and manage AI agent skills"
|
|
1756
1688
|
},
|
|
1757
|
-
args:
|
|
1689
|
+
args: costCommand.args,
|
|
1758
1690
|
async run({ args }) {
|
|
1759
1691
|
if (hasSubcommand(process.argv))
|
|
1760
1692
|
return;
|
|
1761
|
-
await
|
|
1693
|
+
await costCommand.run?.({
|
|
1762
1694
|
args,
|
|
1763
|
-
cmd:
|
|
1695
|
+
cmd: costCommand,
|
|
1764
1696
|
rawArgs: process.argv.slice(2)
|
|
1765
1697
|
});
|
|
1766
1698
|
},
|
|
@@ -1771,8 +1703,10 @@ var main = defineCommand({
|
|
|
1771
1703
|
rm: removeCommand,
|
|
1772
1704
|
cost: costCommand,
|
|
1773
1705
|
co: costCommand,
|
|
1706
|
+
cst: costCommand,
|
|
1774
1707
|
usage: usageCommand,
|
|
1775
|
-
us: usageCommand
|
|
1708
|
+
us: usageCommand,
|
|
1709
|
+
usg: usageCommand
|
|
1776
1710
|
}
|
|
1777
1711
|
});
|
|
1778
1712
|
(async () => {
|