skilldb 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ SkillDBClient: () => SkillDBClient,
34
+ cacheSkill: () => cacheSkill,
35
+ createClient: () => createClient,
36
+ getCachedPath: () => getCachedPath,
37
+ initCache: () => initCache,
38
+ isCached: () => isCached,
39
+ listCached: () => listCached,
40
+ resolveApiKey: () => resolveApiKey,
41
+ resolveBaseUrl: () => resolveBaseUrl,
42
+ saveApiKey: () => saveApiKey
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/config.ts
47
+ var import_node_fs = __toESM(require("fs"), 1);
48
+ var import_node_path = __toESM(require("path"), 1);
49
+ var import_node_os = __toESM(require("os"), 1);
50
+ var RC_FILE = ".skilldbrc";
51
+ var DEFAULT_BASE_URL = "https://skilldb.dev/api/v1";
52
+ function readJson(filePath) {
53
+ try {
54
+ const raw = import_node_fs.default.readFileSync(filePath, "utf-8");
55
+ return JSON.parse(raw);
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+ function resolveApiKey() {
61
+ if (process.env.SKILLDB_API_KEY) {
62
+ return process.env.SKILLDB_API_KEY;
63
+ }
64
+ const projectRc = import_node_path.default.join(process.cwd(), RC_FILE);
65
+ const projectConfig = readJson(projectRc);
66
+ if (projectConfig?.apiKey) return projectConfig.apiKey;
67
+ const homeRc = import_node_path.default.join(import_node_os.default.homedir(), RC_FILE);
68
+ const homeConfig = readJson(homeRc);
69
+ if (homeConfig?.apiKey) return homeConfig.apiKey;
70
+ return void 0;
71
+ }
72
+ function resolveBaseUrl() {
73
+ return process.env.SKILLDB_API_URL || DEFAULT_BASE_URL;
74
+ }
75
+ function saveApiKey(apiKey, global = true) {
76
+ const target = global ? import_node_path.default.join(import_node_os.default.homedir(), RC_FILE) : import_node_path.default.join(process.cwd(), RC_FILE);
77
+ const existing = readJson(target) || {};
78
+ import_node_fs.default.writeFileSync(target, JSON.stringify({ ...existing, apiKey }, null, 2) + "\n", "utf-8");
79
+ return target;
80
+ }
81
+
82
+ // src/client.ts
83
+ var SkillDBClient = class {
84
+ apiKey;
85
+ baseUrl;
86
+ constructor(config) {
87
+ this.apiKey = config?.apiKey ?? resolveApiKey();
88
+ this.baseUrl = config?.baseUrl ?? resolveBaseUrl();
89
+ }
90
+ headers() {
91
+ const h = { "Content-Type": "application/json" };
92
+ if (this.apiKey) {
93
+ h["Authorization"] = `Bearer ${this.apiKey}`;
94
+ }
95
+ return h;
96
+ }
97
+ async request(endpoint, params) {
98
+ const url = new URL(`${this.baseUrl}${endpoint}`);
99
+ if (params) {
100
+ for (const [k, v] of Object.entries(params)) {
101
+ if (v !== void 0 && v !== "") url.searchParams.set(k, v);
102
+ }
103
+ }
104
+ const res = await fetch(url.toString(), { headers: this.headers() });
105
+ if (!res.ok) {
106
+ const body = await res.json().catch(() => ({}));
107
+ const msg = body.error || `HTTP ${res.status}`;
108
+ throw new Error(msg);
109
+ }
110
+ return res.json();
111
+ }
112
+ /** Search skills by keyword. */
113
+ async search(query, options) {
114
+ return this.request("/skills", {
115
+ search: query,
116
+ category: options?.category ?? "",
117
+ pack: options?.pack ?? "",
118
+ limit: String(options?.limit ?? 20),
119
+ offset: String(options?.offset ?? 0),
120
+ include_content: options?.includeContent ? "true" : ""
121
+ });
122
+ }
123
+ /** List skills with optional filters. */
124
+ async list(options) {
125
+ return this.request("/skills", {
126
+ category: options?.category ?? "",
127
+ pack: options?.pack ?? "",
128
+ search: options?.search ?? "",
129
+ limit: String(options?.limit ?? 50),
130
+ offset: String(options?.offset ?? 0),
131
+ include_content: options?.includeContent ? "true" : ""
132
+ });
133
+ }
134
+ /** Get a single skill by ID (e.g. "software-skills/code-review.md"). */
135
+ async get(id) {
136
+ const encoded = encodeURIComponent(id);
137
+ const res = await this.request(`/skills/${encoded}`, {
138
+ include_content: "true"
139
+ });
140
+ return "skill" in res ? res.skill : res;
141
+ }
142
+ /** Validate that the configured API key works. */
143
+ async validate() {
144
+ try {
145
+ await this.list({ limit: 1 });
146
+ return true;
147
+ } catch {
148
+ return false;
149
+ }
150
+ }
151
+ };
152
+
153
+ // src/cache.ts
154
+ var import_node_fs2 = __toESM(require("fs"), 1);
155
+ var import_node_path2 = __toESM(require("path"), 1);
156
+ var SKILLDB_DIR = ".skilldb";
157
+ var SKILLS_DIR = "skills";
158
+ var MANIFEST_FILE = "manifest.json";
159
+ function ensureDir(dir) {
160
+ if (!import_node_fs2.default.existsSync(dir)) {
161
+ import_node_fs2.default.mkdirSync(dir, { recursive: true });
162
+ }
163
+ }
164
+ function skilldbRoot(cwd) {
165
+ return import_node_path2.default.join(cwd ?? process.cwd(), SKILLDB_DIR);
166
+ }
167
+ function initCache(cwd) {
168
+ const root = skilldbRoot(cwd);
169
+ ensureDir(import_node_path2.default.join(root, SKILLS_DIR));
170
+ const manifestPath = import_node_path2.default.join(root, MANIFEST_FILE);
171
+ if (!import_node_fs2.default.existsSync(manifestPath)) {
172
+ import_node_fs2.default.writeFileSync(manifestPath, JSON.stringify({ installed: {} }, null, 2) + "\n");
173
+ }
174
+ return root;
175
+ }
176
+ function readManifest(cwd) {
177
+ const manifestPath = import_node_path2.default.join(skilldbRoot(cwd), MANIFEST_FILE);
178
+ try {
179
+ return JSON.parse(import_node_fs2.default.readFileSync(manifestPath, "utf-8"));
180
+ } catch {
181
+ return { installed: {} };
182
+ }
183
+ }
184
+ function writeManifest(manifest, cwd) {
185
+ const root = skilldbRoot(cwd);
186
+ ensureDir(root);
187
+ import_node_fs2.default.writeFileSync(
188
+ import_node_path2.default.join(root, MANIFEST_FILE),
189
+ JSON.stringify(manifest, null, 2) + "\n"
190
+ );
191
+ }
192
+ function cacheSkill(skill, cwd) {
193
+ const root = skilldbRoot(cwd);
194
+ const skillDir = import_node_path2.default.join(root, SKILLS_DIR, skill.pack);
195
+ ensureDir(skillDir);
196
+ const safeName = skill.name.replace(/[/\\:*?"<>|]/g, "-");
197
+ const filePath = import_node_path2.default.join(skillDir, `${safeName}.md`);
198
+ import_node_fs2.default.writeFileSync(filePath, skill.content ?? `# ${skill.title}
199
+
200
+ ${skill.description}
201
+ `);
202
+ const manifest = readManifest(cwd);
203
+ manifest.installed[skill.id] = {
204
+ addedAt: (/* @__PURE__ */ new Date()).toISOString(),
205
+ lines: skill.lines
206
+ };
207
+ writeManifest(manifest, cwd);
208
+ return filePath;
209
+ }
210
+ function isCached(skillId, cwd) {
211
+ const manifest = readManifest(cwd);
212
+ return skillId in manifest.installed;
213
+ }
214
+ function getCachedPath(skillId, cwd) {
215
+ if (!isCached(skillId, cwd)) return null;
216
+ const [pack, file] = skillId.split("/");
217
+ const name = file.replace(".md", "").replace(/[/\\:*?"<>|]/g, "-");
218
+ const filePath = import_node_path2.default.join(skilldbRoot(cwd), SKILLS_DIR, pack, `${name}.md`);
219
+ return import_node_fs2.default.existsSync(filePath) ? filePath : null;
220
+ }
221
+ function listCached(cwd) {
222
+ return readManifest(cwd);
223
+ }
224
+
225
+ // src/index.ts
226
+ function createClient(config) {
227
+ return new SkillDBClient(config);
228
+ }
229
+ // Annotate the CommonJS export names for ESM import in node:
230
+ 0 && (module.exports = {
231
+ SkillDBClient,
232
+ cacheSkill,
233
+ createClient,
234
+ getCachedPath,
235
+ initCache,
236
+ isCached,
237
+ listCached,
238
+ resolveApiKey,
239
+ resolveBaseUrl,
240
+ saveApiKey
241
+ });
242
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/client.ts","../src/cache.ts"],"sourcesContent":["export { SkillDBClient } from './client.js';\nexport type {\n Skill,\n PackInfo,\n CategoryInfo,\n SkillsResponse,\n SearchOptions,\n ClientConfig,\n Manifest,\n} from './types.js';\nexport { resolveApiKey, resolveBaseUrl, saveApiKey } from './config.js';\nexport { initCache, cacheSkill, isCached, getCachedPath, listCached } from './cache.js';\n\nimport type { ClientConfig } from './types.js';\nimport { SkillDBClient } from './client.js';\n\n/** Create a SkillDB client. Auto-loads API key from env/config. */\nexport function createClient(config?: ClientConfig): SkillDBClient {\n return new SkillDBClient(config);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst RC_FILE = '.skilldbrc';\n\nexport const DEFAULT_BASE_URL = 'https://skilldb.dev/api/v1';\n\ninterface RcConfig {\n apiKey?: string;\n}\n\nfunction readJson(filePath: string): RcConfig | null {\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve API key from (in priority order):\n * 1. SKILLDB_API_KEY env var\n * 2. .skilldbrc in project root (cwd)\n * 3. ~/.skilldbrc in home dir\n */\nexport function resolveApiKey(): string | undefined {\n if (process.env.SKILLDB_API_KEY) {\n return process.env.SKILLDB_API_KEY;\n }\n\n const projectRc = path.join(process.cwd(), RC_FILE);\n const projectConfig = readJson(projectRc);\n if (projectConfig?.apiKey) return projectConfig.apiKey;\n\n const homeRc = path.join(os.homedir(), RC_FILE);\n const homeConfig = readJson(homeRc);\n if (homeConfig?.apiKey) return homeConfig.apiKey;\n\n return undefined;\n}\n\n/**\n * Resolve base URL from env or default.\n */\nexport function resolveBaseUrl(): string {\n return process.env.SKILLDB_API_URL || DEFAULT_BASE_URL;\n}\n\n/**\n * Save API key to ~/.skilldbrc (user-wide) or project .skilldbrc.\n */\nexport function saveApiKey(apiKey: string, global = true): string {\n const target = global\n ? path.join(os.homedir(), RC_FILE)\n : path.join(process.cwd(), RC_FILE);\n\n const existing = readJson(target) || {};\n fs.writeFileSync(target, JSON.stringify({ ...existing, apiKey }, null, 2) + '\\n', 'utf-8');\n return target;\n}\n","import type { ClientConfig, Skill, SkillsResponse, SearchOptions } from './types.js';\nimport { resolveApiKey, resolveBaseUrl } from './config.js';\n\nexport class SkillDBClient {\n private apiKey?: string;\n private baseUrl: string;\n\n constructor(config?: ClientConfig) {\n this.apiKey = config?.apiKey ?? resolveApiKey();\n this.baseUrl = config?.baseUrl ?? resolveBaseUrl();\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this.apiKey) {\n h['Authorization'] = `Bearer ${this.apiKey}`;\n }\n return h;\n }\n\n private async request<T>(endpoint: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${endpoint}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== '') url.searchParams.set(k, v);\n }\n }\n\n const res = await fetch(url.toString(), { headers: this.headers() });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n const msg = (body as Record<string, string>).error || `HTTP ${res.status}`;\n throw new Error(msg);\n }\n\n return res.json() as Promise<T>;\n }\n\n /** Search skills by keyword. */\n async search(query: string, options?: Omit<SearchOptions, 'search'>): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n limit: String(options?.limit ?? 20),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** List skills with optional filters. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n limit: String(options?.limit ?? 50),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** Get a single skill by ID (e.g. \"software-skills/code-review.md\"). */\n async get(id: string): Promise<Skill> {\n const encoded = encodeURIComponent(id);\n const res = await this.request<Skill | { skill: Skill }>(`/skills/${encoded}`, {\n include_content: 'true',\n });\n // Handle both direct and wrapped responses\n return 'skill' in res ? res.skill : res;\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n await this.list({ limit: 1 });\n return true;\n } catch {\n return false;\n }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Manifest, Skill } from './types.js';\n\nconst SKILLDB_DIR = '.skilldb';\nconst SKILLS_DIR = 'skills';\nconst MANIFEST_FILE = 'manifest.json';\n\nfunction ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n\nfunction skilldbRoot(cwd?: string): string {\n return path.join(cwd ?? process.cwd(), SKILLDB_DIR);\n}\n\n/** Ensure .skilldb/ directory structure exists. */\nexport function initCache(cwd?: string): string {\n const root = skilldbRoot(cwd);\n ensureDir(path.join(root, SKILLS_DIR));\n\n const manifestPath = path.join(root, MANIFEST_FILE);\n if (!fs.existsSync(manifestPath)) {\n fs.writeFileSync(manifestPath, JSON.stringify({ installed: {} }, null, 2) + '\\n');\n }\n\n return root;\n}\n\n/** Read the local manifest. */\nexport function readManifest(cwd?: string): Manifest {\n const manifestPath = path.join(skilldbRoot(cwd), MANIFEST_FILE);\n try {\n return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));\n } catch {\n return { installed: {} };\n }\n}\n\n/** Write the local manifest. */\nexport function writeManifest(manifest: Manifest, cwd?: string): void {\n const root = skilldbRoot(cwd);\n ensureDir(root);\n fs.writeFileSync(\n path.join(root, MANIFEST_FILE),\n JSON.stringify(manifest, null, 2) + '\\n'\n );\n}\n\n/** Save a skill to the local cache. */\nexport function cacheSkill(skill: Skill, cwd?: string): string {\n const root = skilldbRoot(cwd);\n const skillDir = path.join(root, SKILLS_DIR, skill.pack);\n ensureDir(skillDir);\n\n const safeName = skill.name.replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skillDir, `${safeName}.md`);\n fs.writeFileSync(filePath, skill.content ?? `# ${skill.title}\\n\\n${skill.description}\\n`);\n\n // Update manifest\n const manifest = readManifest(cwd);\n manifest.installed[skill.id] = {\n addedAt: new Date().toISOString(),\n lines: skill.lines,\n };\n writeManifest(manifest, cwd);\n\n return filePath;\n}\n\n/** Check if a skill is already cached. */\nexport function isCached(skillId: string, cwd?: string): boolean {\n const manifest = readManifest(cwd);\n return skillId in manifest.installed;\n}\n\n/** Get the local path for a cached skill. */\nexport function getCachedPath(skillId: string, cwd?: string): string | null {\n if (!isCached(skillId, cwd)) return null;\n const [pack, file] = skillId.split('/');\n const name = file.replace('.md', '').replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skilldbRoot(cwd), SKILLS_DIR, pack, `${name}.md`);\n return fs.existsSync(filePath) ? filePath : null;\n}\n\n/** List all cached skills. */\nexport function listCached(cwd?: string): Manifest {\n return readManifest(cwd);\n}\n\n/** Ensure .skilldb/ and .skilldbrc are in .gitignore. */\nexport function updateGitignore(cwd?: string): void {\n const root = cwd ?? process.cwd();\n const gitignorePath = path.join(root, '.gitignore');\n\n const entries = ['.skilldb/', '.skilldbrc'];\n let content = '';\n\n if (fs.existsSync(gitignorePath)) {\n content = fs.readFileSync(gitignorePath, 'utf-8');\n }\n\n const toAdd = entries.filter(e => !content.includes(e));\n if (toAdd.length === 0) return;\n\n const suffix = (content && !content.endsWith('\\n') ? '\\n' : '') +\n '\\n# SkillDB\\n' + toAdd.join('\\n') + '\\n';\n\n fs.writeFileSync(gitignorePath, content + suffix);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,qBAAe;AACf,uBAAiB;AACjB,qBAAe;AAEf,IAAM,UAAU;AAET,IAAM,mBAAmB;AAMhC,SAAS,SAAS,UAAmC;AACnD,MAAI;AACF,UAAM,MAAM,eAAAA,QAAG,aAAa,UAAU,OAAO;AAC7C,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,gBAAoC;AAClD,MAAI,QAAQ,IAAI,iBAAiB;AAC/B,WAAO,QAAQ,IAAI;AAAA,EACrB;AAEA,QAAM,YAAY,iBAAAC,QAAK,KAAK,QAAQ,IAAI,GAAG,OAAO;AAClD,QAAM,gBAAgB,SAAS,SAAS;AACxC,MAAI,eAAe,OAAQ,QAAO,cAAc;AAEhD,QAAM,SAAS,iBAAAA,QAAK,KAAK,eAAAC,QAAG,QAAQ,GAAG,OAAO;AAC9C,QAAM,aAAa,SAAS,MAAM;AAClC,MAAI,YAAY,OAAQ,QAAO,WAAW;AAE1C,SAAO;AACT;AAKO,SAAS,iBAAyB;AACvC,SAAO,QAAQ,IAAI,mBAAmB;AACxC;AAKO,SAAS,WAAW,QAAgB,SAAS,MAAc;AAChE,QAAM,SAAS,SACX,iBAAAD,QAAK,KAAK,eAAAC,QAAG,QAAQ,GAAG,OAAO,IAC/B,iBAAAD,QAAK,KAAK,QAAQ,IAAI,GAAG,OAAO;AAEpC,QAAM,WAAW,SAAS,MAAM,KAAK,CAAC;AACtC,iBAAAD,QAAG,cAAc,QAAQ,KAAK,UAAU,EAAE,GAAG,UAAU,OAAO,GAAG,MAAM,CAAC,IAAI,MAAM,OAAO;AACzF,SAAO;AACT;;;AC1DO,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EACA;AAAA,EAER,YAAY,QAAuB;AACjC,SAAK,SAAS,QAAQ,UAAU,cAAc;AAC9C,SAAK,UAAU,QAAQ,WAAW,eAAe;AAAA,EACnD;AAAA,EAEQ,UAAkC;AACxC,UAAM,IAA4B,EAAE,gBAAgB,mBAAmB;AACvE,QAAI,KAAK,QAAQ;AACf,QAAE,eAAe,IAAI,UAAU,KAAK,MAAM;AAAA,IAC5C;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,QAAW,UAAkB,QAA6C;AACtF,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,OAAO,GAAG,QAAQ,EAAE;AAChD,QAAI,QAAQ;AACV,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,YAAI,MAAM,UAAa,MAAM,GAAI,KAAI,aAAa,IAAI,GAAG,CAAC;AAAA,MAC5D;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,MAAM,IAAI,SAAS,GAAG,EAAE,SAAS,KAAK,QAAQ,EAAE,CAAC;AAEnE,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,YAAM,MAAO,KAAgC,SAAS,QAAQ,IAAI,MAAM;AACxE,YAAM,IAAI,MAAM,GAAG;AAAA,IACrB;AAEA,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA;AAAA,EAGA,MAAM,OAAO,OAAe,SAAkE;AAC5F,WAAO,KAAK,QAAwB,WAAW;AAAA,MAC7C,QAAQ;AAAA,MACR,UAAU,SAAS,YAAY;AAAA,MAC/B,MAAM,SAAS,QAAQ;AAAA,MACvB,OAAO,OAAO,SAAS,SAAS,EAAE;AAAA,MAClC,QAAQ,OAAO,SAAS,UAAU,CAAC;AAAA,MACnC,iBAAiB,SAAS,iBAAiB,SAAS;AAAA,IACtD,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,KAAK,SAAkD;AAC3D,WAAO,KAAK,QAAwB,WAAW;AAAA,MAC7C,UAAU,SAAS,YAAY;AAAA,MAC/B,MAAM,SAAS,QAAQ;AAAA,MACvB,QAAQ,SAAS,UAAU;AAAA,MAC3B,OAAO,OAAO,SAAS,SAAS,EAAE;AAAA,MAClC,QAAQ,OAAO,SAAS,UAAU,CAAC;AAAA,MACnC,iBAAiB,SAAS,iBAAiB,SAAS;AAAA,IACtD,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,IAAI,IAA4B;AACpC,UAAM,UAAU,mBAAmB,EAAE;AACrC,UAAM,MAAM,MAAM,KAAK,QAAkC,WAAW,OAAO,IAAI;AAAA,MAC7E,iBAAiB;AAAA,IACnB,CAAC;AAED,WAAO,WAAW,MAAM,IAAI,QAAQ;AAAA,EACtC;AAAA;AAAA,EAGA,MAAM,WAA6B;AACjC,QAAI;AACF,YAAM,KAAK,KAAK,EAAE,OAAO,EAAE,CAAC;AAC5B,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AClFA,IAAAG,kBAAe;AACf,IAAAC,oBAAiB;AAGjB,IAAM,cAAc;AACpB,IAAM,aAAa;AACnB,IAAM,gBAAgB;AAEtB,SAAS,UAAU,KAAmB;AACpC,MAAI,CAAC,gBAAAC,QAAG,WAAW,GAAG,GAAG;AACvB,oBAAAA,QAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACvC;AACF;AAEA,SAAS,YAAY,KAAsB;AACzC,SAAO,kBAAAC,QAAK,KAAK,OAAO,QAAQ,IAAI,GAAG,WAAW;AACpD;AAGO,SAAS,UAAU,KAAsB;AAC9C,QAAM,OAAO,YAAY,GAAG;AAC5B,YAAU,kBAAAA,QAAK,KAAK,MAAM,UAAU,CAAC;AAErC,QAAM,eAAe,kBAAAA,QAAK,KAAK,MAAM,aAAa;AAClD,MAAI,CAAC,gBAAAD,QAAG,WAAW,YAAY,GAAG;AAChC,oBAAAA,QAAG,cAAc,cAAc,KAAK,UAAU,EAAE,WAAW,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,IAAI;AAAA,EAClF;AAEA,SAAO;AACT;AAGO,SAAS,aAAa,KAAwB;AACnD,QAAM,eAAe,kBAAAC,QAAK,KAAK,YAAY,GAAG,GAAG,aAAa;AAC9D,MAAI;AACF,WAAO,KAAK,MAAM,gBAAAD,QAAG,aAAa,cAAc,OAAO,CAAC;AAAA,EAC1D,QAAQ;AACN,WAAO,EAAE,WAAW,CAAC,EAAE;AAAA,EACzB;AACF;AAGO,SAAS,cAAc,UAAoB,KAAoB;AACpE,QAAM,OAAO,YAAY,GAAG;AAC5B,YAAU,IAAI;AACd,kBAAAA,QAAG;AAAA,IACD,kBAAAC,QAAK,KAAK,MAAM,aAAa;AAAA,IAC7B,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI;AAAA,EACtC;AACF;AAGO,SAAS,WAAW,OAAc,KAAsB;AAC7D,QAAM,OAAO,YAAY,GAAG;AAC5B,QAAM,WAAW,kBAAAA,QAAK,KAAK,MAAM,YAAY,MAAM,IAAI;AACvD,YAAU,QAAQ;AAElB,QAAM,WAAW,MAAM,KAAK,QAAQ,iBAAiB,GAAG;AACxD,QAAM,WAAW,kBAAAA,QAAK,KAAK,UAAU,GAAG,QAAQ,KAAK;AACrD,kBAAAD,QAAG,cAAc,UAAU,MAAM,WAAW,KAAK,MAAM,KAAK;AAAA;AAAA,EAAO,MAAM,WAAW;AAAA,CAAI;AAGxF,QAAM,WAAW,aAAa,GAAG;AACjC,WAAS,UAAU,MAAM,EAAE,IAAI;AAAA,IAC7B,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,IAChC,OAAO,MAAM;AAAA,EACf;AACA,gBAAc,UAAU,GAAG;AAE3B,SAAO;AACT;AAGO,SAAS,SAAS,SAAiB,KAAuB;AAC/D,QAAM,WAAW,aAAa,GAAG;AACjC,SAAO,WAAW,SAAS;AAC7B;AAGO,SAAS,cAAc,SAAiB,KAA6B;AAC1E,MAAI,CAAC,SAAS,SAAS,GAAG,EAAG,QAAO;AACpC,QAAM,CAAC,MAAM,IAAI,IAAI,QAAQ,MAAM,GAAG;AACtC,QAAM,OAAO,KAAK,QAAQ,OAAO,EAAE,EAAE,QAAQ,iBAAiB,GAAG;AACjE,QAAM,WAAW,kBAAAC,QAAK,KAAK,YAAY,GAAG,GAAG,YAAY,MAAM,GAAG,IAAI,KAAK;AAC3E,SAAO,gBAAAD,QAAG,WAAW,QAAQ,IAAI,WAAW;AAC9C;AAGO,SAAS,WAAW,KAAwB;AACjD,SAAO,aAAa,GAAG;AACzB;;;AHzEO,SAAS,aAAa,QAAsC;AACjE,SAAO,IAAI,cAAc,MAAM;AACjC;","names":["fs","path","os","import_node_fs","import_node_path","fs","path"]}
@@ -0,0 +1,109 @@
1
+ /** A single skill from the SkillDB catalog. */
2
+ interface Skill {
3
+ id: string;
4
+ name: string;
5
+ title: string;
6
+ description: string;
7
+ file: string;
8
+ pack: string;
9
+ packLabel: string;
10
+ category: string;
11
+ lines: number;
12
+ content?: string;
13
+ }
14
+ /** Metadata about a skill pack. */
15
+ interface PackInfo {
16
+ slug: string;
17
+ label: string;
18
+ category: string;
19
+ count: number;
20
+ }
21
+ /** Category summary. */
22
+ interface CategoryInfo {
23
+ name: string;
24
+ packCount: number;
25
+ skillCount: number;
26
+ }
27
+ /** Paginated API response. */
28
+ interface SkillsResponse {
29
+ skills: Skill[];
30
+ pagination: {
31
+ total: number;
32
+ limit: number;
33
+ offset: number;
34
+ hasMore: boolean;
35
+ };
36
+ meta: {
37
+ categories: string[];
38
+ totalPacks: number;
39
+ };
40
+ }
41
+ /** Options for searching/listing skills. */
42
+ interface SearchOptions {
43
+ category?: string;
44
+ pack?: string;
45
+ search?: string;
46
+ limit?: number;
47
+ offset?: number;
48
+ includeContent?: boolean;
49
+ }
50
+ /** SDK client configuration. */
51
+ interface ClientConfig {
52
+ apiKey?: string;
53
+ baseUrl?: string;
54
+ }
55
+ /** Local manifest tracking installed skills. */
56
+ interface Manifest {
57
+ installed: Record<string, {
58
+ addedAt: string;
59
+ lines: number;
60
+ }>;
61
+ }
62
+
63
+ declare class SkillDBClient {
64
+ private apiKey?;
65
+ private baseUrl;
66
+ constructor(config?: ClientConfig);
67
+ private headers;
68
+ private request;
69
+ /** Search skills by keyword. */
70
+ search(query: string, options?: Omit<SearchOptions, 'search'>): Promise<SkillsResponse>;
71
+ /** List skills with optional filters. */
72
+ list(options?: SearchOptions): Promise<SkillsResponse>;
73
+ /** Get a single skill by ID (e.g. "software-skills/code-review.md"). */
74
+ get(id: string): Promise<Skill>;
75
+ /** Validate that the configured API key works. */
76
+ validate(): Promise<boolean>;
77
+ }
78
+
79
+ /**
80
+ * Resolve API key from (in priority order):
81
+ * 1. SKILLDB_API_KEY env var
82
+ * 2. .skilldbrc in project root (cwd)
83
+ * 3. ~/.skilldbrc in home dir
84
+ */
85
+ declare function resolveApiKey(): string | undefined;
86
+ /**
87
+ * Resolve base URL from env or default.
88
+ */
89
+ declare function resolveBaseUrl(): string;
90
+ /**
91
+ * Save API key to ~/.skilldbrc (user-wide) or project .skilldbrc.
92
+ */
93
+ declare function saveApiKey(apiKey: string, global?: boolean): string;
94
+
95
+ /** Ensure .skilldb/ directory structure exists. */
96
+ declare function initCache(cwd?: string): string;
97
+ /** Save a skill to the local cache. */
98
+ declare function cacheSkill(skill: Skill, cwd?: string): string;
99
+ /** Check if a skill is already cached. */
100
+ declare function isCached(skillId: string, cwd?: string): boolean;
101
+ /** Get the local path for a cached skill. */
102
+ declare function getCachedPath(skillId: string, cwd?: string): string | null;
103
+ /** List all cached skills. */
104
+ declare function listCached(cwd?: string): Manifest;
105
+
106
+ /** Create a SkillDB client. Auto-loads API key from env/config. */
107
+ declare function createClient(config?: ClientConfig): SkillDBClient;
108
+
109
+ export { type CategoryInfo, type ClientConfig, type Manifest, type PackInfo, type SearchOptions, type Skill, SkillDBClient, type SkillsResponse, cacheSkill, createClient, getCachedPath, initCache, isCached, listCached, resolveApiKey, resolveBaseUrl, saveApiKey };
@@ -0,0 +1,109 @@
1
+ /** A single skill from the SkillDB catalog. */
2
+ interface Skill {
3
+ id: string;
4
+ name: string;
5
+ title: string;
6
+ description: string;
7
+ file: string;
8
+ pack: string;
9
+ packLabel: string;
10
+ category: string;
11
+ lines: number;
12
+ content?: string;
13
+ }
14
+ /** Metadata about a skill pack. */
15
+ interface PackInfo {
16
+ slug: string;
17
+ label: string;
18
+ category: string;
19
+ count: number;
20
+ }
21
+ /** Category summary. */
22
+ interface CategoryInfo {
23
+ name: string;
24
+ packCount: number;
25
+ skillCount: number;
26
+ }
27
+ /** Paginated API response. */
28
+ interface SkillsResponse {
29
+ skills: Skill[];
30
+ pagination: {
31
+ total: number;
32
+ limit: number;
33
+ offset: number;
34
+ hasMore: boolean;
35
+ };
36
+ meta: {
37
+ categories: string[];
38
+ totalPacks: number;
39
+ };
40
+ }
41
+ /** Options for searching/listing skills. */
42
+ interface SearchOptions {
43
+ category?: string;
44
+ pack?: string;
45
+ search?: string;
46
+ limit?: number;
47
+ offset?: number;
48
+ includeContent?: boolean;
49
+ }
50
+ /** SDK client configuration. */
51
+ interface ClientConfig {
52
+ apiKey?: string;
53
+ baseUrl?: string;
54
+ }
55
+ /** Local manifest tracking installed skills. */
56
+ interface Manifest {
57
+ installed: Record<string, {
58
+ addedAt: string;
59
+ lines: number;
60
+ }>;
61
+ }
62
+
63
+ declare class SkillDBClient {
64
+ private apiKey?;
65
+ private baseUrl;
66
+ constructor(config?: ClientConfig);
67
+ private headers;
68
+ private request;
69
+ /** Search skills by keyword. */
70
+ search(query: string, options?: Omit<SearchOptions, 'search'>): Promise<SkillsResponse>;
71
+ /** List skills with optional filters. */
72
+ list(options?: SearchOptions): Promise<SkillsResponse>;
73
+ /** Get a single skill by ID (e.g. "software-skills/code-review.md"). */
74
+ get(id: string): Promise<Skill>;
75
+ /** Validate that the configured API key works. */
76
+ validate(): Promise<boolean>;
77
+ }
78
+
79
+ /**
80
+ * Resolve API key from (in priority order):
81
+ * 1. SKILLDB_API_KEY env var
82
+ * 2. .skilldbrc in project root (cwd)
83
+ * 3. ~/.skilldbrc in home dir
84
+ */
85
+ declare function resolveApiKey(): string | undefined;
86
+ /**
87
+ * Resolve base URL from env or default.
88
+ */
89
+ declare function resolveBaseUrl(): string;
90
+ /**
91
+ * Save API key to ~/.skilldbrc (user-wide) or project .skilldbrc.
92
+ */
93
+ declare function saveApiKey(apiKey: string, global?: boolean): string;
94
+
95
+ /** Ensure .skilldb/ directory structure exists. */
96
+ declare function initCache(cwd?: string): string;
97
+ /** Save a skill to the local cache. */
98
+ declare function cacheSkill(skill: Skill, cwd?: string): string;
99
+ /** Check if a skill is already cached. */
100
+ declare function isCached(skillId: string, cwd?: string): boolean;
101
+ /** Get the local path for a cached skill. */
102
+ declare function getCachedPath(skillId: string, cwd?: string): string | null;
103
+ /** List all cached skills. */
104
+ declare function listCached(cwd?: string): Manifest;
105
+
106
+ /** Create a SkillDB client. Auto-loads API key from env/config. */
107
+ declare function createClient(config?: ClientConfig): SkillDBClient;
108
+
109
+ export { type CategoryInfo, type ClientConfig, type Manifest, type PackInfo, type SearchOptions, type Skill, SkillDBClient, type SkillsResponse, cacheSkill, createClient, getCachedPath, initCache, isCached, listCached, resolveApiKey, resolveBaseUrl, saveApiKey };
package/dist/index.js ADDED
@@ -0,0 +1,196 @@
1
+ // src/config.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import os from "os";
5
+ var RC_FILE = ".skilldbrc";
6
+ var DEFAULT_BASE_URL = "https://skilldb.dev/api/v1";
7
+ function readJson(filePath) {
8
+ try {
9
+ const raw = fs.readFileSync(filePath, "utf-8");
10
+ return JSON.parse(raw);
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+ function resolveApiKey() {
16
+ if (process.env.SKILLDB_API_KEY) {
17
+ return process.env.SKILLDB_API_KEY;
18
+ }
19
+ const projectRc = path.join(process.cwd(), RC_FILE);
20
+ const projectConfig = readJson(projectRc);
21
+ if (projectConfig?.apiKey) return projectConfig.apiKey;
22
+ const homeRc = path.join(os.homedir(), RC_FILE);
23
+ const homeConfig = readJson(homeRc);
24
+ if (homeConfig?.apiKey) return homeConfig.apiKey;
25
+ return void 0;
26
+ }
27
+ function resolveBaseUrl() {
28
+ return process.env.SKILLDB_API_URL || DEFAULT_BASE_URL;
29
+ }
30
+ function saveApiKey(apiKey, global = true) {
31
+ const target = global ? path.join(os.homedir(), RC_FILE) : path.join(process.cwd(), RC_FILE);
32
+ const existing = readJson(target) || {};
33
+ fs.writeFileSync(target, JSON.stringify({ ...existing, apiKey }, null, 2) + "\n", "utf-8");
34
+ return target;
35
+ }
36
+
37
+ // src/client.ts
38
+ var SkillDBClient = class {
39
+ apiKey;
40
+ baseUrl;
41
+ constructor(config) {
42
+ this.apiKey = config?.apiKey ?? resolveApiKey();
43
+ this.baseUrl = config?.baseUrl ?? resolveBaseUrl();
44
+ }
45
+ headers() {
46
+ const h = { "Content-Type": "application/json" };
47
+ if (this.apiKey) {
48
+ h["Authorization"] = `Bearer ${this.apiKey}`;
49
+ }
50
+ return h;
51
+ }
52
+ async request(endpoint, params) {
53
+ const url = new URL(`${this.baseUrl}${endpoint}`);
54
+ if (params) {
55
+ for (const [k, v] of Object.entries(params)) {
56
+ if (v !== void 0 && v !== "") url.searchParams.set(k, v);
57
+ }
58
+ }
59
+ const res = await fetch(url.toString(), { headers: this.headers() });
60
+ if (!res.ok) {
61
+ const body = await res.json().catch(() => ({}));
62
+ const msg = body.error || `HTTP ${res.status}`;
63
+ throw new Error(msg);
64
+ }
65
+ return res.json();
66
+ }
67
+ /** Search skills by keyword. */
68
+ async search(query, options) {
69
+ return this.request("/skills", {
70
+ search: query,
71
+ category: options?.category ?? "",
72
+ pack: options?.pack ?? "",
73
+ limit: String(options?.limit ?? 20),
74
+ offset: String(options?.offset ?? 0),
75
+ include_content: options?.includeContent ? "true" : ""
76
+ });
77
+ }
78
+ /** List skills with optional filters. */
79
+ async list(options) {
80
+ return this.request("/skills", {
81
+ category: options?.category ?? "",
82
+ pack: options?.pack ?? "",
83
+ search: options?.search ?? "",
84
+ limit: String(options?.limit ?? 50),
85
+ offset: String(options?.offset ?? 0),
86
+ include_content: options?.includeContent ? "true" : ""
87
+ });
88
+ }
89
+ /** Get a single skill by ID (e.g. "software-skills/code-review.md"). */
90
+ async get(id) {
91
+ const encoded = encodeURIComponent(id);
92
+ const res = await this.request(`/skills/${encoded}`, {
93
+ include_content: "true"
94
+ });
95
+ return "skill" in res ? res.skill : res;
96
+ }
97
+ /** Validate that the configured API key works. */
98
+ async validate() {
99
+ try {
100
+ await this.list({ limit: 1 });
101
+ return true;
102
+ } catch {
103
+ return false;
104
+ }
105
+ }
106
+ };
107
+
108
+ // src/cache.ts
109
+ import fs2 from "fs";
110
+ import path2 from "path";
111
+ var SKILLDB_DIR = ".skilldb";
112
+ var SKILLS_DIR = "skills";
113
+ var MANIFEST_FILE = "manifest.json";
114
+ function ensureDir(dir) {
115
+ if (!fs2.existsSync(dir)) {
116
+ fs2.mkdirSync(dir, { recursive: true });
117
+ }
118
+ }
119
+ function skilldbRoot(cwd) {
120
+ return path2.join(cwd ?? process.cwd(), SKILLDB_DIR);
121
+ }
122
+ function initCache(cwd) {
123
+ const root = skilldbRoot(cwd);
124
+ ensureDir(path2.join(root, SKILLS_DIR));
125
+ const manifestPath = path2.join(root, MANIFEST_FILE);
126
+ if (!fs2.existsSync(manifestPath)) {
127
+ fs2.writeFileSync(manifestPath, JSON.stringify({ installed: {} }, null, 2) + "\n");
128
+ }
129
+ return root;
130
+ }
131
+ function readManifest(cwd) {
132
+ const manifestPath = path2.join(skilldbRoot(cwd), MANIFEST_FILE);
133
+ try {
134
+ return JSON.parse(fs2.readFileSync(manifestPath, "utf-8"));
135
+ } catch {
136
+ return { installed: {} };
137
+ }
138
+ }
139
+ function writeManifest(manifest, cwd) {
140
+ const root = skilldbRoot(cwd);
141
+ ensureDir(root);
142
+ fs2.writeFileSync(
143
+ path2.join(root, MANIFEST_FILE),
144
+ JSON.stringify(manifest, null, 2) + "\n"
145
+ );
146
+ }
147
+ function cacheSkill(skill, cwd) {
148
+ const root = skilldbRoot(cwd);
149
+ const skillDir = path2.join(root, SKILLS_DIR, skill.pack);
150
+ ensureDir(skillDir);
151
+ const safeName = skill.name.replace(/[/\\:*?"<>|]/g, "-");
152
+ const filePath = path2.join(skillDir, `${safeName}.md`);
153
+ fs2.writeFileSync(filePath, skill.content ?? `# ${skill.title}
154
+
155
+ ${skill.description}
156
+ `);
157
+ const manifest = readManifest(cwd);
158
+ manifest.installed[skill.id] = {
159
+ addedAt: (/* @__PURE__ */ new Date()).toISOString(),
160
+ lines: skill.lines
161
+ };
162
+ writeManifest(manifest, cwd);
163
+ return filePath;
164
+ }
165
+ function isCached(skillId, cwd) {
166
+ const manifest = readManifest(cwd);
167
+ return skillId in manifest.installed;
168
+ }
169
+ function getCachedPath(skillId, cwd) {
170
+ if (!isCached(skillId, cwd)) return null;
171
+ const [pack, file] = skillId.split("/");
172
+ const name = file.replace(".md", "").replace(/[/\\:*?"<>|]/g, "-");
173
+ const filePath = path2.join(skilldbRoot(cwd), SKILLS_DIR, pack, `${name}.md`);
174
+ return fs2.existsSync(filePath) ? filePath : null;
175
+ }
176
+ function listCached(cwd) {
177
+ return readManifest(cwd);
178
+ }
179
+
180
+ // src/index.ts
181
+ function createClient(config) {
182
+ return new SkillDBClient(config);
183
+ }
184
+ export {
185
+ SkillDBClient,
186
+ cacheSkill,
187
+ createClient,
188
+ getCachedPath,
189
+ initCache,
190
+ isCached,
191
+ listCached,
192
+ resolveApiKey,
193
+ resolveBaseUrl,
194
+ saveApiKey
195
+ };
196
+ //# sourceMappingURL=index.js.map