skilldb 0.5.0 → 0.6.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 +7 -4
- package/dist/cli.js +187 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +12 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -6
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +281 -15
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,13 +12,16 @@ Connect SkillDB directly to your AI coding tool via the Model Context Protocol:
|
|
|
12
12
|
# Step 1: Install globally (one time)
|
|
13
13
|
npm install -g skilldb
|
|
14
14
|
|
|
15
|
-
# Step 2:
|
|
16
|
-
claude mcp add skilldb -- skilldb-mcp
|
|
15
|
+
# Step 2: Get your free API key at https://skilldb.dev/api-access
|
|
17
16
|
|
|
18
|
-
#
|
|
19
|
-
claude mcp add skilldb -- skilldb-mcp --api-key
|
|
17
|
+
# Step 3: Add to Claude Code (with API key for full content)
|
|
18
|
+
claude mcp add skilldb -- skilldb-mcp --api-key sk_live_YOUR_KEY
|
|
20
19
|
```
|
|
21
20
|
|
|
21
|
+
> **⚠️ Without an API key**, you can search and browse skills (metadata only).
|
|
22
|
+
> **With a free API key**, you get full skill content — the actual markdown instructions your agent uses.
|
|
23
|
+
> Get your key in 30 seconds at [skilldb.dev/api-access](https://skilldb.dev/api-access).
|
|
24
|
+
|
|
22
25
|
**Cursor** — add to `.cursor/mcp.json`:
|
|
23
26
|
```json
|
|
24
27
|
{
|
package/dist/cli.js
CHANGED
|
@@ -57,7 +57,13 @@ var SkillDBClient = class {
|
|
|
57
57
|
}
|
|
58
58
|
return h;
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
/** Make an authenticated request to any endpoint (used by MCP tools for private skills). */
|
|
61
|
+
async rawRequest(endpoint, init) {
|
|
62
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
63
|
+
const headers = { ...this.headers(), ...init?.headers || {} };
|
|
64
|
+
return fetch(url, { ...init, headers });
|
|
65
|
+
}
|
|
66
|
+
async typedRequest(endpoint, params) {
|
|
61
67
|
const url = new URL(`${this.baseUrl}${endpoint}`);
|
|
62
68
|
if (params) {
|
|
63
69
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -74,7 +80,7 @@ var SkillDBClient = class {
|
|
|
74
80
|
}
|
|
75
81
|
/** Search skills by keyword. */
|
|
76
82
|
async search(query, options) {
|
|
77
|
-
return this.
|
|
83
|
+
return this.typedRequest("/skills", {
|
|
78
84
|
search: query,
|
|
79
85
|
category: options?.category ?? "",
|
|
80
86
|
pack: options?.pack ?? "",
|
|
@@ -86,7 +92,7 @@ var SkillDBClient = class {
|
|
|
86
92
|
}
|
|
87
93
|
/** List skills with optional filters and sorting. */
|
|
88
94
|
async list(options) {
|
|
89
|
-
return this.
|
|
95
|
+
return this.typedRequest("/skills", {
|
|
90
96
|
category: options?.category ?? "",
|
|
91
97
|
pack: options?.pack ?? "",
|
|
92
98
|
search: options?.search ?? "",
|
|
@@ -99,21 +105,21 @@ var SkillDBClient = class {
|
|
|
99
105
|
/** Get a single skill by ID (e.g. "software-skills/code-review.md"). */
|
|
100
106
|
async get(id) {
|
|
101
107
|
const encoded = encodeURIComponent(id);
|
|
102
|
-
const res = await this.
|
|
108
|
+
const res = await this.typedRequest(`/skills/${encoded}`, {
|
|
103
109
|
include_content: "true"
|
|
104
110
|
});
|
|
105
111
|
return "skill" in res ? res.skill : res;
|
|
106
112
|
}
|
|
107
113
|
/** Batch retrieve multiple skills by IDs (max 50). */
|
|
108
114
|
async batch(ids) {
|
|
109
|
-
return this.
|
|
115
|
+
return this.typedRequest("/skills", {
|
|
110
116
|
ids: ids.slice(0, 50).join(","),
|
|
111
117
|
include_content: "true"
|
|
112
118
|
});
|
|
113
119
|
}
|
|
114
120
|
/** Get search autocomplete suggestions. */
|
|
115
121
|
async suggest(query) {
|
|
116
|
-
return this.
|
|
122
|
+
return this.typedRequest("/skills/suggest", { q: query });
|
|
117
123
|
}
|
|
118
124
|
/** Validate that the configured API key works. */
|
|
119
125
|
async validate() {
|
|
@@ -1760,6 +1766,178 @@ ${pc17.green(`${upToDate} up to date`)}` + (changed > 0 ? pc17.yellow(`, ${chang
|
|
|
1760
1766
|
}
|
|
1761
1767
|
}
|
|
1762
1768
|
|
|
1769
|
+
// src/commands/purge.ts
|
|
1770
|
+
import fs15 from "fs";
|
|
1771
|
+
import path15 from "path";
|
|
1772
|
+
import pc18 from "picocolors";
|
|
1773
|
+
import readline3 from "readline";
|
|
1774
|
+
var SKILLDB_DIR11 = ".skilldb";
|
|
1775
|
+
var SKILLS_DIR8 = "skills";
|
|
1776
|
+
var ACTIVE_DIR8 = "active";
|
|
1777
|
+
var SLIM_DIR2 = "slim";
|
|
1778
|
+
function prompt3(question) {
|
|
1779
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
1780
|
+
return new Promise((resolve) => {
|
|
1781
|
+
rl.question(question, (answer) => {
|
|
1782
|
+
rl.close();
|
|
1783
|
+
resolve(answer.trim());
|
|
1784
|
+
});
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
function getDirectorySize(dir) {
|
|
1788
|
+
if (!fs15.existsSync(dir)) return 0;
|
|
1789
|
+
let size = 0;
|
|
1790
|
+
for (const entry of fs15.readdirSync(dir, { withFileTypes: true })) {
|
|
1791
|
+
const full = path15.join(dir, entry.name);
|
|
1792
|
+
if (entry.isDirectory()) size += getDirectorySize(full);
|
|
1793
|
+
else size += fs15.statSync(full).size;
|
|
1794
|
+
}
|
|
1795
|
+
return size;
|
|
1796
|
+
}
|
|
1797
|
+
function collectAllSkills(dir) {
|
|
1798
|
+
const skills = [];
|
|
1799
|
+
if (!fs15.existsSync(dir)) return skills;
|
|
1800
|
+
for (const pack of fs15.readdirSync(dir, { withFileTypes: true })) {
|
|
1801
|
+
if (!pack.isDirectory()) continue;
|
|
1802
|
+
for (const file of fs15.readdirSync(path15.join(dir, pack.name))) {
|
|
1803
|
+
if (file.endsWith(".md")) skills.push(`${pack.name}/${file}`);
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
return skills;
|
|
1807
|
+
}
|
|
1808
|
+
function collectActivePacks(cwd) {
|
|
1809
|
+
const activeDir = path15.join(cwd, SKILLDB_DIR11, ACTIVE_DIR8);
|
|
1810
|
+
const packs = /* @__PURE__ */ new Set();
|
|
1811
|
+
if (!fs15.existsSync(activeDir)) return packs;
|
|
1812
|
+
for (const entry of fs15.readdirSync(activeDir, { withFileTypes: true })) {
|
|
1813
|
+
if (entry.isDirectory()) packs.add(entry.name);
|
|
1814
|
+
}
|
|
1815
|
+
return packs;
|
|
1816
|
+
}
|
|
1817
|
+
async function purgeCommand(options) {
|
|
1818
|
+
const cwd = process.cwd();
|
|
1819
|
+
const skillsDir = path15.join(cwd, SKILLDB_DIR11, SKILLS_DIR8);
|
|
1820
|
+
const activeDir = path15.join(cwd, SKILLDB_DIR11, ACTIVE_DIR8);
|
|
1821
|
+
const slimDir = path15.join(cwd, SKILLDB_DIR11, SLIM_DIR2);
|
|
1822
|
+
if (!fs15.existsSync(path15.join(cwd, SKILLDB_DIR11))) {
|
|
1823
|
+
console.log(pc18.red('No .skilldb/ directory found. Run "skilldb init" first.'));
|
|
1824
|
+
process.exit(1);
|
|
1825
|
+
}
|
|
1826
|
+
const allSkills = collectAllSkills(skillsDir);
|
|
1827
|
+
const activePacks = collectActivePacks(cwd);
|
|
1828
|
+
const activeSkills = /* @__PURE__ */ new Set();
|
|
1829
|
+
if (fs15.existsSync(activeDir)) {
|
|
1830
|
+
for (const pack of fs15.readdirSync(activeDir, { withFileTypes: true })) {
|
|
1831
|
+
if (!pack.isDirectory()) continue;
|
|
1832
|
+
for (const file of fs15.readdirSync(path15.join(activeDir, pack.name))) {
|
|
1833
|
+
if (file.endsWith(".md")) activeSkills.add(`${pack.name}/${file}`);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
const inactiveSkills = allSkills.filter((s) => !activeSkills.has(s));
|
|
1838
|
+
const slimSkills = collectAllSkills(slimDir);
|
|
1839
|
+
const totalSize = getDirectorySize(path15.join(cwd, SKILLDB_DIR11));
|
|
1840
|
+
const skillsSize = getDirectorySize(skillsDir);
|
|
1841
|
+
const slimSize = getDirectorySize(slimDir);
|
|
1842
|
+
console.log(pc18.bold("\nSkillDB Purge\n"));
|
|
1843
|
+
console.log(` Total skills cached: ${pc18.cyan(String(allSkills.length))}`);
|
|
1844
|
+
console.log(` Active skills: ${pc18.green(String(activeSkills.size))}`);
|
|
1845
|
+
console.log(` Inactive skills: ${pc18.yellow(String(inactiveSkills.length))}`);
|
|
1846
|
+
console.log(` Slim summaries: ${pc18.dim(String(slimSkills.length))}`);
|
|
1847
|
+
console.log(` Total disk usage: ${pc18.cyan((totalSize / 1024).toFixed(0) + " KB")}`);
|
|
1848
|
+
console.log();
|
|
1849
|
+
if (allSkills.length === 0 && slimSkills.length === 0) {
|
|
1850
|
+
console.log(pc18.dim("Nothing to purge."));
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
const stats = { skillsRemoved: 0, packsRemoved: 0, slimRemoved: 0, bytesFreed: 0, activeKept: activeSkills.size };
|
|
1854
|
+
let toPurge = [];
|
|
1855
|
+
let purgeSlim = options?.slim || options?.all || false;
|
|
1856
|
+
if (options?.all) {
|
|
1857
|
+
toPurge = [...allSkills];
|
|
1858
|
+
purgeSlim = true;
|
|
1859
|
+
console.log(pc18.red(`Will remove ALL ${allSkills.length} cached skills + ${slimSkills.length} slim summaries`));
|
|
1860
|
+
} else if (options?.inactive) {
|
|
1861
|
+
toPurge = inactiveSkills;
|
|
1862
|
+
console.log(pc18.yellow(`Will remove ${inactiveSkills.length} inactive skills (keeping ${activeSkills.size} active)`));
|
|
1863
|
+
} else {
|
|
1864
|
+
toPurge = inactiveSkills;
|
|
1865
|
+
purgeSlim = true;
|
|
1866
|
+
console.log(pc18.yellow(`Will remove ${inactiveSkills.length} inactive skills + ${slimSkills.length} slim summaries`));
|
|
1867
|
+
console.log(pc18.green(` Keeping ${activeSkills.size} active skills`));
|
|
1868
|
+
}
|
|
1869
|
+
if (toPurge.length === 0 && !purgeSlim) {
|
|
1870
|
+
console.log(pc18.dim("\nNothing to purge \u2014 all skills are active."));
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
if (!options?.force && !options?.dryRun) {
|
|
1874
|
+
const answer = await prompt3(`
|
|
1875
|
+
Continue? (y/N) `);
|
|
1876
|
+
if (answer.toLowerCase() !== "y") {
|
|
1877
|
+
console.log(pc18.dim("Cancelled."));
|
|
1878
|
+
return;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
if (options?.dryRun) {
|
|
1882
|
+
console.log(pc18.dim("\n (Dry run \u2014 no files will be deleted)\n"));
|
|
1883
|
+
for (const skill of toPurge.slice(0, 20)) {
|
|
1884
|
+
console.log(pc18.dim(` would remove: ${skill}`));
|
|
1885
|
+
}
|
|
1886
|
+
if (toPurge.length > 20) console.log(pc18.dim(` ... and ${toPurge.length - 20} more`));
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
const manifest = readManifest(cwd);
|
|
1890
|
+
for (const skillId of toPurge) {
|
|
1891
|
+
const [pack, file] = skillId.split("/");
|
|
1892
|
+
const filePath = path15.join(skillsDir, pack, file);
|
|
1893
|
+
if (fs15.existsSync(filePath)) {
|
|
1894
|
+
stats.bytesFreed += fs15.statSync(filePath).size;
|
|
1895
|
+
fs15.unlinkSync(filePath);
|
|
1896
|
+
stats.skillsRemoved++;
|
|
1897
|
+
}
|
|
1898
|
+
if (manifest.installed[skillId]) {
|
|
1899
|
+
delete manifest.installed[skillId];
|
|
1900
|
+
}
|
|
1901
|
+
const packDir = path15.join(skillsDir, pack);
|
|
1902
|
+
if (fs15.existsSync(packDir) && fs15.readdirSync(packDir).length === 0) {
|
|
1903
|
+
fs15.rmdirSync(packDir);
|
|
1904
|
+
stats.packsRemoved++;
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
if (purgeSlim && fs15.existsSync(slimDir)) {
|
|
1908
|
+
for (const pack of fs15.readdirSync(slimDir, { withFileTypes: true })) {
|
|
1909
|
+
if (!pack.isDirectory()) continue;
|
|
1910
|
+
const packPath = path15.join(slimDir, pack.name);
|
|
1911
|
+
for (const file of fs15.readdirSync(packPath)) {
|
|
1912
|
+
const filePath = path15.join(packPath, file);
|
|
1913
|
+
stats.bytesFreed += fs15.statSync(filePath).size;
|
|
1914
|
+
fs15.unlinkSync(filePath);
|
|
1915
|
+
stats.slimRemoved++;
|
|
1916
|
+
}
|
|
1917
|
+
if (fs15.readdirSync(packPath).length === 0) fs15.rmdirSync(packPath);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
if (options?.all && fs15.existsSync(activeDir)) {
|
|
1921
|
+
for (const pack of fs15.readdirSync(activeDir, { withFileTypes: true })) {
|
|
1922
|
+
if (!pack.isDirectory()) continue;
|
|
1923
|
+
const packPath = path15.join(activeDir, pack.name);
|
|
1924
|
+
for (const file of fs15.readdirSync(packPath)) {
|
|
1925
|
+
fs15.unlinkSync(path15.join(packPath, file));
|
|
1926
|
+
}
|
|
1927
|
+
fs15.rmdirSync(packPath);
|
|
1928
|
+
}
|
|
1929
|
+
stats.activeKept = 0;
|
|
1930
|
+
}
|
|
1931
|
+
writeManifest(manifest, cwd);
|
|
1932
|
+
console.log(pc18.green(`
|
|
1933
|
+
\u2713 Purge complete`));
|
|
1934
|
+
console.log(` Skills removed: ${stats.skillsRemoved}`);
|
|
1935
|
+
console.log(` Slim removed: ${stats.slimRemoved}`);
|
|
1936
|
+
console.log(` Packs cleaned: ${stats.packsRemoved}`);
|
|
1937
|
+
console.log(` Space freed: ${(stats.bytesFreed / 1024).toFixed(0)} KB`);
|
|
1938
|
+
console.log(` Active kept: ${stats.activeKept}`);
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1763
1941
|
// src/cli.ts
|
|
1764
1942
|
var program = new Command();
|
|
1765
1943
|
program.name("skilldb").description("SkillDB CLI \u2014 discover, install, and manage AI agent skills").version("0.3.0");
|
|
@@ -1799,5 +1977,8 @@ program.command("stats").description("Show local statistics and coverage").actio
|
|
|
1799
1977
|
program.command("diff [target]").description("Compare local skills with latest remote versions").action((target) => {
|
|
1800
1978
|
diffCommand(target);
|
|
1801
1979
|
});
|
|
1980
|
+
program.command("purge").description("Remove cached skills to free disk space").option("-a, --all", "Remove ALL cached skills (including active)").option("-i, --inactive", "Remove only inactive skills").option("-s, --slim", "Also remove slim summaries").option("-n, --dry-run", "Show what would be removed without deleting").option("-f, --force", "Skip confirmation prompt").action((opts) => {
|
|
1981
|
+
purgeCommand(opts);
|
|
1982
|
+
});
|
|
1802
1983
|
program.parse();
|
|
1803
1984
|
//# sourceMappingURL=cli.js.map
|