skilld 0.15.2 → 0.15.4

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 (72) hide show
  1. package/README.md +1 -0
  2. package/dist/_chunks/{detect-imports.mjs → agent.mjs} +42 -6
  3. package/dist/_chunks/agent.mjs.map +1 -0
  4. package/dist/_chunks/{storage.mjs → cache.mjs} +81 -1
  5. package/dist/_chunks/cache.mjs.map +1 -0
  6. package/dist/_chunks/cache2.mjs +6 -0
  7. package/dist/_chunks/config.mjs +23 -0
  8. package/dist/_chunks/config.mjs.map +1 -1
  9. package/dist/_chunks/config2.mjs +12 -0
  10. package/dist/_chunks/{embedding-cache2.mjs → embedding-cache.mjs} +1 -1
  11. package/dist/_chunks/embedding-cache.mjs.map +1 -0
  12. package/dist/_chunks/formatting.mjs +86 -0
  13. package/dist/_chunks/formatting.mjs.map +1 -0
  14. package/dist/_chunks/{version.d.mts → index.d.mts} +1 -1
  15. package/dist/_chunks/index.d.mts.map +1 -0
  16. package/dist/_chunks/{utils.d.mts → index2.d.mts} +1 -1
  17. package/dist/_chunks/index2.d.mts.map +1 -0
  18. package/dist/_chunks/install.mjs +15 -0
  19. package/dist/_chunks/list.mjs +13 -0
  20. package/dist/_chunks/markdown.mjs +7 -0
  21. package/dist/_chunks/markdown.mjs.map +1 -1
  22. package/dist/_chunks/{pool2.mjs → pool.mjs} +1 -1
  23. package/dist/_chunks/pool.mjs.map +1 -0
  24. package/dist/_chunks/prompts.mjs +232 -0
  25. package/dist/_chunks/prompts.mjs.map +1 -1
  26. package/dist/_chunks/remove.mjs +12 -0
  27. package/dist/_chunks/sanitize.mjs +71 -0
  28. package/dist/_chunks/sanitize.mjs.map +1 -1
  29. package/dist/_chunks/search-interactive.mjs +14 -0
  30. package/dist/_chunks/search-interactive2.mjs +236 -0
  31. package/dist/_chunks/search-interactive2.mjs.map +1 -0
  32. package/dist/_chunks/search.mjs +171 -0
  33. package/dist/_chunks/search.mjs.map +1 -0
  34. package/dist/_chunks/search2.mjs +13 -0
  35. package/dist/_chunks/shared.mjs +4 -0
  36. package/dist/_chunks/shared.mjs.map +1 -1
  37. package/dist/_chunks/skills.mjs +552 -0
  38. package/dist/_chunks/skills.mjs.map +1 -0
  39. package/dist/_chunks/{npm.mjs → sources.mjs} +401 -4
  40. package/dist/_chunks/sources.mjs.map +1 -0
  41. package/dist/_chunks/status.mjs +13 -0
  42. package/dist/_chunks/sync.mjs +2026 -0
  43. package/dist/_chunks/sync.mjs.map +1 -0
  44. package/dist/_chunks/sync2.mjs +14 -0
  45. package/dist/_chunks/uninstall.mjs +15 -0
  46. package/dist/_chunks/validate.mjs +3 -0
  47. package/dist/_chunks/validate.mjs.map +1 -1
  48. package/dist/_chunks/yaml.mjs +19 -0
  49. package/dist/_chunks/yaml.mjs.map +1 -1
  50. package/dist/agent/index.d.mts +1 -1
  51. package/dist/agent/index.mjs +4 -3
  52. package/dist/cache/index.d.mts +2 -2
  53. package/dist/cache/index.mjs +2 -1
  54. package/dist/cli.mjs +173 -3082
  55. package/dist/cli.mjs.map +1 -1
  56. package/dist/index.d.mts +2 -3
  57. package/dist/index.mjs +4 -4
  58. package/dist/retriv/index.d.mts.map +1 -1
  59. package/dist/retriv/index.mjs +26 -5
  60. package/dist/retriv/index.mjs.map +1 -1
  61. package/dist/retriv/worker.mjs +3 -3
  62. package/dist/sources/index.d.mts +2 -2
  63. package/dist/sources/index.mjs +2 -1
  64. package/dist/types.d.mts +2 -3
  65. package/package.json +10 -10
  66. package/dist/_chunks/detect-imports.mjs.map +0 -1
  67. package/dist/_chunks/embedding-cache2.mjs.map +0 -1
  68. package/dist/_chunks/npm.mjs.map +0 -1
  69. package/dist/_chunks/pool2.mjs.map +0 -1
  70. package/dist/_chunks/storage.mjs.map +0 -1
  71. package/dist/_chunks/utils.d.mts.map +0 -1
  72. package/dist/_chunks/version.d.mts.map +0 -1
