skilld 0.0.1 → 0.1.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -88
  3. package/dist/_chunks/config.mjs +20 -0
  4. package/dist/_chunks/config.mjs.map +1 -0
  5. package/dist/_chunks/llm.mjs +877 -0
  6. package/dist/_chunks/llm.mjs.map +1 -0
  7. package/dist/_chunks/releases.mjs +986 -0
  8. package/dist/_chunks/releases.mjs.map +1 -0
  9. package/dist/_chunks/storage.mjs +198 -0
  10. package/dist/_chunks/storage.mjs.map +1 -0
  11. package/dist/_chunks/sync-parallel.mjs +540 -0
  12. package/dist/_chunks/sync-parallel.mjs.map +1 -0
  13. package/dist/_chunks/types.d.mts +87 -0
  14. package/dist/_chunks/types.d.mts.map +1 -0
  15. package/dist/_chunks/utils.d.mts +352 -0
  16. package/dist/_chunks/utils.d.mts.map +1 -0
  17. package/dist/_chunks/version.d.mts +147 -0
  18. package/dist/_chunks/version.d.mts.map +1 -0
  19. package/dist/agent/index.d.mts +205 -0
  20. package/dist/agent/index.d.mts.map +1 -0
  21. package/dist/agent/index.mjs +2 -0
  22. package/dist/cache/index.d.mts +2 -0
  23. package/dist/cache/index.mjs +3 -0
  24. package/dist/cli.mjs +2650 -449
  25. package/dist/cli.mjs.map +1 -1
  26. package/dist/index.d.mts +5 -14
  27. package/dist/index.mjs +7 -181
  28. package/dist/retriv/index.d.mts +12 -0
  29. package/dist/retriv/index.d.mts.map +1 -0
  30. package/dist/retriv/index.mjs +76 -0
  31. package/dist/retriv/index.mjs.map +1 -0
  32. package/dist/sources/index.d.mts +2 -0
  33. package/dist/sources/index.mjs +3 -0
  34. package/dist/types.d.mts +4 -37
  35. package/package.json +39 -13
  36. package/dist/agents.d.mts +0 -56
  37. package/dist/agents.d.mts.map +0 -1
  38. package/dist/agents.mjs +0 -148
  39. package/dist/agents.mjs.map +0 -1
  40. package/dist/index.d.mts.map +0 -1
  41. package/dist/index.mjs.map +0 -1
  42. package/dist/npm.d.mts +0 -48
  43. package/dist/npm.d.mts.map +0 -1
  44. package/dist/npm.mjs +0 -90
  45. package/dist/npm.mjs.map +0 -1
  46. package/dist/split-text.d.mts +0 -24
  47. package/dist/split-text.d.mts.map +0 -1
  48. package/dist/split-text.mjs +0 -87
  49. package/dist/split-text.mjs.map +0 -1
  50. package/dist/types.d.mts.map +0 -1
