skilld 0.0.1

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/cli.mjs ADDED
@@ -0,0 +1,503 @@
1
+ #!/usr/bin/env node
2
+ import { generateSkill } from "./index.mjs";
3
+ import { getInstalledSkillVersion, readLocalDependencies, resolvePackageDocs } from "./npm.mjs";
4
+ import { agents, detectCurrentAgent, generateSkillMd, installSkillForAgents, sanitizeName } from "./agents.mjs";
5
+ import { join } from "node:path";
6
+ import { defineCommand, runMain } from "citty";
7
+ import consola from "consola";
8
+ import { execSync } from "node:child_process";
9
+ async function getAvailableModels() {
10
+ const models = [];
11
+ if ((() => {
12
+ try {
13
+ execSync("which claude", { stdio: "ignore" });
14
+ return true;
15
+ } catch {
16
+ return false;
17
+ }
18
+ })()) models.push({
19
+ id: "haiku",
20
+ name: "Haiku",
21
+ description: "Fast and cheap",
22
+ available: true
23
+ }, {
24
+ id: "sonnet",
25
+ name: "Sonnet",
26
+ description: "Balanced",
27
+ available: true
28
+ }, {
29
+ id: "opus",
30
+ name: "Opus",
31
+ description: "Most capable",
32
+ available: true
33
+ });
34
+ if (process.env.ANTHROPIC_API_KEY) {
35
+ if (models.length === 0) models.push({
36
+ id: "haiku",
37
+ name: "Haiku (API)",
38
+ description: "Fast and cheap",
39
+ available: true
40
+ }, {
41
+ id: "sonnet",
42
+ name: "Sonnet (API)",
43
+ description: "Balanced",
44
+ available: true
45
+ });
46
+ }
47
+ return models;
48
+ }
49
+ const OPTIMIZE_PROMPT = `You are a technical documentation optimizer for coding agents.
50
+
51
+ Your task: Transform raw package documentation into a concise skill reference focused on what an AI coding assistant needs.
52
+
53
+ ## EXTRACT THESE (IMPORTANT):
54
+
55
+ ### 1. Public API Reference
56
+ - All exported functions, components, composables with their signatures
57
+ - Required vs optional parameters and their types
58
+ - Return types and what they represent
59
+ - Export names exactly as they should be imported
60
+
61
+ ### 2. Non-Obvious Best Practices
62
+ - Gotchas and common mistakes developers make
63
+ - Edge cases that cause bugs
64
+ - Performance considerations (what's expensive, what to cache)
65
+ - Order-of-operations requirements
66
+ - Initialization patterns that aren't obvious
67
+ - When NOT to use certain features
68
+
69
+ ### 3. Integration Patterns
70
+ - How this integrates with common frameworks/tools
71
+ - Required peer dependencies and version constraints
72
+ - Configuration that's easy to get wrong
73
+
74
+ ## REMOVE THESE:
75
+ - Installation instructions (npm install, etc.)
76
+ - Badges, shields, contributor info
77
+ - License information
78
+ - Changelog/version history
79
+ - Generic "what is this" introductions
80
+ - Marketing language
81
+ - Links to external resources (keep inline code examples)
82
+
83
+ ## OUTPUT FORMAT:
84
+ Use clear markdown with code blocks. Be dense and direct - no filler. Prefer tables for API references.
85
+ Organize as: Quick Reference → API → Best Practices → Gotchas
86
+
87
+ ## INPUT DOCUMENTATION:
88
+ `;
89
+ async function optimizeDocs(content, packageName, agent, model = "haiku") {
90
+ if (agent === "claude-code" || !agent) {
91
+ const result = await tryClaudeCode(content, packageName, model);
92
+ if (result) return {
93
+ optimized: result,
94
+ wasOptimized: true
95
+ };
96
+ }
97
+ if (process.env.ANTHROPIC_API_KEY) {
98
+ const result = await tryAnthropicSDK(content, packageName, model);
99
+ if (result) return {
100
+ optimized: result,
101
+ wasOptimized: true
102
+ };
103
+ }
104
+ return {
105
+ optimized: content,
106
+ wasOptimized: false
107
+ };
108
+ }
109
+ async function tryClaudeCode(content, packageName, model) {
110
+ try {
111
+ execSync("which claude", { stdio: "ignore" });
112
+ const prompt = `${OPTIMIZE_PROMPT}\n\nPackage: ${packageName}\n\n${content}`;
113
+ const tempFile = `/tmp/skilld-optimize-${Date.now()}.txt`;
114
+ const { writeFileSync, unlinkSync } = await import("node:fs");
115
+ writeFileSync(tempFile, prompt);
116
+ try {
117
+ return execSync(`claude --model ${model} --print < "${tempFile}"`, {
118
+ encoding: "utf-8",
119
+ maxBuffer: 10 * 1024 * 1024,
120
+ timeout: 18e4
121
+ }).trim();
122
+ } finally {
123
+ unlinkSync(tempFile);
124
+ }
125
+ } catch {
126
+ return null;
127
+ }
128
+ }
129
+ const MODEL_MAP = {
130
+ haiku: "claude-3-5-haiku-latest",
131
+ sonnet: "claude-sonnet-4-20250514",
132
+ opus: "claude-opus-4-20250514"
133
+ };
134
+ async function tryAnthropicSDK(content, packageName, model) {
135
+ try {
136
+ const { default: Anthropic } = await import("@anthropic-ai/sdk");
137
+ return (await new Anthropic().messages.create({
138
+ model: MODEL_MAP[model],
139
+ max_tokens: 8192,
140
+ messages: [{
141
+ role: "user",
142
+ content: `${OPTIMIZE_PROMPT}\n\nPackage: ${packageName}\n\n${content}`
143
+ }]
144
+ })).content.find((b) => b.type === "text")?.text || null;
145
+ } catch {
146
+ return null;
147
+ }
148
+ }
149
+ const main = defineCommand({
150
+ meta: {
151
+ name: "skilld",
152
+ description: "Generate searchable skills from documentation sites or package.json dependencies"
153
+ },
154
+ args: {
155
+ url: {
156
+ type: "positional",
157
+ description: "URL or package name (omit to process package.json deps)",
158
+ required: false
159
+ },
160
+ output: {
161
+ type: "string",
162
+ alias: "o",
163
+ description: "Output directory (legacy mode)"
164
+ },
165
+ maxPages: {
166
+ type: "string",
167
+ alias: "m",
168
+ description: "Max pages to fetch",
169
+ default: "100"
170
+ },
171
+ chunkSize: {
172
+ type: "string",
173
+ description: "Chunk size in characters",
174
+ default: "1000"
175
+ },
176
+ model: {
177
+ type: "string",
178
+ description: "Embedding model",
179
+ default: "Xenova/bge-small-en-v1.5"
180
+ },
181
+ crawl: {
182
+ type: "boolean",
183
+ description: "Skip llms.txt and always crawl",
184
+ default: false
185
+ },
186
+ concurrency: {
187
+ type: "string",
188
+ alias: "c",
189
+ description: "Concurrent package processing",
190
+ default: "3"
191
+ },
192
+ global: {
193
+ type: "boolean",
194
+ alias: "g",
195
+ description: "Install skills globally",
196
+ default: false
197
+ },
198
+ agent: {
199
+ type: "string",
200
+ alias: "a",
201
+ description: "Target specific agent (claude-code, cursor, windsurf, etc.)"
202
+ },
203
+ force: {
204
+ type: "boolean",
205
+ alias: "f",
206
+ description: "Force regenerate even if version matches",
207
+ default: false
208
+ },
209
+ optimize: {
210
+ type: "boolean",
211
+ description: "Optimize docs with LLM (extracts APIs & best practices)",
212
+ default: true
213
+ },
214
+ yes: {
215
+ type: "boolean",
216
+ alias: "y",
217
+ description: "Skip prompts, use defaults",
218
+ default: false
219
+ }
220
+ },
221
+ async run({ args }) {
222
+ const maxPages = Number.parseInt(args.maxPages, 10);
223
+ const chunkSize = Number.parseInt(args.chunkSize, 10);
224
+ const concurrency = Number.parseInt(args.concurrency, 10);
225
+ const currentAgent = detectCurrentAgent();
226
+ if (!currentAgent && !args.agent && !args.output) {
227
+ consola.warn("Could not detect which agent is running. Use --agent <name> or --output <dir>");
228
+ consola.info("Supported agents: " + Object.keys(agents).join(", "));
229
+ return;
230
+ }
231
+ const targetAgents = args.agent ? [args.agent] : currentAgent ? [currentAgent] : [];
232
+ if (targetAgents.length > 0) consola.info(`Target agent: ${targetAgents.map((a) => agents[a].displayName).join(", ")}`);
233
+ if (args.url) {
234
+ await processUrl(args.url, {
235
+ outputDir: args.output,
236
+ maxPages,
237
+ chunkSize,
238
+ skipLlmsTxt: args.crawl,
239
+ model: args.model,
240
+ global: args.global,
241
+ agents: targetAgents,
242
+ optimize: args.optimize,
243
+ currentAgent
244
+ });
245
+ return;
246
+ }
247
+ consola.start("Reading package.json dependencies...");
248
+ const allDeps = await readLocalDependencies(process.cwd());
249
+ if (allDeps.length === 0) {
250
+ consola.warn("No dependencies found in package.json");
251
+ return;
252
+ }
253
+ const needsUpdate = [];
254
+ const upToDate = [];
255
+ for (const dep of allDeps) {
256
+ if (args.force) {
257
+ needsUpdate.push(dep);
258
+ continue;
259
+ }
260
+ if (targetAgents.length > 0) {
261
+ const agent = agents[targetAgents[0]];
262
+ const installedVersion = await getInstalledSkillVersion(join(process.cwd(), agent.skillsDir, sanitizeName(dep.name)));
263
+ if (installedVersion && installedVersion === dep.version) upToDate.push(dep);
264
+ else needsUpdate.push(dep);
265
+ } else needsUpdate.push(dep);
266
+ }
267
+ if (needsUpdate.length === 0) {
268
+ consola.success(`All ${allDeps.length} skills are up-to-date`);
269
+ return;
270
+ }
271
+ consola.info(`Found ${needsUpdate.length} packages to sync (${upToDate.length} up-to-date)`);
272
+ let selectedDeps = needsUpdate;
273
+ if (!args.yes && needsUpdate.length > 1) {
274
+ const choices = needsUpdate.map((d) => ({
275
+ label: `${d.name}@${d.version}`,
276
+ value: d.name
277
+ }));
278
+ const selected = await consola.prompt("Select packages to sync:", {
279
+ type: "multiselect",
280
+ options: choices,
281
+ initial: choices.map((c) => c.value)
282
+ });
283
+ if (!selected || selected.length === 0) {
284
+ consola.warn("No packages selected");
285
+ return;
286
+ }
287
+ selectedDeps = needsUpdate.filter((d) => selected.includes(d.name));
288
+ }
289
+ let optimizeModel = null;
290
+ if (args.optimize && !args.yes) {
291
+ const availableModels = await getAvailableModels();
292
+ if (availableModels.length > 0) {
293
+ const modelChoice = await consola.prompt("Optimize with LLM?", {
294
+ type: "select",
295
+ options: [...availableModels.map((m) => ({
296
+ label: `${m.name} ${m.available ? "(available)" : ""}`,
297
+ value: m.id,
298
+ hint: m.description
299
+ })), {
300
+ label: "Skip optimization",
301
+ value: "skip"
302
+ }],
303
+ initial: availableModels[0]?.id || "skip"
304
+ });
305
+ optimizeModel = modelChoice === "skip" ? null : modelChoice;
306
+ }
307
+ } else if (args.optimize) optimizeModel = "haiku";
308
+ const queue = [...selectedDeps];
309
+ let processed = 0;
310
+ let failed = 0;
311
+ const worker = async () => {
312
+ while (queue.length > 0) {
313
+ const dep = queue.shift();
314
+ if (!dep) break;
315
+ const { name: packageName, version: localVersion } = dep;
316
+ try {
317
+ consola.start(`[${processed + failed + 1}/${selectedDeps.length}] ${packageName}@${localVersion}`);
318
+ const resolved = await resolvePackageDocs(packageName);
319
+ if (!resolved) {
320
+ consola.warn(` No docs found for ${packageName}`);
321
+ failed++;
322
+ continue;
323
+ }
324
+ const url = resolved.llmsUrl || resolved.docsUrl || resolved.readmeUrl;
325
+ if (!url) {
326
+ consola.warn(` No URL resolved for ${packageName}`);
327
+ failed++;
328
+ continue;
329
+ }
330
+ await processUrl(url, {
331
+ outputDir: args.output,
332
+ maxPages,
333
+ chunkSize,
334
+ skipLlmsTxt: args.crawl,
335
+ model: args.model,
336
+ global: args.global,
337
+ agents: targetAgents,
338
+ packageName,
339
+ packageVersion: localVersion,
340
+ quiet: true,
341
+ optimize: !!optimizeModel,
342
+ optimizeModel,
343
+ currentAgent
344
+ });
345
+ processed++;
346
+ consola.success(` Generated skill for ${packageName}@${localVersion}`);
347
+ } catch (e) {
348
+ consola.error(` Failed: ${e.message}`);
349
+ failed++;
350
+ }
351
+ }
352
+ };
353
+ await Promise.all(Array.from({ length: concurrency }, () => worker()));
354
+ consola.box(`Generated ${processed} skills (${upToDate.length} up-to-date, ${failed} failed)`);
355
+ }
356
+ });
357
+ async function processUrl(url, config) {
358
+ let resolvedUrl = url;
359
+ let skillName = config.packageName;
360
+ let skillVersion = config.packageVersion;
361
+ let isRawReadme = false;
362
+ let description = "";
363
+ if (!url.includes("://") && !url.startsWith("http")) {
364
+ if (!config.quiet) consola.start(`Resolving docs for package: ${url}`);
365
+ skillName = url;
366
+ const resolved = await resolvePackageDocs(url);
367
+ if (!resolved) {
368
+ consola.error(`Could not find docs for package: ${url}`);
369
+ return;
370
+ }
371
+ description = resolved.description || "";
372
+ skillVersion = skillVersion || resolved.version;
373
+ if (resolved.llmsUrl) resolvedUrl = resolved.llmsUrl;
374
+ else if (resolved.docsUrl) resolvedUrl = resolved.docsUrl;
375
+ else if (resolved.readmeUrl) {
376
+ resolvedUrl = resolved.readmeUrl;
377
+ isRawReadme = true;
378
+ } else {
379
+ consola.error(`No documentation found for ${url}`);
380
+ return;
381
+ }
382
+ if (!config.quiet) consola.info(`Resolved to: ${resolvedUrl}${isRawReadme ? " (README)" : ""}`);
383
+ }
384
+ if (isRawReadme || resolvedUrl.includes("raw.githubusercontent.com") || resolvedUrl.startsWith("ungh://")) {
385
+ if (!config.quiet) consola.start(`Fetching README for ${skillName || "package"}`);
386
+ let readmeContent;
387
+ if (resolvedUrl.startsWith("ungh://")) {
388
+ const parts = resolvedUrl.replace("ungh://", "").split("/");
389
+ const owner = parts[0];
390
+ const repo = parts[1];
391
+ const subdir = parts.slice(2).join("/");
392
+ const unghUrl = subdir ? `https://ungh.cc/repos/${owner}/${repo}/files/main/${subdir}/README.md` : `https://ungh.cc/repos/${owner}/${repo}/readme`;
393
+ const res = await fetch(unghUrl, { headers: { "User-Agent": "skilld/1.0" } }).catch(() => null);
394
+ if (!res?.ok) {
395
+ consola.error(`Failed to fetch README from ungh: ${res?.status}`);
396
+ return;
397
+ }
398
+ const text = await res.text();
399
+ try {
400
+ const json = JSON.parse(text);
401
+ readmeContent = json.markdown || json.file?.contents || "";
402
+ } catch {
403
+ readmeContent = text;
404
+ }
405
+ } else {
406
+ const res = await fetch(resolvedUrl, { headers: { "User-Agent": "skilld/1.0" } }).catch(() => null);
407
+ if (!res?.ok) {
408
+ consola.error(`Failed to fetch README: ${res?.status}`);
409
+ return;
410
+ }
411
+ readmeContent = await res.text();
412
+ }
413
+ if (!readmeContent) {
414
+ consola.error("README content is empty");
415
+ return;
416
+ }
417
+ const name = skillName || "package";
418
+ let finalContent = readmeContent;
419
+ if (config.optimize !== false && config.optimizeModel) {
420
+ !config.quiet && consola.start(`Optimizing ${name} with ${config.optimizeModel}...`);
421
+ const { optimized, wasOptimized } = await optimizeDocs(readmeContent, name, config.currentAgent || null, config.optimizeModel);
422
+ finalContent = optimized;
423
+ if (!config.quiet && wasOptimized) consola.success(`Optimized: ${readmeContent.length} → ${optimized.length} chars`);
424
+ else if (!config.quiet && !wasOptimized) consola.warn(`Optimization skipped (no LLM available)`);
425
+ }
426
+ const { installed, paths } = installSkillForAgents(name, generateSkillMd({
427
+ name,
428
+ version: skillVersion,
429
+ description
430
+ }, finalContent), {
431
+ global: config.global,
432
+ agents: config.agents
433
+ });
434
+ if (!config.quiet) {
435
+ consola.success(`Installed skill: ${name}`);
436
+ consola.info(` Agents: ${installed.map((a) => agents[a].displayName).join(", ")}`);
437
+ consola.info(` Paths: ${paths.join(", ")}`);
438
+ }
439
+ return;
440
+ }
441
+ if (!config.quiet) consola.start(`Generating skill from ${resolvedUrl}`);
442
+ if (config.outputDir) {
443
+ const result = await generateSkill({
444
+ url: resolvedUrl,
445
+ outputDir: config.outputDir,
446
+ maxPages: config.maxPages,
447
+ chunkSize: config.chunkSize,
448
+ skipLlmsTxt: config.skipLlmsTxt,
449
+ model: config.model
450
+ }, config.quiet ? void 0 : ({ url: pageUrl, count, phase }) => {
451
+ const icon = phase === "fetch" ? "📄" : "🔍";
452
+ consola.info(`${icon} [${count}] ${pageUrl}`);
453
+ });
454
+ if (!config.quiet) {
455
+ consola.success(`Generated skill: ${result.siteName}`);
456
+ consola.info(` SKILL.md: ${result.skillPath}`);
457
+ consola.info(` References: ${result.referencesDir}`);
458
+ consola.info(` Database: ${result.dbPath}`);
459
+ consola.info(` Chunks: ${result.chunkCount}`);
460
+ }
461
+ return;
462
+ }
463
+ const result = await generateSkill({
464
+ url: resolvedUrl,
465
+ outputDir: ".skilld-temp",
466
+ maxPages: config.maxPages,
467
+ chunkSize: config.chunkSize,
468
+ skipLlmsTxt: config.skipLlmsTxt,
469
+ model: config.model
470
+ }, config.quiet ? void 0 : ({ url: pageUrl, count, phase }) => {
471
+ if (phase === "fetch" && count % 5 === 0) consola.info(` [${count}] ${pageUrl}`);
472
+ });
473
+ const name = skillName || result.siteName;
474
+ const body = `# ${name}
475
+
476
+ This skill provides searchable documentation for ${name}.
477
+
478
+ ## Search Database
479
+ - Path: ${result.dbPath}
480
+ - Chunks indexed: ${result.chunkCount}
481
+
482
+ ## References
483
+ Individual documentation chunks are available in the references directory.
484
+ `;
485
+ const { installed, paths } = installSkillForAgents(name, generateSkillMd({
486
+ name,
487
+ version: skillVersion,
488
+ description
489
+ }, body), {
490
+ global: config.global,
491
+ agents: config.agents
492
+ });
493
+ if (!config.quiet) {
494
+ consola.success(`Installed skill: ${name}`);
495
+ consola.info(` Agents: ${installed.map((a) => agents[a].displayName).join(", ")}`);
496
+ consola.info(` Paths: ${paths.join(", ")}`);
497
+ consola.info(` Chunks: ${result.chunkCount}`);
498
+ }
499
+ }
500
+ runMain(main);
501
+ export {};
502
+
503
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/optimize.ts","../src/cli.ts"],"sourcesContent":["/**\n * LLM-based documentation optimization\n * Extracts non-obvious best practices and public APIs from raw docs\n */\n\nimport { execSync } from 'node:child_process'\nimport type { AgentType } from './agents'\n\nexport interface AvailableModel {\n id: 'haiku' | 'sonnet' | 'opus'\n name: string\n description: string\n available: boolean\n}\n\n/**\n * Get available optimization models\n */\nexport async function getAvailableModels(): Promise<AvailableModel[]> {\n const models: AvailableModel[] = []\n\n // Check if claude CLI is available\n const hasClaude = (() => {\n try {\n execSync('which claude', { stdio: 'ignore' })\n return true\n }\n catch {\n return false\n }\n })()\n\n if (hasClaude) {\n models.push(\n { id: 'haiku', name: 'Haiku', description: 'Fast and cheap', available: true },\n { id: 'sonnet', name: 'Sonnet', description: 'Balanced', available: true },\n { id: 'opus', name: 'Opus', description: 'Most capable', available: true },\n )\n }\n\n // Check Anthropic SDK\n if (process.env.ANTHROPIC_API_KEY) {\n if (models.length === 0) {\n models.push(\n { id: 'haiku', name: 'Haiku (API)', description: 'Fast and cheap', available: true },\n { id: 'sonnet', name: 'Sonnet (API)', description: 'Balanced', available: true },\n )\n }\n }\n\n return models\n}\n\nconst OPTIMIZE_PROMPT = `You are a technical documentation optimizer for coding agents.\n\nYour task: Transform raw package documentation into a concise skill reference focused on what an AI coding assistant needs.\n\n## EXTRACT THESE (IMPORTANT):\n\n### 1. Public API Reference\n- All exported functions, components, composables with their signatures\n- Required vs optional parameters and their types\n- Return types and what they represent\n- Export names exactly as they should be imported\n\n### 2. Non-Obvious Best Practices\n- Gotchas and common mistakes developers make\n- Edge cases that cause bugs\n- Performance considerations (what's expensive, what to cache)\n- Order-of-operations requirements\n- Initialization patterns that aren't obvious\n- When NOT to use certain features\n\n### 3. Integration Patterns\n- How this integrates with common frameworks/tools\n- Required peer dependencies and version constraints\n- Configuration that's easy to get wrong\n\n## REMOVE THESE:\n- Installation instructions (npm install, etc.)\n- Badges, shields, contributor info\n- License information\n- Changelog/version history\n- Generic \"what is this\" introductions\n- Marketing language\n- Links to external resources (keep inline code examples)\n\n## OUTPUT FORMAT:\nUse clear markdown with code blocks. Be dense and direct - no filler. Prefer tables for API references.\nOrganize as: Quick Reference → API → Best Practices → Gotchas\n\n## INPUT DOCUMENTATION:\n`\n\nexport type OptimizeModel = AvailableModel['id']\n\n/**\n * Optimize documentation using the detected LLM agent\n * Falls back gracefully if no LLM available\n */\nexport async function optimizeDocs(\n content: string,\n packageName: string,\n agent: AgentType | null,\n model: OptimizeModel = 'haiku',\n): Promise<{ optimized: string, wasOptimized: boolean }> {\n // Try Claude Code first (most common)\n if (agent === 'claude-code' || !agent) {\n const result = await tryClaudeCode(content, packageName, model)\n if (result) return { optimized: result, wasOptimized: true }\n }\n\n // Fallback: try Anthropic SDK if API key available\n if (process.env.ANTHROPIC_API_KEY) {\n const result = await tryAnthropicSDK(content, packageName, model)\n if (result) return { optimized: result, wasOptimized: true }\n }\n\n // No LLM available, return original\n return { optimized: content, wasOptimized: false }\n}\n\nasync function tryClaudeCode(content: string, packageName: string, model: OptimizeModel): Promise<string | null> {\n try {\n // Check if claude CLI exists\n execSync('which claude', { stdio: 'ignore' })\n\n const prompt = `${OPTIMIZE_PROMPT}\\n\\nPackage: ${packageName}\\n\\n${content}`\n\n // Write prompt to temp file to avoid shell escaping issues\n const tempFile = `/tmp/skilld-optimize-${Date.now()}.txt`\n const { writeFileSync, unlinkSync } = await import('node:fs')\n writeFileSync(tempFile, prompt)\n\n try {\n const result = execSync(\n `claude --model ${model} --print < \"${tempFile}\"`,\n {\n encoding: 'utf-8',\n maxBuffer: 10 * 1024 * 1024, // 10MB\n timeout: 180_000, // 3 min for larger models\n },\n )\n return result.trim()\n }\n finally {\n unlinkSync(tempFile)\n }\n }\n catch {\n return null\n }\n}\n\nconst MODEL_MAP: Record<OptimizeModel, string> = {\n haiku: 'claude-3-5-haiku-latest',\n sonnet: 'claude-sonnet-4-20250514',\n opus: 'claude-opus-4-20250514',\n}\n\nasync function tryAnthropicSDK(content: string, packageName: string, model: OptimizeModel): Promise<string | null> {\n try {\n // Dynamic import to avoid requiring the SDK if not used\n // @ts-expect-error - optional dependency\n const { default: Anthropic } = await import('@anthropic-ai/sdk')\n const client = new Anthropic()\n\n const response = await client.messages.create({\n model: MODEL_MAP[model],\n max_tokens: 8192,\n messages: [{\n role: 'user',\n content: `${OPTIMIZE_PROMPT}\\n\\nPackage: ${packageName}\\n\\n${content}`,\n }],\n })\n\n const textBlock = response.content.find((b: { type: string, text?: string }) => b.type === 'text') as { type: 'text', text: string } | undefined\n return textBlock?.text || null\n }\n catch {\n return null\n }\n}\n","#!/usr/bin/env node\nimport { join } from 'node:path'\nimport { defineCommand, runMain } from 'citty'\nimport consola from 'consola'\nimport { generateSkill } from './index'\nimport { getInstalledSkillVersion, readLocalDependencies, resolvePackageDocs } from './npm'\nimport { agents, detectCurrentAgent, generateSkillMd, installSkillForAgents, sanitizeName } from './agents'\nimport { type OptimizeModel, getAvailableModels, optimizeDocs } from './optimize'\n\nconst main = defineCommand({\n meta: {\n name: 'skilld',\n description: 'Generate searchable skills from documentation sites or package.json dependencies',\n },\n args: {\n url: {\n type: 'positional',\n description: 'URL or package name (omit to process package.json deps)',\n required: false,\n },\n output: {\n type: 'string',\n alias: 'o',\n description: 'Output directory (legacy mode)',\n },\n maxPages: {\n type: 'string',\n alias: 'm',\n description: 'Max pages to fetch',\n default: '100',\n },\n chunkSize: {\n type: 'string',\n description: 'Chunk size in characters',\n default: '1000',\n },\n model: {\n type: 'string',\n description: 'Embedding model',\n default: 'Xenova/bge-small-en-v1.5',\n },\n crawl: {\n type: 'boolean',\n description: 'Skip llms.txt and always crawl',\n default: false,\n },\n concurrency: {\n type: 'string',\n alias: 'c',\n description: 'Concurrent package processing',\n default: '3',\n },\n global: {\n type: 'boolean',\n alias: 'g',\n description: 'Install skills globally',\n default: false,\n },\n agent: {\n type: 'string',\n alias: 'a',\n description: 'Target specific agent (claude-code, cursor, windsurf, etc.)',\n },\n force: {\n type: 'boolean',\n alias: 'f',\n description: 'Force regenerate even if version matches',\n default: false,\n },\n optimize: {\n type: 'boolean',\n description: 'Optimize docs with LLM (extracts APIs & best practices)',\n default: true,\n },\n yes: {\n type: 'boolean',\n alias: 'y',\n description: 'Skip prompts, use defaults',\n default: false,\n },\n },\n async run({ args }) {\n const maxPages = Number.parseInt(args.maxPages, 10)\n const chunkSize = Number.parseInt(args.chunkSize, 10)\n const concurrency = Number.parseInt(args.concurrency, 10)\n\n // Detect current agent (the one running this command)\n const currentAgent = detectCurrentAgent()\n\n if (!currentAgent && !args.agent && !args.output) {\n consola.warn('Could not detect which agent is running. Use --agent <name> or --output <dir>')\n consola.info('Supported agents: ' + Object.keys(agents).join(', '))\n return\n }\n\n const targetAgents = args.agent\n ? [args.agent as keyof typeof agents]\n : currentAgent\n ? [currentAgent]\n : []\n\n if (targetAgents.length > 0) {\n consola.info(`Target agent: ${targetAgents.map(a => agents[a].displayName).join(', ')}`)\n }\n\n // Single URL/package mode\n if (args.url) {\n await processUrl(args.url, {\n outputDir: args.output,\n maxPages,\n chunkSize,\n skipLlmsTxt: args.crawl,\n model: args.model,\n global: args.global,\n agents: targetAgents,\n optimize: args.optimize,\n currentAgent,\n })\n return\n }\n\n // Package.json mode\n consola.start('Reading package.json dependencies...')\n\n const allDeps = await readLocalDependencies(process.cwd())\n if (allDeps.length === 0) {\n consola.warn('No dependencies found in package.json')\n return\n }\n\n // Categorize deps: needs update vs up-to-date\n const needsUpdate: typeof allDeps = []\n const upToDate: typeof allDeps = []\n\n for (const dep of allDeps) {\n if (args.force) {\n needsUpdate.push(dep)\n continue\n }\n\n if (targetAgents.length > 0) {\n const agent = agents[targetAgents[0]]\n const skillDir = join(process.cwd(), agent.skillsDir, sanitizeName(dep.name))\n const installedVersion = await getInstalledSkillVersion(skillDir)\n\n if (installedVersion && installedVersion === dep.version) {\n upToDate.push(dep)\n }\n else {\n needsUpdate.push(dep)\n }\n }\n else {\n needsUpdate.push(dep)\n }\n }\n\n if (needsUpdate.length === 0) {\n consola.success(`All ${allDeps.length} skills are up-to-date`)\n return\n }\n\n consola.info(`Found ${needsUpdate.length} packages to sync (${upToDate.length} up-to-date)`)\n\n // Prompt for package selection (unless --yes)\n let selectedDeps = needsUpdate\n if (!args.yes && needsUpdate.length > 1) {\n const choices = needsUpdate.map(d => ({\n label: `${d.name}@${d.version}`,\n value: d.name,\n }))\n\n const selected = await consola.prompt('Select packages to sync:', {\n type: 'multiselect',\n options: choices,\n initial: choices.map(c => c.value), // all selected by default\n }) as string[]\n\n if (!selected || selected.length === 0) {\n consola.warn('No packages selected')\n return\n }\n\n selectedDeps = needsUpdate.filter(d => selected.includes(d.name))\n }\n\n // Prompt for optimization model (unless --yes or --no-optimize)\n let optimizeModel: OptimizeModel | null = null\n if (args.optimize && !args.yes) {\n const availableModels = await getAvailableModels()\n if (availableModels.length > 0) {\n const modelChoice = await consola.prompt('Optimize with LLM?', {\n type: 'select',\n options: [\n ...availableModels.map(m => ({\n label: `${m.name} ${m.available ? '(available)' : ''}`,\n value: m.id,\n hint: m.description,\n })),\n { label: 'Skip optimization', value: 'skip' },\n ],\n initial: availableModels[0]?.id || 'skip',\n }) as string\n\n optimizeModel = modelChoice === 'skip' ? null : modelChoice as OptimizeModel\n }\n }\n else if (args.optimize) {\n optimizeModel = 'haiku' // default for --yes\n }\n\n const queue = [...selectedDeps]\n let processed = 0\n let failed = 0\n\n const worker = async () => {\n while (queue.length > 0) {\n const dep = queue.shift()\n if (!dep) break\n\n const { name: packageName, version: localVersion } = dep\n\n try {\n consola.start(`[${processed + failed + 1}/${selectedDeps.length}] ${packageName}@${localVersion}`)\n\n const resolved = await resolvePackageDocs(packageName)\n if (!resolved) {\n consola.warn(` No docs found for ${packageName}`)\n failed++\n continue\n }\n\n const url = resolved.llmsUrl || resolved.docsUrl || resolved.readmeUrl\n if (!url) {\n consola.warn(` No URL resolved for ${packageName}`)\n failed++\n continue\n }\n\n await processUrl(url, {\n outputDir: args.output,\n maxPages,\n chunkSize,\n skipLlmsTxt: args.crawl,\n model: args.model,\n global: args.global,\n agents: targetAgents,\n packageName,\n packageVersion: localVersion,\n quiet: true,\n optimize: !!optimizeModel,\n optimizeModel,\n currentAgent,\n })\n\n processed++\n consola.success(` Generated skill for ${packageName}@${localVersion}`)\n }\n catch (e: any) {\n consola.error(` Failed: ${e.message}`)\n failed++\n }\n }\n }\n\n await Promise.all(Array.from({ length: concurrency }, () => worker()))\n\n consola.box(`Generated ${processed} skills (${upToDate.length} up-to-date, ${failed} failed)`)\n },\n})\n\nasync function processUrl(url: string, config: {\n outputDir?: string\n maxPages: number\n chunkSize: number\n skipLlmsTxt: boolean\n model: string\n global: boolean\n agents: Array<keyof typeof agents>\n packageName?: string\n packageVersion?: string\n quiet?: boolean\n optimize?: boolean\n optimizeModel?: OptimizeModel | null\n currentAgent?: ReturnType<typeof detectCurrentAgent>\n}) {\n let resolvedUrl = url\n let skillName = config.packageName\n let skillVersion: string | undefined = config.packageVersion\n let isRawReadme = false\n let description = ''\n\n // Check if it's a package name (no protocol)\n if (!url.includes('://') && !url.startsWith('http')) {\n if (!config.quiet) consola.start(`Resolving docs for package: ${url}`)\n skillName = url\n\n const resolved = await resolvePackageDocs(url)\n if (!resolved) {\n consola.error(`Could not find docs for package: ${url}`)\n return\n }\n\n description = resolved.description || ''\n // Use provided version (from package.json) or fallback to npm registry version\n skillVersion = skillVersion || resolved.version\n\n // Prioritize: llms.txt > docs site > raw README\n if (resolved.llmsUrl) {\n resolvedUrl = resolved.llmsUrl\n }\n else if (resolved.docsUrl) {\n resolvedUrl = resolved.docsUrl\n }\n else if (resolved.readmeUrl) {\n resolvedUrl = resolved.readmeUrl\n isRawReadme = true\n }\n else {\n consola.error(`No documentation found for ${url}`)\n return\n }\n\n if (!config.quiet) consola.info(`Resolved to: ${resolvedUrl}${isRawReadme ? ' (README)' : ''}`)\n }\n\n // Raw README: fetch directly and create simple skill\n if (isRawReadme || resolvedUrl.includes('raw.githubusercontent.com') || resolvedUrl.startsWith('ungh://')) {\n if (!config.quiet) consola.start(`Fetching README for ${skillName || 'package'}`)\n\n let readmeContent: string\n\n // Handle ungh:// URLs (JSON API)\n if (resolvedUrl.startsWith('ungh://')) {\n const parts = resolvedUrl.replace('ungh://', '').split('/')\n const owner = parts[0]\n const repo = parts[1]\n const subdir = parts.slice(2).join('/')\n\n const unghUrl = subdir\n ? `https://ungh.cc/repos/${owner}/${repo}/files/main/${subdir}/README.md`\n : `https://ungh.cc/repos/${owner}/${repo}/readme`\n\n const res = await fetch(unghUrl, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n if (!res?.ok) {\n consola.error(`Failed to fetch README from ungh: ${res?.status}`)\n return\n }\n\n const text = await res.text()\n\n // Try to parse as JSON first\n try {\n const json = JSON.parse(text) as { markdown?: string, file?: { contents?: string } }\n readmeContent = json.markdown || json.file?.contents || ''\n }\n catch {\n // If JSON parse fails, treat as raw markdown\n readmeContent = text\n }\n }\n else {\n // Direct raw URL\n const res = await fetch(resolvedUrl, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n if (!res?.ok) {\n consola.error(`Failed to fetch README: ${res?.status}`)\n return\n }\n\n readmeContent = await res.text()\n }\n\n if (!readmeContent) {\n consola.error('README content is empty')\n return\n }\n\n const name = skillName || 'package'\n\n // Optimize docs with LLM if enabled\n let finalContent = readmeContent\n if (config.optimize !== false && config.optimizeModel) {\n const spinner = !config.quiet ? consola.start(`Optimizing ${name} with ${config.optimizeModel}...`) : null\n const { optimized, wasOptimized } = await optimizeDocs(\n readmeContent,\n name,\n config.currentAgent || null,\n config.optimizeModel,\n )\n finalContent = optimized\n if (!config.quiet && wasOptimized) {\n consola.success(`Optimized: ${readmeContent.length} → ${optimized.length} chars`)\n }\n else if (!config.quiet && !wasOptimized) {\n consola.warn(`Optimization skipped (no LLM available)`)\n }\n }\n\n const skillContent = generateSkillMd(\n { name, version: skillVersion, description },\n finalContent,\n )\n\n // Install to agents\n const { installed, paths } = installSkillForAgents(name, skillContent, {\n global: config.global,\n agents: config.agents,\n })\n\n if (!config.quiet) {\n consola.success(`Installed skill: ${name}`)\n consola.info(` Agents: ${installed.map(a => agents[a].displayName).join(', ')}`)\n consola.info(` Paths: ${paths.join(', ')}`)\n }\n return\n }\n\n if (!config.quiet) consola.start(`Generating skill from ${resolvedUrl}`)\n\n // Legacy mode: output to specific directory\n if (config.outputDir) {\n const result = await generateSkill({\n url: resolvedUrl,\n outputDir: config.outputDir,\n maxPages: config.maxPages,\n chunkSize: config.chunkSize,\n skipLlmsTxt: config.skipLlmsTxt,\n model: config.model,\n }, config.quiet ? undefined : ({ url: pageUrl, count, phase }) => {\n const icon = phase === 'fetch' ? '📄' : '🔍'\n consola.info(`${icon} [${count}] ${pageUrl}`)\n })\n\n if (!config.quiet) {\n consola.success(`Generated skill: ${result.siteName}`)\n consola.info(` SKILL.md: ${result.skillPath}`)\n consola.info(` References: ${result.referencesDir}`)\n consola.info(` Database: ${result.dbPath}`)\n consola.info(` Chunks: ${result.chunkCount}`)\n }\n return\n }\n\n // Agent mode: crawl/index then install to detected agents\n const result = await generateSkill({\n url: resolvedUrl,\n outputDir: '.skilld-temp',\n maxPages: config.maxPages,\n chunkSize: config.chunkSize,\n skipLlmsTxt: config.skipLlmsTxt,\n model: config.model,\n }, config.quiet ? undefined : ({ url: pageUrl, count, phase }) => {\n if (phase === 'fetch' && count % 5 === 0) {\n consola.info(` [${count}] ${pageUrl}`)\n }\n })\n\n // Generate skill content\n const name = skillName || result.siteName\n const body = `# ${name}\n\nThis skill provides searchable documentation for ${name}.\n\n## Search Database\n- Path: ${result.dbPath}\n- Chunks indexed: ${result.chunkCount}\n\n## References\nIndividual documentation chunks are available in the references directory.\n`\n\n const skillContent = generateSkillMd(\n { name, version: skillVersion, description },\n body,\n )\n\n // Install to agents\n const { installed, paths } = installSkillForAgents(name, skillContent, {\n global: config.global,\n agents: config.agents,\n })\n\n if (!config.quiet) {\n consola.success(`Installed skill: ${name}`)\n consola.info(` Agents: ${installed.map(a => agents[a].displayName).join(', ')}`)\n consola.info(` Paths: ${paths.join(', ')}`)\n consola.info(` Chunks: ${result.chunkCount}`)\n }\n}\n\nrunMain(main)\n"],"mappings":";;;;;;;;AAkBA,eAAsB,qBAAgD;CACpE,MAAM,SAA2B,EAAE;AAanC,YAVyB;AACvB,MAAI;AACF,YAAS,gBAAgB,EAAE,OAAO,UAAU,CAAC;AAC7C,UAAO;UAEH;AACJ,UAAO;;KAEP,CAGF,QAAO,KACL;EAAE,IAAI;EAAS,MAAM;EAAS,aAAa;EAAkB,WAAW;EAAM,EAC9E;EAAE,IAAI;EAAU,MAAM;EAAU,aAAa;EAAY,WAAW;EAAM,EAC1E;EAAE,IAAI;EAAQ,MAAM;EAAQ,aAAa;EAAgB,WAAW;EAAM,CAC3E;AAIH,KAAI,QAAQ,IAAI;MACV,OAAO,WAAW,EACpB,QAAO,KACL;GAAE,IAAI;GAAS,MAAM;GAAe,aAAa;GAAkB,WAAW;GAAM,EACpF;GAAE,IAAI;GAAU,MAAM;GAAgB,aAAa;GAAY,WAAW;GAAM,CACjF;;AAIL,QAAO;;AAGT,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CxB,eAAsB,aACpB,SACA,aACA,OACA,QAAuB,SACgC;AAEvD,KAAI,UAAU,iBAAiB,CAAC,OAAO;EACrC,MAAM,SAAS,MAAM,cAAc,SAAS,aAAa,MAAM;AAC/D,MAAI,OAAQ,QAAO;GAAE,WAAW;GAAQ,cAAc;GAAM;;AAI9D,KAAI,QAAQ,IAAI,mBAAmB;EACjC,MAAM,SAAS,MAAM,gBAAgB,SAAS,aAAa,MAAM;AACjE,MAAI,OAAQ,QAAO;GAAE,WAAW;GAAQ,cAAc;GAAM;;AAI9D,QAAO;EAAE,WAAW;EAAS,cAAc;EAAO;;AAGpD,eAAe,cAAc,SAAiB,aAAqB,OAA8C;AAC/G,KAAI;AAEF,WAAS,gBAAgB,EAAE,OAAO,UAAU,CAAC;EAE7C,MAAM,SAAS,GAAG,gBAAgB,eAAe,YAAY,MAAM;EAGnE,MAAM,WAAW,wBAAwB,KAAK,KAAK,CAAC;EACpD,MAAM,EAAE,eAAe,eAAe,MAAM,OAAO;AACnD,gBAAc,UAAU,OAAO;AAE/B,MAAI;AASF,UARe,SACb,kBAAkB,MAAM,cAAc,SAAS,IAC/C;IACE,UAAU;IACV,WAAW,KAAK,OAAO;IACvB,SAAS;IACV,CACF,CACa,MAAM;YAEd;AACN,cAAW,SAAS;;SAGlB;AACJ,SAAO;;;AAIX,MAAM,YAA2C;CAC/C,OAAO;CACP,QAAQ;CACR,MAAM;CACP;AAED,eAAe,gBAAgB,SAAiB,aAAqB,OAA8C;AACjH,KAAI;EAGF,MAAM,EAAE,SAAS,cAAc,MAAM,OAAO;AAa5C,UAViB,MAFF,IAAI,WAAW,CAEA,SAAS,OAAO;GAC5C,OAAO,UAAU;GACjB,YAAY;GACZ,UAAU,CAAC;IACT,MAAM;IACN,SAAS,GAAG,gBAAgB,eAAe,YAAY,MAAM;IAC9D,CAAC;GACH,CAAC,EAEyB,QAAQ,MAAM,MAAuC,EAAE,SAAS,OAAO,EAChF,QAAQ;SAEtB;AACJ,SAAO;;;AC3KX,MAAM,OAAO,cAAc;CACzB,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,KAAK;GACH,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,QAAQ;GACN,MAAM;GACN,OAAO;GACP,aAAa;GACd;EACD,UAAU;GACR,MAAM;GACN,OAAO;GACP,aAAa;GACb,SAAS;GACV;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,OAAO;GACL,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,OAAO;GACL,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,aAAa;GACX,MAAM;GACN,OAAO;GACP,aAAa;GACb,SAAS;GACV;EACD,QAAQ;GACN,MAAM;GACN,OAAO;GACP,aAAa;GACb,SAAS;GACV;EACD,OAAO;GACL,MAAM;GACN,OAAO;GACP,aAAa;GACd;EACD,OAAO;GACL,MAAM;GACN,OAAO;GACP,aAAa;GACb,SAAS;GACV;EACD,UAAU;GACR,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,KAAK;GACH,MAAM;GACN,OAAO;GACP,aAAa;GACb,SAAS;GACV;EACF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,WAAW,OAAO,SAAS,KAAK,UAAU,GAAG;EACnD,MAAM,YAAY,OAAO,SAAS,KAAK,WAAW,GAAG;EACrD,MAAM,cAAc,OAAO,SAAS,KAAK,aAAa,GAAG;EAGzD,MAAM,eAAe,oBAAoB;AAEzC,MAAI,CAAC,gBAAgB,CAAC,KAAK,SAAS,CAAC,KAAK,QAAQ;AAChD,WAAQ,KAAK,gFAAgF;AAC7F,WAAQ,KAAK,uBAAuB,OAAO,KAAK,OAAO,CAAC,KAAK,KAAK,CAAC;AACnE;;EAGF,MAAM,eAAe,KAAK,QACtB,CAAC,KAAK,MAA6B,GACnC,eACE,CAAC,aAAa,GACd,EAAE;AAER,MAAI,aAAa,SAAS,EACxB,SAAQ,KAAK,iBAAiB,aAAa,KAAI,MAAK,OAAO,GAAG,YAAY,CAAC,KAAK,KAAK,GAAG;AAI1F,MAAI,KAAK,KAAK;AACZ,SAAM,WAAW,KAAK,KAAK;IACzB,WAAW,KAAK;IAChB;IACA;IACA,aAAa,KAAK;IAClB,OAAO,KAAK;IACZ,QAAQ,KAAK;IACb,QAAQ;IACR,UAAU,KAAK;IACf;IACD,CAAC;AACF;;AAIF,UAAQ,MAAM,uCAAuC;EAErD,MAAM,UAAU,MAAM,sBAAsB,QAAQ,KAAK,CAAC;AAC1D,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAQ,KAAK,wCAAwC;AACrD;;EAIF,MAAM,cAA8B,EAAE;EACtC,MAAM,WAA2B,EAAE;AAEnC,OAAK,MAAM,OAAO,SAAS;AACzB,OAAI,KAAK,OAAO;AACd,gBAAY,KAAK,IAAI;AACrB;;AAGF,OAAI,aAAa,SAAS,GAAG;IAC3B,MAAM,QAAQ,OAAO,aAAa;IAElC,MAAM,mBAAmB,MAAM,yBADd,KAAK,QAAQ,KAAK,EAAE,MAAM,WAAW,aAAa,IAAI,KAAK,CAAC,CACZ;AAEjE,QAAI,oBAAoB,qBAAqB,IAAI,QAC/C,UAAS,KAAK,IAAI;QAGlB,aAAY,KAAK,IAAI;SAIvB,aAAY,KAAK,IAAI;;AAIzB,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAQ,QAAQ,OAAO,QAAQ,OAAO,wBAAwB;AAC9D;;AAGF,UAAQ,KAAK,SAAS,YAAY,OAAO,qBAAqB,SAAS,OAAO,cAAc;EAG5F,IAAI,eAAe;AACnB,MAAI,CAAC,KAAK,OAAO,YAAY,SAAS,GAAG;GACvC,MAAM,UAAU,YAAY,KAAI,OAAM;IACpC,OAAO,GAAG,EAAE,KAAK,GAAG,EAAE;IACtB,OAAO,EAAE;IACV,EAAE;GAEH,MAAM,WAAW,MAAM,QAAQ,OAAO,4BAA4B;IAChE,MAAM;IACN,SAAS;IACT,SAAS,QAAQ,KAAI,MAAK,EAAE,MAAM;IACnC,CAAC;AAEF,OAAI,CAAC,YAAY,SAAS,WAAW,GAAG;AACtC,YAAQ,KAAK,uBAAuB;AACpC;;AAGF,kBAAe,YAAY,QAAO,MAAK,SAAS,SAAS,EAAE,KAAK,CAAC;;EAInE,IAAI,gBAAsC;AAC1C,MAAI,KAAK,YAAY,CAAC,KAAK,KAAK;GAC9B,MAAM,kBAAkB,MAAM,oBAAoB;AAClD,OAAI,gBAAgB,SAAS,GAAG;IAC9B,MAAM,cAAc,MAAM,QAAQ,OAAO,sBAAsB;KAC7D,MAAM;KACN,SAAS,CACP,GAAG,gBAAgB,KAAI,OAAM;MAC3B,OAAO,GAAG,EAAE,KAAK,GAAG,EAAE,YAAY,gBAAgB;MAClD,OAAO,EAAE;MACT,MAAM,EAAE;MACT,EAAE,EACH;MAAE,OAAO;MAAqB,OAAO;MAAQ,CAC9C;KACD,SAAS,gBAAgB,IAAI,MAAM;KACpC,CAAC;AAEF,oBAAgB,gBAAgB,SAAS,OAAO;;aAG3C,KAAK,SACZ,iBAAgB;EAGlB,MAAM,QAAQ,CAAC,GAAG,aAAa;EAC/B,IAAI,YAAY;EAChB,IAAI,SAAS;EAEb,MAAM,SAAS,YAAY;AACzB,UAAO,MAAM,SAAS,GAAG;IACvB,MAAM,MAAM,MAAM,OAAO;AACzB,QAAI,CAAC,IAAK;IAEV,MAAM,EAAE,MAAM,aAAa,SAAS,iBAAiB;AAErD,QAAI;AACF,aAAQ,MAAM,IAAI,YAAY,SAAS,EAAE,GAAG,aAAa,OAAO,IAAI,YAAY,GAAG,eAAe;KAElG,MAAM,WAAW,MAAM,mBAAmB,YAAY;AACtD,SAAI,CAAC,UAAU;AACb,cAAQ,KAAK,uBAAuB,cAAc;AAClD;AACA;;KAGF,MAAM,MAAM,SAAS,WAAW,SAAS,WAAW,SAAS;AAC7D,SAAI,CAAC,KAAK;AACR,cAAQ,KAAK,yBAAyB,cAAc;AACpD;AACA;;AAGF,WAAM,WAAW,KAAK;MACpB,WAAW,KAAK;MAChB;MACA;MACA,aAAa,KAAK;MAClB,OAAO,KAAK;MACZ,QAAQ,KAAK;MACb,QAAQ;MACR;MACA,gBAAgB;MAChB,OAAO;MACP,UAAU,CAAC,CAAC;MACZ;MACA;MACD,CAAC;AAEF;AACA,aAAQ,QAAQ,yBAAyB,YAAY,GAAG,eAAe;aAElE,GAAQ;AACb,aAAQ,MAAM,aAAa,EAAE,UAAU;AACvC;;;;AAKN,QAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,aAAa,QAAQ,QAAQ,CAAC,CAAC;AAEtE,UAAQ,IAAI,aAAa,UAAU,WAAW,SAAS,OAAO,eAAe,OAAO,UAAU;;CAEjG,CAAC;AAEF,eAAe,WAAW,KAAa,QAcpC;CACD,IAAI,cAAc;CAClB,IAAI,YAAY,OAAO;CACvB,IAAI,eAAmC,OAAO;CAC9C,IAAI,cAAc;CAClB,IAAI,cAAc;AAGlB,KAAI,CAAC,IAAI,SAAS,MAAM,IAAI,CAAC,IAAI,WAAW,OAAO,EAAE;AACnD,MAAI,CAAC,OAAO,MAAO,SAAQ,MAAM,+BAA+B,MAAM;AACtE,cAAY;EAEZ,MAAM,WAAW,MAAM,mBAAmB,IAAI;AAC9C,MAAI,CAAC,UAAU;AACb,WAAQ,MAAM,oCAAoC,MAAM;AACxD;;AAGF,gBAAc,SAAS,eAAe;AAEtC,iBAAe,gBAAgB,SAAS;AAGxC,MAAI,SAAS,QACX,eAAc,SAAS;WAEhB,SAAS,QAChB,eAAc,SAAS;WAEhB,SAAS,WAAW;AAC3B,iBAAc,SAAS;AACvB,iBAAc;SAEX;AACH,WAAQ,MAAM,8BAA8B,MAAM;AAClD;;AAGF,MAAI,CAAC,OAAO,MAAO,SAAQ,KAAK,gBAAgB,cAAc,cAAc,cAAc,KAAK;;AAIjG,KAAI,eAAe,YAAY,SAAS,4BAA4B,IAAI,YAAY,WAAW,UAAU,EAAE;AACzG,MAAI,CAAC,OAAO,MAAO,SAAQ,MAAM,uBAAuB,aAAa,YAAY;EAEjF,IAAI;AAGJ,MAAI,YAAY,WAAW,UAAU,EAAE;GACrC,MAAM,QAAQ,YAAY,QAAQ,WAAW,GAAG,CAAC,MAAM,IAAI;GAC3D,MAAM,QAAQ,MAAM;GACpB,MAAM,OAAO,MAAM;GACnB,MAAM,SAAS,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI;GAEvC,MAAM,UAAU,SACZ,yBAAyB,MAAM,GAAG,KAAK,cAAc,OAAO,cAC5D,yBAAyB,MAAM,GAAG,KAAK;GAE3C,MAAM,MAAM,MAAM,MAAM,SAAS,EAC/B,SAAS,EAAE,cAAc,cAAc,EACxC,CAAC,CAAC,YAAY,KAAK;AAEpB,OAAI,CAAC,KAAK,IAAI;AACZ,YAAQ,MAAM,qCAAqC,KAAK,SAAS;AACjE;;GAGF,MAAM,OAAO,MAAM,IAAI,MAAM;AAG7B,OAAI;IACF,MAAM,OAAO,KAAK,MAAM,KAAK;AAC7B,oBAAgB,KAAK,YAAY,KAAK,MAAM,YAAY;WAEpD;AAEJ,oBAAgB;;SAGf;GAEH,MAAM,MAAM,MAAM,MAAM,aAAa,EACnC,SAAS,EAAE,cAAc,cAAc,EACxC,CAAC,CAAC,YAAY,KAAK;AAEpB,OAAI,CAAC,KAAK,IAAI;AACZ,YAAQ,MAAM,2BAA2B,KAAK,SAAS;AACvD;;AAGF,mBAAgB,MAAM,IAAI,MAAM;;AAGlC,MAAI,CAAC,eAAe;AAClB,WAAQ,MAAM,0BAA0B;AACxC;;EAGF,MAAM,OAAO,aAAa;EAG1B,IAAI,eAAe;AACnB,MAAI,OAAO,aAAa,SAAS,OAAO,eAAe;AACrC,IAAC,OAAO,SAAQ,QAAQ,MAAM,cAAc,KAAK,QAAQ,OAAO,cAAc,KAAK;GACnG,MAAM,EAAE,WAAW,iBAAiB,MAAM,aACxC,eACA,MACA,OAAO,gBAAgB,MACvB,OAAO,cACR;AACD,kBAAe;AACf,OAAI,CAAC,OAAO,SAAS,aACnB,SAAQ,QAAQ,cAAc,cAAc,OAAO,KAAK,UAAU,OAAO,QAAQ;YAE1E,CAAC,OAAO,SAAS,CAAC,aACzB,SAAQ,KAAK,0CAA0C;;EAU3D,MAAM,EAAE,WAAW,UAAU,sBAAsB,MAN9B,gBACnB;GAAE;GAAM,SAAS;GAAc;GAAa,EAC5C,aACD,EAGsE;GACrE,QAAQ,OAAO;GACf,QAAQ,OAAO;GAChB,CAAC;AAEF,MAAI,CAAC,OAAO,OAAO;AACjB,WAAQ,QAAQ,oBAAoB,OAAO;AAC3C,WAAQ,KAAK,aAAa,UAAU,KAAI,MAAK,OAAO,GAAG,YAAY,CAAC,KAAK,KAAK,GAAG;AACjF,WAAQ,KAAK,YAAY,MAAM,KAAK,KAAK,GAAG;;AAE9C;;AAGF,KAAI,CAAC,OAAO,MAAO,SAAQ,MAAM,yBAAyB,cAAc;AAGxE,KAAI,OAAO,WAAW;EACpB,MAAM,SAAS,MAAM,cAAc;GACjC,KAAK;GACL,WAAW,OAAO;GAClB,UAAU,OAAO;GACjB,WAAW,OAAO;GAClB,aAAa,OAAO;GACpB,OAAO,OAAO;GACf,EAAE,OAAO,QAAQ,KAAA,KAAa,EAAE,KAAK,SAAS,OAAO,YAAY;GAChE,MAAM,OAAO,UAAU,UAAU,OAAO;AACxC,WAAQ,KAAK,GAAG,KAAK,IAAI,MAAM,IAAI,UAAU;IAC7C;AAEF,MAAI,CAAC,OAAO,OAAO;AACjB,WAAQ,QAAQ,oBAAoB,OAAO,WAAW;AACtD,WAAQ,KAAK,eAAe,OAAO,YAAY;AAC/C,WAAQ,KAAK,iBAAiB,OAAO,gBAAgB;AACrD,WAAQ,KAAK,eAAe,OAAO,SAAS;AAC5C,WAAQ,KAAK,aAAa,OAAO,aAAa;;AAEhD;;CAIF,MAAM,SAAS,MAAM,cAAc;EACjC,KAAK;EACL,WAAW;EACX,UAAU,OAAO;EACjB,WAAW,OAAO;EAClB,aAAa,OAAO;EACpB,OAAO,OAAO;EACf,EAAE,OAAO,QAAQ,KAAA,KAAa,EAAE,KAAK,SAAS,OAAO,YAAY;AAChE,MAAI,UAAU,WAAW,QAAQ,MAAM,EACrC,SAAQ,KAAK,MAAM,MAAM,IAAI,UAAU;GAEzC;CAGF,MAAM,OAAO,aAAa,OAAO;CACjC,MAAM,OAAO,KAAK,KAAK;;mDAE0B,KAAK;;;UAG9C,OAAO,OAAO;oBACJ,OAAO,WAAW;;;;;CAYpC,MAAM,EAAE,WAAW,UAAU,sBAAsB,MAN9B,gBACnB;EAAE;EAAM,SAAS;EAAc;EAAa,EAC5C,KACD,EAGsE;EACrE,QAAQ,OAAO;EACf,QAAQ,OAAO;EAChB,CAAC;AAEF,KAAI,CAAC,OAAO,OAAO;AACjB,UAAQ,QAAQ,oBAAoB,OAAO;AAC3C,UAAQ,KAAK,aAAa,UAAU,KAAI,MAAK,OAAO,GAAG,YAAY,CAAC,KAAK,KAAK,GAAG;AACjF,UAAQ,KAAK,YAAY,MAAM,KAAK,KAAK,GAAG;AAC5C,UAAQ,KAAK,aAAa,OAAO,aAAa;;;AAIlD,QAAQ,KAAK"}
@@ -0,0 +1,14 @@
1
+ import { FetchedDoc, SkillConfig, SkillResult } from "./types.mjs";
2
+
3
+ //#region src/index.d.ts
4
+ /**
5
+ * Generate a skill from a documentation site
6
+ */
7
+ declare function generateSkill(config: SkillConfig, onProgress?: (info: {
8
+ url: string;
9
+ count: number;
10
+ phase: 'fetch' | 'index';
11
+ }) => void): Promise<SkillResult>;
12
+ //#endregion
13
+ export { type FetchedDoc, type SkillConfig, type SkillResult, generateSkill };
14
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;AASA;;iBAAsB,aAAA,CACpB,MAAA,EAAQ,WAAA,EACR,UAAA,IAAc,IAAA;EAAQ,GAAA;EAAa,KAAA;EAAe,KAAA;AAAA,aACjD,OAAA,CAAQ,WAAA"}