skilldb 0.6.0 → 0.7.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 CHANGED
@@ -109,8 +109,26 @@ var SkillDBClient = class {
109
109
  }
110
110
  const res = await fetch(url.toString(), { headers: this.headers() });
111
111
  if (!res.ok) {
112
- const body = await res.json().catch(() => ({}));
113
- const msg = body.error || `HTTP ${res.status}`;
112
+ const body = await res.json().catch(() => null);
113
+ const apiError = body && typeof body === "object" ? body.error : null;
114
+ const status = res.status;
115
+ const statusText = res.statusText || "Unknown";
116
+ let msg;
117
+ if (apiError) {
118
+ msg = apiError;
119
+ } else if (status === 500) {
120
+ msg = `SkillDB API internal error (HTTP 500). The service may be experiencing issues. Check status at https://skilldb.dev/api/v1/status`;
121
+ } else if (status === 503) {
122
+ msg = `SkillDB API unavailable (HTTP 503). The skills index may not be loaded. Check status at https://skilldb.dev/api/v1/status`;
123
+ } else if (status === 401) {
124
+ msg = `Invalid or expired API key (HTTP 401). Get a free key at https://skilldb.dev/api-access`;
125
+ } else if (status === 429) {
126
+ msg = `Rate limit exceeded (HTTP 429). Authenticate with an API key for higher limits: https://skilldb.dev/api-access`;
127
+ } else if (status === 404) {
128
+ msg = `Resource not found (HTTP 404). The requested skill or endpoint does not exist.`;
129
+ } else {
130
+ msg = `SkillDB API error: HTTP ${status} ${statusText}. Check https://skilldb.dev/api/v1/status for service health.`;
131
+ }
114
132
  throw new Error(msg);
115
133
  }
116
134
  return res.json();
@@ -1 +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 ApiKeyScope,\n ApiKeyInfo,\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 /** Make an authenticated request to any endpoint (used by MCP tools for private skills). */\n public async rawRequest(endpoint: string, init?: RequestInit): Promise<Response> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = { ...this.headers(), ...(init?.headers || {}) };\n return fetch(url, { ...init, headers });\n }\n\n\n private async typedRequest<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.typedRequest<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\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 and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\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.typedRequest<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 /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.typedRequest('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\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":"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","names":["fs","path","os","import_node_fs","import_node_path","fs","path"]}
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 ApiKeyScope,\n ApiKeyInfo,\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 /** Make an authenticated request to any endpoint (used by MCP tools for private skills). */\n public async rawRequest(endpoint: string, init?: RequestInit): Promise<Response> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = { ...this.headers(), ...(init?.headers || {}) };\n return fetch(url, { ...init, headers });\n }\n\n\n private async typedRequest<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(() => null);\n const apiError = body && typeof body === 'object' ? (body as Record<string, string>).error : null;\n const status = res.status;\n const statusText = res.statusText || 'Unknown';\n\n // Build a descriptive error with actionable guidance\n let msg: string;\n if (apiError) {\n msg = apiError;\n } else if (status === 500) {\n msg = `SkillDB API internal error (HTTP 500). The service may be experiencing issues. Check status at https://skilldb.dev/api/v1/status`;\n } else if (status === 503) {\n msg = `SkillDB API unavailable (HTTP 503). The skills index may not be loaded. Check status at https://skilldb.dev/api/v1/status`;\n } else if (status === 401) {\n msg = `Invalid or expired API key (HTTP 401). Get a free key at https://skilldb.dev/api-access`;\n } else if (status === 429) {\n msg = `Rate limit exceeded (HTTP 429). Authenticate with an API key for higher limits: https://skilldb.dev/api-access`;\n } else if (status === 404) {\n msg = `Resource not found (HTTP 404). The requested skill or endpoint does not exist.`;\n } else {\n msg = `SkillDB API error: HTTP ${status} ${statusText}. Check https://skilldb.dev/api/v1/status for service health.`;\n }\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.typedRequest<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\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 and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\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.typedRequest<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 /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.typedRequest('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\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":"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","names":["fs","path","os","import_node_fs","import_node_path","fs","path"]}
package/dist/index.js CHANGED
@@ -64,8 +64,26 @@ var SkillDBClient = class {
64
64
  }
65
65
  const res = await fetch(url.toString(), { headers: this.headers() });
66
66
  if (!res.ok) {
67
- const body = await res.json().catch(() => ({}));
68
- const msg = body.error || `HTTP ${res.status}`;
67
+ const body = await res.json().catch(() => null);
68
+ const apiError = body && typeof body === "object" ? body.error : null;
69
+ const status = res.status;
70
+ const statusText = res.statusText || "Unknown";
71
+ let msg;
72
+ if (apiError) {
73
+ msg = apiError;
74
+ } else if (status === 500) {
75
+ msg = `SkillDB API internal error (HTTP 500). The service may be experiencing issues. Check status at https://skilldb.dev/api/v1/status`;
76
+ } else if (status === 503) {
77
+ msg = `SkillDB API unavailable (HTTP 503). The skills index may not be loaded. Check status at https://skilldb.dev/api/v1/status`;
78
+ } else if (status === 401) {
79
+ msg = `Invalid or expired API key (HTTP 401). Get a free key at https://skilldb.dev/api-access`;
80
+ } else if (status === 429) {
81
+ msg = `Rate limit exceeded (HTTP 429). Authenticate with an API key for higher limits: https://skilldb.dev/api-access`;
82
+ } else if (status === 404) {
83
+ msg = `Resource not found (HTTP 404). The requested skill or endpoint does not exist.`;
84
+ } else {
85
+ msg = `SkillDB API error: HTTP ${status} ${statusText}. Check https://skilldb.dev/api/v1/status for service health.`;
86
+ }
69
87
  throw new Error(msg);
70
88
  }
