skilld 0.9.6 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -611,7 +611,15 @@ function apiChangesSection({ packageName, version, hasReleases, hasChangelog, ha
611
611
  useFor: "Skip unless searching a specific removed API"
612
612
  });
613
613
  const releaseGuidance = hasReleases ? `\n\n**Scan release history:** Read \`./.skilld/releases/_INDEX.md\` for a timeline. Focus on [MAJOR] and [MINOR] releases — these contain breaking changes and renamed/deprecated APIs that LLMs trained on older data will get wrong.` : "";
614
- const versionGuidance = major && minor ? `\n\n**New APIs in recent releases are the highest-priority gaps** — the LLM was trained on older data and will use outdated or non-existent APIs instead. Search for recent version tags and "Features" in releases/changelog to find new composables, components, hooks, or utilities added in recent major/minor versions.` : "";
614
+ const versionGuidance = major && minor ? `\n\n**Item scoring** — include only items scoring 3:
615
+
616
+ | Change type | v${major}.x | v${Number(major) - 1}.x | Older |
617
+ |-------------|:---:|:---:|:---:|
618
+ | Silent breakage (compiles, wrong result) | 5 | 4 | 2 |
619
+ | Removed/breaking API | 5 | 3 | 0 |
620
+ | New API unknown to LLMs | 4 | 1 | 0 |
621
+ | Deprecated (still works) | 3 | 1 | 0 |
622
+ | Renamed/moved | 3 | 1 | 0 |` : "";
615
623
  return {
616
624
  referenceWeights,
617
625
  task: `**Find new, deprecated, and renamed APIs from version history.** Focus exclusively on APIs that changed between versions — LLMs trained on older data will use the wrong names, wrong signatures, or non-existent functions.
@@ -622,26 +630,24 @@ Find from releases/changelog:
622
630
  - **Signature changes** where old code compiles but behaves wrong (changed parameter order, return types, default values)
623
631
  - **Breaking changes** in recent versions (v2 → v3 migrations, major version bumps)
624
632
  ${searchHints.length ? `\nSearch: ${searchHints.join(", ")}` : ""}${releaseGuidance}${versionGuidance}`,
625
- format: `## API Changes
633
+ format: `<format-example note="Illustrative structure only — replace placeholder names with real ${packageName} APIs">
634
+ ## API Changes
626
635
 
627
636
  This section documents version-specific API changes — prioritize recent major/minor releases.
628
637
 
629
- \`\`\`
630
- ## API Changes
631
-
632
- ⚠️ \`createClient(url, key)\` — v2 changed to \`createClient({ url, key })\`, old positional args silently ignored [source](./.skilld/releases/v2.0.0.md)
638
+ - BREAKING: \`createClient(url, key)\` — v2 changed to \`createClient({ url, key })\`, old positional args silently ignored [source](./.skilld/releases/v2.0.0.md)
633
639
 
634
- \`useTemplateRef()\` — new in v3.5, replaces \`$refs\` pattern [source](./.skilld/releases/v3.5.0.md)
640
+ - NEW: \`useTemplateRef()\` — new in v3.5, replaces \`$refs\` pattern [source](./.skilld/releases/v3.5.0.md)
635
641
 
636
- ⚠️ \`db.query()\` — returns \`{ rows }\` not raw array since v4 [source](./.skilld/docs/migration.md)
637
- \`\`\`
642
+ - BREAKING: \`db.query()\` — returns \`{ rows }\` not raw array since v4 [source](./.skilld/docs/migration.md)
643
+ </format-example>
638
644
 
639
- Each item: ⚠️ (breaking/deprecated) or ✨ (new) + API name + what changed + source link.`,
645
+ Each item: BREAKING/DEPRECATED/NEW label + API name + what changed + source link. All source links MUST use \`./.skilld/\` prefix (e.g., \`[source](./.skilld/releases/v2.0.0.md)\`). Do NOT use emoji — use plain text markers only.`,
640
646
  rules: [
641
647
  `- **API Changes:** ${maxItems(6, 12, enabledSectionCount)} items from version history, MAX ${maxLines(50, 80, enabledSectionCount)} lines`,
642
648
  "- Prioritize recent major/minor releases over old patch versions",
643
649
  "- Focus on APIs that CHANGED, not general conventions or gotchas",
644
- "- New APIs get ✨, deprecated/breaking get ⚠️",
650
+ "- New APIs get NEW: prefix, deprecated/breaking get BREAKING: or DEPRECATED: prefix",
645
651
  hasReleases ? "- Start with `./.skilld/releases/_INDEX.md` to identify recent major/minor releases, then read specific release files" : "",
646
652
  hasChangelog ? "- Scan CHANGELOG.md for version headings, focus on Features/Breaking Changes sections" : ""
647
653
  ].filter(Boolean)
@@ -660,14 +666,14 @@ function bestPracticesSection({ packageName, hasIssues, hasDiscussions, hasRelea
660
666
  if (hasDiscussions) referenceWeights.push({
661
667
  name: "Discussions",
662
668
  path: "./.skilld/discussions/_INDEX.md",
663
- score: 8,
664
- useFor: "Q&A with accepted answers reveal \"the right way\""
669
+ score: 5,
670
+ useFor: "Only maintainer-confirmed patterns community workarounds are lower confidence"
665
671
  });
666
672
  if (hasIssues) referenceWeights.push({
667
673
  name: "Issues",
668
674
  path: "./.skilld/issues/_INDEX.md",
669
- score: 7,
670
- useFor: "Questions reveal what users find confusing"
675
+ score: 4,
676
+ useFor: "Only workarounds confirmed by maintainers or with broad adoption"
671
677
  });
672
678
  if (hasReleases) referenceWeights.push({
673
679
  name: "Releases",
@@ -683,33 +689,38 @@ function bestPracticesSection({ packageName, hasIssues, hasDiscussions, hasRelea
683
689
  });
684
690
  return {
685
691
  referenceWeights,
686
- task: `**Extract non-obvious best practices from the references.** Focus on recommended patterns Claude wouldn't already know: idiomatic usage, preferred configurations, performance tips, patterns that differ from what a developer would assume. Surface new patterns from recent minor releases that may post-date training data. Every item must link to a verified source file.
692
+ task: `**Extract non-obvious best practices from the references.** Focus on recommended patterns the LLM wouldn't already know: idiomatic usage, preferred configurations, performance tips, patterns that differ from what a developer would assume. Surface new patterns from recent minor releases that may post-date training data.
687
693
 
688
- Skip: obvious API usage, installation steps, general TypeScript/programming patterns, anything a developer would naturally write without reading the docs.
694
+ Skip: obvious API usage, installation steps, general TypeScript/programming patterns not specific to this package, anything a developer would naturally write without reading the docs. Every item must be specific to ${packageName} — reject general programming advice that applies to any project.
689
695
  ${searchHints.length ? `\nSearch: ${searchHints.join(", ")}` : ""}`,
690
- format: `\`\`\`
696
+ format: `<format-example note="Illustrative structure only — replace placeholder names with real ${packageName} APIs">
697
+ \`\`\`
691
698
  ## Best Practices
692
699
 
693
- Pass \`AbortSignal\` to long-lived operationsenables caller-controlled cancellation [source](./.skilld/docs/api.md)
700
+ - Use ${packageName}'s built-in \`createX()\` helper over manual wiring handles cleanup and edge cases automatically [source](./.skilld/docs/api.md)
694
701
 
695
702
  \`\`\`ts
696
- async function fetchUser(id: string, signal?: AbortSignal) {
697
- return fetch(\`/api/users/\${id}\`, { signal })
698
- }
699
- \`\`\`
703
+ // Preferred
704
+ const instance = createX({ ... })
700
705
 
701
- Use \`satisfies\` for config objects preserves literal types while validating shape [source](./.skilld/docs/config.md)
706
+ // Avoidmisses cleanup, error boundaries
707
+ const instance = new X()
708
+ instance.init({ ... })
709
+ \`\`\`
702
710
 
703
- Prefer \`structuredClone()\` over spread for deep copies handles nested objects, Maps, Sets [source](./.skilld/docs/utilities.md)
711
+ - Pass config through \`defineConfig()\` enables type inference and plugin merging [source](./.skilld/docs/config.md)
704
712
 
705
- Set \`isolatedDeclarations: true\` enables parallel .d.ts emit without full type-checking [source](./.skilld/docs/typescript.md)
713
+ - Prefer \`useComposable()\` over direct imports in reactive contexts ensures proper lifecycle binding [source](./.skilld/docs/composables.md)
706
714
  \`\`\`
715
+ </format-example>
707
716
 
708
- Each item: + pattern name + why it's preferred + source link. Code block only when the pattern isn't obvious from the title. Use the most relevant language tag (ts, vue, css, json, etc).`,
717
+ Each item: markdown list item (-) + ${packageName}-specific pattern + why it's preferred + source link. Code block only when the pattern isn't obvious from the title. Use the most relevant language tag (ts, vue, css, json, etc). Every example must be specific to ${packageName} — never generic TypeScript/JS advice. All source links MUST use \`./.skilld/\` prefix (e.g., \`[source](./.skilld/docs/guide.md)\`). Do NOT use emoji — use plain text markers only.`,
709
718
  rules: [
710
719
  `- **${maxItems(4, 10, enabledSectionCount)} best practice items**`,
711
720
  `- **MAX ${maxLines(80, 150, enabledSectionCount)} lines** for best practices section`,
712
- "- **Only link files confirmed to exist** via Glob or Read no guessed paths"
721
+ "- **Verify before including:** Confirm file paths exist via Glob/Read before linking. Confirm functions/composables are real exports in `./.skilld/pkg/` `.d.ts` files before documenting",
722
+ "- **Diversity:** Cover at least 3 distinct areas of the library. No single feature should have more than 40% of items",
723
+ "- **Experimental APIs:** Mark unstable/experimental features with `(experimental)` in the description. Prioritize stable patterns"
713
724
  ]
714
725
  };
715
726
  }
@@ -814,8 +825,11 @@ function formatDocTree(files) {
814
825
  }
815
826
  return [...dirs.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([dir, count]) => `- \`${dir}/\` (${count} .md files)`).join("\n");
816
827
  }
817
- function generateImportantBlock({ packageName, hasIssues, hasDiscussions, hasReleases, hasChangelog, docsType, hasShippedDocs, skillDir, features }) {
818
- const rows = [["Docs", 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/\``], ["Package", `\`${skillDir}/.skilld/pkg/\``]];
828
+ function generateImportantBlock({ packageName, hasIssues, hasDiscussions, hasReleases, hasChangelog, docsType, hasShippedDocs, skillDir, features, pkgFiles }) {
829
+ 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/\``;
830
+ const typesFile = pkgFiles?.find((f) => f.endsWith(".d.ts"));
831
+ const rows = [["Docs", docsPath], ["Package", `\`${skillDir}/.skilld/pkg/\``]];
832
+ if (typesFile) rows.push(["Types", `\`${skillDir}/.skilld/pkg/${typesFile}\` — **read this file directly** to verify exports`]);
819
833
  if (hasIssues) rows.push(["Issues", `\`${skillDir}/.skilld/issues/\``]);
820
834
  if (hasDiscussions) rows.push(["Discussions", `\`${skillDir}/.skilld/discussions/\``]);
821
835
  if (hasChangelog) rows.push(["Changelog", `\`${skillDir}/.skilld/${hasChangelog}\``]);
@@ -858,18 +872,10 @@ ${generateImportantBlock({
858
872
  docsType,
859
873
  hasShippedDocs,
860
874
  skillDir,
861
- features: opts.features
875
+ features: opts.features,
876
+ pkgFiles: opts.pkgFiles
862
877
  })}
863
- ${docsSection ? `${docsSection}\n` : ""}
864
-
865
- ## Skill Quality Principles
866
-
867
- The context window is a shared resource. Skills share it with system prompt, conversation history, other skills, and the user request.
868
-
869
- - **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?"
870
- - **Prefer concise examples over verbose explanations.** A 2-line code example beats a paragraph.
871
- - **Skip:** API signatures, installation steps, tutorials, marketing, general programming knowledge, anything in the package README that's obvious
872
- - **Include:** Non-obvious gotchas, surprising defaults, version-specific breaking changes, pitfalls from issues, patterns that differ from what Claude would assume`;
878
+ ${docsSection ? `${docsSection}\n` : ""}`;
873
879
  }
874
880
  function getSectionDef(section, ctx, customPrompt) {
875
881
  switch (section) {
@@ -902,14 +908,14 @@ function buildSectionPrompt(opts) {
902
908
  const packageRules = getPackageRules(packageName);
903
909
  const rules = [
904
910
  ...sectionDef.rules ?? [],
905
- "- Link to exact source file where you found info",
906
- "- TypeScript only",
907
911
  ...packageRules.map((r) => `- ${r}`),
908
- "- Imperative voice (\"Use X\" not \"You should use X\")",
909
912
  `- **NEVER fetch external URLs.** All information is in the local \`./.skilld/\` directory. Use Read, Glob${opts.features?.search !== false ? ", and `skilld search`" : ""} only.`,
910
913
  "- **Do NOT use Task tool or spawn subagents.** Work directly.",
911
914
  "- **Do NOT re-read files** you have already read in this session.",
912
- "- **Read `_INDEX.md` first** in issues/releases/discussions — only drill into files that look relevant. Skip stub/placeholder files."
915
+ "- **Read `_INDEX.md` first** in issues/releases/discussions — only drill into files that look relevant. Skip stub/placeholder files.",
916
+ "- **Skip files starting with `PROMPT_`** — these are generation prompts, not reference material.",
917
+ "- **Stop exploring once you have enough high-quality items** to fill the budget. Do not read additional files just to be thorough.",
918
+ "- **To verify API exports:** Read the `.d.ts` file directly (see Types row in references). Do NOT use search with relative paths or `include` filters on package directories — they may silently return no results."
913
919
  ];
914
920
  return `${preamble}${sectionDef.referenceWeights?.length ? `\n\n## Reference Priority\n\n| Reference | Path | Score | Use For |\n|-----------|------|:-----:|--------|\n${sectionDef.referenceWeights.map((w) => `| ${w.name} | [\`${w.path.split("/").pop()}\`](${w.path}) | ${w.score}/10 | ${w.useFor} |`).join("\n")}` : ""}
915
921
 
@@ -1004,23 +1010,28 @@ function unlinkSkillFromAgents(skillName, cwd) {
1004
1010
  function generateSkillMd(opts) {
1005
1011
  const header = generatePackageHeader(opts);
1006
1012
  const search = !opts.eject && opts.features?.search !== false ? generateSearchBlock(opts.name, opts.hasIssues, opts.hasReleases) : "";
1007
- const content = opts.body ? search ? `${header}\n\n${search}\n\n${opts.body}` : `${header}\n\n${opts.body}` : search ? `${header}\n\n${search}` : header;
1013
+ const body = opts.body && opts.eject ? opts.body.replace(/\.\/\.skilld\//g, "./references/") : opts.body;
1014
+ const content = body ? search ? `${header}\n\n${search}\n\n${body}` : `${header}\n\n${body}` : search ? `${header}\n\n${search}` : header;
1008
1015
  const footer = generateFooter(opts.relatedSkills);
1009
1016
  return sanitizeMarkdown(repairMarkdown(`${generateFrontmatter(opts)}${content}\n${footer}`));
1010
1017
  }
1011
- function formatRelativeDate(isoDate) {
1018
+ function formatShortDate(isoDate) {
1012
1019
  const date = new Date(isoDate);
1013
- const diffMs = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
1014
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
1015
- if (diffDays === 0) return "today";
1016
- if (diffDays === 1) return "yesterday";
1017
- if (diffDays < 7) return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
1018
- const weeks = Math.floor(diffDays / 7);
1019
- if (diffDays < 30) return `${weeks} week${weeks === 1 ? "" : "s"} ago`;
1020
- const months = Math.floor(diffDays / 30);
1021
- if (diffDays < 365) return `${months} month${months === 1 ? "" : "s"} ago`;
1022
- const years = Math.floor(diffDays / 365);
1023
- return `${years} year${years === 1 ? "" : "s"} ago`;
1020
+ if (Number.isNaN(date.getTime())) return "";
1021
+ return `${[
1022
+ "Jan",
1023
+ "Feb",
1024
+ "Mar",
1025
+ "Apr",
1026
+ "May",
1027
+ "Jun",
1028
+ "Jul",
1029
+ "Aug",
1030
+ "Sep",
1031
+ "Oct",
1032
+ "Nov",
1033
+ "Dec"
1034
+ ][date.getUTCMonth()]} ${date.getUTCFullYear()}`;
1024
1035
  }
1025
1036
  function generatePackageHeader({ name, description, version, releasedAt, dependencies, distTags, repoUrl, hasIssues, hasDiscussions, hasReleases, pkgFiles, packages, eject }) {
1026
1037
  let title = `# ${name}`;
@@ -1031,8 +1042,8 @@ function generatePackageHeader({ name, description, version, releasedAt, depende
1031
1042
  const lines = [title];
1032
1043
  if (description) lines.push("", `> ${description}`);
1033
1044
  if (version) {
1034
- const relativeDate = releasedAt ? formatRelativeDate(releasedAt) : "";
1035
- const versionStr = relativeDate ? `${version} (${relativeDate})` : version;
1045
+ const dateStr = releasedAt ? formatShortDate(releasedAt) : "";
1046
+ const versionStr = dateStr ? `${version} (${dateStr})` : version;
1036
1047
  lines.push("", `**Version:** ${versionStr}`);
1037
1048
  }
1038
1049
  if (dependencies && Object.keys(dependencies).length > 0) {
@@ -1041,7 +1052,7 @@ function generatePackageHeader({ name, description, version, releasedAt, depende
1041
1052
  }
1042
1053
  if (distTags && Object.keys(distTags).length > 0) {
1043
1054
  const tags = Object.entries(distTags).map(([tag, info]) => {
1044
- const relDate = info.releasedAt ? ` (${formatRelativeDate(info.releasedAt)})` : "";
1055
+ const relDate = info.releasedAt ? ` (${formatShortDate(info.releasedAt)})` : "";
1045
1056
  return `${tag}: ${info.version}${relDate}`;
1046
1057
  }).join(", ");
1047
1058
  lines.push(`**Tags:** ${tags}`);
@@ -1360,11 +1371,17 @@ function parseLine(line) {
1360
1371
  if (obj.type === "message" && obj.role === "assistant" && obj.content) return obj.delta ? { textDelta: obj.content } : { fullText: obj.content };
1361
1372
  if (obj.type === "tool_use" || obj.type === "tool_call") {
1362
1373
  const name = obj.tool_name || obj.name || obj.tool || "tool";
1363
- if (name === "write_file" && obj.args?.content) return {
1374
+ const params = obj.parameters || obj.args || obj.input || {};
1375
+ const hint = params.file_path || params.path || params.dir_path || params.pattern || params.query || params.command || "";
1376
+ if (name === "write_file" && params.content) return {
1364
1377
  toolName: name,
1365
- writeContent: obj.args.content
1378
+ toolHint: hint || void 0,
1379
+ writeContent: params.content
1380
+ };
1381
+ return {
1382
+ toolName: name,
1383
+ toolHint: hint || void 0
1366
1384
  };
1367
- return { toolName: name };
1368
1385
  }
1369
1386
  if (obj.type === "result") {
1370
1387
  const s = obj.stats;
@@ -1647,7 +1664,7 @@ function optimizeSection(opts) {
1647
1664
  });
1648
1665
  }
1649
1666
  async function optimizeDocs(opts) {
1650
- const { packageName, skillDir, model = "sonnet", version, hasGithub, hasReleases, hasChangelog, docFiles, docsType, hasShippedDocs, onProgress, timeout = 18e4, debug, noCache, sections, customPrompt, features } = opts;
1667
+ const { packageName, skillDir, model = "sonnet", version, hasGithub, hasReleases, hasChangelog, docFiles, docsType, hasShippedDocs, onProgress, timeout = 18e4, debug, noCache, sections, customPrompt, features, pkgFiles } = opts;
1651
1668
  const sectionPrompts = buildAllSectionPrompts({
1652
1669
  packageName,
1653
1670
  skillDir,
@@ -1661,6 +1678,7 @@ async function optimizeDocs(opts) {
1661
1678
  hasShippedDocs,
1662
1679
  customPrompt,
1663
1680
  features,
1681
+ pkgFiles,
1664
1682
  sections: sections ?? [
1665
1683
  "api-changes",
1666
1684
  "best-practices",
@@ -1877,7 +1895,12 @@ function validateSectionOutput(content, section) {
1877
1895
  return warnings;
1878
1896
  }
1879
1897
  function cleanSectionOutput(content) {
1880
- let cleaned = content.replace(/^```markdown\n?/m, "").replace(/\n?```$/m, "").trim();
1898
+ let cleaned = content.trim();
1899
+ const wrapMatch = cleaned.match(/^```(?:markdown|md)?[^\S\n]*\n([\s\S]+)\n```[^\S\n]*$/);
1900
+ if (wrapMatch) {
1901
+ const inner = wrapMatch[1].trim();
1902
+ if (/^```(?:markdown|md)/.test(cleaned) || /^##\s/m.test(inner) || /^- (?:BREAKING|DEPRECATED|NEW): /m.test(inner)) cleaned = inner;
1903
+ }
1881
1904
  const fmMatch = cleaned.match(/^-{3,}\n/);
1882
1905
  if (fmMatch) {
1883
1906
  const afterOpen = fmMatch[0].length;
@@ -1885,13 +1908,23 @@ function cleanSectionOutput(content) {
1885
1908
  if (closeMatch) cleaned = cleaned.slice(afterOpen + closeMatch.index + closeMatch[0].length).trim();
1886
1909
  else cleaned = cleaned.slice(afterOpen).trim();
1887
1910
  }
1888
- const firstMarker = cleaned.match(/^(##\s|⚠️|✅)/m);
1911
+ const firstMarker = cleaned.match(/^(##\s|- (?:BREAKING|DEPRECATED|NEW): )/m);
1889
1912
  if (firstMarker?.index && firstMarker.index > 0) {
1890
1913
  const preamble = cleaned.slice(0, firstMarker.index);
1891
1914
  if (/\b(?:function|const |let |var |export |return |import |async |class )\b/.test(preamble)) cleaned = cleaned.slice(firstMarker.index).trim();
1892
1915
  }
1916
+ const headingMatch = cleaned.match(/^(## .+)\n/);
1917
+ if (headingMatch) {
1918
+ const heading = headingMatch[1];
1919
+ const afterFirst = headingMatch[0].length;
1920
+ const secondIdx = cleaned.indexOf(heading, afterFirst);
1921
+ if (secondIdx !== -1) {
1922
+ if (secondIdx - afterFirst < 200) cleaned = cleaned.slice(secondIdx).trim();
1923
+ }
1924
+ }
1925
+ cleaned = cleaned.replace(/\[source\]\(\.\/((docs|issues|discussions|releases|pkg|guide)\/)/g, "[source](./.skilld/$1");
1893
1926
  cleaned = sanitizeMarkdown(cleaned);
1894
- if (!/^##\s/m.test(cleaned) && !/⚠️|✅|✨/.test(cleaned)) return "";
1927
+ if (!/^##\s/m.test(cleaned) && !/^- (?:BREAKING|DEPRECATED|NEW): /m.test(cleaned) && !/\[source\]/.test(cleaned)) return "";
1895
1928
  return cleaned;
1896
1929
  }
1897
1930
  const NUXT_CONFIG_FILES = [