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/README.md +171 -0
- package/dist/agents.d.mts +56 -0
- package/dist/agents.d.mts.map +1 -0
- package/dist/agents.mjs +148 -0
- package/dist/agents.mjs.map +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +503 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +14 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +181 -0
- package/dist/index.mjs.map +1 -0
- package/dist/npm.d.mts +48 -0
- package/dist/npm.d.mts.map +1 -0
- package/dist/npm.mjs +90 -0
- package/dist/npm.mjs.map +1 -0
- package/dist/split-text.d.mts +24 -0
- package/dist/split-text.d.mts.map +1 -0
- package/dist/split-text.mjs +87 -0
- package/dist/split-text.mjs.map +1 -0
- package/dist/types.d.mts +37 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +1 -0
- package/package.json +52 -0
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
|
package/dist/cli.mjs.map
ADDED
|
@@ -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"}
|
package/dist/index.d.mts
ADDED
|
@@ -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"}
|