skilld 0.3.2 → 0.4.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.
package/README.md CHANGED
@@ -34,22 +34,23 @@ Skilld leverages maintainers existing effort. Maintainers write great docs for u
34
34
 
35
35
  ## Features
36
36
 
37
- - 🌍 **Full Context SKILL.md** - Generates with the repo code, docs, releases, issues, discussions
38
- - 🤖 **BYO Agent** - Generate SKILL.md with or without an LLM
39
- - 📚 **Customizable** - `Best practices`, `LLM Gaps`, `Doc Map` or your own prompts
40
- - 🔍 **Semantic Search** - Token-optimized search via [retriv](https://github.com/harlan-zw/retriv)
41
- - 🎯 **Safe** - Prompt injection sanitization, version-aware output
42
- - 🤝 **Ecosystem** - [skills-npm](https://github.com/antfu/skills-npm) and `/llms.txt` support
37
+ - 🌍 **Any Source: Opt-in** - Any NPM dependency or GitHub source, docs auto-resolved
38
+ - 📦 **Bleeding Edge Context** - Latest issues, discussions, and releases synced on
39
+ every update. Always use the latest best practices and avoid deprecated patterns.
40
+ - 📚 **Opt-in LLM Sections** - Enhance skills with LLM-generated `Best practices`, `LLM Gaps`, `Doc Map`, or your own prompts
41
+ - 🔍 **Semantic Search** - Query indexed docs across all skills via [retriv](https://github.com/harlan-zw/retriv) embeddings
42
+ - 🎯 **Safe & Versioned** - Prompt injection sanitization, version-aware caching, auto-updates on new releases
43
+ - 🤝 **Ecosystem** - Compatible with [`npx skills`](https://skills.sh/) and [skills-npm](https://github.com/antfu/skills-npm)
43
44
 
44
45
  ## Quick Start
45
46
 
46
47
  Run skilld in a project to generate skills for your dependencies through a simple interactive wizard:
47
48
 
48
49
  ```bash
49
- npx skilld
50
+ npx -y skilld
50
51
  ```
51
52
 
52
- If you need to re-configure skilld, just run `npx skilld config` to update your agent, model, or preferences.
53
+ If you need to re-configure skilld, just run `npx -y skilld config` to update your agent, model, or preferences.
53
54
 
54
55
  ### Tips
55
56
 
@@ -1,8 +1,10 @@
1
- import { _ as writeSections, b as sanitizeMarkdown, h as readCachedSection, y as repairMarkdown } from "./storage.mjs";
1
+ import { n as sanitizeMarkdown, t as repairMarkdown } from "./sanitize2.mjs";
2
+ import { _ as writeSections, h as readCachedSection } from "./storage.mjs";
3
+ import { t as yamlEscape } from "./yaml.mjs";
2
4
  import { createRequire } from "node:module";
3
5
  import { homedir } from "node:os";
4
- import { dirname, join } from "pathe";
5
- import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, unlinkSync, writeFileSync } from "node:fs";
6
+ import { dirname, join, relative } from "pathe";
7
+ import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
6
8
  import { exec, spawn, spawnSync } from "node:child_process";
7
9
  import { globby } from "globby";
8
10
  import { findDynamicImports, findStaticImports } from "mlly";
@@ -695,16 +697,24 @@ function generateImportantBlock({ packageName, hasIssues, hasDiscussions, hasRel
695
697
  if (hasDiscussions) rows.push(["Discussions", `\`${skillDir}/.skilld/discussions/\``]);
696
698
  if (hasChangelog) rows.push(["Changelog", `\`${skillDir}/.skilld/pkg/${hasChangelog}\``]);
697
699
  if (hasReleases) rows.push(["Releases", `\`${skillDir}/.skilld/releases/\``]);
698
- return `**IMPORTANT:** Use these references
699
-
700
- | Resource | Command |
701
- |----------|---------|
702
- | Search all | \`Bash 'npx skilld search "<query>" -p ${packageName}'\` |
703
- ${[
700
+ const table = [
704
701
  "| Resource | Path |",
705
702
  "|----------|------|",
706
703
  ...rows.map(([desc, cmd]) => `| ${desc} | ${cmd} |`)
707
- ].join("\n")}`;
704
+ ].join("\n");
705
+ return `**IMPORTANT:** Use these references
706
+
707
+ ## Search
708
+
709
+ Use \`npx -y skilld search\` as your primary research tool — search before manually reading files. Hybrid semantic + keyword search across all indexed docs, issues, and releases.
710
+
711
+ \`\`\`bash
712
+ npx -y skilld search "<query>" -p ${packageName}
713
+ ${hasIssues ? `npx -y skilld search "issues:<query>" -p ${packageName}\n` : ""}${hasReleases ? `npx -y skilld search "releases:<query>" -p ${packageName}\n` : ""}\`\`\`
714
+
715
+ Filters: \`docs:\`, \`issues:\`, \`releases:\` prefix narrows by source type.
716
+
717
+ ${table}`;
708
718
  }
709
719
  function buildPreamble(opts) {
710
720
  const { packageName, skillDir, hasIssues, hasDiscussions, hasReleases, hasChangelog, docFiles, docsType = "docs", hasShippedDocs = false, versionContext } = opts;
@@ -803,27 +813,6 @@ function buildAllSectionPrompts(opts) {
803
813
  }
804
814
  return result;
805
815
  }
806
- const NEEDS_QUOTING = /[:"'\\\n\r\t#{}[\],&*!|>%@`]/;
807
- function yamlEscape(value) {
808
- if (!NEEDS_QUOTING.test(value)) return value;
809
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
810
- }
811
- function yamlUnescape(raw) {
812
- const trimmed = raw.trim();
813
- if (!trimmed) return "";
814
- if (trimmed.startsWith("\"") && trimmed.endsWith("\"")) return trimmed.slice(1, -1).replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, "\"").replace(/\\\\/g, "\\");
815
- if (trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
816
- return trimmed;
817
- }
818
- function yamlParseKV(line) {
819
- const trimmed = line.trim();
820
- const colonIdx = trimmed.indexOf(":");
821
- if (colonIdx === -1) return null;
822
- const key = trimmed.slice(0, colonIdx).trim();
823
- const rawValue = trimmed.slice(colonIdx + 1);
824
- if (!key) return null;
825
- return [key, yamlUnescape(rawValue)];
826
- }
827
816
  function sanitizeName(name) {
828
817
  return name.toLowerCase().replace(/[^a-z0-9._]+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "").slice(0, 255) || "unnamed-skill";
829
818
  }
@@ -857,6 +846,32 @@ function installSkillForAgents(skillName, skillContent, options = {}) {
857
846
  paths
858
847
  };
859
848
  }
849
+ function linkSkillToAgents(skillName, sharedDir, cwd) {
850
+ for (const [, agent] of Object.entries(targets)) {
851
+ const agentSkillsDir = join(cwd, agent.skillsDir);
852
+ if (!existsSync(join(cwd, agent.skillsDir.split("/")[0]))) continue;
853
+ const target = join(agentSkillsDir, skillName);
854
+ let isSymlink = false;
855
+ let targetExists = false;
856
+ try {
857
+ const stat = lstatSync(target);
858
+ targetExists = true;
859
+ isSymlink = stat.isSymbolicLink();
860
+ } catch {}
861
+ if (targetExists && !isSymlink) continue;
862
+ if (isSymlink) unlinkSync(target);
863
+ mkdirSync(agentSkillsDir, { recursive: true });
864
+ symlinkSync(relative(agentSkillsDir, join(sharedDir, skillName)), target);
865
+ }
866
+ }
867
+ function unlinkSkillFromAgents(skillName, cwd) {
868
+ for (const [, agent] of Object.entries(targets)) {
869
+ const target = join(cwd, agent.skillsDir, skillName);
870
+ try {
871
+ if (lstatSync(target).isSymbolicLink()) unlinkSync(target);
872
+ } catch {}
873
+ }
874
+ }
860
875
  const FILE_PATTERN_MAP = {
861
876
  "vue": ["*.vue"],
862
877
  "svelte": ["*.svelte"],
@@ -1018,12 +1033,12 @@ function generateFrontmatter({ name, version, description: pkgDescription, globs
1018
1033
  return lines.join("\n");
1019
1034
  }
1020
1035
  function generateSearchBlock(name, hasIssues, hasReleases) {
1021
- const examples = [`npx skilld search "query" -p ${name}`];
1022
- if (hasIssues) examples.push(`npx skilld search "issues:error handling" -p ${name}`);
1023
- if (hasReleases) examples.push(`npx skilld search "releases:deprecated" -p ${name}`);
1036
+ const examples = [`npx -y skilld search "query" -p ${name}`];
1037
+ if (hasIssues) examples.push(`npx -y skilld search "issues:error handling" -p ${name}`);
1038
+ if (hasReleases) examples.push(`npx -y skilld search "releases:deprecated" -p ${name}`);
1024
1039
  return `## Search
1025
1040
 
1026
- Use \`npx skilld search\` instead of grepping \`.skilld/\` directories — hybrid semantic + keyword search across all indexed docs, issues, and releases.
1041
+ Use \`npx -y skilld search\` instead of grepping \`.skilld/\` directories — hybrid semantic + keyword search across all indexed docs, issues, and releases.
1027
1042
 
1028
1043
  \`\`\`bash
1029
1044
  ${examples.join("\n")}
@@ -1832,6 +1847,6 @@ function isNodeBuiltin(pkg) {
1832
1847
  const base = pkg.startsWith("node:") ? pkg.slice(5) : pkg;
1833
1848
  return NODE_BUILTINS.has(base.split("/")[0]);
1834
1849
  }
1835
- export { buildSectionPrompt as _, optimizeDocs as a, getAgentVersion as b, computeSkillDirName as c, yamlEscape as d, yamlParseKV as f, buildAllSectionPrompts as g, SECTION_OUTPUT_FILES as h, getModelName as i, installSkillForAgents as l, SECTION_MERGE_ORDER as m, getAvailableModels as n, generateSkillMd as o, yamlUnescape as p, getModelLabel as r, FILE_PATTERN_MAP as s, detectImportedPackages as t, sanitizeName as u, detectInstalledAgents as v, targets as x, detectTargetAgent as y };
1850
+ export { detectInstalledAgents as _, optimizeDocs as a, targets as b, computeSkillDirName as c, sanitizeName as d, unlinkSkillFromAgents as f, buildSectionPrompt as g, buildAllSectionPrompts as h, getModelName as i, installSkillForAgents as l, SECTION_OUTPUT_FILES as m, getAvailableModels as n, generateSkillMd as o, SECTION_MERGE_ORDER as p, getModelLabel as r, FILE_PATTERN_MAP as s, detectImportedPackages as t, linkSkillToAgents as u, detectTargetAgent as v, __exportAll as x, getAgentVersion as y };
1836
1851
 
1837
1852
  //# sourceMappingURL=detect-imports.mjs.map