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
package/dist/index.mjs CHANGED
@@ -1,181 +1,7 @@
1
- import { mkdirSync, writeFileSync } from "node:fs";
2
- import { join as join$1 } from "node:path";
3
- async function generateSkill(config, onProgress) {
4
- const { url, outputDir = ".skilld", chunkSize = 1e3, chunkOverlap = 200, maxPages = 100, skipLlmsTxt = false, model = "Xenova/bge-small-en-v1.5" } = config;
5
- const siteName = getSiteName(url);
6
- const skillDir = join$1(outputDir, siteName);
7
- const referencesDir = join$1(skillDir, "references");
8
- const dbPath = join$1(skillDir, "search.db");
9
- mkdirSync(referencesDir, { recursive: true });
10
- let docs;
11
- let skillContent;
12
- if (!skipLlmsTxt) {
13
- const llmsResult = await fetchFromLlmsTxt(url, maxPages, onProgress);
14
- if (llmsResult) {
15
- docs = llmsResult.docs;
16
- skillContent = llmsResult.llmsContent;
17
- } else docs = await crawlSite(url, maxPages, onProgress);
18
- } else docs = await crawlSite(url, maxPages, onProgress);
19
- if (docs.length === 0) throw new Error("No documents found to index");
20
- const skillPath = join$1(skillDir, "SKILL.md");
21
- if (skillContent) writeFileSync(skillPath, skillContent);
22
- const { splitText } = await import("./split-text.mjs");
23
- const { sqliteVec } = await import("retriv/db/sqlite-vec");
24
- const { transformers } = await import("retriv/embeddings/transformers");
25
- const documents = [];
26
- for (const doc of docs) {
27
- const chunks = splitText(doc.content, {
28
- chunkSize,
29
- chunkOverlap
30
- });
31
- for (const chunk of chunks) {
32
- const section = extractSection(chunk.text);
33
- const docId = chunks.length > 1 ? `${doc.url}#chunk-${chunk.index}` : doc.url;
34
- const prefix = [doc.title, section].filter(Boolean).join(" > ");
35
- const content = prefix ? `${prefix}\n\n${chunk.text}` : chunk.text;
36
- documents.push({
37
- id: docId,
38
- content,
39
- metadata: {
40
- source: doc.url,
41
- title: doc.title,
42
- ...section && { section },
43
- ...chunks.length > 1 && {
44
- chunkIndex: chunk.index,
45
- chunkTotal: chunks.length
46
- }
47
- }
48
- });
49
- writeFileSync(join$1(referencesDir, sanitizeFilename(docId) + ".md"), formatReferenceFile(docId, doc, section, chunk, chunks.length));
50
- }
51
- }
52
- onProgress?.({
53
- url: "embedding",
54
- count: documents.length,
55
- phase: "index"
56
- });
57
- const db = await sqliteVec({
58
- path: dbPath,
59
- embeddings: transformers({ model })
60
- });
61
- await db.index(documents);
62
- await db.close?.();
63
- return {
64
- siteName,
65
- skillPath,
66
- referencesDir,
67
- dbPath,
68
- chunkCount: documents.length
69
- };
70
- }
71
- function getSiteName(url) {
72
- return new URL(url).hostname.replace(/^www\./, "");
73
- }
74
- function sanitizeFilename(id) {
75
- return id.replace(/^https?:\/\//, "").replace(/[#?]/g, "-").replace(/[^a-z0-9.-]/gi, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 100);
76
- }
77
- function extractSection(text) {
78
- const headings = [];
79
- for (const line of text.split("\n")) {
80
- const match = line.match(/^(#{1,6}) ([^\n]+)$/);
81
- if (match) {
82
- const level = match[1].length;
83
- const heading = match[2].trim();
84
- headings.length = level - 1;
85
- headings[level - 1] = heading;
86
- }
87
- }
88
- return headings.filter(Boolean).join(" > ") || void 0;
89
- }
90
- function formatReferenceFile(docId, doc, section, chunk, totalChunks) {
91
- const frontmatter = [
92
- "---",
93
- `id: "${docId}"`,
94
- `source: "${doc.url}"`,
95
- `title: "${doc.title}"`
96
- ];
97
- if (section) frontmatter.push(`section: "${section}"`);
98
- if (totalChunks > 1) frontmatter.push(`chunk: ${chunk.index + 1}/${totalChunks}`);
99
- frontmatter.push("---", "");
100
- const prefix = [doc.title, section].filter(Boolean).join(" > ");
101
- return frontmatter.join("\n") + (prefix ? `${prefix}\n\n` : "") + chunk.text;
102
- }
103
- async function fetchFromLlmsTxt(baseUrl, maxPages, onProgress) {
104
- const llmsUrl = `${new URL(baseUrl).origin}/llms.txt`;
105
- const res = await fetch(llmsUrl, { headers: { "User-Agent": "skilld/1.0" } }).catch(() => null);
106
- if (!res?.ok) return null;
107
- const llmsContent = await res.text();
108
- if (llmsContent.length < 50) return null;
109
- const links = parseLinks(llmsContent);
110
- const docs = [];
111
- let count = 0;
112
- for (const { title, url } of links.slice(0, maxPages)) {
113
- count++;
114
- onProgress?.({
115
- url,
116
- count,
117
- phase: "fetch"
118
- });
119
- const content = await fetchMarkdown(url);
120
- if (content && content.length >= 50) docs.push({
121
- url,
122
- title,
123
- content
124
- });
125
- }
126
- return {
127
- docs,
128
- llmsContent
129
- };
130
- }
131
- function parseLinks(content) {
132
- const links = [];
133
- const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
134
- let match;
135
- while ((match = linkRegex.exec(content)) !== null) {
136
- const [, title, url] = match;
137
- if (url.includes("/raw/") || url.endsWith(".md")) links.push({
138
- title,
139
- url
140
- });
141
- }
142
- return links;
143
- }
144
- async function fetchMarkdown(url) {
145
- const res = await fetch(url, { headers: { "User-Agent": "skilld/1.0" } }).catch(() => null);
146
- if (!res?.ok) return null;
147
- return res.text();
148
- }
149
- async function crawlSite(url, maxPages, onProgress) {
150
- const { htmlToMarkdown } = await import("mdream");
151
- const { crawlAndGenerate } = await import("@mdream/crawl");
152
- const { tmpdir } = await import("node:os");
153
- const { join } = await import("node:path");
154
- const docs = [];
155
- let count = 0;
156
- const outputDir = join(tmpdir(), `skilld-crawl-${Date.now()}`);
157
- await crawlAndGenerate({
158
- urls: [url],
159
- outputDir,
160
- maxRequestsPerCrawl: maxPages,
161
- followLinks: true,
162
- onPage: async ({ url: pageUrl, html, title }) => {
163
- count++;
164
- onProgress?.({
165
- url: pageUrl,
166
- count,
167
- phase: "fetch"
168
- });
169
- const markdown = htmlToMarkdown(html, { origin: new URL(pageUrl).origin });
170
- if (markdown && markdown.length >= 50) docs.push({
171
- url: pageUrl,
172
- title: title || pageUrl,
173
- content: markdown
174
- });
175
- }
176
- });
177
- return docs;
178
- }
179
- export { generateSkill };
180
-
181
- //# sourceMappingURL=index.mjs.map
1
+ import { a as getCacheDir, n as REFERENCES_DIR, o as getCacheKey, r as SEARCH_DB, s as getVersionKey, t as CACHE_DIR } from "./_chunks/config.mjs";
2
+ import { _ as writeToCache, h as readCachedDocs, n as clearCache, p as listCached, r as ensureCacheDir, s as isCached, t as clearAllCache, u as linkReferences } from "./_chunks/storage.mjs";
3
+ import "./cache/index.mjs";
4
+ import { createIndex, search, searchSnippets } from "./retriv/index.mjs";
5
+ import { S as fetchReadmeContent, _ as normalizeLlmsLinks, c as readLocalDependencies, d as resolvePackageDocs, h as fetchLlmsTxt, p as downloadLlmsDocs, r as fetchNpmPackage, v as parseMarkdownLinks } from "./_chunks/releases.mjs";
6
+ import "./sources/index.mjs";
7
+ export { CACHE_DIR, REFERENCES_DIR, SEARCH_DB, clearAllCache, clearCache, createIndex, downloadLlmsDocs, ensureCacheDir, fetchLlmsTxt, fetchNpmPackage, fetchReadmeContent, getCacheDir, getCacheKey, getVersionKey, isCached, linkReferences, listCached, normalizeLlmsLinks, parseMarkdownLinks, readCachedDocs, readLocalDependencies, resolvePackageDocs, search, searchSnippets, writeToCache };
@@ -0,0 +1,12 @@
1
+ import { a as SearchOptions, i as SearchFilter, n as Document, o as SearchResult, r as IndexConfig, s as SearchSnippet, t as ChunkEntity } from "../_chunks/types.mjs";
2
+
3
+ //#region src/retriv/index.d.ts
4
+ declare function createIndex(documents: Document[], config: IndexConfig): Promise<void>;
5
+ declare function search(query: string, config: IndexConfig, options?: SearchOptions): Promise<SearchResult[]>;
6
+ /**
7
+ * Search and return formatted snippets
8
+ */
9
+ declare function searchSnippets(query: string, config: IndexConfig, options?: SearchOptions): Promise<SearchSnippet[]>;
10
+ //#endregion
11
+ export { type ChunkEntity, type Document, type IndexConfig, type SearchFilter, type SearchOptions, type SearchResult, type SearchSnippet, createIndex, search, searchSnippets };
12
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/retriv/index.ts"],"mappings":";;;iBAkBsB,WAAA,CACpB,SAAA,EAAW,QAAA,IACX,MAAA,EAAQ,WAAA,GACP,OAAA;AAAA,iBAkBmB,MAAA,CACpB,KAAA,UACA,MAAA,EAAQ,WAAA,EACR,OAAA,GAAS,aAAA,GACR,OAAA,CAAQ,YAAA;;;;iBA6BW,cAAA,CACpB,KAAA,UACA,MAAA,EAAQ,WAAA,EACR,OAAA,GAAS,aAAA,GACR,OAAA,CAAQ,aAAA"}
@@ -0,0 +1,76 @@
1
+ import { createRetriv } from "retriv";
2
+ import { autoChunker } from "retriv/chunkers/auto";
3
+ import sqlite from "retriv/db/sqlite";
4
+ import { transformersJs } from "retriv/embeddings/transformers-js";
5
+ function getDb(config) {
6
+ return createRetriv({
7
+ driver: sqlite({
8
+ path: config.dbPath,
9
+ embeddings: transformersJs()
10
+ }),
11
+ chunking: autoChunker()
12
+ });
13
+ }
14
+ async function createIndex(documents, config) {
15
+ const db = await getDb(config);
16
+ const BATCH_SIZE = 5;
17
+ let indexed = 0;
18
+ for (let i = 0; i < documents.length; i += BATCH_SIZE) {
19
+ const batch = documents.slice(i, i + BATCH_SIZE);
20
+ await db.index(batch);
21
+ indexed += batch.length;
22
+ const last = batch[batch.length - 1];
23
+ config.onProgress?.(indexed, documents.length, {
24
+ id: last.id,
25
+ type: last.metadata?.type
26
+ });
27
+ }
28
+ await db.close?.();
29
+ }
30
+ async function search(query, config, options = {}) {
31
+ const { limit = 10, filter } = options;
32
+ const db = await getDb(config);
33
+ const results = await db.search(query, {
34
+ limit,
35
+ filter,
36
+ returnContent: true,
37
+ returnMetadata: true,
38
+ returnMeta: true
39
+ });
40
+ await db.close?.();
41
+ return results.map((r) => ({
42
+ id: r.id,
43
+ content: r.content ?? "",
44
+ score: r.score,
45
+ metadata: r.metadata ?? {},
46
+ highlights: r._meta?.highlights ?? [],
47
+ lineRange: r._chunk?.lineRange,
48
+ entities: r._chunk?.entities,
49
+ scope: r._chunk?.scope
50
+ }));
51
+ }
52
+ function stripFrontmatter(content) {
53
+ const match = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);
54
+ return match ? content.slice(match[0].length).trim() : content;
55
+ }
56
+ async function searchSnippets(query, config, options = {}) {
57
+ return (await search(query, config, options)).map((r) => {
58
+ const content = stripFrontmatter(r.content);
59
+ const source = r.metadata.source || r.id;
60
+ const lines = content.split("\n").length;
61
+ return {
62
+ package: r.metadata.package || "unknown",
63
+ source,
64
+ lineStart: r.lineRange?.[0] ?? 1,
65
+ lineEnd: r.lineRange?.[1] ?? lines,
66
+ content,
67
+ score: r.score,
68
+ highlights: r.highlights,
69
+ entities: r.entities,
70
+ scope: r.scope
71
+ };
72
+ });
73
+ }
74
+ export { createIndex, search, searchSnippets };
75
+
76
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/retriv/index.ts"],"sourcesContent":["import type { ChunkEntity, Document, IndexConfig, SearchFilter, SearchOptions, SearchResult, SearchSnippet } from './types'\nimport { createRetriv } from 'retriv'\nimport { autoChunker } from 'retriv/chunkers/auto'\nimport sqlite from 'retriv/db/sqlite'\nimport { transformersJs } from 'retriv/embeddings/transformers-js'\n\nexport type { ChunkEntity, Document, IndexConfig, SearchFilter, SearchOptions, SearchResult, SearchSnippet }\n\nfunction getDb(config: IndexConfig) {\n return createRetriv({\n driver: sqlite({\n path: config.dbPath,\n embeddings: transformersJs(),\n }),\n chunking: autoChunker(),\n })\n}\n\nexport async function createIndex(\n documents: Document[],\n config: IndexConfig,\n): Promise<void> {\n const db = await getDb(config)\n\n // Batch documents to report progress\n const BATCH_SIZE = 5\n let indexed = 0\n\n for (let i = 0; i < documents.length; i += BATCH_SIZE) {\n const batch = documents.slice(i, i + BATCH_SIZE)\n await db.index(batch)\n indexed += batch.length\n const last = batch[batch.length - 1]!\n config.onProgress?.(indexed, documents.length, { id: last.id, type: last.metadata?.type })\n }\n\n await db.close?.()\n}\n\nexport async function search(\n query: string,\n config: IndexConfig,\n options: SearchOptions = {},\n): Promise<SearchResult[]> {\n const { limit = 10, filter } = options\n const db = await getDb(config)\n const results = await db.search(query, { limit, filter, returnContent: true, returnMetadata: true, returnMeta: true })\n await db.close?.()\n\n return results.map(r => ({\n id: r.id,\n content: r.content ?? '',\n score: r.score,\n metadata: r.metadata ?? {},\n highlights: r._meta?.highlights ?? [],\n lineRange: r._chunk?.lineRange,\n entities: r._chunk?.entities,\n scope: r._chunk?.scope,\n }))\n}\n\n/**\n * Strip YAML frontmatter from markdown content\n */\nfunction stripFrontmatter(content: string): string {\n const match = content.match(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n/)\n return match ? content.slice(match[0].length).trim() : content\n}\n\n/**\n * Search and return formatted snippets\n */\nexport async function searchSnippets(\n query: string,\n config: IndexConfig,\n options: SearchOptions = {},\n): Promise<SearchSnippet[]> {\n const results = await search(query, config, options)\n\n return results.map((r) => {\n const content = stripFrontmatter(r.content)\n const source = r.metadata.source || r.id\n const lines = content.split('\\n').length\n\n return {\n package: r.metadata.package || 'unknown',\n source,\n lineStart: r.lineRange?.[0] ?? 1,\n lineEnd: r.lineRange?.[1] ?? lines,\n content,\n score: r.score,\n highlights: r.highlights,\n entities: r.entities,\n scope: r.scope,\n }\n })\n}\n"],"mappings":";;;;AAQA,SAAS,MAAM,QAAqB;AAClC,QAAO,aAAa;EAClB,QAAQ,OAAO;GACb,MAAM,OAAO;GACb,YAAY,gBAAA;GACb,CAAC;EACF,UAAU,aAAA;EACX,CAAC;;AAGJ,eAAsB,YACpB,WACA,QACe;CACf,MAAM,KAAK,MAAM,MAAM,OAAO;CAG9B,MAAM,aAAa;CACnB,IAAI,UAAU;AAEd,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,YAAY;EACrD,MAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,WAAW;AAChD,QAAM,GAAG,MAAM,MAAM;AACrB,aAAW,MAAM;EACjB,MAAM,OAAO,MAAM,MAAM,SAAS;AAClC,SAAO,aAAa,SAAS,UAAU,QAAQ;GAAE,IAAI,KAAK;GAAI,MAAM,KAAK,UAAU;GAAM,CAAC;;AAG5F,OAAM,GAAG,SAAS;;AAGpB,eAAsB,OACpB,OACA,QACA,UAAyB,EAAE,EACF;CACzB,MAAM,EAAE,QAAQ,IAAI,WAAW;CAC/B,MAAM,KAAK,MAAM,MAAM,OAAO;CAC9B,MAAM,UAAU,MAAM,GAAG,OAAO,OAAO;EAAE;EAAO;EAAQ,eAAe;EAAM,gBAAgB;EAAM,YAAY;EAAM,CAAC;AACtH,OAAM,GAAG,SAAS;AAElB,QAAO,QAAQ,KAAI,OAAM;EACvB,IAAI,EAAE;EACN,SAAS,EAAE,WAAW;EACtB,OAAO,EAAE;EACT,UAAU,EAAE,YAAY,EAAE;EAC1B,YAAY,EAAE,OAAO,cAAc,EAAE;EACrC,WAAW,EAAE,QAAQ;EACrB,UAAU,EAAE,QAAQ;EACpB,OAAO,EAAE,QAAQ;EAClB,EAAE;;AAML,SAAS,iBAAiB,SAAyB;CACjD,MAAM,QAAQ,QAAQ,MAAM,iCAAiC;AAC7D,QAAO,QAAQ,QAAQ,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG;;AAMzD,eAAsB,eACpB,OACA,QACA,UAAyB,EAAE,EACD;AAG1B,SAFgB,MAAM,OAAO,OAAO,QAAQ,QAAQ,EAErC,KAAK,MAAM;EACxB,MAAM,UAAU,iBAAiB,EAAE,QAAQ;EAC3C,MAAM,SAAS,EAAE,SAAS,UAAU,EAAE;EACtC,MAAM,QAAQ,QAAQ,MAAM,KAAK,CAAC;AAElC,SAAO;GACL,SAAS,EAAE,SAAS,WAAW;GAC/B;GACA,WAAW,EAAE,YAAY,MAAM;GAC/B,SAAS,EAAE,YAAY,MAAM;GAC7B;GACA,OAAO,EAAE;GACT,YAAY,EAAE;GACd,UAAU,EAAE;GACZ,OAAO,EAAE;GACV;GACD"}
@@ -0,0 +1,2 @@
1
+ import { A as parseMarkdownLinks, B as fetchGitHubIssues, C as resolvePackageDocs, D as fetchLlmsTxt, E as extractSections, F as NpmPackageInfo, G as fetchGitHubRepoMeta, H as isGhAvailable, I as ResolveAttempt, J as EntryFile, K as fetchReadme, L as ResolveResult, M as LlmsContent, N as LlmsLink, O as fetchLlmsUrl, P as LocalDependency, Q as formatDiscussionsAsMarkdown, R as ResolvedPackage, S as resolveLocalPackageDocs, T as downloadLlmsDocs, U as GitDocsResult, V as formatIssuesAsMarkdown, W as fetchGitDocs, X as GitHubDiscussion, Y as resolveEntryFiles, Z as fetchGitHubDiscussions, _ as fetchPkgDist, a as verifyUrl, b as readLocalDependencies, c as DOC_OVERRIDES, d as LocalPackageInfo, f as ResolveOptions, g as fetchNpmRegistryMeta, h as fetchNpmPackage, i as parseGitHubUrl, j as FetchedDoc, k as normalizeLlmsLinks, l as DocOverride, m as fetchLatestVersion, n as isGitHubRepoUrl, o as GitHubRelease, p as ResolveStep, q as fetchReadmeContent, r as normalizeRepoUrl, s as fetchReleaseNotes, t as fetchText, u as getDocOverride, v as getInstalledSkillVersion, w as resolvePackageDocsWithAttempts, x as readLocalPackageInfo, y as parseVersionSpecifier, z as GitHubIssue } from "../_chunks/utils.mjs";
2
+ export { DOC_OVERRIDES, type DocOverride, type EntryFile, type FetchedDoc, type GitDocsResult, type GitHubDiscussion, type GitHubIssue, type GitHubRelease, type LlmsContent, type LlmsLink, type LocalDependency, type LocalPackageInfo, type NpmPackageInfo, type ResolveAttempt, type ResolveOptions, type ResolveResult, type ResolveStep, type ResolvedPackage, downloadLlmsDocs, extractSections, fetchGitDocs, fetchGitHubDiscussions, fetchGitHubIssues, fetchGitHubRepoMeta, fetchLatestVersion, fetchLlmsTxt, fetchLlmsUrl, fetchNpmPackage, fetchNpmRegistryMeta, fetchPkgDist, fetchReadme, fetchReadmeContent, fetchReleaseNotes, fetchText, formatDiscussionsAsMarkdown, formatIssuesAsMarkdown, getDocOverride, getInstalledSkillVersion, isGhAvailable, isGitHubRepoUrl, normalizeLlmsLinks, normalizeRepoUrl, parseGitHubUrl, parseMarkdownLinks, parseVersionSpecifier, readLocalDependencies, readLocalPackageInfo, resolveEntryFiles, resolveLocalPackageDocs, resolvePackageDocs, resolvePackageDocsWithAttempts, verifyUrl };
@@ -0,0 +1,3 @@
1
+ import "../_chunks/config.mjs";
2
+ import { A as resolveEntryFiles, C as fetchText, D as verifyUrl, E as parseGitHubUrl, F as isGhAvailable, M as formatDiscussionsAsMarkdown, N as fetchGitHubIssues, O as DOC_OVERRIDES, P as formatIssuesAsMarkdown, S as fetchReadmeContent, T as normalizeRepoUrl, _ as normalizeLlmsLinks, a as fetchPkgDist, b as fetchGitHubRepoMeta, c as readLocalDependencies, d as resolvePackageDocs, f as resolvePackageDocsWithAttempts, g as fetchLlmsUrl, h as fetchLlmsTxt, i as fetchNpmRegistryMeta, j as fetchGitHubDiscussions, k as getDocOverride, l as readLocalPackageInfo, m as extractSections, n as fetchLatestVersion, o as getInstalledSkillVersion, p as downloadLlmsDocs, r as fetchNpmPackage, s as parseVersionSpecifier, t as fetchReleaseNotes, u as resolveLocalPackageDocs, v as parseMarkdownLinks, w as isGitHubRepoUrl, x as fetchReadme, y as fetchGitDocs } from "../_chunks/releases.mjs";
3
+ export { DOC_OVERRIDES, downloadLlmsDocs, extractSections, fetchGitDocs, fetchGitHubDiscussions, fetchGitHubIssues, fetchGitHubRepoMeta, fetchLatestVersion, fetchLlmsTxt, fetchLlmsUrl, fetchNpmPackage, fetchNpmRegistryMeta, fetchPkgDist, fetchReadme, fetchReadmeContent, fetchReleaseNotes, fetchText, formatDiscussionsAsMarkdown, formatIssuesAsMarkdown, getDocOverride, getInstalledSkillVersion, isGhAvailable, isGitHubRepoUrl, normalizeLlmsLinks, normalizeRepoUrl, parseGitHubUrl, parseMarkdownLinks, parseVersionSpecifier, readLocalDependencies, readLocalPackageInfo, resolveEntryFiles, resolveLocalPackageDocs, resolvePackageDocs, resolvePackageDocsWithAttempts, verifyUrl };
package/dist/types.d.mts CHANGED
@@ -1,37 +1,4 @@
1
- //#region src/types.d.ts
2
- interface SkillConfig {
3
- /** Base URL or llms.txt URL */
4
- url: string;
5
- /** Output directory for skill files */
6
- outputDir?: string;
7
- /** Chunk size in characters */
8
- chunkSize?: number;
9
- /** Chunk overlap in characters */
10
- chunkOverlap?: number;
11
- /** Max pages to fetch */
12
- maxPages?: number;
13
- /** Skip llms.txt check and always crawl */
14
- skipLlmsTxt?: boolean;
15
- /** Embedding model */
16
- model?: string;
17
- }
18
- interface SkillResult {
19
- /** Site name (hostname) */
20
- siteName: string;
21
- /** Path to SKILL.md */
22
- skillPath: string;
23
- /** Path to references directory */
24
- referencesDir: string;
25
- /** Path to search database */
26
- dbPath: string;
27
- /** Number of chunks indexed */
28
- chunkCount: number;
29
- }
30
- interface FetchedDoc {
31
- url: string;
32
- title: string;
33
- content: string;
34
- }
35
- //#endregion
36
- export { FetchedDoc, SkillConfig, SkillResult };
37
- //# sourceMappingURL=types.d.mts.map
1
+ import { S as CacheConfig, w as CachedPackage } from "./_chunks/version.mjs";
2
+ import { a as SearchOptions, i as SearchFilter, n as Document, o as SearchResult, r as IndexConfig, s as SearchSnippet } from "./_chunks/types.mjs";
3
+ import { M as LlmsContent, N as LlmsLink, j as FetchedDoc } from "./_chunks/utils.mjs";
4
+ export { type CacheConfig, type CachedPackage, type Document, type FetchedDoc, type IndexConfig, type LlmsContent, type LlmsLink, type SearchFilter, type SearchOptions, type SearchResult, type SearchSnippet };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "skilld",
3
3
  "type": "module",
4
- "version": "0.0.1",
5
- "description": "Generate searchable skills from documentation sites using llms.txt and crawling.",
4
+ "version": "0.1.1",
5
+ "description": "Generate AI agent skills from npm package documentation",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
8
8
  "email": "harlan@harlanzw.com",
@@ -13,6 +13,17 @@
13
13
  "type": "git",
14
14
  "url": "https://github.com/harlan-zw/skilld"
15
15
  },
16
+ "keywords": [
17
+ "ai",
18
+ "agent",
19
+ "skills",
20
+ "npm",
21
+ "documentation",
22
+ "claude",
23
+ "llm",
24
+ "cursor",
25
+ "codex"
26
+ ],
16
27
  "exports": {
17
28
  ".": {
18
29
  "types": "./dist/index.d.mts",
@@ -25,28 +36,43 @@
25
36
  "skilld": "./dist/cli.mjs"
26
37
  },
27
38
  "files": [
28
- "dist"
39
+ "dist",
40
+ "skills"
29
41
  ],
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
30
45
  "dependencies": {
46
+ "@clack/prompts": "^1.0.0",
31
47
  "@huggingface/transformers": "^3.8.1",
32
- "@mdream/crawl": "^0.15.3",
33
- "citty": "^0.1.6",
48
+ "citty": "^0.2.0",
34
49
  "consola": "^3.4.2",
35
- "mdream": "^0.15.3",
36
- "retriv": "link:../retriv"
50
+ "globby": "^16.1.0",
51
+ "log-update": "^7.1.0",
52
+ "mlly": "^1.8.0",
53
+ "oxc-parser": "^0.112.0",
54
+ "p-limit": "^7.3.0",
55
+ "retriv": "^0.8.3",
56
+ "sqlite-vec": "^0.1.6",
57
+ "typescript": "^5.9.3"
37
58
  },
38
59
  "devDependencies": {
39
- "@antfu/eslint-config": "^6.7.3",
40
- "@types/node": "^22.10.0",
41
- "obuild": "^0.4.14",
42
- "typescript": "^5.9.3",
43
- "vitest": "^4.0.16"
60
+ "@antfu/eslint-config": "^7.2.0",
61
+ "@types/node": "^25.2.1",
62
+ "@vitest/coverage-v8": "^4.0.18",
63
+ "bumpp": "^10.4.1",
64
+ "evalite": "^0.19.0",
65
+ "obuild": "^0.4.27",
66
+ "vitest": "^4.0.18"
44
67
  },
45
68
  "scripts": {
46
69
  "build": "obuild",
47
70
  "dev:prepare": "obuild --stub",
48
71
  "lint": "eslint .",
72
+ "lint:fix": "eslint . --fix",
49
73
  "typecheck": "tsc --noEmit",
50
- "test": "vitest"
74
+ "test": "vitest",
75
+ "test:run": "vitest run",
76
+ "release": "bumpp && pnpm publish"
51
77
  }
52
78
  }
package/dist/agents.d.mts DELETED
@@ -1,56 +0,0 @@
1
- //#region src/agents.d.ts
2
- /**
3
- * Agent detection and skill installation
4
- * Writes directly to agent skill directories in the project
5
- */
6
- type AgentType = 'claude-code' | 'cursor' | 'windsurf' | 'cline' | 'codex' | 'github-copilot' | 'gemini-cli' | 'goose' | 'amp' | 'opencode' | 'roo';
7
- interface AgentConfig {
8
- name: AgentType;
9
- displayName: string;
10
- /** Project-level skills directory (e.g., .claude/skills) */
11
- skillsDir: string;
12
- /** Global skills directory (e.g., ~/.claude/skills) */
13
- globalSkillsDir: string | undefined;
14
- /** Check if agent is installed on the system */
15
- detectInstalled: () => boolean;
16
- }
17
- declare const agents: Record<AgentType, AgentConfig>;
18
- /**
19
- * Detect which agents are installed on the system
20
- */
21
- declare function detectInstalledAgents(): AgentType[];
22
- /**
23
- * Detect which agent is currently running this command
24
- * Returns the active agent based on environment variables and context
25
- */
26
- declare function detectCurrentAgent(): AgentType | null;
27
- /**
28
- * Sanitize skill name for filesystem
29
- */
30
- declare function sanitizeName(name: string): string;
31
- /**
32
- * Install a skill directly to agent skill directories
33
- * Writes to each agent's skill folder in the project (e.g., .claude/skills/package-name/)
34
- */
35
- declare function installSkillForAgents(skillName: string, skillContent: string, options?: {
36
- global?: boolean;
37
- cwd?: string;
38
- agents?: AgentType[]; /** Additional files to write (filename -> content) */
39
- files?: Record<string, string>;
40
- }): {
41
- installed: AgentType[];
42
- paths: string[];
43
- };
44
- interface SkillMetadata {
45
- name: string;
46
- version?: string;
47
- description?: string;
48
- }
49
- /**
50
- * Generate SKILL.md frontmatter content
51
- * The description tells the agent when to use this skill
52
- */
53
- declare function generateSkillMd(meta: SkillMetadata, body: string): string;
54
- //#endregion
55
- export { AgentConfig, AgentType, SkillMetadata, agents, detectCurrentAgent, detectInstalledAgents, generateSkillMd, installSkillForAgents, sanitizeName };
56
- //# sourceMappingURL=agents.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"agents.d.mts","names":[],"sources":["../src/agents.ts"],"mappings":";;AAcA;;;KAAY,SAAA;AAAA,UAaK,WAAA;EACf,IAAA,EAAM,SAAA;EACN,WAAA;;EAEA,SAAA;EAHA;EAKA,eAAA;EAJA;EAMA,eAAA;AAAA;AAAA,cAGW,MAAA,EAAQ,MAAA,CAAO,SAAA,EAAW,WAAA;;;AAAvC;iBAmFgB,qBAAA,CAAA,GAAyB,SAAA;;;;;iBAUzB,kBAAA,CAAA,GAAsB,SAAA;;;;iBAyDtB,YAAA,CAAa,IAAA;;;AAnE7B;;iBA+EgB,qBAAA,CACd,SAAA,UACA,YAAA,UACA,OAAA;EACE,MAAA;EACA,GAAA;EACA,MAAA,GAAS,SAAA,IA3EqB;EA6E9B,KAAA,GAAQ,MAAA;AAAA;EAEP,SAAA,EAAW,SAAA;EAAa,KAAA;AAAA;AAAA,UAuCZ,aAAA;EACf,IAAA;EACA,OAAA;EACA,WAAA;AAAA;;;;;iBAOc,eAAA,CACd,IAAA,EAAM,aAAA,EACN,IAAA"}
package/dist/agents.mjs DELETED
@@ -1,148 +0,0 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { homedir } from "node:os";
4
- const home = homedir();
5
- const configHome = process.env.XDG_CONFIG_HOME || join(home, ".config");
6
- const claudeHome = process.env.CLAUDE_CONFIG_DIR || join(home, ".claude");
7
- const codexHome = process.env.CODEX_HOME || join(home, ".codex");
8
- const agents = {
9
- "claude-code": {
10
- name: "claude-code",
11
- displayName: "Claude Code",
12
- skillsDir: ".claude/skills",
13
- globalSkillsDir: join(claudeHome, "skills"),
14
- detectInstalled: () => existsSync(claudeHome)
15
- },
16
- cursor: {
17
- name: "cursor",
18
- displayName: "Cursor",
19
- skillsDir: ".cursor/skills",
20
- globalSkillsDir: join(home, ".cursor/skills"),
21
- detectInstalled: () => existsSync(join(home, ".cursor"))
22
- },
23
- windsurf: {
24
- name: "windsurf",
25
- displayName: "Windsurf",
26
- skillsDir: ".windsurf/skills",
27
- globalSkillsDir: join(home, ".codeium/windsurf/skills"),
28
- detectInstalled: () => existsSync(join(home, ".codeium/windsurf"))
29
- },
30
- cline: {
31
- name: "cline",
32
- displayName: "Cline",
33
- skillsDir: ".cline/skills",
34
- globalSkillsDir: join(home, ".cline/skills"),
35
- detectInstalled: () => existsSync(join(home, ".cline"))
36
- },
37
- codex: {
38
- name: "codex",
39
- displayName: "Codex",
40
- skillsDir: ".codex/skills",
41
- globalSkillsDir: join(codexHome, "skills"),
42
- detectInstalled: () => existsSync(codexHome)
43
- },
44
- "github-copilot": {
45
- name: "github-copilot",
46
- displayName: "GitHub Copilot",
47
- skillsDir: ".github/skills",
48
- globalSkillsDir: join(home, ".copilot/skills"),
49
- detectInstalled: () => existsSync(join(home, ".copilot"))
50
- },
51
- "gemini-cli": {
52
- name: "gemini-cli",
53
- displayName: "Gemini CLI",
54
- skillsDir: ".gemini/skills",
55
- globalSkillsDir: join(home, ".gemini/skills"),
56
- detectInstalled: () => existsSync(join(home, ".gemini"))
57
- },
58
- goose: {
59
- name: "goose",
60
- displayName: "Goose",
61
- skillsDir: ".goose/skills",
62
- globalSkillsDir: join(configHome, "goose/skills"),
63
- detectInstalled: () => existsSync(join(configHome, "goose"))
64
- },
65
- amp: {
66
- name: "amp",
67
- displayName: "Amp",
68
- skillsDir: ".agents/skills",
69
- globalSkillsDir: join(configHome, "agents/skills"),
70
- detectInstalled: () => existsSync(join(configHome, "amp"))
71
- },
72
- opencode: {
73
- name: "opencode",
74
- displayName: "OpenCode",
75
- skillsDir: ".opencode/skills",
76
- globalSkillsDir: join(configHome, "opencode/skills"),
77
- detectInstalled: () => existsSync(join(configHome, "opencode"))
78
- },
79
- roo: {
80
- name: "roo",
81
- displayName: "Roo Code",
82
- skillsDir: ".roo/skills",
83
- globalSkillsDir: join(home, ".roo/skills"),
84
- detectInstalled: () => existsSync(join(home, ".roo"))
85
- }
86
- };
87
- function detectInstalledAgents() {
88
- return Object.entries(agents).filter(([_, config]) => config.detectInstalled()).map(([type]) => type);
89
- }
90
- function detectCurrentAgent() {
91
- if (process.env.CLAUDE_CODE || process.env.CLAUDE_CONFIG_DIR) return "claude-code";
92
- if (process.env.CURSOR_SESSION || process.env.CURSOR_TRACE_ID) return "cursor";
93
- if (process.env.WINDSURF_SESSION) return "windsurf";
94
- if (process.env.CLINE_TASK_ID) return "cline";
95
- if (process.env.CODEX_HOME || process.env.CODEX_SESSION) return "codex";
96
- if (process.env.GITHUB_COPILOT_SESSION) return "github-copilot";
97
- if (process.env.GEMINI_API_KEY && process.env.GEMINI_SESSION) return "gemini-cli";
98
- if (process.env.GOOSE_SESSION) return "goose";
99
- if (process.env.AMP_SESSION) return "amp";
100
- if (process.env.OPENCODE_SESSION) return "opencode";
101
- if (process.env.ROO_SESSION) return "roo";
102
- const cwd = process.cwd();
103
- if (existsSync(join(cwd, ".claude"))) return "claude-code";
104
- if (existsSync(join(cwd, ".cursor"))) return "cursor";
105
- if (existsSync(join(cwd, ".windsurf"))) return "windsurf";
106
- if (existsSync(join(cwd, ".cline"))) return "cline";
107
- return null;
108
- }
109
- function sanitizeName(name) {
110
- return name.toLowerCase().replace(/[^a-z0-9._]+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "").slice(0, 255) || "unnamed-skill";
111
- }
112
- function installSkillForAgents(skillName, skillContent, options = {}) {
113
- const isGlobal = options.global ?? false;
114
- const cwd = options.cwd || process.cwd();
115
- const sanitized = sanitizeName(skillName);
116
- const targetAgents = options.agents || detectInstalledAgents();
117
- const installed = [];
118
- const paths = [];
119
- for (const agentType of targetAgents) {
120
- const agent = agents[agentType];
121
- if (isGlobal && !agent.globalSkillsDir) continue;
122
- const skillDir = join(isGlobal ? agent.globalSkillsDir : join(cwd, agent.skillsDir), sanitized);
123
- mkdirSync(skillDir, { recursive: true });
124
- writeFileSync(join(skillDir, "SKILL.md"), skillContent);
125
- if (options.files) for (const [filename, content] of Object.entries(options.files)) writeFileSync(join(skillDir, filename), content);
126
- installed.push(agentType);
127
- paths.push(skillDir);
128
- }
129
- return {
130
- installed,
131
- paths
132
- };
133
- }
134
- function generateSkillMd(meta, body) {
135
- const { name, version, description: packageDescription } = meta;
136
- const description = packageDescription ? `${packageDescription} Use this skill when working with ${name}, importing from "${name}", or when the user asks about ${name} features, API, or usage.` : `Documentation for ${name}. Use this skill when working with ${name} or importing from "${name}".`;
137
- const frontmatter = [
138
- "---",
139
- `name: ${name}`,
140
- `description: ${description}`
141
- ];
142
- if (version) frontmatter.push(`version: "${version}"`);
143
- frontmatter.push("---");
144
- return frontmatter.join("\n") + "\n\n" + body;
145
- }
146
- export { agents, detectCurrentAgent, detectInstalledAgents, generateSkillMd, installSkillForAgents, sanitizeName };
147
-
148
- //# sourceMappingURL=agents.mjs.map