@@ -0,0 +1,877 @@
1
+ import { homedir } from "node:os";
2
+ import { dirname, join } from "node:path";
3
+ import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "node:fs";
4
+ import { exec, execSync, spawn } from "node:child_process";
5
+ import { globby } from "globby";
6
+ import { readFile } from "node:fs/promises";
7
+ import { findDynamicImports, findStaticImports } from "mlly";
8
+ import { createHash } from "node:crypto";
9
+ const home = homedir();
10
+ const configHome = process.env.XDG_CONFIG_HOME || join(home, ".config");
11
+ const claudeHome = process.env.CLAUDE_CONFIG_DIR || join(home, ".claude");
12
+ const codexHome = process.env.CODEX_HOME || join(home, ".codex");
13
+ const agents = {
14
+ "claude-code": {
15
+ name: "claude-code",
16
+ displayName: "Claude Code",
17
+ skillsDir: ".claude/skills",
18
+ globalSkillsDir: join(claudeHome, "skills"),
19
+ detectInstalled: () => existsSync(claudeHome),
20
+ cli: "claude"
21
+ },
22
+ "cursor": {
23
+ name: "cursor",
24
+ displayName: "Cursor",
25
+ skillsDir: ".cursor/skills",
26
+ globalSkillsDir: join(home, ".cursor/skills"),
27
+ detectInstalled: () => existsSync(join(home, ".cursor"))
28
+ },
29
+ "windsurf": {
30
+ name: "windsurf",
31
+ displayName: "Windsurf",
32
+ skillsDir: ".windsurf/skills",
33
+ globalSkillsDir: join(home, ".codeium/windsurf/skills"),
34
+ detectInstalled: () => existsSync(join(home, ".codeium/windsurf"))
35
+ },
36
+ "cline": {
37
+ name: "cline",
38
+ displayName: "Cline",
39
+ skillsDir: ".cline/skills",
40
+ globalSkillsDir: join(home, ".cline/skills"),
41
+ detectInstalled: () => existsSync(join(home, ".cline"))
42
+ },
43
+ "codex": {
44
+ name: "codex",
45
+ displayName: "Codex",
46
+ skillsDir: ".codex/skills",
47
+ globalSkillsDir: join(codexHome, "skills"),
48
+ detectInstalled: () => existsSync(codexHome),
49
+ cli: "codex"
50
+ },
51
+ "github-copilot": {
52
+ name: "github-copilot",
53
+ displayName: "GitHub Copilot",
54
+ skillsDir: ".github/skills",
55
+ globalSkillsDir: join(home, ".copilot/skills"),
56
+ detectInstalled: () => existsSync(join(home, ".copilot"))
57
+ },
58
+ "gemini-cli": {
59
+ name: "gemini-cli",
60
+ displayName: "Gemini CLI",
61
+ skillsDir: ".gemini/skills",
62
+ globalSkillsDir: join(home, ".gemini/skills"),
63
+ detectInstalled: () => existsSync(join(home, ".gemini")),
64
+ cli: "gemini"
65
+ },
66
+ "goose": {
67
+ name: "goose",
68
+ displayName: "Goose",
69
+ skillsDir: ".goose/skills",
70
+ globalSkillsDir: join(configHome, "goose/skills"),
71
+ detectInstalled: () => existsSync(join(configHome, "goose")),
72
+ cli: "goose"
73
+ },
74
+ "amp": {
75
+ name: "amp",
76
+ displayName: "Amp",
77
+ skillsDir: ".agents/skills",
78
+ globalSkillsDir: join(configHome, "agents/skills"),
79
+ detectInstalled: () => existsSync(join(configHome, "amp"))
80
+ },
81
+ "opencode": {
82
+ name: "opencode",
83
+ displayName: "OpenCode",
84
+ skillsDir: ".opencode/skills",
85
+ globalSkillsDir: join(configHome, "opencode/skills"),
86
+ detectInstalled: () => existsSync(join(configHome, "opencode"))
87
+ },
88
+ "roo": {
89
+ name: "roo",
90
+ displayName: "Roo Code",
91
+ skillsDir: ".roo/skills",
92
+ globalSkillsDir: join(home, ".roo/skills"),
93
+ detectInstalled: () => existsSync(join(home, ".roo"))
94
+ }
95
+ };
96
+ function detectInstalledAgents() {
97
+ return Object.entries(agents).filter(([_, config]) => config.detectInstalled()).map(([type]) => type);
98
+ }
99
+ function detectTargetAgent() {
100
+ if (process.env.CLAUDE_CODE || process.env.CLAUDE_CONFIG_DIR) return "claude-code";
101
+ if (process.env.CURSOR_SESSION || process.env.CURSOR_TRACE_ID) return "cursor";
102
+ if (process.env.WINDSURF_SESSION) return "windsurf";
103
+ if (process.env.CLINE_TASK_ID) return "cline";
104
+ if (process.env.CODEX_HOME || process.env.CODEX_SESSION) return "codex";
105
+ if (process.env.GITHUB_COPILOT_SESSION) return "github-copilot";
106
+ if (process.env.GEMINI_API_KEY && process.env.GEMINI_SESSION) return "gemini-cli";
107
+ if (process.env.GOOSE_SESSION) return "goose";
108
+ if (process.env.AMP_SESSION) return "amp";
109
+ if (process.env.OPENCODE_SESSION) return "opencode";
110
+ if (process.env.ROO_SESSION) return "roo";
111
+ const cwd = process.cwd();
112
+ if (existsSync(join(cwd, ".claude")) || existsSync(join(cwd, "CLAUDE.md"))) return "claude-code";
113
+ if (existsSync(join(cwd, ".cursor")) || existsSync(join(cwd, ".cursorrules"))) return "cursor";
114
+ if (existsSync(join(cwd, ".windsurf")) || existsSync(join(cwd, ".windsurfrules"))) return "windsurf";
115
+ if (existsSync(join(cwd, ".cline"))) return "cline";
116
+ if (existsSync(join(cwd, ".codex"))) return "codex";
117
+ if (existsSync(join(cwd, ".github", "copilot-instructions.md"))) return "github-copilot";
118
+ if (existsSync(join(cwd, ".gemini")) || existsSync(join(cwd, "AGENTS.md"))) return "gemini-cli";
119
+ if (existsSync(join(cwd, ".goose"))) return "goose";
120
+ if (existsSync(join(cwd, ".roo"))) return "roo";
121
+ return null;
122
+ }
123
+ function getAgentVersion(agentType) {
124
+ const agent = agents[agentType];
125
+ if (!agent.cli) return null;
126
+ try {
127
+ const output = execSync(`${agent.cli} --version`, {
128
+ encoding: "utf-8",
129
+ timeout: 3e3,
130
+ stdio: [
131
+ "pipe",
132
+ "pipe",
133
+ "pipe"
134
+ ]
135
+ }).trim();
136
+ const match = output.match(/v?(\d+\.\d+\.\d+(?:-[a-z0-9.]+)?)/);
137
+ return match ? match[1] : output.split("\n")[0];
138
+ } catch {
139
+ return null;
140
+ }
141
+ }
142
+ const NUXT_CONFIG_FILES = [
143
+ "nuxt.config.ts",
144
+ "nuxt.config.js",
145
+ "nuxt.config.mjs"
146
+ ];
147
+ const NUXT_ECOSYSTEM = [
148
+ "vue",
149
+ "nitro",
150
+ "h3"
151
+ ];
152
+ async function findNuxtConfig(cwd) {
153
+ for (const name of NUXT_CONFIG_FILES) {
154
+ const path = join(cwd, name);
155
+ const content = await readFile(path, "utf8").catch(() => null);
156
+ if (content) return {
157
+ path,
158
+ content
159
+ };
160
+ }
161
+ return null;
162
+ }
163
+ function extractModuleStrings(node) {
164
+ if (!node || typeof node !== "object") return [];
165
+ if (node.type === "Property" && !node.computed && node.key?.type === "Identifier" && node.key.name === "modules" && node.value?.type === "ArrayExpression") return node.value.elements.filter((el) => el?.type === "Literal" && typeof el.value === "string").map((el) => el.value);
166
+ const results = [];
167
+ if (Array.isArray(node)) for (const child of node) results.push(...extractModuleStrings(child));
168
+ else for (const key of Object.keys(node)) {
169
+ if (key === "start" || key === "end" || key === "type") continue;
170
+ const val = node[key];
171
+ if (val && typeof val === "object") results.push(...extractModuleStrings(val));
172
+ }
173
+ return results;
174
+ }
175
+ async function detectNuxtModules(cwd) {
176
+ const config = await findNuxtConfig(cwd);
177
+ if (!config) return [];
178
+ const { parseSync } = await import("oxc-parser");
179
+ const modules = extractModuleStrings(parseSync(config.path, config.content).program);
180
+ const seen = /* @__PURE__ */ new Set();
181
+ const packages = [];
182
+ for (const mod of modules) if (!seen.has(mod)) {
183
+ seen.add(mod);
184
+ packages.push({
185
+ name: mod,
186
+ count: 0,
187
+ source: "preset"
188
+ });
189
+ }
190
+ for (const pkg of NUXT_ECOSYSTEM) if (!seen.has(pkg)) {
191
+ seen.add(pkg);
192
+ packages.push({
193
+ name: pkg,
194
+ count: 0,
195
+ source: "preset"
196
+ });
197
+ }
198
+ return packages;
199
+ }
200
+ async function detectPresetPackages(cwd) {
201
+ return detectNuxtModules(cwd);
202
+ }
203
+ const PATTERNS = ["**/*.{ts,js,vue,mjs,cjs,tsx,jsx,mts,cts}"];
204
+ const IGNORE = [
205
+ "**/node_modules/**",
206
+ "**/dist/**",
207
+ "**/.nuxt/**",
208
+ "**/.output/**",
209
+ "**/coverage/**"
210
+ ];
211
+ function addPackage(counts, specifier) {
212
+ if (!specifier || specifier.startsWith(".") || specifier.startsWith("/")) return;
213
+ const name = specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
214
+ if (!isNodeBuiltin(name)) counts.set(name, (counts.get(name) || 0) + 1);
215
+ }
216
+ async function detectImportedPackages(cwd = process.cwd()) {
217
+ try {
218
+ const counts = /* @__PURE__ */ new Map();
219
+ const files = await globby(PATTERNS, {
220
+ cwd,
221
+ ignore: IGNORE,
222
+ gitignore: true,
223
+ absolute: true
224
+ });
225
+ await Promise.all(files.map(async (file) => {
226
+ const content = await readFile(file, "utf8");
227
+ for (const imp of findStaticImports(content)) addPackage(counts, imp.specifier);
228
+ for (const imp of findDynamicImports(content)) {
229
+ const match = imp.expression.match(/^['"]([^'"]+)['"]$/);
230
+ if (match) addPackage(counts, match[1]);
231
+ }
232
+ }));
233
+ const packages = [...counts.entries()].map(([name, count]) => ({
234
+ name,
235
+ count,
236
+ source: "import"
237
+ })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
238
+ const presets = await detectPresetPackages(cwd);
239
+ const importNames = new Set(packages.map((p) => p.name));
240
+ for (const preset of presets) if (!importNames.has(preset.name)) packages.push(preset);
241
+ return { packages };
242
+ } catch (err) {
243
+ return {
244
+ packages: [],
245
+ error: String(err)
246
+ };
247
+ }
248
+ }
249
+ const NODE_BUILTINS = new Set([
250
+ "assert",
251
+ "buffer",
252
+ "child_process",
253
+ "cluster",
254
+ "console",
255
+ "constants",
256
+ "crypto",
257
+ "dgram",
258
+ "dns",
259
+ "domain",
260
+ "events",
261
+ "fs",
262
+ "http",
263
+ "https",
264
+ "module",
265
+ "net",
266
+ "os",
267
+ "path",
268
+ "perf_hooks",
269
+ "process",
270
+ "punycode",
271
+ "querystring",
272
+ "readline",
273
+ "repl",
274
+ "stream",
275
+ "string_decoder",
276
+ "sys",
277
+ "timers",
278
+ "tls",
279
+ "tty",
280
+ "url",
281
+ "util",
282
+ "v8",
283
+ "vm",
284
+ "wasi",
285
+ "worker_threads",
286
+ "zlib"
287
+ ]);
288
+ function isNodeBuiltin(pkg) {
289
+ const base = pkg.startsWith("node:") ? pkg.slice(5) : pkg;
290
+ return NODE_BUILTINS.has(base.split("/")[0]);
291
+ }
292
+ function sanitizeName(name) {
293
+ return name.toLowerCase().replace(/[^a-z0-9._]+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "").slice(0, 255) || "unnamed-skill";
294
+ }
295
+ function installSkillForAgents(skillName, skillContent, options = {}) {
296
+ const isGlobal = options.global ?? false;
297
+ const cwd = options.cwd || process.cwd();
298
+ const sanitized = sanitizeName(skillName);
299
+ const targetAgents = options.agents || detectInstalledAgents();
300
+ const installed = [];
301
+ const paths = [];
302
+ for (const agentType of targetAgents) {
303
+ const agent = agents[agentType];
304
+ if (isGlobal && !agent.globalSkillsDir) continue;
305
+ const skillDir = join(isGlobal ? agent.globalSkillsDir : join(cwd, agent.skillsDir), sanitized);
306
+ mkdirSync(skillDir, { recursive: true });
307
+ writeFileSync(join(skillDir, "_SKILL.md"), skillContent);
308
+ if (options.files) for (const [filename, content] of Object.entries(options.files)) writeFileSync(join(skillDir, filename), content);
309
+ installed.push(agentType);
310
+ paths.push(skillDir);
311
+ }
312
+ return {
313
+ installed,
314
+ paths
315
+ };
316
+ }
317
+ function formatDocTree(files) {
318
+ const dirs = /* @__PURE__ */ new Map();
319
+ for (const f of files) {
320
+ const dir = dirname(f);
321
+ dirs.set(dir, (dirs.get(dir) || 0) + 1);
322
+ }
323
+ return [...dirs.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([dir, count]) => `- \`${dir}/\` (${count} .md files)`).join("\n");
324
+ }
325
+ function generateImportantBlock({ packageName, hasGithub, hasReleases, hasChangelog, docsType, hasShippedDocs, skillDir }) {
326
+ const searchDesc = hasGithub ? "Docs + GitHub" : "Docs";
327
+ const searchCmd = `\`Bash 'npx skilld ${packageName} -q "<query>"'\``;
328
+ const docsPath = hasShippedDocs ? `\`${skillDir}/.skilld/pkg/docs/\` or \`${skillDir}/.skilld/pkg/README.md\`` : docsType === "llms.txt" ? `\`${skillDir}/.skilld/docs/llms.txt\`` : docsType === "readme" ? `\`${skillDir}/.skilld/pkg/README.md\`` : `\`${skillDir}/.skilld/docs/\``;
329
+ const rows = [
330
+ [searchDesc, searchCmd],
331
+ ["Docs", docsPath],
332
+ ["Package", `\`${skillDir}/.skilld/pkg/\``]
333
+ ];
334
+ if (hasGithub) rows.push(["GitHub", `\`${skillDir}/.skilld/github/\``]);
335
+ if (hasChangelog) rows.push(["Changelog", `\`${skillDir}/.skilld/pkg/${hasChangelog}\``]);
336
+ if (hasReleases) rows.push(["Releases", `\`${skillDir}/.skilld/releases/\``]);
337
+ return `**IMPORTANT:** Use these references\n\n${[
338
+ "| Resource | Command |",
339
+ "|----------|---------|",
340
+ ...rows.map(([desc, cmd]) => `| ${desc} | ${cmd} |`)
341
+ ].join("\n")}`;
342
+ }
343
+ function buildSkillPrompt(opts) {
344
+ const { packageName, skillDir, version, hasGithub, hasReleases, hasChangelog, docFiles, docsType = "docs", hasShippedDocs = false, sections, customPrompt } = opts;
345
+ const hasBestPractices = !sections || sections.includes("best-practices");
346
+ const hasApi = !sections || sections.includes("api");
347
+ const hasCustom = sections?.includes("custom") && customPrompt;
348
+ const versionContext = version ? ` v${version}` : "";
349
+ const docsSection = docFiles?.length ? `**Documentation** (use Read tool to explore):\n${formatDocTree(docFiles)}` : "";
350
+ const importantBlock = generateImportantBlock({
351
+ packageName,
352
+ hasGithub,
353
+ hasReleases,
354
+ hasChangelog,
355
+ docsType,
356
+ hasShippedDocs,
357
+ skillDir
358
+ });
359
+ const taskParts = [];
360
+ if (hasBestPractices) taskParts.push(`Find novel best practices from the references. Every item must link to its source.
361
+
362
+ Look for: tip, warning, best practice, avoid, pitfall, note, important.`);
363
+ if (hasApi) taskParts.push(`**Generate an API reference section.** List the package's exported functions/composables grouped by documentation page or category. Each function gets a one-liner description. Link group headings to the source doc URL when available.`);
364
+ if (hasCustom) taskParts.push(`**Custom instructions from the user:**\n${customPrompt}`);
365
+ const formatParts = [];
366
+ if (hasBestPractices) formatParts.push(`\`\`\`
367
+ [✅ descriptive title](./.skilld/path/to/source.md)
368
+ \`\`\`ts
369
+ code example (1-3 lines)
370
+ \`\`\`
371
+
372
+ [❌ pitfall title](./.skilld/path/to/source.md#section)
373
+ \`\`\`ts
374
+ wrong // correct way
375
+ \`\`\`
376
+ \`\`\``);
377
+ if (hasApi) formatParts.push(`API reference format${hasBestPractices ? " (place at end, after best practices)" : ""}:
378
+ \`\`\`
379
+ ## API
380
+
381
+ ### [Category Name](./.skilld/docs/category.md)
382
+ - functionName — one-line description
383
+ - anotherFn — one-line description
384
+ \`\`\`
385
+
386
+ Link group headings to the local \`./.skilld/\` source file.
387
+
388
+ For single-page-docs packages, use a flat list without grouping. Skip the API section entirely for packages with fewer than 3 exports.`);
389
+ const rules = [];
390
+ if (hasBestPractices) rules.push("- **5-10 best practice items**, MAX 150 lines for best practices");
391
+ if (hasApi) rules.push("- **API section:** list all public exports, grouped by doc page, MAX 80 lines");
392
+ rules.push("- Link to exact source file where you found info", "- TypeScript only, Vue uses `<script setup lang=\"ts\">`", "- Imperative voice (\"Use X\" not \"You should use X\")", "- **NEVER fetch external URLs.** All information is in the local `./.skilld/` directory. Use Read/Glob only.");
393
+ return `Generate SKILL.md body for "${packageName}"${versionContext}.
394
+
395
+ ${importantBlock}
396
+ ${docsSection ? `${docsSection}\n` : ""}
397
+
398
+ ## Skill Quality Principles
399
+
400
+ The context window is a shared resource. Skills share it with system prompt, conversation history, other skills, and the user request.
401
+
402
+ - **Only add what Claude doesn't know.** Claude already knows general programming, popular APIs, common patterns. Challenge every line: "Does this justify its token cost?"
403
+ - **Prefer concise examples over verbose explanations.** A 2-line code example beats a paragraph.
404
+ - **Skip:** API signatures, installation steps, tutorials, marketing, general programming knowledge, anything in the package README that's obvious
405
+ - **Include:** Non-obvious gotchas, surprising defaults, version-specific breaking changes, pitfalls from issues, patterns that differ from what Claude would assume
406
+
407
+ ## Task
408
+
409
+ ${taskParts.join("\n\n")}
410
+
411
+ ## Format
412
+
413
+ ${formatParts.join("\n\n")}
414
+
415
+ ## Rules
416
+
417
+ ${rules.join("\n")}
418
+
419
+ ## Output
420
+
421
+ Write the body content to \`${skillDir}/_SKILL.md\` using the Write tool.
422
+ Do NOT output the content to stdout. Write it to the file only.
423
+ `;
424
+ }
425
+ const FILE_PATTERN_MAP = {
426
+ "vue": ["*.vue"],
427
+ "svelte": ["*.svelte"],
428
+ "astro": ["*.astro"],
429
+ "solid-js": ["*.jsx", "*.tsx"],
430
+ "qwik": ["*.tsx"],
431
+ "marko": ["*.marko"],
432
+ "riot": ["*.riot"],
433
+ "typescript": [
434
+ "*.ts",
435
+ "*.tsx",
436
+ "*.mts",
437
+ "*.cts"
438
+ ],
439
+ "coffeescript": ["*.coffee"],
440
+ "livescript": ["*.ls"],
441
+ "elm": ["*.elm"],
442
+ "sass": ["*.scss", "*.sass"],
443
+ "less": ["*.less"],
444
+ "stylus": ["*.styl"],
445
+ "postcss": ["*.css", "*.pcss"],
446
+ "pug": ["*.pug"],
447
+ "ejs": ["*.ejs"],
448
+ "handlebars": ["*.hbs", "*.handlebars"],
449
+ "mustache": ["*.mustache"],
450
+ "nunjucks": ["*.njk"],
451
+ "liquid": ["*.liquid"],
452
+ "yaml": ["*.yaml", "*.yml"],
453
+ "js-yaml": ["*.yaml", "*.yml"],
454
+ "toml": ["*.toml"],
455
+ "@iarna/toml": ["*.toml"],
456
+ "json5": ["*.json5"],
457
+ "jsonc-parser": ["*.jsonc"],
458
+ "markdown-it": ["*.md"],
459
+ "marked": ["*.md"],
460
+ "remark": ["*.md", "*.mdx"],
461
+ "@mdx-js/mdx": ["*.mdx"],
462
+ "graphql": ["*.graphql", "*.gql"],
463
+ "graphql-tag": ["*.graphql", "*.gql"],
464
+ "@graphql-codegen/cli": ["*.graphql", "*.gql"],
465
+ "prisma": ["*.prisma"],
466
+ "@prisma/client": ["*.prisma"],
467
+ "wasm-pack": ["*.wasm"]
468
+ };
469
+ function generateSkillMd(opts) {
470
+ const header = generatePackageHeader(opts);
471
+ const refs = generateReferencesBlock(opts);
472
+ const content = opts.body ? `${header}\n\n${refs}${opts.body}` : `${header}\n\n${refs}`;
473
+ return `${generateFrontmatter(opts)}${content}
474
+ ${generateFooter(opts.relatedSkills)}`;
475
+ }
476
+ function formatRelativeDate(isoDate) {
477
+ const date = new Date(isoDate);
478
+ const diffMs = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
479
+ const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
480
+ if (diffDays === 0) return "today";
481
+ if (diffDays === 1) return "yesterday";
482
+ if (diffDays < 7) return `${diffDays} days ago`;
483
+ if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
484
+ if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`;
485
+ return `${Math.floor(diffDays / 365)} years ago`;
486
+ }
487
+ function generatePackageHeader({ name, description, version, releasedAt, dependencies, distTags, hasGithub }) {
488
+ const lines = [`# ${name}`];
489
+ if (description) lines.push("", `> ${description}`);
490
+ if (version) {
491
+ const relativeDate = releasedAt ? formatRelativeDate(releasedAt) : "";
492
+ const versionStr = relativeDate ? `${version} (${relativeDate})` : version;
493
+ lines.push("", `**Version:** ${versionStr}`);
494
+ }
495
+ if (dependencies && Object.keys(dependencies).length > 0) {
496
+ const deps = Object.entries(dependencies).map(([n, v]) => `${n}@${v}`).join(", ");
497
+ lines.push(`**Deps:** ${deps}`);
498
+ }
499
+ if (distTags && Object.keys(distTags).length > 0) {
500
+ const tags = Object.entries(distTags).map(([tag, info]) => {
501
+ const relDate = info.releasedAt ? ` (${formatRelativeDate(info.releasedAt)})` : "";
502
+ return `${tag}: ${info.version}${relDate}`;
503
+ }).join(", ");
504
+ lines.push(`**Tags:** ${tags}`);
505
+ }
506
+ if (hasGithub) lines.push(`**GitHub:** \`./.skilld/github/\``);
507
+ return lines.join("\n");
508
+ }
509
+ function generateFrontmatter({ name, version, globs }) {
510
+ const patterns = globs ?? FILE_PATTERN_MAP[name];
511
+ const description = patterns?.length ? `Load skill when working with ${patterns.join(", ")} files or importing from "${name}".` : `Load skill when using anything from the package "${name}".`;
512
+ const lines = [
513
+ "---",
514
+ `name: ${sanitizeName(name)}-skilld`,
515
+ `description: ${description}`
516
+ ];
517
+ if (patterns?.length) lines.push(`globs: ${JSON.stringify(patterns)}`);
518
+ if (version) lines.push(`version: "${version}"`);
519
+ lines.push("---", "", "");
520
+ return lines.join("\n");
521
+ }
522
+ function generateReferencesBlock({ name, hasGithub, hasReleases, docsType = "docs", hasShippedDocs = false, pkgFiles = [] }) {
523
+ const lines = [
524
+ "## References",
525
+ "",
526
+ `IMPORTANT: Search all references (semantic and keyword) using \`skilld ${name} -q "<query>"\`.`,
527
+ ""
528
+ ];
529
+ const fileList = pkgFiles.length ? ` — ${pkgFiles.map((f) => `\`${f}\``).join(", ")}` : "";
530
+ lines.push(`**Package:** \`./.skilld/pkg/\`${fileList}`);
531
+ if (hasShippedDocs) lines.push(`**Docs:** \`./.skilld/pkg/docs/\``);
532
+ else if (docsType === "llms.txt") lines.push(`**Docs:** \`./.skilld/docs/llms.txt\``);
533
+ else if (docsType === "docs") lines.push(`**Docs:** \`./.skilld/docs/\``);
534
+ if (hasGithub) lines.push(`**GitHub:** \`./.skilld/github/\``);
535
+ if (hasReleases) lines.push(`**Releases:** \`./.skilld/releases/\``);
536
+ lines.push("");
537
+ return lines.join("\n");
538
+ }
539
+ function generateFooter(relatedSkills) {
540
+ if (relatedSkills.length === 0) return "";
541
+ return `\nRelated: ${relatedSkills.join(", ")}\n`;
542
+ }
543
+ const CACHE_DIR = join(homedir(), ".skilld", "llm-cache");
544
+ const CLI_MODELS = {
545
+ "opus": {
546
+ cli: "claude",
547
+ model: "opus",
548
+ name: "Opus 4.5",
549
+ hint: "Most capable",
550
+ agentId: "claude-code"
551
+ },
552
+ "sonnet": {
553
+ cli: "claude",
554
+ model: "sonnet",
555
+ name: "Sonnet 4.5",
556
+ hint: "Balanced",
557
+ recommended: true,
558
+ agentId: "claude-code"
559
+ },
560
+ "haiku": {
561
+ cli: "claude",
562
+ model: "haiku",
563
+ name: "Haiku 4.5",
564
+ hint: "Fastest",
565
+ agentId: "claude-code"
566
+ },
567
+ "gemini-2.5-pro": {
568
+ cli: "gemini",
569
+ model: "gemini-2.5-pro",
570
+ name: "Gemini 2.5 Pro",
571
+ hint: "Most capable",
572
+ agentId: "gemini-cli"
573
+ },
574
+ "gemini-2.5-flash": {
575
+ cli: "gemini",
576
+ model: "gemini-2.5-flash",
577
+ name: "Gemini 2.5 Flash",
578
+ hint: "Balanced",
579
+ agentId: "gemini-cli"
580
+ },
581
+ "gemini-2.5-flash-lite": {
582
+ cli: "gemini",
583
+ model: "gemini-2.5-flash-lite",
584
+ name: "Gemini 2.5 Flash Lite",
585
+ hint: "Fastest",
586
+ agentId: "gemini-cli"
587
+ },
588
+ "gemini-3-pro": {
589
+ cli: "gemini",
590
+ model: "gemini-3-pro-preview",
591
+ name: "Gemini 3 Pro",
592
+ hint: "Most capable",
593
+ agentId: "gemini-cli"
594
+ },
595
+ "gemini-3-flash": {
596
+ cli: "gemini",
597
+ model: "gemini-3-flash-preview",
598
+ name: "Gemini 3 Flash",
599
+ hint: "Balanced",
600
+ agentId: "gemini-cli"
601
+ }
602
+ };
603
+ function getModelName(id) {
604
+ return CLI_MODELS[id]?.name ?? id;
605
+ }
606
+ async function getAvailableModels() {
607
+ const { promisify } = await import("node:util");
608
+ const execAsync = promisify(exec);
609
+ const agentsWithCli = detectInstalledAgents().filter((id) => agents[id].cli);
610
+ const cliChecks = await Promise.all(agentsWithCli.map(async (agentId) => {
611
+ const cli = agents[agentId].cli;
612
+ try {
613
+ await execAsync(`which ${cli}`);
614
+ return agentId;
615
+ } catch {
616
+ return null;
617
+ }
618
+ }));
619
+ const availableAgentIds = new Set(cliChecks.filter((id) => id != null));
620
+ return Object.entries(CLI_MODELS).filter(([_, config]) => availableAgentIds.has(config.agentId)).map(([id, config]) => ({
621
+ id,
622
+ name: config.name,
623
+ hint: config.hint,
624
+ recommended: config.recommended,
625
+ agentId: config.agentId,
626
+ agentName: agents[config.agentId]?.displayName ?? config.agentId
627
+ }));
628
+ }
629
+ function resolveReferenceDirs(skillDir) {
630
+ const refsDir = join(skillDir, ".skilld");
631
+ if (!existsSync(refsDir)) return [];
632
+ return readdirSync(refsDir).map((entry) => join(refsDir, entry)).filter((p) => lstatSync(p).isSymbolicLink()).map((p) => realpathSync(p));
633
+ }
634
+ function buildCliArgs(cli, model, skillDir) {
635
+ const symlinkDirs = resolveReferenceDirs(skillDir);
636
+ if (cli === "claude") return [
637
+ "-p",
638
+ "--model",
639
+ model,
640
+ "--output-format",
641
+ "stream-json",
642
+ "--verbose",
643
+ "--include-partial-messages",
644
+ "--allowedTools",
645
+ "Read Glob Grep Write",
646
+ "--add-dir",
647
+ skillDir,
648
+ ...symlinkDirs.flatMap((d) => ["--add-dir", d]),
649
+ "--dangerously-skip-permissions",
650
+ "--no-session-persistence"
651
+ ];
652
+ return [
653
+ "-o",
654
+ "stream-json",
655
+ "-m",
656
+ model,
657
+ "-y",
658
+ "--include-directories",
659
+ skillDir,
660
+ ...symlinkDirs.flatMap((d) => ["--include-directories", d])
661
+ ];
662
+ }
663
+ function hashPrompt(prompt, model) {
664
+ return createHash("sha256").update(`exec:${model}:${prompt}`).digest("hex").slice(0, 16);
665
+ }
666
+ function getCached(prompt, model, maxAge = 10080 * 60 * 1e3) {
667
+ const path = join(CACHE_DIR, `${hashPrompt(prompt, model)}.json`);
668
+ if (!existsSync(path)) return null;
669
+ try {
670
+ const { text, timestamp } = JSON.parse(readFileSync(path, "utf-8"));
671
+ return Date.now() - timestamp > maxAge ? null : text;
672
+ } catch {
673
+ return null;
674
+ }
675
+ }
676
+ function setCache(prompt, model, text) {
677
+ mkdirSync(CACHE_DIR, { recursive: true });
678
+ writeFileSync(join(CACHE_DIR, `${hashPrompt(prompt, model)}.json`), JSON.stringify({
679
+ text,
680
+ model,
681
+ timestamp: Date.now()
682
+ }));
683
+ }
684
+ function parseClaudeLine(line) {
685
+ try {
686
+ const obj = JSON.parse(line);
687
+ if (obj.type === "stream_event") {
688
+ const evt = obj.event;
689
+ if (!evt) return {};
690
+ if (evt.type === "content_block_delta" && evt.delta?.type === "text_delta") return { textDelta: evt.delta.text };
691
+ if (evt.type === "content_block_start" && evt.content_block?.type === "tool_use") return { toolName: evt.content_block.name };
692
+ return {};
693
+ }
694
+ if (obj.type === "assistant" && obj.message?.content) {
695
+ const content = obj.message.content;
696
+ const tools = content.filter((c) => c.type === "tool_use");
697
+ if (tools.length) {
698
+ const names = tools.map((t) => t.name);
699
+ const hint = tools.map((t) => {
700
+ const input = t.input || {};
701
+ return input.file_path || input.path || input.pattern || input.query || input.command || "";
702
+ }).filter(Boolean).join(", ");
703
+ return {
704
+ toolName: names.join(", "),
705
+ toolHint: hint || void 0
706
+ };
707
+ }
708
+ const text = content.filter((c) => c.type === "text").map((c) => c.text).join("");
709
+ if (text) return { fullText: text };
710
+ }
711
+ if (obj.type === "result") {
712
+ const u = obj.usage;
713
+ return {
714
+ done: true,
715
+ usage: u ? {
716
+ input: u.input_tokens ?? u.inputTokens ?? 0,
717
+ output: u.output_tokens ?? u.outputTokens ?? 0
718
+ } : void 0,
719
+ cost: obj.total_cost_usd,
720
+ turns: obj.num_turns
721
+ };
722
+ }
723
+ } catch {}
724
+ return {};
725
+ }
726
+ function parseGeminiLine(line) {
727
+ try {
728
+ const obj = JSON.parse(line);
729
+ if (obj.type === "message" && obj.role === "assistant" && obj.content) return obj.delta ? { textDelta: obj.content } : { fullText: obj.content };
730
+ if (obj.type === "tool_use" || obj.type === "tool_call") return { toolName: obj.name || obj.tool || "tool" };
731
+ if (obj.type === "result") {
732
+ const s = obj.stats;
733
+ return {
734
+ done: true,
735
+ usage: s ? {
736
+ input: s.input_tokens ?? s.input ?? 0,
737
+ output: s.output_tokens ?? s.output ?? 0
738
+ } : void 0,
739
+ turns: s?.tool_calls
740
+ };
741
+ }
742
+ } catch {}
743
+ return {};
744
+ }
745
+ async function optimizeDocs(opts) {
746
+ const { packageName, skillDir, model = "sonnet", version, hasGithub, docFiles, onProgress, timeout = 18e4, noCache, sections, customPrompt } = opts;
747
+ const prompt = buildSkillPrompt({
748
+ packageName,
749
+ skillDir,
750
+ version,
751
+ hasGithub,
752
+ docFiles,
753
+ sections,
754
+ customPrompt
755
+ });
756
+ if (!noCache) {
757
+ const cached = getCached(prompt, model);
758
+ if (cached) {
759
+ onProgress?.({
760
+ chunk: "[cached]",
761
+ type: "text",
762
+ text: cached,
763
+ reasoning: ""
764
+ });
765
+ return {
766
+ optimized: cached,
767
+ wasOptimized: true,
768
+ finishReason: "cached"
769
+ };
770
+ }
771
+ }
772
+ const cliConfig = CLI_MODELS[model];
773
+ if (!cliConfig) return {
774
+ optimized: "",
775
+ wasOptimized: false,
776
+ error: `No CLI mapping for model: ${model}`
777
+ };
778
+ const { cli, model: cliModel } = cliConfig;
779
+ const args = buildCliArgs(cli, cliModel, skillDir);
780
+ const parseLine = cli === "claude" ? parseClaudeLine : parseGeminiLine;
781
+ writeFileSync(join(skillDir, "PROMPT.md"), prompt);
782
+ const outputPath = join(skillDir, "__SKILL.md");
783
+ return new Promise((resolve) => {
784
+ const proc = spawn(cli, args, {
785
+ stdio: [
786
+ "pipe",
787
+ "pipe",
788
+ "pipe"
789
+ ],
790
+ timeout,
791
+ env: {
792
+ ...process.env,
793
+ NO_COLOR: "1"
794
+ }
795
+ });
796
+ let buffer = "";
797
+ let usage;
798
+ let cost;
799
+ onProgress?.({
800
+ chunk: "[starting...]",
801
+ type: "reasoning",
802
+ text: "",
803
+ reasoning: ""
804
+ });
805
+ proc.stdin.write(prompt);
806
+ proc.stdin.end();
807
+ proc.stdout.on("data", (chunk) => {
808
+ buffer += chunk.toString();
809
+ const lines = buffer.split("\n");
810
+ buffer = lines.pop() || "";
811
+ for (const line of lines) {
812
+ if (!line.trim()) continue;
813
+ const evt = parseLine(line);
814
+ if (evt.toolName) {
815
+ const hint = evt.toolHint ? `[${evt.toolName}: ${shortenPath(evt.toolHint)}]` : `[${evt.toolName}]`;
816
+ onProgress?.({
817
+ chunk: hint,
818
+ type: "reasoning",
819
+ text: "",
820
+ reasoning: hint
821
+ });
822
+ }
823
+ if (evt.usage) usage = evt.usage;
824
+ if (evt.cost != null) cost = evt.cost;
825
+ }
826
+ });
827
+ let stderr = "";
828
+ proc.stderr.on("data", (chunk) => {
829
+ stderr += chunk.toString();
830
+ });
831
+ proc.on("close", (code) => {
832
+ if (buffer.trim()) {
833
+ const evt = parseLine(buffer);
834
+ if (evt.usage) usage = evt.usage;
835
+ if (evt.cost != null) cost = evt.cost;
836
+ }
837
+ const optimized = existsSync(outputPath) ? readFileSync(outputPath, "utf-8").trim() : "";
838
+ if (!optimized && code !== 0) {
839
+ resolve({
840
+ optimized: "",
841
+ wasOptimized: false,
842
+ error: stderr.trim() || `CLI exited with code ${code}`
843
+ });
844
+ return;
845
+ }
846
+ if (!noCache && optimized) setCache(prompt, model, optimized);
847
+ const usageResult = usage ? {
848
+ inputTokens: usage.input,
849
+ outputTokens: usage.output,
850
+ totalTokens: usage.input + usage.output
851
+ } : void 0;
852
+ resolve({
853
+ optimized,
854
+ wasOptimized: !!optimized,
855
+ finishReason: code === 0 ? "stop" : "error",
856
+ usage: usageResult,
857
+ cost
858
+ });
859
+ });
860
+ proc.on("error", (err) => {
861
+ resolve({
862
+ optimized: "",
863
+ wasOptimized: false,
864
+ error: err.message
865
+ });
866
+ });
867
+ });
868
+ }
869
+ function shortenPath(p) {
870
+ const refIdx = p.indexOf(".skilld/");
871
+ if (refIdx !== -1) return p.slice(refIdx + 8);
872
+ const parts = p.split("/");
873
+ return parts.length > 2 ? `.../${parts.slice(-2).join("/")}` : p;
874
+ }
875
+ export { FILE_PATTERN_MAP as a, sanitizeName as c, detectTargetAgent as d, getAgentVersion as f, generateSkillMd as i, detectImportedPackages as l, getModelName as n, buildSkillPrompt as o, agents as p, optimizeDocs as r, installSkillForAgents as s, getAvailableModels as t, detectInstalledAgents as u };
876
+
877
+ //# sourceMappingURL=llm.mjs.map