71
89
  return res.json();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/client.ts","../src/cache.ts","../src/index.ts"],"sourcesContent":["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 /** Make an authenticated request to any endpoint (used by MCP tools for private skills). */\n public async rawRequest(endpoint: string, init?: RequestInit): Promise<Response> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = { ...this.headers(), ...(init?.headers || {}) };\n return fetch(url, { ...init, headers });\n }\n\n\n private async typedRequest<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.typedRequest<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\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 and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\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.typedRequest<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 /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.typedRequest('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\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","export { SkillDBClient } from './client.js';\nexport type {\n Skill,\n PackInfo,\n CategoryInfo,\n SkillsResponse,\n SearchOptions,\n ClientConfig,\n Manifest,\n ApiKeyScope,\n ApiKeyInfo,\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"],"mappings":"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","names":["fs","path"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/client.ts","../src/cache.ts","../src/index.ts"],"sourcesContent":["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 /** Make an authenticated request to any endpoint (used by MCP tools for private skills). */\n public async rawRequest(endpoint: string, init?: RequestInit): Promise<Response> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = { ...this.headers(), ...(init?.headers || {}) };\n return fetch(url, { ...init, headers });\n }\n\n\n private async typedRequest<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(() => null);\n const apiError = body && typeof body === 'object' ? (body as Record<string, string>).error : null;\n const status = res.status;\n const statusText = res.statusText || 'Unknown';\n\n // Build a descriptive error with actionable guidance\n let msg: string;\n if (apiError) {\n msg = apiError;\n } else if (status === 500) {\n msg = `SkillDB API internal error (HTTP 500). The service may be experiencing issues. Check status at https://skilldb.dev/api/v1/status`;\n } else if (status === 503) {\n msg = `SkillDB API unavailable (HTTP 503). The skills index may not be loaded. Check status at https://skilldb.dev/api/v1/status`;\n } else if (status === 401) {\n msg = `Invalid or expired API key (HTTP 401). Get a free key at https://skilldb.dev/api-access`;\n } else if (status === 429) {\n msg = `Rate limit exceeded (HTTP 429). Authenticate with an API key for higher limits: https://skilldb.dev/api-access`;\n } else if (status === 404) {\n msg = `Resource not found (HTTP 404). The requested skill or endpoint does not exist.`;\n } else {\n msg = `SkillDB API error: HTTP ${status} ${statusText}. Check https://skilldb.dev/api/v1/status for service health.`;\n }\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.typedRequest<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\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 and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\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.typedRequest<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 /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.typedRequest('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\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","export { SkillDBClient } from './client.js';\nexport type {\n Skill,\n PackInfo,\n CategoryInfo,\n SkillsResponse,\n SearchOptions,\n ClientConfig,\n Manifest,\n ApiKeyScope,\n ApiKeyInfo,\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"],"mappings":"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","names":["fs","path"]}
package/dist/mcp.js CHANGED
@@ -65,8 +65,26 @@ var SkillDBClient = class {
65
65
  }
66
66
  const res = await fetch(url.toString(), { headers: this.headers() });
67
67
  if (!res.ok) {
68
- const body = await res.json().catch(() => ({}));
69
- const msg = body.error || `HTTP ${res.status}`;
68
+ const body = await res.json().catch(() => null);
69
+ const apiError = body && typeof body === "object" ? body.error : null;
70
+ const status = res.status;
71
+ const statusText = res.statusText || "Unknown";
72
+ let msg;
73
+ if (apiError) {
74
+ msg = apiError;
75
+ } else if (status === 500) {
76
+ msg = `SkillDB API internal error (HTTP 500). The service may be experiencing issues. Check status at https://skilldb.dev/api/v1/status`;
77
+ } else if (status === 503) {
78
+ msg = `SkillDB API unavailable (HTTP 503). The skills index may not be loaded. Check status at https://skilldb.dev/api/v1/status`;
79
+ } else if (status === 401) {
80
+ msg = `Invalid or expired API key (HTTP 401). Get a free key at https://skilldb.dev/api-access`;
81
+ } else if (status === 429) {
82
+ msg = `Rate limit exceeded (HTTP 429). Authenticate with an API key for higher limits: https://skilldb.dev/api-access`;
83
+ } else if (status === 404) {
84
+ msg = `Resource not found (HTTP 404). The requested skill or endpoint does not exist.`;
85
+ } else {
86
+ msg = `SkillDB API error: HTTP ${status} ${statusText}. Check https://skilldb.dev/api/v1/status for service health.`;
87
+ }
70
88
  throw new Error(msg);
71
89
  }
72
90
  return res.json();
@@ -135,13 +153,47 @@ var hasApiKey = !!apiKey;
135
153
  var client = new SkillDBClient({ apiKey, baseUrl });
136
154
  if (!hasApiKey) {
137
155
  process.stderr.write(
138
- "\n\u26A0\uFE0F SkillDB MCP: No API key configured.\n You can search and browse skills, but full content requires a Pro key.\n Get a free key at: https://skilldb.dev/api-access\n Then run: claude mcp remove skilldb && claude mcp add skilldb -- skilldb-mcp --api-key YOUR_KEY\n\n"
156
+ "\n\u26A0\uFE0F SkillDB MCP: No API key configured.\n You can search and browse skills, but full content requires an API key.\n Get a free key at: https://skilldb.dev/api-access\n Then use: skilldb_set_key to configure it (no restart needed)\n Or restart with: claude mcp remove skilldb && claude mcp add skilldb -- skilldb-mcp --api-key YOUR_KEY\n\n"
139
157
  );
140
158
  }
141
159
  var server = new McpServer({
142
160
  name: "skilldb",
143
- version: "0.5.2"
161
+ version: "0.7.0"
144
162
  });
163
+ server.registerTool(
164
+ "skilldb_set_key",
165
+ {
166
+ title: "Set SkillDB API Key",
167
+ description: "Configure your SkillDB API key without restarting the session. Once set, all subsequent calls (search, get, list) will include full skill content. Get a free key at skilldb.dev/api-access.",
168
+ inputSchema: z.object({
169
+ key: z.string().describe("Your SkillDB API key (starts with sk_live_ or sk_test_)")
170
+ })
171
+ },
172
+ async ({ key }) => {
173
+ if (!key.startsWith("sk_")) {
174
+ return { content: [{ type: "text", text: "Invalid key format. SkillDB API keys start with `sk_live_` or `sk_test_`. Get one at https://skilldb.dev/api-access" }], isError: true };
175
+ }
176
+ apiKey = key;
177
+ hasApiKey = true;
178
+ client = new SkillDBClient({ apiKey: key, baseUrl });
179
+ try {
180
+ const valid = await client.validate();
181
+ if (valid) {
182
+ return { content: [{ type: "text", text: `\u2705 **API key configured successfully!**
183
+
184
+ All SkillDB tools now return full skill content (instructions, patterns, best practices, code examples).
185
+
186
+ Try: \`skilldb_search\` or \`skilldb_get\` to load skills with full content.` }] };
187
+ } else {
188
+ return { content: [{ type: "text", text: `\u26A0\uFE0F **Key set but validation failed.** The key may be invalid or expired. Calls will still be attempted.
189
+
190
+ Get a new key at https://skilldb.dev/api-access` }] };
191
+ }
192
+ } catch {
193
+ return { content: [{ type: "text", text: `\u2705 **API key set.** Couldn't validate (API may be temporarily unavailable), but the key will be used for all subsequent calls.` }] };
194
+ }
195
+ }
196
+ );
145
197
  server.registerTool(
146
198
  "skilldb_search",
147
199
  {
@@ -165,7 +217,7 @@ server.registerTool(
165
217
  const noKey = !hasApiKey;
166
218
  const text = res.skills.length === 0 ? `No skills found for "${query}".` : (noKey ? `\u26A0\uFE0F **No API key configured** \u2014 showing metadata only. Full skill content (instructions, patterns, code examples) requires a free API key.
167
219
  \u{1F449} Get yours in 30 seconds: https://skilldb.dev/api-access
168
- \u{1F449} Then: \`claude mcp remove skilldb && claude mcp add skilldb -- skilldb-mcp --api-key YOUR_KEY\`
220
+ \u{1F449} Then use \`skilldb_set_key\` with your key (no restart needed)
169
221
 
170
222
  ---
171
223
 
@@ -180,7 +232,7 @@ server.registerTool(
180
232
  ---
181
233
  Found ${res.pagination.total} total results. Showing ${res.skills.length}.` + (noKey ? `
182
234
 
183
- \u{1F511} **To unlock full content:** Get a free API key at https://skilldb.dev/api-access` : "");
235
+ \u{1F511} **To unlock full content:** Get a free API key at https://skilldb.dev/api-access then use \`skilldb_set_key\`` : "");
184
236
  return { content: [{ type: "text", text }] };
185
237
  } catch (e) {
186
238
  return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
@@ -216,7 +268,7 @@ ${skill.description}
216
268
 
217
269
  To load the complete ${skill.lines}-line skill with instructions, patterns, and best practices:
218
270
  1. Get a free API key at https://skilldb.dev/api-access
219
- 2. Reconfigure: \`claude mcp remove skilldb && claude mcp add skilldb -- skilldb-mcp --api-key YOUR_KEY\`
271
+ 2. Use \`skilldb_set_key\` with your key (no restart needed)
220
272
 
221
273
  Free keys get 100 calls/month. Pro ($9/mo) gets unlimited access.`;
222
274
  }
package/dist/mcp.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp.ts","../src/config.ts","../src/client.ts"],"sourcesContent":["/**\r\n * SkillDB MCP Server\r\n *\r\n * Model Context Protocol server that exposes SkillDB skills\r\n * to any MCP-compatible AI coding tool (Claude Code, Cursor,\r\n * Windsurf, VS Code Copilot, and 30+ others).\r\n *\r\n * Installation:\r\n * claude mcp add skilldb -- npx skilldb-mcp\r\n * # or with API key:\r\n * claude mcp add skilldb -- npx skilldb-mcp --api-key sk_live_xxx\r\n *\r\n * Cursor (settings.json):\r\n * { \"mcpServers\": { \"skilldb\": { \"command\": \"npx\", \"args\": [\"skilldb-mcp\"] } } }\r\n */\r\n\r\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\r\nimport { z } from \"zod\";\r\nimport { SkillDBClient } from \"./client.js\";\r\nimport { resolveApiKey } from \"./config.js\";\r\n\r\n// Parse CLI args for API key\r\nconst args = process.argv.slice(2);\r\nconst apiKeyIdx = args.indexOf(\"--api-key\");\r\nconst apiKey = apiKeyIdx >= 0 ? args[apiKeyIdx + 1] : resolveApiKey();\r\nconst baseUrl = args.includes(\"--base-url\")\r\n ? args[args.indexOf(\"--base-url\") + 1]\r\n : undefined;\r\n\r\nconst hasApiKey = !!apiKey;\r\nconst client = new SkillDBClient({ apiKey, baseUrl });\r\n\r\n// Warn on startup if no API key\r\nif (!hasApiKey) {\r\n process.stderr.write(\r\n \"\\n⚠️ SkillDB MCP: No API key configured.\\n\" +\r\n \" You can search and browse skills, but full content requires a Pro key.\\n\" +\r\n \" Get a free key at: https://skilldb.dev/api-access\\n\" +\r\n \" Then run: claude mcp remove skilldb && claude mcp add skilldb -- skilldb-mcp --api-key YOUR_KEY\\n\\n\"\r\n );\r\n}\r\n\r\nconst server = new McpServer({\r\n name: \"skilldb\",\r\n version: \"0.5.2\",\r\n});\r\n\r\n// ─── Tool: Search Skills ───\r\nserver.registerTool(\r\n \"skilldb_search\",\r\n {\r\n title: \"Search SkillDB Skills\",\r\n description:\r\n \"Search the SkillDB library of 5,000+ AI agent skills by keyword. Returns skill metadata (name, description, pack, category, line count). Without an API key, only metadata is returned. With a key, full skill content is included. Get a free key at skilldb.dev/api-access.\",\r\n inputSchema: z.object({\r\n query: z.string().describe(\"Search query (e.g. 'code review', 'react hooks', 'security')\"),\r\n category: z.string().optional().describe(\"Filter by category name\"),\r\n pack: z.string().optional().describe(\"Filter by pack name\"),\r\n limit: z.number().optional().default(10).describe(\"Max results (1-50)\"),\r\n }),\r\n },\r\n async ({ query, category, pack, limit }) => {\r\n try {\r\n const res = await client.search(query, {\r\n category,\r\n pack,\r\n limit: Math.min(limit || 10, 50),\r\n includeContent: !!apiKey,\r\n });\r\n\r\n const noKey = !hasApiKey;\r\n const text = res.skills.length === 0\r\n ? `No skills found for \"${query}\".`\r\n : (noKey ? `⚠️ **No API key configured** — showing metadata only. Full skill content (instructions, patterns, code examples) requires a free API key.\\n👉 Get yours in 30 seconds: https://skilldb.dev/api-access\\n👉 Then: \\`claude mcp remove skilldb && claude mcp add skilldb -- skilldb-mcp --api-key YOUR_KEY\\`\\n\\n---\\n\\n` : '') +\r\n res.skills\r\n .map(\r\n (s, i) =>\r\n `${i + 1}. **${s.title}** (${s.pack})\\n ${s.description}\\n ID: \\`${s.id}\\` | ${s.lines} lines | Category: ${s.category}` +\r\n (s.content ? `\\n ✅ Full content available (${s.lines} lines)` : `\\n 🔒 Content locked — needs API key`)\r\n )\r\n .join(\"\\n\\n\") +\r\n `\\n\\n---\\nFound ${res.pagination.total} total results. Showing ${res.skills.length}.` +\r\n (noKey ? `\\n\\n🔑 **To unlock full content:** Get a free API key at https://skilldb.dev/api-access` : '');\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Get Skill Content ───\r\nserver.registerTool(\r\n \"skilldb_get\",\r\n {\r\n title: \"Get Skill Content\",\r\n description:\r\n \"Retrieve a SkillDB skill by ID. Without an API key: returns metadata + description only. With an API key: returns the FULL markdown content (instructions, patterns, best practices). Get a free key at skilldb.dev/api-access for full access.\",\r\n inputSchema: z.object({\r\n id: z.string().describe(\"Skill ID (e.g. 'software-skills/code-review.md')\"),\r\n }),\r\n },\r\n async ({ id }) => {\r\n try {\r\n const skill = await client.get(id);\r\n let text: string;\r\n if (skill.content) {\r\n text = `# ${skill.title}\\n**Pack:** ${skill.pack} | **Category:** ${skill.category} | **Lines:** ${skill.lines}\\n\\n${skill.content}`;\r\n } else {\r\n text = `# ${skill.title}\\n**Pack:** ${skill.pack} | **Category:** ${skill.category} | **Lines:** ${skill.lines}\\n\\n${skill.description}\\n\\n` +\r\n `---\\n` +\r\n `⚠️ **Full content not available** — you're seeing metadata only.\\n\\n` +\r\n `To load the complete ${skill.lines}-line skill with instructions, patterns, and best practices:\\n` +\r\n `1. Get a free API key at https://skilldb.dev/api-access\\n` +\r\n `2. Reconfigure: \\`claude mcp remove skilldb && claude mcp add skilldb -- skilldb-mcp --api-key YOUR_KEY\\`\\n\\n` +\r\n `Free keys get 100 calls/month. Pro ($9/mo) gets unlimited access.`;\r\n }\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: List Skills ───\r\nserver.registerTool(\r\n \"skilldb_list\",\r\n {\r\n title: \"List SkillDB Skills\",\r\n description:\r\n \"Browse all available skills with optional filtering by category or pack. Use this to explore what's available in the SkillDB library.\",\r\n inputSchema: z.object({\r\n category: z.string().optional().describe(\"Filter by category (e.g. 'Software Engineering', 'Security')\"),\r\n pack: z.string().optional().describe(\"Filter by pack (e.g. 'software-skills', 'react-patterns-skills')\"),\r\n sort: z.enum([\"name\", \"-name\", \"lines\", \"-lines\", \"pack\", \"category\"]).optional().describe(\"Sort order\"),\r\n limit: z.number().optional().default(20).describe(\"Max results (1-100)\"),\r\n offset: z.number().optional().default(0).describe(\"Pagination offset\"),\r\n }),\r\n },\r\n async ({ category, pack, sort, limit, offset }) => {\r\n try {\r\n const res = await client.list({ category, pack, sort, limit, offset });\r\n\r\n const text = res.skills.length === 0\r\n ? \"No skills found with those filters.\"\r\n : `## SkillDB Skills${category ? ` — ${category}` : \"\"}${pack ? ` — ${pack}` : \"\"}\\n\\n` +\r\n res.skills\r\n .map((s) => `- **${s.title}** (${s.id}) — ${s.lines} lines\\n ${s.description}`)\r\n .join(\"\\n\") +\r\n `\\n\\n---\\n${res.pagination.total} total | Showing ${res.skills.length} (offset ${offset || 0})\\nCategories: ${res.meta.categories.join(\", \")}`;\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Suggest / Autocomplete ───\r\nserver.registerTool(\r\n \"skilldb_suggest\",\r\n {\r\n title: \"Suggest Skills\",\r\n description:\r\n \"Get autocomplete suggestions for skill names. Fast, lightweight — useful for quick lookups.\",\r\n inputSchema: z.object({\r\n query: z.string().min(2).describe(\"Partial skill name (min 2 chars)\"),\r\n }),\r\n },\r\n async ({ query }) => {\r\n try {\r\n const res = await client.suggest(query);\r\n const text = res.suggestions.length === 0\r\n ? `No suggestions for \"${query}\".`\r\n : res.suggestions\r\n .map((s) => `- **${s.title}** (${s.pack} / ${s.category})\\n ID: ${s.id}`)\r\n .join(\"\\n\");\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Recommend Skills for Project ───\r\nserver.registerTool(\r\n \"skilldb_recommend\",\r\n {\r\n title: \"Recommend Skills for Project\",\r\n description:\r\n \"Analyze the current project context and recommend relevant SkillDB skill packs. Provide information about the tech stack (languages, frameworks, tools) and get tailored skill pack recommendations.\",\r\n inputSchema: z.object({\r\n technologies: z.array(z.string()).describe(\"Technologies in your project (e.g. ['react', 'typescript', 'docker', 'postgresql'])\"),\r\n role: z.string().optional().describe(\"Your role or focus area (e.g. 'frontend', 'backend', 'devops', 'security')\"),\r\n }),\r\n },\r\n async ({ technologies, role }) => {\r\n // Map technologies to known skill packs\r\n const TECH_MAP: Record<string, string[]> = {\r\n react: [\"react-patterns-skills\", \"web-polish-skills\"],\r\n vue: [\"vue-skills\", \"web-polish-skills\"],\r\n angular: [\"angular-skills\", \"web-polish-skills\"],\r\n svelte: [\"svelte-skills\", \"web-polish-skills\"],\r\n next: [\"react-patterns-skills\", \"web-polish-skills\"],\r\n typescript: [\"typescript-skills\"],\r\n javascript: [\"software-skills\", \"typescript-skills\"],\r\n python: [\"python-skills\"],\r\n rust: [\"rust-skills\"],\r\n go: [\"go-skills\"],\r\n java: [\"java-skills\"],\r\n docker: [\"devops-skills\", \"docker-skills\"],\r\n kubernetes: [\"devops-skills\", \"infrastructure-skills\"],\r\n aws: [\"cloud-skills\", \"infrastructure-skills\"],\r\n gcp: [\"cloud-skills\"],\r\n azure: [\"cloud-skills\"],\r\n postgresql: [\"database-skills\", \"sql-skills\"],\r\n mongodb: [\"database-skills\"],\r\n redis: [\"database-skills\"],\r\n graphql: [\"api-design-skills\"],\r\n rest: [\"api-design-skills\"],\r\n express: [\"nodejs-skills\", \"api-design-skills\"],\r\n fastify: [\"nodejs-skills\"],\r\n nestjs: [\"nodejs-skills\", \"api-design-skills\"],\r\n prisma: [\"database-skills\"],\r\n \"react-native\": [\"react-native-skills\", \"mobile-skills\"],\r\n flutter: [\"mobile-skills\"],\r\n openai: [\"ai-agent-skills\", \"prompt-engineering-skills\"],\r\n langchain: [\"ai-agent-skills\", \"llm-skills\"],\r\n claude: [\"ai-agent-skills\", \"prompt-engineering-skills\"],\r\n };\r\n\r\n const ROLE_MAP: Record<string, string[]> = {\r\n frontend: [\"css-skills\", \"web-polish-skills\", \"typescript-skills\"],\r\n backend: [\"software-skills\", \"api-design-skills\", \"database-skills\"],\r\n devops: [\"devops-skills\", \"ci-cd-skills\", \"infrastructure-skills\"],\r\n security: [\"security-skills\", \"appsec-skills\", \"auth-skills\"],\r\n fullstack: [\"software-skills\", \"react-patterns-skills\", \"api-design-skills\"],\r\n data: [\"data-engineering-skills\", \"sql-skills\", \"analytics-skills\"],\r\n mobile: [\"mobile-skills\", \"react-native-skills\"],\r\n ai: [\"ai-agent-skills\", \"prompt-engineering-skills\", \"llm-skills\"],\r\n };\r\n\r\n const recommended = new Set<string>([\"software-skills\"]); // always recommend baseline\r\n for (const tech of technologies) {\r\n const key = tech.toLowerCase().replace(/[^a-z]/g, \"\");\r\n for (const [pattern, packs] of Object.entries(TECH_MAP)) {\r\n if (key.includes(pattern) || pattern.includes(key)) {\r\n packs.forEach((p) => recommended.add(p));\r\n }\r\n }\r\n }\r\n if (role) {\r\n const rolePacks = ROLE_MAP[role.toLowerCase()] || [];\r\n rolePacks.forEach((p) => recommended.add(p));\r\n }\r\n\r\n const packList = [...recommended];\r\n const text =\r\n `## Recommended Skill Packs\\n\\n` +\r\n `Based on: ${technologies.join(\", \")}${role ? ` (${role})` : \"\"}\\n\\n` +\r\n packList.map((p) => `- \\`${p}\\` — install with \\`skilldb add ${p}\\``).join(\"\\n\") +\r\n `\\n\\n**Install all:** \\`${packList.map((p) => `skilldb add ${p}`).join(\" && \")}\\`\\n` +\r\n `\\nUse \\`skilldb_search\\` to explore individual skills within each pack.`;\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n }\r\n);\r\n\r\n// ─── Tool: Purge Cached Skills ───\r\nserver.registerTool(\r\n \"skilldb_purge\",\r\n {\r\n title: \"Purge Cached Skills\",\r\n description:\r\n \"Remove cached SkillDB skills from the local .skilldb/ directory to free disk space. By default removes inactive skills + slim summaries. Use 'all' to clear everything, or 'inactive' to only remove unused skills.\",\r\n inputSchema: z.object({\r\n mode: z.enum([\"default\", \"all\", \"inactive\", \"slim\"]).optional().default(\"default\")\r\n .describe(\"What to purge: 'default' (inactive + slim), 'all' (everything), 'inactive' (unused only), 'slim' (summaries only)\"),\r\n dryRun: z.boolean().optional().default(false).describe(\"Preview what would be removed without deleting\"),\r\n }),\r\n },\r\n async ({ mode, dryRun }) => {\r\n try {\r\n const fs = await import(\"fs\");\r\n const path = await import(\"path\");\r\n const cwd = process.cwd();\r\n const skilldbDir = path.join(cwd, \".skilldb\");\r\n const skillsDir = path.join(skilldbDir, \"skills\");\r\n const activeDir = path.join(skilldbDir, \"active\");\r\n const slimDir = path.join(skilldbDir, \"slim\");\r\n\r\n if (!fs.existsSync(skilldbDir)) {\r\n return { content: [{ type: \"text\", text: \"No .skilldb/ directory found. Run `skilldb init` first.\" }] };\r\n }\r\n\r\n // Collect skills\r\n const collectSkills = (dir: string): string[] => {\r\n const skills: string[] = [];\r\n if (!fs.existsSync(dir)) return skills;\r\n for (const pack of fs.readdirSync(dir, { withFileTypes: true })) {\r\n if (!pack.isDirectory()) continue;\r\n for (const file of fs.readdirSync(path.join(dir, pack.name))) {\r\n if (file.endsWith(\".md\")) skills.push(`${pack.name}/${file}`);\r\n }\r\n }\r\n return skills;\r\n };\r\n\r\n const allSkills = collectSkills(skillsDir);\r\n const activeSkills = new Set(collectSkills(activeDir));\r\n const inactiveSkills = allSkills.filter(s => !activeSkills.has(s));\r\n const slimSkills = collectSkills(slimDir);\r\n\r\n let toPurge: string[] = [];\r\n let purgeSlim = false;\r\n\r\n if (mode === \"all\") {\r\n toPurge = [...allSkills];\r\n purgeSlim = true;\r\n } else if (mode === \"inactive\") {\r\n toPurge = inactiveSkills;\r\n } else if (mode === \"slim\") {\r\n purgeSlim = true;\r\n } else {\r\n toPurge = inactiveSkills;\r\n purgeSlim = true;\r\n }\r\n\r\n if (dryRun) {\r\n const lines = [\r\n `## Purge Preview (dry run)`,\r\n `- Skills to remove: ${toPurge.length}`,\r\n `- Slim summaries to remove: ${purgeSlim ? slimSkills.length : 0}`,\r\n `- Active skills kept: ${activeSkills.size}`,\r\n \"\",\r\n ...toPurge.slice(0, 30).map(s => ` - ${s}`),\r\n toPurge.length > 30 ? ` ... and ${toPurge.length - 30} more` : \"\",\r\n ];\r\n return { content: [{ type: \"text\", text: lines.filter(Boolean).join(\"\\n\") }] };\r\n }\r\n\r\n // Actually purge\r\n let removed = 0;\r\n let bytesFreed = 0;\r\n for (const skillId of toPurge) {\r\n const [pack, file] = skillId.split(\"/\");\r\n const filePath = path.join(skillsDir, pack, file);\r\n if (fs.existsSync(filePath)) {\r\n bytesFreed += fs.statSync(filePath).size;\r\n fs.unlinkSync(filePath);\r\n removed++;\r\n }\r\n const packDir = path.join(skillsDir, pack);\r\n if (fs.existsSync(packDir) && fs.readdirSync(packDir).length === 0) {\r\n fs.rmdirSync(packDir);\r\n }\r\n }\r\n\r\n let slimRemoved = 0;\r\n if (purgeSlim && fs.existsSync(slimDir)) {\r\n for (const pack of fs.readdirSync(slimDir, { withFileTypes: true })) {\r\n if (!pack.isDirectory()) continue;\r\n const packPath = path.join(slimDir, pack.name);\r\n for (const file of fs.readdirSync(packPath)) {\r\n const fp = path.join(packPath, file);\r\n bytesFreed += fs.statSync(fp).size;\r\n fs.unlinkSync(fp);\r\n slimRemoved++;\r\n }\r\n if (fs.readdirSync(packPath).length === 0) fs.rmdirSync(packPath);\r\n }\r\n }\r\n\r\n if (mode === \"all\" && fs.existsSync(activeDir)) {\r\n for (const pack of fs.readdirSync(activeDir, { withFileTypes: true })) {\r\n if (!pack.isDirectory()) continue;\r\n const packPath = path.join(activeDir, pack.name);\r\n for (const file of fs.readdirSync(packPath)) {\r\n fs.unlinkSync(path.join(packPath, file));\r\n }\r\n fs.rmdirSync(packPath);\r\n }\r\n }\r\n\r\n const text = [\r\n `## Purge Complete`,\r\n `- Skills removed: ${removed}`,\r\n `- Slim summaries removed: ${slimRemoved}`,\r\n `- Space freed: ${(bytesFreed / 1024).toFixed(0)} KB`,\r\n `- Active skills kept: ${mode === \"all\" ? 0 : activeSkills.size}`,\r\n ].join(\"\\n\");\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: List My Private Skills ───\r\nserver.registerTool(\r\n \"skilldb_my_skills\",\r\n {\r\n title: \"List My Private Skills\",\r\n description:\r\n \"List your private skills stored on SkillDB. Requires an API key with 'write' scope. Returns all skills you own plus skills shared with you.\",\r\n inputSchema: z.object({}),\r\n },\r\n async () => {\r\n if (!apiKey) {\r\n return { content: [{ type: \"text\", text: \"API key required for private skills. Set one with: skilldb-mcp --api-key sk_live_xxx\" }], isError: true };\r\n }\r\n try {\r\n const res = await client.rawRequest(\"/api/v1/my-skills\", { method: \"GET\" });\r\n const data = await res.json();\r\n if (!res.ok) return { content: [{ type: \"text\", text: `Error: ${data.error || res.statusText}` }], isError: true };\r\n\r\n const own = (data.skills || []);\r\n const shared = (data.shared || []);\r\n\r\n if (own.length === 0 && shared.length === 0) {\r\n return { content: [{ type: \"text\", text: \"No private skills yet. Create one with `skilldb_create_skill`.\" }] };\r\n }\r\n\r\n let text = `## My Private Skills (${own.length})\\n\\n`;\r\n text += own.map((s: Record<string, unknown>, i: number) => `${i + 1}. **${s.title}** (${s.pack})\\n ID: \\`${s.id}\\` | ${s.lines} lines | ${s.visibility}`).join(\"\\n\\n\");\r\n\r\n if (shared.length > 0) {\r\n text += `\\n\\n## Shared With Me (${shared.length})\\n\\n`;\r\n text += shared.map((s: Record<string, unknown>, i: number) => `${i + 1}. **${s.title}** (${s.pack})\\n ID: \\`${s.id}\\` | ${s.lines} lines | shared by ${s.owner}`).join(\"\\n\\n\");\r\n }\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Create Private Skill ───\r\nserver.registerTool(\r\n \"skilldb_create_skill\",\r\n {\r\n title: \"Create Private Skill\",\r\n description:\r\n \"Create a new private skill in your SkillDB account. The skill is only visible to you (and anyone you share it with). Requires an API key with 'write' scope.\",\r\n inputSchema: z.object({\r\n name: z.string().describe(\"Skill filename (e.g. 'my-react-patterns')\"),\r\n title: z.string().describe(\"Human-readable title\"),\r\n content: z.string().describe(\"Full skill content in markdown\"),\r\n pack: z.string().optional().default(\"personal\").describe(\"Pack/folder name (default: 'personal')\"),\r\n tags: z.array(z.string()).optional().describe(\"Tags for searchability\"),\r\n description: z.string().optional().describe(\"Short description (auto-generated from content if omitted)\"),\r\n }),\r\n },\r\n async ({ name, title, content, pack, tags, description }) => {\r\n if (!apiKey) {\r\n return { content: [{ type: \"text\", text: \"API key required. Set one with: skilldb-mcp --api-key sk_live_xxx\" }], isError: true };\r\n }\r\n try {\r\n const res = await client.rawRequest(\"/api/v1/my-skills\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ name, title, content, pack, tags, description }),\r\n });\r\n const data = await res.json();\r\n if (!res.ok) return { content: [{ type: \"text\", text: `Error: ${data.error || res.statusText}` }], isError: true };\r\n\r\n const s = data.skill;\r\n return { content: [{ type: \"text\", text: `✅ Private skill created!\\n\\n**${s.title}**\\nID: \\`${s.id}\\`\\nPack: ${s.pack}\\nLines: ${s.lines}\\nVisibility: ${s.visibility}\\n\\nYou can now find this skill via \\`skilldb_search\\` or \\`skilldb_my_skills\\`.` }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Update Private Skill ───\r\nserver.registerTool(\r\n \"skilldb_update_skill\",\r\n {\r\n title: \"Update Private Skill\",\r\n description:\r\n \"Update an existing private skill's content, title, tags, or visibility. Requires an API key with 'write' scope.\",\r\n inputSchema: z.object({\r\n id: z.string().describe(\"Skill ID (e.g. 'personal/my-react-patterns.md')\"),\r\n title: z.string().optional().describe(\"New title\"),\r\n content: z.string().optional().describe(\"New content\"),\r\n tags: z.array(z.string()).optional().describe(\"New tags\"),\r\n visibility: z.enum([\"private\", \"shared\"]).optional().describe(\"Visibility setting\"),\r\n }),\r\n },\r\n async ({ id, title, content, tags, visibility }) => {\r\n if (!apiKey) {\r\n return { content: [{ type: \"text\", text: \"API key required.\" }], isError: true };\r\n }\r\n try {\r\n const body: Record<string, unknown> = {};\r\n if (title) body.title = title;\r\n if (content) body.content = content;\r\n if (tags) body.tags = tags;\r\n if (visibility) body.visibility = visibility;\r\n\r\n const res = await client.rawRequest(`/api/v1/my-skills/${encodeURIComponent(id)}`, {\r\n method: \"PUT\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(body),\r\n });\r\n const data = await res.json();\r\n if (!res.ok) return { content: [{ type: \"text\", text: `Error: ${data.error || res.statusText}` }], isError: true };\r\n\r\n return { content: [{ type: \"text\", text: `✅ Skill updated: **${data.skill.title}** (${data.skill.lines} lines)` }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Start server ───\r\nconst transport = new StdioServerTransport();\r\nawait server.connect(transport);\r\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 /** Make an authenticated request to any endpoint (used by MCP tools for private skills). */\n public async rawRequest(endpoint: string, init?: RequestInit): Promise<Response> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = { ...this.headers(), ...(init?.headers || {}) };\n return fetch(url, { ...init, headers });\n }\n\n\n private async typedRequest<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.typedRequest<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\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 and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\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.typedRequest<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 /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.typedRequest('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":["fs","path"]}
1
+ {"version":3,"sources":["../src/mcp.ts","../src/config.ts","../src/client.ts"],"sourcesContent":["/**\r\n * SkillDB MCP Server\r\n *\r\n * Model Context Protocol server that exposes SkillDB skills\r\n * to any MCP-compatible AI coding tool (Claude Code, Cursor,\r\n * Windsurf, VS Code Copilot, and 30+ others).\r\n *\r\n * Installation:\r\n * claude mcp add skilldb -- npx skilldb-mcp\r\n * # or with API key:\r\n * claude mcp add skilldb -- npx skilldb-mcp --api-key sk_live_xxx\r\n *\r\n * Cursor (settings.json):\r\n * { \"mcpServers\": { \"skilldb\": { \"command\": \"npx\", \"args\": [\"skilldb-mcp\"] } } }\r\n */\r\n\r\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\r\nimport { z } from \"zod\";\r\nimport { SkillDBClient } from \"./client.js\";\r\nimport { resolveApiKey } from \"./config.js\";\r\n\r\n// Parse CLI args for API key\r\nconst args = process.argv.slice(2);\r\nconst apiKeyIdx = args.indexOf(\"--api-key\");\r\nlet apiKey = apiKeyIdx >= 0 ? args[apiKeyIdx + 1] : resolveApiKey();\r\nconst baseUrl = args.includes(\"--base-url\")\r\n ? args[args.indexOf(\"--base-url\") + 1]\r\n : undefined;\r\n\r\nlet hasApiKey = !!apiKey;\r\nlet client = new SkillDBClient({ apiKey, baseUrl });\r\n\r\n// Warn on startup if no API key\r\nif (!hasApiKey) {\r\n process.stderr.write(\r\n \"\\n⚠️ SkillDB MCP: No API key configured.\\n\" +\r\n \" You can search and browse skills, but full content requires an API key.\\n\" +\r\n \" Get a free key at: https://skilldb.dev/api-access\\n\" +\r\n \" Then use: skilldb_set_key to configure it (no restart needed)\\n\" +\r\n \" Or restart with: claude mcp remove skilldb && claude mcp add skilldb -- skilldb-mcp --api-key YOUR_KEY\\n\\n\"\r\n );\r\n}\r\n\r\nconst server = new McpServer({\r\n name: \"skilldb\",\r\n version: \"0.7.0\",\r\n});\r\n\r\n// ─── Tool: Set API Key (mid-session) ───\r\nserver.registerTool(\r\n \"skilldb_set_key\",\r\n {\r\n title: \"Set SkillDB API Key\",\r\n description:\r\n \"Configure your SkillDB API key without restarting the session. Once set, all subsequent calls (search, get, list) will include full skill content. Get a free key at skilldb.dev/api-access.\",\r\n inputSchema: z.object({\r\n key: z.string().describe(\"Your SkillDB API key (starts with sk_live_ or sk_test_)\"),\r\n }),\r\n },\r\n async ({ key }) => {\r\n if (!key.startsWith(\"sk_\")) {\r\n return { content: [{ type: \"text\", text: \"Invalid key format. SkillDB API keys start with `sk_live_` or `sk_test_`. Get one at https://skilldb.dev/api-access\" }], isError: true };\r\n }\r\n\r\n // Update the mutable state\r\n apiKey = key;\r\n hasApiKey = true;\r\n client = new SkillDBClient({ apiKey: key, baseUrl });\r\n\r\n // Validate the key\r\n try {\r\n const valid = await client.validate();\r\n if (valid) {\r\n return { content: [{ type: \"text\", text: `✅ **API key configured successfully!**\\n\\nAll SkillDB tools now return full skill content (instructions, patterns, best practices, code examples).\\n\\nTry: \\`skilldb_search\\` or \\`skilldb_get\\` to load skills with full content.` }] };\r\n } else {\r\n return { content: [{ type: \"text\", text: `⚠️ **Key set but validation failed.** The key may be invalid or expired. Calls will still be attempted.\\n\\nGet a new key at https://skilldb.dev/api-access` }] };\r\n }\r\n } catch {\r\n // Key set but couldn't reach the API to validate — still usable\r\n return { content: [{ type: \"text\", text: `✅ **API key set.** Couldn't validate (API may be temporarily unavailable), but the key will be used for all subsequent calls.` }] };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Search Skills ───\r\nserver.registerTool(\r\n \"skilldb_search\",\r\n {\r\n title: \"Search SkillDB Skills\",\r\n description:\r\n \"Search the SkillDB library of 5,000+ AI agent skills by keyword. Returns skill metadata (name, description, pack, category, line count). Without an API key, only metadata is returned. With a key, full skill content is included. Get a free key at skilldb.dev/api-access.\",\r\n inputSchema: z.object({\r\n query: z.string().describe(\"Search query (e.g. 'code review', 'react hooks', 'security')\"),\r\n category: z.string().optional().describe(\"Filter by category name\"),\r\n pack: z.string().optional().describe(\"Filter by pack name\"),\r\n limit: z.number().optional().default(10).describe(\"Max results (1-50)\"),\r\n }),\r\n },\r\n async ({ query, category, pack, limit }) => {\r\n try {\r\n const res = await client.search(query, {\r\n category,\r\n pack,\r\n limit: Math.min(limit || 10, 50),\r\n includeContent: !!apiKey,\r\n });\r\n\r\n const noKey = !hasApiKey;\r\n const text = res.skills.length === 0\r\n ? `No skills found for \"${query}\".`\r\n : (noKey ? `⚠️ **No API key configured** — showing metadata only. Full skill content (instructions, patterns, code examples) requires a free API key.\\n👉 Get yours in 30 seconds: https://skilldb.dev/api-access\\n👉 Then use \\`skilldb_set_key\\` with your key (no restart needed)\\n\\n---\\n\\n` : '') +\r\n res.skills\r\n .map(\r\n (s, i) =>\r\n `${i + 1}. **${s.title}** (${s.pack})\\n ${s.description}\\n ID: \\`${s.id}\\` | ${s.lines} lines | Category: ${s.category}` +\r\n (s.content ? `\\n ✅ Full content available (${s.lines} lines)` : `\\n 🔒 Content locked — needs API key`)\r\n )\r\n .join(\"\\n\\n\") +\r\n `\\n\\n---\\nFound ${res.pagination.total} total results. Showing ${res.skills.length}.` +\r\n (noKey ? `\\n\\n🔑 **To unlock full content:** Get a free API key at https://skilldb.dev/api-access then use \\`skilldb_set_key\\`` : '');\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Get Skill Content ───\r\nserver.registerTool(\r\n \"skilldb_get\",\r\n {\r\n title: \"Get Skill Content\",\r\n description:\r\n \"Retrieve a SkillDB skill by ID. Without an API key: returns metadata + description only. With an API key: returns the FULL markdown content (instructions, patterns, best practices). Get a free key at skilldb.dev/api-access for full access.\",\r\n inputSchema: z.object({\r\n id: z.string().describe(\"Skill ID (e.g. 'software-skills/code-review.md')\"),\r\n }),\r\n },\r\n async ({ id }) => {\r\n try {\r\n const skill = await client.get(id);\r\n let text: string;\r\n if (skill.content) {\r\n text = `# ${skill.title}\\n**Pack:** ${skill.pack} | **Category:** ${skill.category} | **Lines:** ${skill.lines}\\n\\n${skill.content}`;\r\n } else {\r\n text = `# ${skill.title}\\n**Pack:** ${skill.pack} | **Category:** ${skill.category} | **Lines:** ${skill.lines}\\n\\n${skill.description}\\n\\n` +\r\n `---\\n` +\r\n `⚠️ **Full content not available** — you're seeing metadata only.\\n\\n` +\r\n `To load the complete ${skill.lines}-line skill with instructions, patterns, and best practices:\\n` +\r\n `1. Get a free API key at https://skilldb.dev/api-access\\n` +\r\n `2. Use \\`skilldb_set_key\\` with your key (no restart needed)\\n\\n` +\r\n `Free keys get 100 calls/month. Pro ($9/mo) gets unlimited access.`;\r\n }\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: List Skills ───\r\nserver.registerTool(\r\n \"skilldb_list\",\r\n {\r\n title: \"List SkillDB Skills\",\r\n description:\r\n \"Browse all available skills with optional filtering by category or pack. Use this to explore what's available in the SkillDB library.\",\r\n inputSchema: z.object({\r\n category: z.string().optional().describe(\"Filter by category (e.g. 'Software Engineering', 'Security')\"),\r\n pack: z.string().optional().describe(\"Filter by pack (e.g. 'software-skills', 'react-patterns-skills')\"),\r\n sort: z.enum([\"name\", \"-name\", \"lines\", \"-lines\", \"pack\", \"category\"]).optional().describe(\"Sort order\"),\r\n limit: z.number().optional().default(20).describe(\"Max results (1-100)\"),\r\n offset: z.number().optional().default(0).describe(\"Pagination offset\"),\r\n }),\r\n },\r\n async ({ category, pack, sort, limit, offset }) => {\r\n try {\r\n const res = await client.list({ category, pack, sort, limit, offset });\r\n\r\n const text = res.skills.length === 0\r\n ? \"No skills found with those filters.\"\r\n : `## SkillDB Skills${category ? ` — ${category}` : \"\"}${pack ? ` — ${pack}` : \"\"}\\n\\n` +\r\n res.skills\r\n .map((s) => `- **${s.title}** (${s.id}) — ${s.lines} lines\\n ${s.description}`)\r\n .join(\"\\n\") +\r\n `\\n\\n---\\n${res.pagination.total} total | Showing ${res.skills.length} (offset ${offset || 0})\\nCategories: ${res.meta.categories.join(\", \")}`;\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Suggest / Autocomplete ───\r\nserver.registerTool(\r\n \"skilldb_suggest\",\r\n {\r\n title: \"Suggest Skills\",\r\n description:\r\n \"Get autocomplete suggestions for skill names. Fast, lightweight — useful for quick lookups.\",\r\n inputSchema: z.object({\r\n query: z.string().min(2).describe(\"Partial skill name (min 2 chars)\"),\r\n }),\r\n },\r\n async ({ query }) => {\r\n try {\r\n const res = await client.suggest(query);\r\n const text = res.suggestions.length === 0\r\n ? `No suggestions for \"${query}\".`\r\n : res.suggestions\r\n .map((s) => `- **${s.title}** (${s.pack} / ${s.category})\\n ID: ${s.id}`)\r\n .join(\"\\n\");\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Recommend Skills for Project ───\r\nserver.registerTool(\r\n \"skilldb_recommend\",\r\n {\r\n title: \"Recommend Skills for Project\",\r\n description:\r\n \"Analyze the current project context and recommend relevant SkillDB skill packs. Provide information about the tech stack (languages, frameworks, tools) and get tailored skill pack recommendations.\",\r\n inputSchema: z.object({\r\n technologies: z.array(z.string()).describe(\"Technologies in your project (e.g. ['react', 'typescript', 'docker', 'postgresql'])\"),\r\n role: z.string().optional().describe(\"Your role or focus area (e.g. 'frontend', 'backend', 'devops', 'security')\"),\r\n }),\r\n },\r\n async ({ technologies, role }) => {\r\n // Map technologies to known skill packs\r\n const TECH_MAP: Record<string, string[]> = {\r\n react: [\"react-patterns-skills\", \"web-polish-skills\"],\r\n vue: [\"vue-skills\", \"web-polish-skills\"],\r\n angular: [\"angular-skills\", \"web-polish-skills\"],\r\n svelte: [\"svelte-skills\", \"web-polish-skills\"],\r\n next: [\"react-patterns-skills\", \"web-polish-skills\"],\r\n typescript: [\"typescript-skills\"],\r\n javascript: [\"software-skills\", \"typescript-skills\"],\r\n python: [\"python-skills\"],\r\n rust: [\"rust-skills\"],\r\n go: [\"go-skills\"],\r\n java: [\"java-skills\"],\r\n docker: [\"devops-skills\", \"docker-skills\"],\r\n kubernetes: [\"devops-skills\", \"infrastructure-skills\"],\r\n aws: [\"cloud-skills\", \"infrastructure-skills\"],\r\n gcp: [\"cloud-skills\"],\r\n azure: [\"cloud-skills\"],\r\n postgresql: [\"database-skills\", \"sql-skills\"],\r\n mongodb: [\"database-skills\"],\r\n redis: [\"database-skills\"],\r\n graphql: [\"api-design-skills\"],\r\n rest: [\"api-design-skills\"],\r\n express: [\"nodejs-skills\", \"api-design-skills\"],\r\n fastify: [\"nodejs-skills\"],\r\n nestjs: [\"nodejs-skills\", \"api-design-skills\"],\r\n prisma: [\"database-skills\"],\r\n \"react-native\": [\"react-native-skills\", \"mobile-skills\"],\r\n flutter: [\"mobile-skills\"],\r\n openai: [\"ai-agent-skills\", \"prompt-engineering-skills\"],\r\n langchain: [\"ai-agent-skills\", \"llm-skills\"],\r\n claude: [\"ai-agent-skills\", \"prompt-engineering-skills\"],\r\n };\r\n\r\n const ROLE_MAP: Record<string, string[]> = {\r\n frontend: [\"css-skills\", \"web-polish-skills\", \"typescript-skills\"],\r\n backend: [\"software-skills\", \"api-design-skills\", \"database-skills\"],\r\n devops: [\"devops-skills\", \"ci-cd-skills\", \"infrastructure-skills\"],\r\n security: [\"security-skills\", \"appsec-skills\", \"auth-skills\"],\r\n fullstack: [\"software-skills\", \"react-patterns-skills\", \"api-design-skills\"],\r\n data: [\"data-engineering-skills\", \"sql-skills\", \"analytics-skills\"],\r\n mobile: [\"mobile-skills\", \"react-native-skills\"],\r\n ai: [\"ai-agent-skills\", \"prompt-engineering-skills\", \"llm-skills\"],\r\n };\r\n\r\n const recommended = new Set<string>([\"software-skills\"]); // always recommend baseline\r\n for (const tech of technologies) {\r\n const key = tech.toLowerCase().replace(/[^a-z]/g, \"\");\r\n for (const [pattern, packs] of Object.entries(TECH_MAP)) {\r\n if (key.includes(pattern) || pattern.includes(key)) {\r\n packs.forEach((p) => recommended.add(p));\r\n }\r\n }\r\n }\r\n if (role) {\r\n const rolePacks = ROLE_MAP[role.toLowerCase()] || [];\r\n rolePacks.forEach((p) => recommended.add(p));\r\n }\r\n\r\n const packList = [...recommended];\r\n const text =\r\n `## Recommended Skill Packs\\n\\n` +\r\n `Based on: ${technologies.join(\", \")}${role ? ` (${role})` : \"\"}\\n\\n` +\r\n packList.map((p) => `- \\`${p}\\` — install with \\`skilldb add ${p}\\``).join(\"\\n\") +\r\n `\\n\\n**Install all:** \\`${packList.map((p) => `skilldb add ${p}`).join(\" && \")}\\`\\n` +\r\n `\\nUse \\`skilldb_search\\` to explore individual skills within each pack.`;\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n }\r\n);\r\n\r\n// ─── Tool: Purge Cached Skills ───\r\nserver.registerTool(\r\n \"skilldb_purge\",\r\n {\r\n title: \"Purge Cached Skills\",\r\n description:\r\n \"Remove cached SkillDB skills from the local .skilldb/ directory to free disk space. By default removes inactive skills + slim summaries. Use 'all' to clear everything, or 'inactive' to only remove unused skills.\",\r\n inputSchema: z.object({\r\n mode: z.enum([\"default\", \"all\", \"inactive\", \"slim\"]).optional().default(\"default\")\r\n .describe(\"What to purge: 'default' (inactive + slim), 'all' (everything), 'inactive' (unused only), 'slim' (summaries only)\"),\r\n dryRun: z.boolean().optional().default(false).describe(\"Preview what would be removed without deleting\"),\r\n }),\r\n },\r\n async ({ mode, dryRun }) => {\r\n try {\r\n const fs = await import(\"fs\");\r\n const path = await import(\"path\");\r\n const cwd = process.cwd();\r\n const skilldbDir = path.join(cwd, \".skilldb\");\r\n const skillsDir = path.join(skilldbDir, \"skills\");\r\n const activeDir = path.join(skilldbDir, \"active\");\r\n const slimDir = path.join(skilldbDir, \"slim\");\r\n\r\n if (!fs.existsSync(skilldbDir)) {\r\n return { content: [{ type: \"text\", text: \"No .skilldb/ directory found. Run `skilldb init` first.\" }] };\r\n }\r\n\r\n // Collect skills\r\n const collectSkills = (dir: string): string[] => {\r\n const skills: string[] = [];\r\n if (!fs.existsSync(dir)) return skills;\r\n for (const pack of fs.readdirSync(dir, { withFileTypes: true })) {\r\n if (!pack.isDirectory()) continue;\r\n for (const file of fs.readdirSync(path.join(dir, pack.name))) {\r\n if (file.endsWith(\".md\")) skills.push(`${pack.name}/${file}`);\r\n }\r\n }\r\n return skills;\r\n };\r\n\r\n const allSkills = collectSkills(skillsDir);\r\n const activeSkills = new Set(collectSkills(activeDir));\r\n const inactiveSkills = allSkills.filter(s => !activeSkills.has(s));\r\n const slimSkills = collectSkills(slimDir);\r\n\r\n let toPurge: string[] = [];\r\n let purgeSlim = false;\r\n\r\n if (mode === \"all\") {\r\n toPurge = [...allSkills];\r\n purgeSlim = true;\r\n } else if (mode === \"inactive\") {\r\n toPurge = inactiveSkills;\r\n } else if (mode === \"slim\") {\r\n purgeSlim = true;\r\n } else {\r\n toPurge = inactiveSkills;\r\n purgeSlim = true;\r\n }\r\n\r\n if (dryRun) {\r\n const lines = [\r\n `## Purge Preview (dry run)`,\r\n `- Skills to remove: ${toPurge.length}`,\r\n `- Slim summaries to remove: ${purgeSlim ? slimSkills.length : 0}`,\r\n `- Active skills kept: ${activeSkills.size}`,\r\n \"\",\r\n ...toPurge.slice(0, 30).map(s => ` - ${s}`),\r\n toPurge.length > 30 ? ` ... and ${toPurge.length - 30} more` : \"\",\r\n ];\r\n return { content: [{ type: \"text\", text: lines.filter(Boolean).join(\"\\n\") }] };\r\n }\r\n\r\n // Actually purge\r\n let removed = 0;\r\n let bytesFreed = 0;\r\n for (const skillId of toPurge) {\r\n const [pack, file] = skillId.split(\"/\");\r\n const filePath = path.join(skillsDir, pack, file);\r\n if (fs.existsSync(filePath)) {\r\n bytesFreed += fs.statSync(filePath).size;\r\n fs.unlinkSync(filePath);\r\n removed++;\r\n }\r\n const packDir = path.join(skillsDir, pack);\r\n if (fs.existsSync(packDir) && fs.readdirSync(packDir).length === 0) {\r\n fs.rmdirSync(packDir);\r\n }\r\n }\r\n\r\n let slimRemoved = 0;\r\n if (purgeSlim && fs.existsSync(slimDir)) {\r\n for (const pack of fs.readdirSync(slimDir, { withFileTypes: true })) {\r\n if (!pack.isDirectory()) continue;\r\n const packPath = path.join(slimDir, pack.name);\r\n for (const file of fs.readdirSync(packPath)) {\r\n const fp = path.join(packPath, file);\r\n bytesFreed += fs.statSync(fp).size;\r\n fs.unlinkSync(fp);\r\n slimRemoved++;\r\n }\r\n if (fs.readdirSync(packPath).length === 0) fs.rmdirSync(packPath);\r\n }\r\n }\r\n\r\n if (mode === \"all\" && fs.existsSync(activeDir)) {\r\n for (const pack of fs.readdirSync(activeDir, { withFileTypes: true })) {\r\n if (!pack.isDirectory()) continue;\r\n const packPath = path.join(activeDir, pack.name);\r\n for (const file of fs.readdirSync(packPath)) {\r\n fs.unlinkSync(path.join(packPath, file));\r\n }\r\n fs.rmdirSync(packPath);\r\n }\r\n }\r\n\r\n const text = [\r\n `## Purge Complete`,\r\n `- Skills removed: ${removed}`,\r\n `- Slim summaries removed: ${slimRemoved}`,\r\n `- Space freed: ${(bytesFreed / 1024).toFixed(0)} KB`,\r\n `- Active skills kept: ${mode === \"all\" ? 0 : activeSkills.size}`,\r\n ].join(\"\\n\");\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: List My Private Skills ───\r\nserver.registerTool(\r\n \"skilldb_my_skills\",\r\n {\r\n title: \"List My Private Skills\",\r\n description:\r\n \"List your private skills stored on SkillDB. Requires an API key with 'write' scope. Returns all skills you own plus skills shared with you.\",\r\n inputSchema: z.object({}),\r\n },\r\n async () => {\r\n if (!apiKey) {\r\n return { content: [{ type: \"text\", text: \"API key required for private skills. Set one with: skilldb-mcp --api-key sk_live_xxx\" }], isError: true };\r\n }\r\n try {\r\n const res = await client.rawRequest(\"/api/v1/my-skills\", { method: \"GET\" });\r\n const data = await res.json();\r\n if (!res.ok) return { content: [{ type: \"text\", text: `Error: ${data.error || res.statusText}` }], isError: true };\r\n\r\n const own = (data.skills || []);\r\n const shared = (data.shared || []);\r\n\r\n if (own.length === 0 && shared.length === 0) {\r\n return { content: [{ type: \"text\", text: \"No private skills yet. Create one with `skilldb_create_skill`.\" }] };\r\n }\r\n\r\n let text = `## My Private Skills (${own.length})\\n\\n`;\r\n text += own.map((s: Record<string, unknown>, i: number) => `${i + 1}. **${s.title}** (${s.pack})\\n ID: \\`${s.id}\\` | ${s.lines} lines | ${s.visibility}`).join(\"\\n\\n\");\r\n\r\n if (shared.length > 0) {\r\n text += `\\n\\n## Shared With Me (${shared.length})\\n\\n`;\r\n text += shared.map((s: Record<string, unknown>, i: number) => `${i + 1}. **${s.title}** (${s.pack})\\n ID: \\`${s.id}\\` | ${s.lines} lines | shared by ${s.owner}`).join(\"\\n\\n\");\r\n }\r\n\r\n return { content: [{ type: \"text\", text }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Create Private Skill ───\r\nserver.registerTool(\r\n \"skilldb_create_skill\",\r\n {\r\n title: \"Create Private Skill\",\r\n description:\r\n \"Create a new private skill in your SkillDB account. The skill is only visible to you (and anyone you share it with). Requires an API key with 'write' scope.\",\r\n inputSchema: z.object({\r\n name: z.string().describe(\"Skill filename (e.g. 'my-react-patterns')\"),\r\n title: z.string().describe(\"Human-readable title\"),\r\n content: z.string().describe(\"Full skill content in markdown\"),\r\n pack: z.string().optional().default(\"personal\").describe(\"Pack/folder name (default: 'personal')\"),\r\n tags: z.array(z.string()).optional().describe(\"Tags for searchability\"),\r\n description: z.string().optional().describe(\"Short description (auto-generated from content if omitted)\"),\r\n }),\r\n },\r\n async ({ name, title, content, pack, tags, description }) => {\r\n if (!apiKey) {\r\n return { content: [{ type: \"text\", text: \"API key required. Set one with: skilldb-mcp --api-key sk_live_xxx\" }], isError: true };\r\n }\r\n try {\r\n const res = await client.rawRequest(\"/api/v1/my-skills\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ name, title, content, pack, tags, description }),\r\n });\r\n const data = await res.json();\r\n if (!res.ok) return { content: [{ type: \"text\", text: `Error: ${data.error || res.statusText}` }], isError: true };\r\n\r\n const s = data.skill;\r\n return { content: [{ type: \"text\", text: `✅ Private skill created!\\n\\n**${s.title}**\\nID: \\`${s.id}\\`\\nPack: ${s.pack}\\nLines: ${s.lines}\\nVisibility: ${s.visibility}\\n\\nYou can now find this skill via \\`skilldb_search\\` or \\`skilldb_my_skills\\`.` }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Tool: Update Private Skill ───\r\nserver.registerTool(\r\n \"skilldb_update_skill\",\r\n {\r\n title: \"Update Private Skill\",\r\n description:\r\n \"Update an existing private skill's content, title, tags, or visibility. Requires an API key with 'write' scope.\",\r\n inputSchema: z.object({\r\n id: z.string().describe(\"Skill ID (e.g. 'personal/my-react-patterns.md')\"),\r\n title: z.string().optional().describe(\"New title\"),\r\n content: z.string().optional().describe(\"New content\"),\r\n tags: z.array(z.string()).optional().describe(\"New tags\"),\r\n visibility: z.enum([\"private\", \"shared\"]).optional().describe(\"Visibility setting\"),\r\n }),\r\n },\r\n async ({ id, title, content, tags, visibility }) => {\r\n if (!apiKey) {\r\n return { content: [{ type: \"text\", text: \"API key required.\" }], isError: true };\r\n }\r\n try {\r\n const body: Record<string, unknown> = {};\r\n if (title) body.title = title;\r\n if (content) body.content = content;\r\n if (tags) body.tags = tags;\r\n if (visibility) body.visibility = visibility;\r\n\r\n const res = await client.rawRequest(`/api/v1/my-skills/${encodeURIComponent(id)}`, {\r\n method: \"PUT\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(body),\r\n });\r\n const data = await res.json();\r\n if (!res.ok) return { content: [{ type: \"text\", text: `Error: ${data.error || res.statusText}` }], isError: true };\r\n\r\n return { content: [{ type: \"text\", text: `✅ Skill updated: **${data.skill.title}** (${data.skill.lines} lines)` }] };\r\n } catch (e) {\r\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\r\n }\r\n }\r\n);\r\n\r\n// ─── Start server ───\r\nconst transport = new StdioServerTransport();\r\nawait server.connect(transport);\r\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 /** Make an authenticated request to any endpoint (used by MCP tools for private skills). */\n public async rawRequest(endpoint: string, init?: RequestInit): Promise<Response> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers = { ...this.headers(), ...(init?.headers || {}) };\n return fetch(url, { ...init, headers });\n }\n\n\n private async typedRequest<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(() => null);\n const apiError = body && typeof body === 'object' ? (body as Record<string, string>).error : null;\n const status = res.status;\n const statusText = res.statusText || 'Unknown';\n\n // Build a descriptive error with actionable guidance\n let msg: string;\n if (apiError) {\n msg = apiError;\n } else if (status === 500) {\n msg = `SkillDB API internal error (HTTP 500). The service may be experiencing issues. Check status at https://skilldb.dev/api/v1/status`;\n } else if (status === 503) {\n msg = `SkillDB API unavailable (HTTP 503). The skills index may not be loaded. Check status at https://skilldb.dev/api/v1/status`;\n } else if (status === 401) {\n msg = `Invalid or expired API key (HTTP 401). Get a free key at https://skilldb.dev/api-access`;\n } else if (status === 429) {\n msg = `Rate limit exceeded (HTTP 429). Authenticate with an API key for higher limits: https://skilldb.dev/api-access`;\n } else if (status === 404) {\n msg = `Resource not found (HTTP 404). The requested skill or endpoint does not exist.`;\n } else {\n msg = `SkillDB API error: HTTP ${status} ${statusText}. Check https://skilldb.dev/api/v1/status for service health.`;\n }\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.typedRequest<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\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 and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\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.typedRequest<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 /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.typedRequest<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.typedRequest('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":["fs","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skilldb",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "SDK and CLI for SkillDB — discover, install, and manage AI agent skills",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",