@@ -6,6 +6,7 @@ import { dirname, join, relative } from "pathe";
6
6
  import { existsSync, lstatSync, mkdirSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
7
7
  import { spawnSync } from "node:child_process";
8
8
  import { isWindows } from "std-env";
9
+ /** Common frontmatter fields from agentskills.io spec */
9
10
  const SPEC_FRONTMATTER = {
10
11
  "name": {
11
12
  name: "name",
@@ -41,6 +42,7 @@ const SPEC_FRONTMATTER = {
41
42
  description: "Space-delimited pre-approved tools (experimental)"
42
43
  }
43
44
  };
45
+ /** Shared defaults for all agent targets */
44
46
  const BASE_DEFAULTS = {
45
47
  skillFilename: "SKILL.md",
46
48
  nameMatchesDir: true,
@@ -49,6 +51,7 @@ const BASE_DEFAULTS = {
49
51
  extensions: [],
50
52
  notes: []
51
53
  };
54
+ /** Define an agent target with shared defaults applied */
52
55
  function defineTarget(target) {
53
56
  return {
54
57
  ...BASE_DEFAULTS,
@@ -56,6 +59,18 @@ function defineTarget(target) {
56
59
  };
57
60
  }
58
61
  const configHome$2 = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
62
+ /**
63
+ * Amp (Sourcegraph)
64
+ *
65
+ * Uses .agents/skills/ as primary project path. Also reads .claude/skills/.
66
+ * Skills can bundle MCP servers via mcp.json in the skill directory.
67
+ *
68
+ * AGENTS.md (or AGENT.md / CLAUDE.md fallback) for general instructions,
69
+ * supports @-mentions to reference other files and glob-based conditional includes.
70
+ *
71
+ * @see https://ampcode.com/news/agent-skills
72
+ * @see https://ampcode.com/manual
73
+ */
59
74
  const amp = defineTarget({
60
75
  agent: "amp",
61
76
  displayName: "Amp",
@@ -90,6 +105,19 @@ const amp = defineTarget({
90
105
  ]
91
106
  });
92
107
  const home$6 = homedir();
108
+ /**
109
+ * Antigravity (Google)
110
+ *
111
+ * Agent-first IDE (VS Code fork) powered by Gemini. Skills live in
112
+ * .agent/skills/ (workspace) or ~/.gemini/antigravity/skills/ (global).
113
+ * Uses semantic matching on description to auto-invoke skills.
114
+ *
115
+ * Adopted the Agent Skills open standard (agentskills.io) in Jan 2026.
116
+ * Only `name` and `description` are used for routing; body loads on demand.
117
+ *
118
+ * @see https://antigravity.google/docs/skills
119
+ * @see https://codelabs.developers.google.com/getting-started-with-antigravity-skills
120
+ */
93
121
  const antigravity = defineTarget({
94
122
  agent: "antigravity",
95
123
  displayName: "Antigravity",
@@ -117,6 +145,18 @@ const antigravity = defineTarget({
117
145
  ]
118
146
  });
119
147
  const claudeHome = process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
148
+ /**
149
+ * Claude Code (Anthropic CLI)
150
+ *
151
+ * Follows the Agent Skills open standard (agentskills.io) plus Claude-specific
152
+ * extensions like `disable-model-invocation`, `user-invocable`, `context`, etc.
153
+ *
154
+ * Skills are discovered at startup — only `name` + `description` are read initially.
155
+ * Full SKILL.md body loads when the agent invokes the skill (via Skill tool or auto-match).
156
+ *
157
+ * @see https://code.claude.com/docs/en/skills
158
+ * @see https://agentskills.io/specification
159
+ */
120
160
  const claudeCode = defineTarget({
121
161
  agent: "claude-code",
122
162
  displayName: "Claude Code",
@@ -198,6 +238,19 @@ const claudeCode = defineTarget({
198
238
  ]
199
239
  });
200
240
  const home$5 = homedir();
241
+ /**
242
+ * Cline (VS Code extension)
243
+ *
244
+ * Has TWO systems: Rules (.clinerules/) and Skills (.cline/skills/).
245
+ * We target Skills. Cline also reads .claude/skills/ as a fallback,
246
+ * so emitting to .claude/skills/ covers both Claude Code and Cline.
247
+ *
248
+ * Only `name` and `description` are parsed from frontmatter.
249
+ * All other fields are stripped/ignored.
250
+ *
251
+ * @see https://docs.cline.bot/features/skills
252
+ * @see https://docs.cline.bot/features/cline-rules
253
+ */
201
254
  const cline = defineTarget({
202
255
  agent: "cline",
203
256
  displayName: "Cline",
@@ -228,6 +281,19 @@ const cline = defineTarget({
228
281
  ]
229
282
  });
230
283
  const codexHome = process.env.CODEX_HOME || join(homedir(), ".codex");
284
+ /**
285
+ * OpenAI Codex CLI
286
+ *
287
+ * IMPORTANT: Codex uses `.agents/skills/` for project-level skills,
288
+ * NOT `.codex/skills/`. The `.codex/` directory is for config (config.toml).
289
+ * `~/.codex/skills/` works only as a legacy user-global path.
290
+ *
291
+ * Codex also has AGENTS.md (or AGENTS.override.md) for general instructions,
292
+ * which walks from git root to CWD concatenating found files.
293
+ *
294
+ * @see https://developers.openai.com/codex/skills
295
+ * @see https://developers.openai.com/codex/guides/agents-md/
296
+ */
231
297
  const codex = defineTarget({
232
298
  agent: "codex",
233
299
  displayName: "Codex",
@@ -273,6 +339,19 @@ const codex = defineTarget({
273
339
  ]
274
340
  });
275
341
  const home$4 = homedir();
342
+ /**
343
+ * Cursor (AI code editor)
344
+ *
345
+ * Has TWO systems: Rules (.cursor/rules/*.mdc) and Skills (.cursor/skills/).
346
+ * We target the Skills system which follows the Agent Skills spec.
347
+ *
348
+ * Cursor natively scans .claude/skills/ and .codex/skills/ in addition to
349
+ * its own .cursor/skills/ — so .claude/skills/ output works for both
350
+ * Claude Code and Cursor with zero duplication.
351
+ *
352
+ * @see https://cursor.com/docs/context/skills
353
+ * @see https://cursor.com/docs/context/rules
354
+ */
276
355
  const cursor = defineTarget({
277
356
  agent: "cursor",
278
357
  displayName: "Cursor",
@@ -316,6 +395,18 @@ const cursor = defineTarget({
316
395
  ]
317
396
  });
318
397
  const home$3 = homedir();
398
+ /**
399
+ * Google Gemini CLI
400
+ *
401
+ * Follows the Agent Skills open standard (agentskills.io).
402
+ * Skills are activated via `activate_skill` tool with user confirmation.
403
+ *
404
+ * Also has GEMINI.md context files (analogous to CLAUDE.md) which support
405
+ * @file.md import syntax for modular composition.
406
+ *
407
+ * @see https://geminicli.com/docs/cli/skills/
408
+ * @see https://geminicli.com/docs/cli/creating-skills/
409
+ */
319
410
  const geminiCli = defineTarget({
320
411
  agent: "gemini-cli",
321
412
  displayName: "Gemini CLI",
@@ -349,6 +440,18 @@ const geminiCli = defineTarget({
349
440
  ]
350
441
  });
351
442
  const home$2 = homedir();
443
+ /**
444
+ * GitHub Copilot
445
+ *
446
+ * Has TWO systems: Instructions (.github/instructions/*.instructions.md)
447
+ * and Skills (.github/skills/). We target Skills.
448
+ *
449
+ * Copilot also auto-detects .claude/skills/ as a legacy path,
450
+ * so .claude/skills/ output works for Claude Code, Cursor, Cline, AND Copilot.
451
+ *
452
+ * @see https://docs.github.com/en/copilot/concepts/agents/about-agent-skills
453
+ * @see https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot
454
+ */
352
455
  const githubCopilot = defineTarget({
353
456
  agent: "github-copilot",
354
457
  displayName: "GitHub Copilot",
@@ -387,6 +490,15 @@ const githubCopilot = defineTarget({
387
490
  ]
388
491
  });
389
492
  const configHome$1 = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
493
+ /**
494
+ * Goose (Block)
495
+ *
496
+ * Scans 6 directories for skills, including .claude/skills/ and .agents/skills/
497
+ * for cross-agent compatibility. Later directories override earlier ones on
498
+ * name conflict.
499
+ *
500
+ * @see https://block.github.io/goose/docs/guides/context-engineering/using-skills/
501
+ */
390
502
  const goose = defineTarget({
391
503
  agent: "goose",
392
504
  displayName: "Goose",
@@ -421,6 +533,18 @@ const goose = defineTarget({
421
533
  ]
422
534
  });
423
535
  const configHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
536
+ /**
537
+ * OpenCode (SST)
538
+ *
539
+ * Walks from CWD up to git worktree root searching for skill dirs.
540
+ * Reads .claude/skills/ and .agents/skills/ in addition to .opencode/skills/.
541
+ *
542
+ * Has a rich agent system: .opencode/agents/ with per-agent model/tool configuration.
543
+ * Skills can be permission-controlled per-agent with allow/deny/ask + glob patterns.
544
+ *
545
+ * @see https://opencode.ai/docs/skills/
546
+ * @see https://opencode.ai/docs/rules/
547
+ */
424
548
  const opencode = defineTarget({
425
549
  agent: "opencode",
426
550
  displayName: "OpenCode",
@@ -462,6 +586,18 @@ const opencode = defineTarget({
462
586
  ]
463
587
  });
464
588
  const home$1 = homedir();
589
+ /**
590
+ * Roo Code (VS Code extension)
591
+ *
592
+ * IMPORTANT: Roo does NOT read .claude/skills/ or .agents/skills/.
593
+ * It requires its own .roo/skills/ directory — no cross-compat shortcuts.
594
+ *
595
+ * Unique feature: mode-specific skill directories (.roo/skills-{modeSlug}/)
596
+ * allow targeting skills to specific modes (code, architect, etc.).
597
+ *
598
+ * @see https://docs.roocode.com/features/skills
599
+ * @see https://docs.roocode.com/features/custom-instructions
600
+ */
465
601
  const roo = defineTarget({
466
602
  agent: "roo",
467
603
  displayName: "Roo Code",
@@ -494,6 +630,18 @@ const roo = defineTarget({
494
630
  ]
495
631
  });
496
632
  const home = homedir();
633
+ /**
634
+ * Windsurf (Codeium editor)
635
+ *
636
+ * Has TWO systems: Rules (.windsurf/rules/*.md) and Skills (.windsurf/skills/).
637
+ * We target Skills. Rules have a separate frontmatter schema with trigger/globs.
638
+ *
639
+ * Skills only document `name` and `description` as frontmatter fields.
640
+ * Cascade uses "progressive disclosure" for supporting files.
641
+ *
642
+ * @see https://docs.windsurf.com/windsurf/cascade/skills
643
+ * @see https://docs.windsurf.com/windsurf/cascade/memories
644
+ */
497
645
  const windsurf = defineTarget({
498
646
  agent: "windsurf",
499
647
  displayName: "Windsurf",
@@ -537,15 +685,28 @@ const targets = {
537
685
  "roo": roo,
538
686
  "antigravity": antigravity
539
687
  };
688
+ /**
689
+ * Detect which agents are installed on the system
690
+ */
540
691
  function detectInstalledAgents() {
541
692
  return Object.entries(targets).filter(([_, config]) => config.detectInstalled()).map(([type]) => type);
542
693
  }
694
+ /**
695
+ * Detect the target agent (where skills are installed) from env vars and cwd.
696
+ * This is NOT the generator LLM — it determines the skills directory.
697
+ *
698
+ * Priority: env vars first (running inside agent), then project dirs.
699
+ * Iteration order of the agents record determines priority.
700
+ */
543
701
  function detectTargetAgent() {
544
702
  for (const [type, target] of Object.entries(targets)) if (target.detectEnv()) return type;
545
703
  const cwd = process.cwd();
546
704
  for (const [type, target] of Object.entries(targets)) if (target.detectProject(cwd)) return type;
547
705
  return null;
548
706
  }
707
+ /**
708
+ * Get the version of an agent's CLI (if available)
709
+ */
549
710
  function getAgentVersion(agentType) {
550
711
  const agent = targets[agentType];
551
712
  if (!agent.cli) return null;
@@ -568,14 +729,30 @@ function getAgentVersion(agentType) {
568
729
  return null;
569
730
  }
570
731
  }
732
+ /**
733
+ * Dynamic budget allocation for skill sections.
734
+ *
735
+ * Total SKILL.md body should stay under ~300 lines (≈5,000 words per Agent Skills guide).
736
+ * When more sections are enabled, each gets proportionally less space.
737
+ * When a package has many releases, API changes budget scales up to capture more churn.
738
+ */
739
+ /** Scale max lines based on enabled section count. Solo sections get full budget, 4 sections ~60%. */
571
740
  function maxLines(min, max, sectionCount) {
572
741
  const scale = budgetScale(sectionCount);
573
742
  return Math.max(min, Math.round(max * scale));
574
743
  }
744
+ /** Scale item count based on enabled section count. */
575
745
  function maxItems(min, max, sectionCount) {
576
746
  const scale = budgetScale(sectionCount);
577
747
  return Math.max(min, Math.round(max * scale));
578
748
  }
749
+ /**
750
+ * Boost budget for high-churn packages based on API-level release density.
751
+ * Combines major/minor release count with current minor version as a churn signal.
752
+ *
753
+ * @param significantReleases - Count of major/minor releases (patch releases excluded)
754
+ * @param minorVersion - Current minor version number (e.g., 15 for v3.15.0)
755
+ */
579
756
  function releaseBoost(significantReleases, minorVersion) {
580
757
  const combined = (!significantReleases ? 0 : significantReleases <= 5 ? 0 : significantReleases <= 15 ? 1 : 2) + (!minorVersion ? 0 : minorVersion <= 3 ? 0 : minorVersion <= 10 ? 1 : 2);
581
758
  if (combined <= 0) return 1;
@@ -588,27 +765,32 @@ function budgetScale(sectionCount) {
588
765
  if (sectionCount === 3) return .7;
589
766
  return .6;
590
767
  }
768
+ /** Warns if content exceeds 150% of max lines */
591
769
  function checkLineCount(content, max) {
592
770
  const lines = content.split("\n").length;
593
771
  if (lines > Math.round(max * 1.5)) return [{ warning: `Output ${lines} lines exceeds ${max} max by >50%` }];
594
772
  return [];
595
773
  }
774
+ /** Warns if content is fewer than 3 lines */
596
775
  function checkSparseness(content) {
597
776
  const lines = content.split("\n").length;
598
777
  if (lines < 3) return [{ warning: `Output only ${lines} lines — likely too sparse` }];
599
778
  return [];
600
779
  }
780
+ /** Warns if sourced/bullets ratio is below minRatio */
601
781
  function checkSourceCoverage(content, minRatio = .8) {
602
782
  const bullets = (content.match(/^- /gm) || []).length;
603
783
  const sourced = (content.match(/\[source\]/g) || []).length;
604
784
  if (bullets > 2 && sourced / bullets < minRatio) return [{ warning: `Only ${sourced}/${bullets} items have source citations (need ${Math.round(minRatio * 100)}% coverage)` }];
605
785
  return [];
606
786
  }
787
+ /** Warns if source links are missing .skilld/ prefix */
607
788
  function checkSourcePaths(content) {
608
789
  const badPaths = content.match(/\[source\]\(\.\/(docs|issues|discussions|releases|pkg|guide)\//g);
609
790
  if (badPaths?.length) return [{ warning: `${badPaths.length} source links missing .skilld/ prefix` }];
610
791
  return [];
611
792
  }
793
+ /** Warns if source links use absolute filesystem paths instead of relative ./.skilld/ paths */
612
794
  function checkAbsolutePaths(content) {
613
795
  const absPaths = content.match(/\[source\]\(\/[^)]+\)/g);
614
796
  if (absPaths?.length) return [{ warning: `${absPaths.length} source links use absolute paths — must use relative ./.skilld/ paths` }];
@@ -849,16 +1031,22 @@ Content addressing the user's instructions above, using concise examples and sou
849
1031
  rules: [`- **Custom section "${heading}":** MAX ${customMaxLines} lines, use \`## ${heading}\` heading`]
850
1032
  };
851
1033
  }
1034
+ /** Output file per section (inside .skilld/) */
852
1035
  const SECTION_OUTPUT_FILES = {
853
1036
  "best-practices": "_BEST_PRACTICES.md",
854
1037
  "api-changes": "_API_CHANGES.md",
855
1038
  "custom": "_CUSTOM.md"
856
1039
  };
1040
+ /** Merge order for final SKILL.md body */
857
1041
  const SECTION_MERGE_ORDER = [
858
1042
  "api-changes",
859
1043
  "best-practices",
860
1044
  "custom"
861
1045
  ];
1046
+ /**
1047
+ * Group files by parent directory with counts
1048
+ * e.g. `/path/to/docs/api/ (15 .md files)`
1049
+ */
862
1050
  function formatDocTree(files) {
863
1051
  const dirs = /* @__PURE__ */ new Map();
864
1052
  for (const f of files) {
@@ -895,6 +1083,7 @@ Filters: \`docs:\`, \`issues:\`, \`releases:\` prefix narrows by source type.` :
895
1083
 
896
1084
  ${table}`;
897
1085
  }
1086
+ /** Shared preamble: Security, references table, Quality Principles, doc tree */
898
1087
  function buildPreamble(opts) {
899
1088
  const { packageName, skillDir, hasIssues, hasDiscussions, hasReleases, hasChangelog, docFiles, docsType = "docs", hasShippedDocs = false, versionContext } = opts;
900
1089
  const docsSection = docFiles?.length ? `<external-docs>\n**Documentation** (use Read tool to explore):\n${formatDocTree(docFiles)}\n</external-docs>` : "";
@@ -928,12 +1117,19 @@ function getSectionDef(section, ctx, customPrompt) {
928
1117
  case "custom": return customPrompt ? customSection(customPrompt, ctx.enabledSectionCount) : null;
929
1118
  }
930
1119
  }
1120
+ /**
1121
+ * Get the validate function for a section using default context (validators use fixed thresholds).
1122
+ * Returns null if section has no validator.
1123
+ */
931
1124
  function getSectionValidator(section) {
932
1125
  return getSectionDef(section, { packageName: "" }, section === "custom" ? {
933
1126
  heading: "Custom",
934
1127
  body: ""
935
1128
  } : void 0)?.validate ?? null;
936
1129
  }
1130
+ /**
1131
+ * Build prompt for a single section
1132
+ */
937
1133
  function buildSectionPrompt(opts) {
938
1134
  const { packageName, hasIssues, hasDiscussions, hasReleases, hasChangelog, version, section, customPrompt, skillDir } = opts;
939
1135
  const versionContext = version ? ` v${version}` : "";
@@ -998,6 +1194,9 @@ Write your final output to the file \`${skillDir}/.skilld/${outputFile}\` using
998
1194
  After writing, run \`${cmd} validate ${skillDir}/.skilld/${outputFile}\` and fix any warnings before finishing. If unavailable, use \`${fallbackCmd} validate ${skillDir}/.skilld/${outputFile}\`.
999
1195
  `;
1000
1196
  }
1197
+ /**
1198
+ * Build prompts for all selected sections, sharing the computed preamble
1199
+ */
1001
1200
  function buildAllSectionPrompts(opts) {
1002
1201
  const result = /* @__PURE__ */ new Map();
1003
1202
  for (const section of opts.sections) {
@@ -1010,12 +1209,28 @@ function buildAllSectionPrompts(opts) {
1010
1209
  }
1011
1210
  return result;
1012
1211
  }
1212
+ /**
1213
+ * Sanitize skill name for filesystem
1214
+ */
1013
1215
  function sanitizeName(name) {
1014
1216
  return name.toLowerCase().replace(/[^a-z0-9._]+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "").slice(0, 255) || "unnamed-skill";
1015
1217
  }
1218
+ /**
1219
+ * Compute skill directory name from package name with -skilld suffix.
1220
+ * No collisions for monorepo packages (each gets a unique name).
1221
+ *
1222
+ * Examples:
1223
+ * vue → vue-skilld
1224
+ * @unhead/vue → unhead-vue-skilld
1225
+ * @unhead/react → unhead-react-skilld
1226
+ */
1016
1227
  function computeSkillDirName(packageName) {
1017
1228
  return `${sanitizeName(packageName)}-skilld`;
1018
1229
  }
1230
+ /**
1231
+ * Install a skill directly to agent skill directories
1232
+ * Writes to each agent's skill folder in the project (e.g., .claude/skills/package-name/)
1233
+ */
1019
1234
  function installSkillForAgents(skillName, skillContent, options = {}) {
1020
1235
  const isGlobal = options.global ?? false;
1021
1236
  const cwd = options.cwd || process.cwd();
@@ -1039,6 +1254,11 @@ function installSkillForAgents(skillName, skillContent, options = {}) {
1039
1254
  paths
1040
1255
  };
1041
1256
  }
1257
+ /**
1258
+ * Create relative symlinks from each detected agent's skills dir to the shared .skills/ dir.
1259
+ * Only targets agents whose config dir already exists in the project.
1260
+ * Replaces existing symlinks, skips real directories (user's custom skills).
1261
+ */
1042
1262
  function linkSkillToAgents(skillName, sharedDir, cwd) {
1043
1263
  for (const [, agent] of Object.entries(targets)) {
1044
1264
  const agentSkillsDir = join(cwd, agent.skillsDir);
@@ -1057,6 +1277,9 @@ function linkSkillToAgents(skillName, sharedDir, cwd) {
1057
1277
  symlinkSync(relative(agentSkillsDir, join(sharedDir, skillName)), target);
1058
1278
  }
1059
1279
  }
1280
+ /**
1281
+ * Remove per-agent symlinks for a skill when removing from shared dir.
1282
+ */
1060
1283
  function unlinkSkillFromAgents(skillName, cwd) {
1061
1284
  for (const [, agent] of Object.entries(targets)) {
1062
1285
  const target = join(cwd, agent.skillsDir, skillName);
@@ -1077,6 +1300,7 @@ function generateSkillMd(opts) {
1077
1300
  const footer = generateFooter(opts.relatedSkills);
1078
1301
  return sanitizeMarkdown(repairMarkdown(`${generateFrontmatter(opts)}${content}\n${footer}`));
1079
1302
  }
1303
+ /** Format ISO date as short absolute date: "Jan 2025", "Dec 2024" */
1080
1304
  function formatShortDate(isoDate) {
1081
1305
  const date = new Date(isoDate);
1082
1306
  if (Number.isNaN(date.getTime())) return "";
@@ -1137,6 +1361,10 @@ function generatePackageHeader({ name, description, version, releasedAt, depende
1137
1361
  if (refs.length > 0) lines.push(`**References:** ${refs.join(" • ")}`);
1138
1362
  return lines.join("\n");
1139
1363
  }
1364
+ /**
1365
+ * Expand a package name into keyword variants for better trigger matching.
1366
+ * e.g. "@nuxt/ui" → ["nuxt ui", "nuxt/ui"], "vue-router" → ["vue router"]
1367
+ */
1140
1368
  function expandPackageName(name) {
1141
1369
  const variants = /* @__PURE__ */ new Set();
1142
1370
  const unscoped = name.replace(/^@/, "");
@@ -1151,6 +1379,10 @@ function expandPackageName(name) {
1151
1379
  variants.delete(name);
1152
1380
  return [...variants];
1153
1381
  }
1382
+ /**
1383
+ * Extract and expand GitHub repo name into keyword variants.
1384
+ * e.g. "motion-v" → ["motion-v", "motion v"]
1385
+ */
1154
1386
  function expandRepoName(repoUrl) {
1155
1387
  const variants = /* @__PURE__ */ new Set();
1156
1388
  const repoName = repoUrl.startsWith("http") ? repoUrl.split("/").pop() : repoUrl.split("/").pop();