skilld 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -88
  3. package/dist/_chunks/config.mjs +20 -0
  4. package/dist/_chunks/config.mjs.map +1 -0
  5. package/dist/_chunks/llm.mjs +877 -0
  6. package/dist/_chunks/llm.mjs.map +1 -0
  7. package/dist/_chunks/releases.mjs +986 -0
  8. package/dist/_chunks/releases.mjs.map +1 -0
  9. package/dist/_chunks/storage.mjs +198 -0
  10. package/dist/_chunks/storage.mjs.map +1 -0
  11. package/dist/_chunks/sync-parallel.mjs +540 -0
  12. package/dist/_chunks/sync-parallel.mjs.map +1 -0
  13. package/dist/_chunks/types.d.mts +87 -0
  14. package/dist/_chunks/types.d.mts.map +1 -0
  15. package/dist/_chunks/utils.d.mts +352 -0
  16. package/dist/_chunks/utils.d.mts.map +1 -0
  17. package/dist/_chunks/version.d.mts +147 -0
  18. package/dist/_chunks/version.d.mts.map +1 -0
  19. package/dist/agent/index.d.mts +205 -0
  20. package/dist/agent/index.d.mts.map +1 -0
  21. package/dist/agent/index.mjs +2 -0
  22. package/dist/cache/index.d.mts +2 -0
  23. package/dist/cache/index.mjs +3 -0
  24. package/dist/cli.mjs +2650 -449
  25. package/dist/cli.mjs.map +1 -1
  26. package/dist/index.d.mts +5 -14
  27. package/dist/index.mjs +7 -181
  28. package/dist/retriv/index.d.mts +12 -0
  29. package/dist/retriv/index.d.mts.map +1 -0
  30. package/dist/retriv/index.mjs +76 -0
  31. package/dist/retriv/index.mjs.map +1 -0
  32. package/dist/sources/index.d.mts +2 -0
  33. package/dist/sources/index.mjs +3 -0
  34. package/dist/types.d.mts +4 -37
  35. package/package.json +39 -13
  36. package/dist/agents.d.mts +0 -56
  37. package/dist/agents.d.mts.map +0 -1
  38. package/dist/agents.mjs +0 -148
  39. package/dist/agents.mjs.map +0 -1
  40. package/dist/index.d.mts.map +0 -1
  41. package/dist/index.mjs.map +0 -1
  42. package/dist/npm.d.mts +0 -48
  43. package/dist/npm.d.mts.map +0 -1
  44. package/dist/npm.mjs +0 -90
  45. package/dist/npm.mjs.map +0 -1
  46. package/dist/split-text.d.mts +0 -24
  47. package/dist/split-text.d.mts.map +0 -1
  48. package/dist/split-text.mjs +0 -87
  49. package/dist/split-text.mjs.map +0 -1
  50. package/dist/types.d.mts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"releases.mjs","names":[],"sources":["../../src/sources/issues.ts","../../src/sources/discussions.ts","../../src/sources/entries.ts","../../src/sources/overrides.ts","../../src/sources/utils.ts","../../src/sources/github.ts","../../src/sources/llms.ts","../../src/sources/npm.ts","../../src/sources/releases.ts"],"sourcesContent":["/**\n * GitHub issues fetching via gh CLI\n */\n\nimport { execSync } from 'node:child_process'\n\nexport interface GitHubIssue {\n number: number\n title: string\n state: string\n labels: string[]\n body: string\n createdAt: string\n url: string\n}\n\nlet _ghAvailable: boolean | undefined\n\n/**\n * Check if gh CLI is installed and authenticated (cached)\n */\nexport function isGhAvailable(): boolean {\n if (_ghAvailable !== undefined)\n return _ghAvailable\n try {\n execSync('gh auth status', { stdio: 'ignore' })\n return (_ghAvailable = true)\n }\n catch {\n return (_ghAvailable = false)\n }\n}\n\n/**\n * Fetch last N issues from a GitHub repo using gh CLI\n */\nexport async function fetchGitHubIssues(\n owner: string,\n repo: string,\n limit = 20,\n): Promise<GitHubIssue[]> {\n if (!isGhAvailable())\n return []\n\n try {\n // Fetch more than limit to compensate for filtered PRs/bots, use per_page query param\n const fetchCount = Math.min(limit * 3, 100)\n const result = execSync(\n `gh api \"repos/${owner}/${repo}/issues?per_page=${fetchCount}&state=all\" -q '.[] | {number, title, state, labels: [.labels[].name], body, createdAt: .created_at, url: .html_url, isPr: (.pull_request != null), user: .user.login, userType: .user.type}'`,\n { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 },\n )\n\n const BOT_USERS = new Set(['renovate[bot]', 'dependabot[bot]', 'renovate-bot', 'dependabot', 'github-actions[bot]'])\n\n // gh outputs one JSON object per line — filter out PRs and bot-created issues\n return result\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map(line => JSON.parse(line) as GitHubIssue & { isPr: boolean, user: string, userType: string })\n .filter(issue => !issue.isPr && !BOT_USERS.has(issue.user) && issue.userType !== 'Bot')\n .slice(0, limit)\n .map(({ isPr: _, user: __, userType: ___, ...issue }) => issue)\n }\n catch {\n return []\n }\n}\n\n/**\n * Format issues as markdown for agent consumption\n */\nexport function formatIssuesAsMarkdown(issues: GitHubIssue[]): string {\n if (issues.length === 0)\n return ''\n\n const lines = ['# Recent Issues\\n']\n\n for (const issue of issues) {\n const labels = issue.labels.length > 0 ? ` [${issue.labels.join(', ')}]` : ''\n lines.push(`## #${issue.number}: ${issue.title}${labels}`)\n lines.push(`State: ${issue.state} | Created: ${issue.createdAt.split('T')[0]}`)\n lines.push(`URL: ${issue.url}\\n`)\n\n if (issue.body) {\n // Truncate long bodies\n const body = issue.body.length > 500\n ? `${issue.body.slice(0, 500)}...`\n : issue.body\n lines.push(body)\n }\n lines.push('\\n---\\n')\n }\n\n return lines.join('\\n')\n}\n","/**\n * GitHub discussions fetching via gh CLI GraphQL\n */\n\nimport { execSync } from 'node:child_process'\nimport { isGhAvailable } from './issues'\n\nexport interface GitHubDiscussion {\n number: number\n title: string\n body: string\n category: string\n createdAt: string\n url: string\n upvoteCount: number\n comments: number\n}\n\n/**\n * Fetch last N discussions from a GitHub repo using gh CLI GraphQL\n */\nexport async function fetchGitHubDiscussions(\n owner: string,\n repo: string,\n limit = 20,\n): Promise<GitHubDiscussion[]> {\n if (!isGhAvailable())\n return []\n\n try {\n const query = `query { repository(owner: \"${owner}\", name: \"${repo}\") { discussions(first: ${Math.min(limit * 2, 50)}, orderBy: {field: CREATED_AT, direction: DESC}) { nodes { number title body category { name } createdAt url upvoteCount comments { totalCount } author { login } } } } }`\n\n const result = execSync(\n `gh api graphql -f query='${query}'`,\n { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 },\n )\n\n const data = JSON.parse(result)\n const nodes = data?.data?.repository?.discussions?.nodes\n if (!Array.isArray(nodes))\n return []\n\n const BOT_USERS = new Set(['renovate[bot]', 'dependabot[bot]', 'renovate-bot', 'dependabot', 'github-actions[bot]'])\n\n return nodes\n .filter((d: any) => d.author && !BOT_USERS.has(d.author.login))\n .slice(0, limit)\n .map((d: any) => ({\n number: d.number,\n title: d.title,\n body: d.body || '',\n category: d.category?.name || '',\n createdAt: d.createdAt,\n url: d.url,\n upvoteCount: d.upvoteCount || 0,\n comments: d.comments?.totalCount || 0,\n }))\n }\n catch {\n return []\n }\n}\n\n/**\n * Format discussions as markdown for agent consumption\n */\nexport function formatDiscussionsAsMarkdown(discussions: GitHubDiscussion[]): string {\n if (discussions.length === 0)\n return ''\n\n const lines = ['# Recent Discussions\\n']\n\n for (const d of discussions) {\n const meta = [\n d.category && `Category: ${d.category}`,\n `Created: ${d.createdAt.split('T')[0]}`,\n d.upvoteCount > 0 && `Upvotes: ${d.upvoteCount}`,\n d.comments > 0 && `Comments: ${d.comments}`,\n ].filter(Boolean).join(' | ')\n\n lines.push(`## #${d.number}: ${d.title}`)\n lines.push(meta)\n lines.push(`URL: ${d.url}\\n`)\n\n if (d.body) {\n const body = d.body.length > 500\n ? `${d.body.slice(0, 500)}...`\n : d.body\n lines.push(body)\n }\n lines.push('\\n---\\n')\n }\n\n return lines.join('\\n')\n}\n","/**\n * Globs .d.ts type definition files from a package for search indexing.\n * Only types — source code is too verbose.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { globby } from 'globby'\n\nexport interface EntryFile {\n path: string\n content: string\n type: 'types' | 'source'\n}\n\nconst SKIP_DIRS = [\n 'node_modules',\n '_vendor',\n '__tests__',\n '__mocks__',\n '__fixtures__',\n 'test',\n 'tests',\n 'fixture',\n 'fixtures',\n 'locales',\n 'locale',\n 'i18n',\n '.git',\n]\n\nconst SKIP_PATTERNS = [\n '*.min.*',\n '*.prod.*',\n '*.global.*',\n '*.browser.*',\n '*.map',\n '*.map.js',\n 'CHANGELOG*',\n 'LICENSE*',\n 'README*',\n]\n\nconst MAX_FILE_SIZE = 500 * 1024 // 500KB per file\n\n/**\n * Glob .d.ts type definition files from a package directory, skipping junk.\n */\nexport async function resolveEntryFiles(packageDir: string): Promise<EntryFile[]> {\n if (!existsSync(join(packageDir, 'package.json')))\n return []\n\n const ignore = [\n ...SKIP_DIRS.map(d => `**/${d}/**`),\n ...SKIP_PATTERNS,\n ]\n\n const files = await globby(['**/*.d.{ts,mts,cts}'], {\n cwd: packageDir,\n ignore,\n absolute: false,\n })\n\n const entries: EntryFile[] = []\n\n for (const file of files) {\n const absPath = join(packageDir, file)\n let content: string\n try {\n content = readFileSync(absPath, 'utf-8')\n }\n catch {\n continue\n }\n\n if (content.length > MAX_FILE_SIZE)\n continue\n\n entries.push({ path: file, content, type: 'types' })\n }\n\n return entries\n}\n","/**\n * Hardcoded overrides for packages whose docs live in a different repo\n * than what npm registry points to.\n */\n\nexport interface DocOverride {\n /** GitHub owner */\n owner: string\n /** GitHub repo */\n repo: string\n /** Path prefix to filter markdown files (e.g. 'src') */\n path: string\n /** Branch or ref to use (default: 'main') */\n ref?: string\n /** Homepage/docs URL */\n homepage?: string\n}\n\n/**\n * Map of package name -> doc source override.\n * Keyed by npm package name.\n */\nexport const DOC_OVERRIDES: Record<string, DocOverride> = {\n vue: {\n owner: 'vuejs',\n repo: 'docs',\n path: 'src',\n homepage: 'https://vuejs.org',\n },\n}\n\nexport function getDocOverride(packageName: string): DocOverride | undefined {\n return DOC_OVERRIDES[packageName]\n}\n","/**\n * Shared utilities for doc resolution\n */\n\nconst USER_AGENT = 'skilld/1.0'\n\n/**\n * Fetch text content from URL\n */\nexport async function fetchText(url: string): Promise<string | null> {\n const res = await fetch(url, {\n headers: { 'User-Agent': USER_AGENT },\n }).catch(() => null)\n\n if (!res?.ok)\n return null\n return res.text()\n}\n\n/**\n * Verify URL exists and is not HTML (likely 404 page)\n */\nexport async function verifyUrl(url: string): Promise<boolean> {\n const res = await fetch(url, {\n method: 'HEAD',\n headers: { 'User-Agent': USER_AGENT },\n }).catch(() => null)\n\n if (!res?.ok)\n return false\n\n const contentType = res.headers.get('content-type') || ''\n return !contentType.includes('text/html')\n}\n\n/**\n * Check if URL is a GitHub repo URL (not a docs site)\n */\nexport function isGitHubRepoUrl(url: string): boolean {\n try {\n const parsed = new URL(url)\n return parsed.hostname === 'github.com' || parsed.hostname === 'www.github.com'\n }\n catch {\n return false\n }\n}\n\n/**\n * Parse owner/repo from GitHub URL\n */\nexport function parseGitHubUrl(url: string): { owner: string, repo: string } | null {\n const match = url.match(/github\\.com\\/([^/]+)\\/([^/]+)/)\n if (!match)\n return null\n return { owner: match[1]!, repo: match[2]! }\n}\n\n/**\n * Normalize git repo URL to https\n */\nexport function normalizeRepoUrl(url: string): string {\n return url\n .replace(/^git\\+/, '')\n .replace(/\\.git$/, '')\n .replace(/^git:\\/\\//, 'https://')\n .replace(/^ssh:\\/\\/git@github\\.com/, 'https://github.com')\n}\n","/**\n * GitHub/ungh README resolution + versioned docs\n */\n\nimport { execSync } from 'node:child_process'\nimport { isGhAvailable } from './issues'\nimport { getDocOverride } from './overrides'\nimport { fetchText, verifyUrl } from './utils'\n\nexport interface GitDocsResult {\n /** URL pattern for fetching docs (use with ref) */\n baseUrl: string\n /** Git ref (tag) used */\n ref: string\n /** List of doc file paths relative to repo root */\n files: string[]\n /** Prefix to strip when normalizing paths to docs/ (e.g. 'apps/evalite-docs/src/content/') for nested monorepo docs */\n docsPrefix?: string\n}\n\ninterface UnghFilesResponse {\n meta: { sha: string }\n files: Array<{ path: string, mode: string, sha: string, size: number }>\n}\n\n/**\n * List files at a git ref using ungh (no rate limits)\n */\nasync function listFilesAtRef(owner: string, repo: string, ref: string): Promise<string[]> {\n const res = await fetch(\n `https://ungh.cc/repos/${owner}/${repo}/files/${ref}`,\n { headers: { 'User-Agent': 'skilld/1.0' } },\n ).catch(() => null)\n\n if (!res?.ok)\n return []\n\n const data = await res.json().catch(() => null) as UnghFilesResponse | null\n return data?.files?.map(f => f.path) ?? []\n}\n\ninterface TagResult {\n ref: string\n files: string[]\n}\n\n/**\n * Find git tag for a version by checking if ungh can list files at that ref.\n * Tries v{version}, {version}, and optionally {packageName}@{version} (changeset convention).\n */\nasync function findGitTag(owner: string, repo: string, version: string, packageName?: string): Promise<TagResult | null> {\n const candidates = [`v${version}`, version]\n if (packageName)\n candidates.push(`${packageName}@${version}`)\n\n for (const tag of candidates) {\n const files = await listFilesAtRef(owner, repo, tag)\n if (files.length > 0)\n return { ref: tag, files }\n }\n\n // Fallback: find latest release tag matching {packageName}@* (version mismatch in monorepos)\n if (packageName) {\n const latestTag = await findLatestReleaseTag(owner, repo, packageName)\n if (latestTag) {\n const files = await listFilesAtRef(owner, repo, latestTag)\n if (files.length > 0)\n return { ref: latestTag, files }\n }\n }\n\n // Last resort: try default branch\n for (const branch of ['main', 'master']) {\n const files = await listFilesAtRef(owner, repo, branch)\n if (files.length > 0)\n return { ref: branch, files }\n }\n\n return null\n}\n\n/**\n * Find the latest release tag matching `{packageName}@*` via ungh releases API.\n * Handles monorepos where npm version doesn't match git tag version.\n */\nasync function findLatestReleaseTag(owner: string, repo: string, packageName: string): Promise<string | null> {\n const res = await fetch(\n `https://ungh.cc/repos/${owner}/${repo}/releases`,\n { headers: { 'User-Agent': 'skilld/1.0' } },\n ).catch(() => null)\n\n if (!res?.ok)\n return null\n\n const data = await res.json().catch(() => null) as { releases?: Array<{ tag: string }> } | null\n const prefix = `${packageName}@`\n return data?.releases?.find(r => r.tag.startsWith(prefix))?.tag ?? null\n}\n\n/**\n * Filter file paths by prefix and md/mdx extension\n */\nfunction filterDocFiles(files: string[], pathPrefix: string): string[] {\n return files.filter(f => f.startsWith(pathPrefix) && /\\.(?:md|mdx)$/.test(f))\n}\n\n/** Known noise paths to exclude from doc discovery */\nconst NOISE_PATTERNS = [\n /^\\.changeset\\//,\n /CHANGELOG\\.md$/i,\n /CONTRIBUTING\\.md$/i,\n /^\\.github\\//,\n]\n\n/** Directories to exclude from \"best directory\" heuristic */\nconst EXCLUDE_DIRS = new Set([\n 'test',\n 'tests',\n '__tests__',\n 'fixtures',\n 'fixture',\n 'examples',\n 'example',\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n 'coverage',\n 'e2e',\n 'spec',\n 'mocks',\n '__mocks__',\n])\n\n/** Directory names that suggest documentation */\nconst DOC_DIR_BONUS = new Set([\n 'docs',\n 'documentation',\n 'pages',\n 'content',\n 'website',\n 'guide',\n 'guides',\n 'wiki',\n 'manual',\n 'api',\n])\n\ninterface DiscoveredDocs {\n files: string[]\n /** Prefix before 'docs/' to strip when normalizing (e.g. 'apps/evalite-docs/src/content/') */\n prefix: string\n}\n\n/**\n * Check if a path contains any excluded directory\n */\nfunction hasExcludedDir(path: string): boolean {\n const parts = path.split('/')\n return parts.some(p => EXCLUDE_DIRS.has(p.toLowerCase()))\n}\n\n/**\n * Get the depth of a path (number of directory levels)\n */\nfunction getPathDepth(path: string): number {\n return path.split('/').filter(Boolean).length\n}\n\n/**\n * Check if path contains a doc-related directory name\n */\nfunction hasDocDirBonus(path: string): boolean {\n const parts = path.split('/')\n return parts.some(p => DOC_DIR_BONUS.has(p.toLowerCase()))\n}\n\n/**\n * Score a directory for doc likelihood.\n * Higher = better. Formula: count * nameBonus / depth\n */\nfunction scoreDocDir(dir: string, fileCount: number): number {\n const depth = getPathDepth(dir) || 1\n const nameBonus = hasDocDirBonus(dir) ? 1.5 : 1\n return (fileCount * nameBonus) / depth\n}\n\n/**\n * Discover doc files in non-standard locations.\n * First tries to find clusters of md/mdx files in paths containing /docs/.\n * Falls back to finding the directory with the most markdown files (≥5).\n */\nfunction discoverDocFiles(allFiles: string[]): DiscoveredDocs | null {\n const mdFiles = allFiles\n .filter(f => /\\.(?:md|mdx)$/.test(f))\n .filter(f => !NOISE_PATTERNS.some(p => p.test(f)))\n .filter(f => f.includes('/'))\n\n // Strategy 1: Look for /docs/ clusters (existing behavior)\n const docsGroups = new Map<string, string[]>()\n\n for (const file of mdFiles) {\n const docsIdx = file.lastIndexOf('/docs/')\n if (docsIdx === -1)\n continue\n\n const prefix = file.slice(0, docsIdx + '/docs/'.length)\n const group = docsGroups.get(prefix) || []\n group.push(file)\n docsGroups.set(prefix, group)\n }\n\n if (docsGroups.size > 0) {\n const largest = [...docsGroups.entries()].sort((a, b) => b[1].length - a[1].length)[0]!\n if (largest[1].length >= 3) {\n const fullPrefix = largest[0]\n const docsIdx = fullPrefix.lastIndexOf('docs/')\n const stripPrefix = docsIdx > 0 ? fullPrefix.slice(0, docsIdx) : ''\n return { files: largest[1], prefix: stripPrefix }\n }\n }\n\n // Strategy 2: Find best directory by file count (for non-standard structures)\n const dirGroups = new Map<string, string[]>()\n\n for (const file of mdFiles) {\n if (hasExcludedDir(file))\n continue\n\n // Group by immediate parent directory\n const lastSlash = file.lastIndexOf('/')\n if (lastSlash === -1)\n continue\n\n const dir = file.slice(0, lastSlash + 1)\n const group = dirGroups.get(dir) || []\n group.push(file)\n dirGroups.set(dir, group)\n }\n\n if (dirGroups.size === 0)\n return null\n\n // Score and sort directories\n const scored = [...dirGroups.entries()]\n .map(([dir, files]) => ({ dir, files, score: scoreDocDir(dir, files.length) }))\n .filter(d => d.files.length >= 5) // Minimum threshold\n .sort((a, b) => b.score - a.score)\n\n if (scored.length === 0)\n return null\n\n const best = scored[0]!\n\n // For non-docs paths, the prefix is everything up to (but not including) the final dir\n // e.g. 'website/pages/' -> prefix is 'website/' so files normalize to 'pages/...'\n // But actually we want the full prefix so downstream can strip it\n return { files: best.files, prefix: best.dir }\n}\n\n/**\n * List markdown files in a folder at a specific git ref\n */\nasync function listDocsAtRef(owner: string, repo: string, ref: string, pathPrefix = 'docs/'): Promise<string[]> {\n const files = await listFilesAtRef(owner, repo, ref)\n return filterDocFiles(files, pathPrefix)\n}\n\n/**\n * Fetch versioned docs from GitHub repo's docs/ folder.\n * Pass packageName to check doc overrides (e.g. vue -> vuejs/docs).\n */\nexport async function fetchGitDocs(owner: string, repo: string, version: string, packageName?: string): Promise<GitDocsResult | null> {\n const override = packageName ? getDocOverride(packageName) : undefined\n if (override) {\n const ref = override.ref || 'main'\n const files = await listDocsAtRef(override.owner, override.repo, ref, `${override.path}/`)\n if (files.length === 0)\n return null\n return {\n baseUrl: `https://raw.githubusercontent.com/${override.owner}/${override.repo}/${ref}`,\n ref,\n files,\n }\n }\n\n const tag = await findGitTag(owner, repo, version, packageName)\n if (!tag)\n return null\n\n let docs = filterDocFiles(tag.files, 'docs/')\n let docsPrefix: string | undefined\n\n // Fallback: discover docs in nested paths (monorepos, content collections)\n if (docs.length === 0) {\n const discovered = discoverDocFiles(tag.files)\n if (discovered) {\n docs = discovered.files\n docsPrefix = discovered.prefix || undefined\n }\n }\n\n if (docs.length === 0)\n return null\n\n return {\n baseUrl: `https://raw.githubusercontent.com/${owner}/${repo}/${tag.ref}`,\n ref: tag.ref,\n files: docs,\n docsPrefix,\n }\n}\n\n/**\n * Search GitHub for a repo matching a package name (when npm has no repository field).\n * Uses gh CLI search, falls back to GitHub REST search API.\n * Returns a GitHub URL like \"https://github.com/owner/repo\" or null.\n */\nexport async function searchGitHubRepo(packageName: string): Promise<string | null> {\n // Try gh CLI first\n if (isGhAvailable()) {\n try {\n const json = execSync(\n `gh search repos \"${packageName}\" --json fullName --limit 5`,\n { encoding: 'utf-8', timeout: 15_000 },\n )\n const repos = JSON.parse(json) as Array<{ fullName: string }>\n const match = repos.find(r =>\n r.fullName.toLowerCase().endsWith(`/${packageName.toLowerCase()}`)\n || r.fullName.toLowerCase().endsWith(`/${packageName.replace(/^@.*\\//, '').toLowerCase()}`),\n )\n if (match)\n return `https://github.com/${match.fullName}`\n // If no exact match but we got results, return the first one\n if (repos.length > 0)\n return `https://github.com/${repos[0].fullName}`\n }\n catch {\n // fall through to REST API\n }\n }\n\n // Fallback: GitHub REST search API (no auth needed, but rate-limited)\n const query = encodeURIComponent(`${packageName} in:name`)\n const res = await fetch(`https://api.github.com/search/repositories?q=${query}&per_page=5`, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n if (!res?.ok)\n return null\n\n const data = await res.json().catch(() => null) as { items?: Array<{ full_name: string }> } | null\n if (!data?.items?.length)\n return null\n\n const match = data.items.find(r =>\n r.full_name.toLowerCase().endsWith(`/${packageName.toLowerCase()}`)\n || r.full_name.toLowerCase().endsWith(`/${packageName.replace(/^@.*\\//, '').toLowerCase()}`),\n )\n\n return match\n ? `https://github.com/${match.full_name}`\n : `https://github.com/${data.items[0].full_name}`\n}\n\n/**\n * Fetch GitHub repo metadata to get website URL.\n * Pass packageName to check doc overrides first (avoids API call).\n */\nexport async function fetchGitHubRepoMeta(owner: string, repo: string, packageName?: string): Promise<{ homepage?: string } | null> {\n const override = packageName ? getDocOverride(packageName) : undefined\n if (override?.homepage)\n return { homepage: override.homepage }\n\n // Prefer gh CLI to avoid rate limits\n if (isGhAvailable()) {\n try {\n const json = execSync(`gh api \"repos/${owner}/${repo}\" -q '{homepage}'`, {\n encoding: 'utf-8',\n timeout: 10_000,\n })\n const data = JSON.parse(json) as { homepage?: string }\n return data?.homepage ? { homepage: data.homepage } : null\n }\n catch {\n // fall through to fetch\n }\n }\n\n const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n if (!res?.ok)\n return null\n const data = await res.json().catch(() => null)\n return data?.homepage ? { homepage: data.homepage } : null\n}\n\n/**\n * Resolve README URL for a GitHub repo, returns ungh:// pseudo-URL or raw URL\n */\nexport async function fetchReadme(owner: string, repo: string, subdir?: string): Promise<string | null> {\n // Try ungh first\n const unghUrl = subdir\n ? `https://ungh.cc/repos/${owner}/${repo}/files/main/${subdir}/README.md`\n : `https://ungh.cc/repos/${owner}/${repo}/readme`\n\n const unghRes = await fetch(unghUrl, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n if (unghRes?.ok) {\n return `ungh://${owner}/${repo}${subdir ? `/${subdir}` : ''}`\n }\n\n // Fallback to raw.githubusercontent.com\n const basePath = subdir ? `${subdir}/` : ''\n for (const branch of ['main', 'master']) {\n for (const filename of ['README.md', 'readme.md']) {\n const readmeUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${basePath}${filename}`\n if (await verifyUrl(readmeUrl)) {\n return readmeUrl\n }\n }\n }\n\n return null\n}\n\n/**\n * Fetch README content from ungh:// pseudo-URL, file:// URL, or regular URL\n */\nexport interface GitSourceResult {\n /** URL pattern for fetching source */\n baseUrl: string\n /** Git ref (tag) used */\n ref: string\n /** List of source file paths relative to repo root */\n files: string[]\n}\n\n/** Source file extensions to include */\nconst SOURCE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.mts',\n '.cts',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.vue',\n '.svelte',\n '.astro',\n])\n\n/** Paths/patterns to exclude */\nconst EXCLUDE_PATTERNS = [\n /\\.test\\./,\n /\\.spec\\./,\n /\\.d\\.ts$/,\n /__tests__/,\n /__mocks__/,\n /\\.config\\./,\n /fixtures?\\//,\n]\n\n/**\n * Filter source files from a file list\n */\nfunction filterSourceFiles(files: string[]): string[] {\n return files.filter((path) => {\n if (!path.startsWith('src/'))\n return false\n\n const ext = path.slice(path.lastIndexOf('.'))\n if (!SOURCE_EXTENSIONS.has(ext))\n return false\n if (EXCLUDE_PATTERNS.some(p => p.test(path)))\n return false\n\n return true\n })\n}\n\n/**\n * Fetch source files from GitHub repo's src/ folder\n */\nexport async function fetchGitSource(owner: string, repo: string, version: string, packageName?: string): Promise<GitSourceResult | null> {\n const tag = await findGitTag(owner, repo, version, packageName)\n if (!tag)\n return null\n\n const files = filterSourceFiles(tag.files)\n if (files.length === 0)\n return null\n\n return {\n baseUrl: `https://raw.githubusercontent.com/${owner}/${repo}/${tag.ref}`,\n ref: tag.ref,\n files,\n }\n}\n\n/**\n * Fetch README content from ungh:// pseudo-URL, file:// URL, or regular URL\n */\nexport async function fetchReadmeContent(url: string): Promise<string | null> {\n // Local file\n if (url.startsWith('file://')) {\n const { readFileSync, existsSync } = await import('node:fs')\n const { fileURLToPath } = await import('node:url')\n const filePath = fileURLToPath(url)\n if (!existsSync(filePath))\n return null\n return readFileSync(filePath, 'utf-8')\n }\n\n if (url.startsWith('ungh://')) {\n const parts = url.replace('ungh://', '').split('/')\n const owner = parts[0]\n const repo = parts[1]\n const subdir = parts.slice(2).join('/')\n\n const unghUrl = subdir\n ? `https://ungh.cc/repos/${owner}/${repo}/files/main/${subdir}/README.md`\n : `https://ungh.cc/repos/${owner}/${repo}/readme`\n\n const res = await fetch(unghUrl, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n if (!res?.ok)\n return null\n\n const text = await res.text()\n try {\n const json = JSON.parse(text) as { markdown?: string, file?: { contents?: string } }\n return json.markdown || json.file?.contents || null\n }\n catch {\n return text\n }\n }\n\n return fetchText(url)\n}\n","/**\n * llms.txt fetching and parsing\n */\n\nimport type { FetchedDoc, LlmsContent, LlmsLink } from './types'\nimport { fetchText, verifyUrl } from './utils'\n\n/**\n * Check for llms.txt at a docs URL, returns the llms.txt URL if found\n */\nexport async function fetchLlmsUrl(docsUrl: string): Promise<string | null> {\n const llmsUrl = `${docsUrl.replace(/\\/$/, '')}/llms.txt`\n if (await verifyUrl(llmsUrl)) {\n return llmsUrl\n }\n return null\n}\n\n/**\n * Fetch and parse llms.txt content\n */\nexport async function fetchLlmsTxt(url: string): Promise<LlmsContent | null> {\n const content = await fetchText(url)\n if (!content || content.length < 50)\n return null\n\n return {\n raw: content,\n links: parseMarkdownLinks(content),\n }\n}\n\n/**\n * Parse markdown links from llms.txt to get .md file paths\n */\nexport function parseMarkdownLinks(content: string): LlmsLink[] {\n const links: LlmsLink[] = []\n const seen = new Set<string>()\n const linkRegex = /\\[([^\\]]+)\\]\\(([^)]+\\.md)\\)/g\n for (let match = linkRegex.exec(content); match !== null; match = linkRegex.exec(content)) {\n const url = match[2]!\n if (!seen.has(url)) {\n seen.add(url)\n links.push({ title: match[1]!, url })\n }\n }\n\n return links\n}\n\n/**\n * Download all .md files referenced in llms.txt\n */\nexport async function downloadLlmsDocs(\n llmsContent: LlmsContent,\n baseUrl: string,\n onProgress?: (url: string, index: number, total: number) => void,\n): Promise<FetchedDoc[]> {\n const docs: FetchedDoc[] = []\n\n for (let i = 0; i < llmsContent.links.length; i++) {\n const link = llmsContent.links[i]!\n onProgress?.(link.url, i, llmsContent.links.length)\n\n const url = link.url.startsWith('http')\n ? link.url\n : `${baseUrl.replace(/\\/$/, '')}${link.url.startsWith('/') ? '' : '/'}${link.url}`\n\n const content = await fetchText(url)\n if (content && content.length > 100) {\n docs.push({ url: link.url, title: link.title, content })\n }\n }\n\n return docs\n}\n\n/**\n * Normalize llms.txt links to relative paths for local access\n * Handles: absolute URLs, root-relative paths, and relative paths\n */\nexport function normalizeLlmsLinks(content: string, baseUrl?: string): string {\n let normalized = content\n\n // Handle absolute URLs: https://example.com/docs/foo.md → ./docs/foo.md\n if (baseUrl) {\n const base = baseUrl.replace(/\\/$/, '')\n const escaped = base.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n normalized = normalized.replace(\n new RegExp(`\\\\]\\\\(${escaped}(/[^)]+\\\\.md)\\\\)`, 'g'),\n '](./docs$1)',\n )\n }\n\n // Handle root-relative paths: /foo.md → ./docs/foo.md\n normalized = normalized.replace(/\\]\\(\\/([^)]+\\.md)\\)/g, '](./docs/$1)')\n\n return normalized\n}\n\n/**\n * Extract sections from llms-full.txt by URL patterns\n * Format: ---\\nurl: /path.md\\n---\\n<content>\\n\\n---\\nurl: ...\n */\nexport function extractSections(content: string, patterns: string[]): string | null {\n const sections: string[] = []\n const parts = content.split(/\\n---\\n/)\n\n for (const part of parts) {\n const urlMatch = part.match(/^url: *(\\S.*)$/m)\n if (!urlMatch)\n continue\n\n const url = urlMatch[1]!\n if (patterns.some(p => url.includes(p))) {\n const contentStart = part.indexOf('\\n', part.indexOf('url:'))\n if (contentStart > -1) {\n sections.push(part.slice(contentStart + 1))\n }\n }\n }\n\n if (sections.length === 0)\n return null\n return sections.join('\\n\\n---\\n\\n')\n}\n","/**\n * NPM registry lookup\n */\n\nimport type { LocalDependency, NpmPackageInfo, ResolveAttempt, ResolvedPackage, ResolveResult } from './types'\nimport { execSync } from 'node:child_process'\nimport { createWriteStream, existsSync, mkdirSync, readFileSync, rmSync, unlinkSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { Writable } from 'node:stream'\nimport { pathToFileURL } from 'node:url'\nimport { getCacheDir } from '../cache/version'\nimport { fetchGitDocs, fetchGitHubRepoMeta, fetchReadme, searchGitHubRepo } from './github'\nimport { fetchLlmsUrl } from './llms'\nimport { isGitHubRepoUrl, normalizeRepoUrl, parseGitHubUrl } from './utils'\n\n/**\n * Fetch package info from npm registry\n */\nexport async function fetchNpmPackage(packageName: string): Promise<NpmPackageInfo | null> {\n // Try unpkg first (faster, CDN)\n let res = await fetch(`https://unpkg.com/${packageName}/package.json`, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n // Fallback to npm registry\n if (!res?.ok) {\n res = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n }\n\n if (!res?.ok)\n return null\n return res.json()\n}\n\nexport interface DistTagInfo {\n version: string\n releasedAt?: string\n}\n\nexport interface NpmRegistryMeta {\n releasedAt?: string\n distTags?: Record<string, DistTagInfo>\n}\n\n/**\n * Fetch release date and dist-tags from npm registry\n */\nexport async function fetchNpmRegistryMeta(packageName: string, version: string): Promise<NpmRegistryMeta> {\n const res = await fetch(`https://registry.npmjs.org/${packageName}`, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n if (!res?.ok)\n return {}\n\n const data = await res.json() as {\n 'time'?: Record<string, string>\n 'dist-tags'?: Record<string, string>\n }\n\n // Enrich dist-tags with release dates\n const distTags: Record<string, DistTagInfo> | undefined = data['dist-tags']\n ? Object.fromEntries(\n Object.entries(data['dist-tags']).map(([tag, ver]) => [\n tag,\n { version: ver, releasedAt: data.time?.[ver] },\n ]),\n )\n : undefined\n\n return {\n releasedAt: data.time?.[version] || undefined,\n distTags,\n }\n}\n\n/**\n * Fetch release date for a specific version from npm registry\n * @deprecated Use fetchNpmRegistryMeta instead\n */\nexport async function fetchNpmReleaseDate(packageName: string, version: string): Promise<string | null> {\n const meta = await fetchNpmRegistryMeta(packageName, version)\n return meta.releasedAt || null\n}\n\nexport type ResolveStep = 'npm' | 'github-docs' | 'github-meta' | 'github-search' | 'readme' | 'llms.txt' | 'local'\n\nexport interface ResolveOptions {\n /** User's installed version - used to fetch versioned git docs */\n version?: string\n /** Current working directory - for local readme fallback */\n cwd?: string\n /** Progress callback - called before each resolution step */\n onProgress?: (step: ResolveStep) => void\n}\n\n/**\n * Resolve documentation URL for a package (legacy - returns null on failure)\n */\nexport async function resolvePackageDocs(packageName: string, options: ResolveOptions = {}): Promise<ResolvedPackage | null> {\n const result = await resolvePackageDocsWithAttempts(packageName, options)\n return result.package\n}\n\n/**\n * Resolve documentation URL for a package with attempt tracking\n */\nexport async function resolvePackageDocsWithAttempts(packageName: string, options: ResolveOptions = {}): Promise<ResolveResult> {\n const attempts: ResolveAttempt[] = []\n const { onProgress } = options\n\n onProgress?.('npm')\n const pkg = await fetchNpmPackage(packageName)\n if (!pkg) {\n attempts.push({\n source: 'npm',\n url: `https://registry.npmjs.org/${packageName}/latest`,\n status: 'not-found',\n message: 'Package not found on npm registry',\n })\n return { package: null, attempts }\n }\n\n attempts.push({\n source: 'npm',\n url: `https://registry.npmjs.org/${packageName}/latest`,\n status: 'success',\n message: `Found ${pkg.name}@${pkg.version}`,\n })\n\n // Fetch release date and dist-tags for this version\n const registryMeta = pkg.version\n ? await fetchNpmRegistryMeta(packageName, pkg.version)\n : {}\n\n const result: ResolvedPackage = {\n name: pkg.name,\n version: pkg.version,\n releasedAt: registryMeta.releasedAt,\n description: pkg.description,\n dependencies: pkg.dependencies,\n distTags: registryMeta.distTags,\n }\n\n // Extract repo URL (handle both object and shorthand string formats)\n let subdir: string | undefined\n if (typeof pkg.repository === 'object' && pkg.repository?.url) {\n result.repoUrl = normalizeRepoUrl(pkg.repository.url)\n subdir = pkg.repository.directory\n }\n else if (typeof pkg.repository === 'string') {\n // Shorthand: \"owner/repo\" or \"github:owner/repo\"\n const repo = pkg.repository.replace(/^github:/, '')\n if (repo.includes('/') && !repo.includes(':'))\n result.repoUrl = `https://github.com/${repo}`\n }\n\n // GitHub repo handling - try versioned git docs first\n if (result.repoUrl?.includes('github.com')) {\n const gh = parseGitHubUrl(result.repoUrl)\n if (gh) {\n const targetVersion = options.version || pkg.version\n\n // Try versioned git docs first (docs/**/*.md at git tag)\n if (targetVersion) {\n onProgress?.('github-docs')\n const gitDocs = await fetchGitDocs(gh.owner, gh.repo, targetVersion, pkg.name)\n if (gitDocs) {\n result.gitDocsUrl = gitDocs.baseUrl\n result.gitRef = gitDocs.ref\n attempts.push({\n source: 'github-docs',\n url: gitDocs.baseUrl,\n status: 'success',\n message: `Found ${gitDocs.files.length} docs at ${gitDocs.ref}`,\n })\n }\n else {\n attempts.push({\n source: 'github-docs',\n url: `${result.repoUrl}/tree/v${targetVersion}/docs`,\n status: 'not-found',\n message: 'No docs/ folder found at version tag',\n })\n }\n }\n\n // If no docsUrl from homepage, try GitHub repo metadata\n if (!result.docsUrl) {\n onProgress?.('github-meta')\n const repoMeta = await fetchGitHubRepoMeta(gh.owner, gh.repo, pkg.name)\n if (repoMeta?.homepage) {\n result.docsUrl = repoMeta.homepage\n attempts.push({\n source: 'github-meta',\n url: result.repoUrl,\n status: 'success',\n message: `Found homepage: ${repoMeta.homepage}`,\n })\n }\n else {\n attempts.push({\n source: 'github-meta',\n url: result.repoUrl,\n status: 'not-found',\n message: 'No homepage in repo metadata',\n })\n }\n }\n\n // README fallback via ungh\n onProgress?.('readme')\n const readmeUrl = await fetchReadme(gh.owner, gh.repo, subdir)\n if (readmeUrl) {\n result.readmeUrl = readmeUrl\n attempts.push({\n source: 'readme',\n url: readmeUrl,\n status: 'success',\n })\n }\n else {\n attempts.push({\n source: 'readme',\n url: `${result.repoUrl}/README.md`,\n status: 'not-found',\n message: 'No README found',\n })\n }\n }\n }\n else if (!result.repoUrl) {\n // No repo URL in package.json — try to find it via GitHub search\n onProgress?.('github-search')\n const searchedUrl = await searchGitHubRepo(pkg.name)\n if (searchedUrl) {\n result.repoUrl = searchedUrl\n attempts.push({\n source: 'github-search',\n url: searchedUrl,\n status: 'success',\n message: `Found via GitHub search: ${searchedUrl}`,\n })\n\n // Now run the same GitHub resolution as above\n const gh = parseGitHubUrl(searchedUrl)\n if (gh) {\n const targetVersion = options.version || pkg.version\n if (targetVersion) {\n onProgress?.('github-docs')\n const gitDocs = await fetchGitDocs(gh.owner, gh.repo, targetVersion, pkg.name)\n if (gitDocs) {\n result.gitDocsUrl = gitDocs.baseUrl\n result.gitRef = gitDocs.ref\n attempts.push({\n source: 'github-docs',\n url: gitDocs.baseUrl,\n status: 'success',\n message: `Found ${gitDocs.files.length} docs at ${gitDocs.ref}`,\n })\n }\n }\n\n if (!result.docsUrl) {\n onProgress?.('github-meta')\n const repoMeta = await fetchGitHubRepoMeta(gh.owner, gh.repo, pkg.name)\n if (repoMeta?.homepage)\n result.docsUrl = repoMeta.homepage\n }\n\n onProgress?.('readme')\n const readmeUrl = await fetchReadme(gh.owner, gh.repo)\n if (readmeUrl)\n result.readmeUrl = readmeUrl\n }\n }\n else {\n attempts.push({\n source: 'github-search',\n status: 'not-found',\n message: 'No repository URL in package.json and GitHub search found no match',\n })\n }\n }\n\n // Try homepage for docs (skip if it's just a GitHub repo URL)\n if (pkg.homepage && !isGitHubRepoUrl(pkg.homepage)) {\n result.docsUrl = pkg.homepage\n }\n\n // Check for llms.txt on docsUrl\n if (result.docsUrl) {\n onProgress?.('llms.txt')\n const llmsUrl = await fetchLlmsUrl(result.docsUrl)\n if (llmsUrl) {\n result.llmsUrl = llmsUrl\n attempts.push({\n source: 'llms.txt',\n url: llmsUrl,\n status: 'success',\n })\n }\n else {\n attempts.push({\n source: 'llms.txt',\n url: `${result.docsUrl}/llms.txt`,\n status: 'not-found',\n message: 'No llms.txt at docs URL',\n })\n }\n }\n\n // Fallback: check local node_modules readme when all else fails\n if (!result.docsUrl && !result.llmsUrl && !result.readmeUrl && !result.gitDocsUrl && options.cwd) {\n onProgress?.('local')\n const pkgDir = join(options.cwd, 'node_modules', packageName)\n // Check common readme variations\n for (const filename of ['README.md', 'readme.md']) {\n const readmePath = join(pkgDir, filename)\n if (existsSync(readmePath)) {\n result.readmeUrl = pathToFileURL(readmePath).href\n attempts.push({\n source: 'readme',\n url: readmePath,\n status: 'success',\n message: 'Found local readme in node_modules',\n })\n break\n }\n }\n }\n\n // Must have at least one source\n if (!result.docsUrl && !result.llmsUrl && !result.readmeUrl && !result.gitDocsUrl) {\n return { package: null, attempts }\n }\n\n return { package: result, attempts }\n}\n\n/**\n * Parse version specifier, handling protocols like link:, workspace:, npm:, file:\n */\nexport function parseVersionSpecifier(\n name: string,\n version: string,\n cwd: string,\n): LocalDependency | null {\n // link: - resolve local package.json\n if (version.startsWith('link:')) {\n const linkPath = resolve(cwd, version.slice(5))\n const linkedPkgPath = join(linkPath, 'package.json')\n if (existsSync(linkedPkgPath)) {\n const linkedPkg = JSON.parse(readFileSync(linkedPkgPath, 'utf-8'))\n return {\n name: linkedPkg.name || name,\n version: linkedPkg.version || '0.0.0',\n }\n }\n return null // linked package doesn't exist\n }\n\n // workspace: - strip protocol, keep name\n if (version.startsWith('workspace:')) {\n return {\n name,\n version: version.slice(10).replace(/^[\\^~*]/, '') || '*',\n }\n }\n\n // npm: - extract aliased package name and version\n if (version.startsWith('npm:')) {\n const specifier = version.slice(4)\n // Handle @scope/pkg@version vs pkg@version\n const atIndex = specifier.startsWith('@')\n ? specifier.indexOf('@', 1)\n : specifier.indexOf('@')\n if (atIndex > 0) {\n return {\n name: specifier.slice(0, atIndex),\n version: specifier.slice(atIndex + 1),\n }\n }\n // npm:package without version\n return { name: specifier, version: '*' }\n }\n\n // file: and git: - skip (local/custom sources)\n if (version.startsWith('file:') || version.startsWith('git:') || version.startsWith('git+')) {\n return null\n }\n\n // Standard semver\n return {\n name,\n version: version.replace(/^[\\^~>=<]/, ''),\n }\n}\n\n/**\n * Read package.json dependencies with versions\n */\nexport async function readLocalDependencies(cwd: string): Promise<LocalDependency[]> {\n const pkgPath = join(cwd, 'package.json')\n if (!existsSync(pkgPath)) {\n throw new Error('No package.json found in current directory')\n }\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n const deps: Record<string, string> = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n }\n\n const results: LocalDependency[] = []\n\n for (const [name, version] of Object.entries(deps)) {\n // Skip types and dev tools\n if (name.startsWith('@types/') || ['typescript', 'eslint', 'prettier', 'vitest', 'jest'].includes(name)) {\n continue\n }\n\n const parsed = parseVersionSpecifier(name, version, cwd)\n if (parsed) {\n results.push(parsed)\n }\n }\n\n return results\n}\n\nexport interface LocalPackageInfo {\n name: string\n version: string\n description?: string\n repoUrl?: string\n localPath: string\n}\n\n/**\n * Read package info from a local path (for link: deps)\n */\nexport function readLocalPackageInfo(localPath: string): LocalPackageInfo | null {\n const pkgPath = join(localPath, 'package.json')\n if (!existsSync(pkgPath))\n return null\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n\n let repoUrl: string | undefined\n if (pkg.repository?.url) {\n repoUrl = normalizeRepoUrl(pkg.repository.url)\n }\n else if (typeof pkg.repository === 'string') {\n repoUrl = normalizeRepoUrl(pkg.repository)\n }\n\n return {\n name: pkg.name,\n version: pkg.version || '0.0.0',\n description: pkg.description,\n repoUrl,\n localPath,\n }\n}\n\n/**\n * Resolve docs for a local package (link: dependency)\n */\nexport async function resolveLocalPackageDocs(localPath: string): Promise<ResolvedPackage | null> {\n const info = readLocalPackageInfo(localPath)\n if (!info)\n return null\n\n const result: ResolvedPackage = {\n name: info.name,\n version: info.version,\n description: info.description,\n repoUrl: info.repoUrl,\n }\n\n // Try GitHub if repo URL available\n if (info.repoUrl?.includes('github.com')) {\n const gh = parseGitHubUrl(info.repoUrl)\n if (gh) {\n // Try versioned git docs\n const gitDocs = await fetchGitDocs(gh.owner, gh.repo, info.version, info.name)\n if (gitDocs) {\n result.gitDocsUrl = gitDocs.baseUrl\n result.gitRef = gitDocs.ref\n }\n\n // README fallback via ungh\n const readmeUrl = await fetchReadme(gh.owner, gh.repo)\n if (readmeUrl) {\n result.readmeUrl = readmeUrl\n }\n }\n }\n\n // Fallback: read local README.md\n if (!result.readmeUrl && !result.gitDocsUrl) {\n const localReadme = join(localPath, 'README.md')\n if (existsSync(localReadme)) {\n result.readmeUrl = pathToFileURL(localReadme).href\n }\n }\n\n if (!result.readmeUrl && !result.gitDocsUrl) {\n return null\n }\n\n return result\n}\n\n/**\n * Download and extract npm package tarball to cache directory.\n * Used when the package isn't available in node_modules.\n *\n * Extracts to: ~/.skilld/references/<pkg>@<version>/pkg/\n * Returns the extracted directory path, or null on failure.\n */\nexport async function fetchPkgDist(name: string, version: string): Promise<string | null> {\n const cacheDir = getCacheDir(name, version)\n const pkgDir = join(cacheDir, 'pkg')\n\n // Already extracted\n if (existsSync(join(pkgDir, 'package.json')))\n return pkgDir\n\n // Fetch version metadata to get tarball URL\n const res = await fetch(`https://registry.npmjs.org/${name}/${version}`, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n if (!res?.ok)\n return null\n\n const data = await res.json() as { dist?: { tarball?: string } }\n const tarballUrl = data.dist?.tarball\n if (!tarballUrl)\n return null\n\n // Download tarball to temp file\n const tarballRes = await fetch(tarballUrl, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n\n if (!tarballRes?.ok || !tarballRes.body)\n return null\n\n mkdirSync(pkgDir, { recursive: true })\n\n const tmpTarball = join(cacheDir, '_pkg.tgz')\n const fileStream = createWriteStream(tmpTarball)\n\n // Stream response body to file\n const reader = tarballRes.body.getReader()\n await new Promise<void>((res, reject) => {\n const writable = new Writable({\n write(chunk, _encoding, callback) {\n fileStream.write(chunk, callback)\n },\n })\n writable.on('finish', () => {\n fileStream.end()\n res()\n })\n writable.on('error', reject)\n\n function pump() {\n reader.read().then(({ done, value }) => {\n if (done) {\n writable.end()\n return\n }\n writable.write(value, () => pump())\n }).catch(reject)\n }\n pump()\n })\n\n // Extract tarball — npm tarballs have a \"package/\" prefix\n try {\n execSync(`tar xzf \"${tmpTarball}\" --strip-components=1 -C \"${pkgDir}\"`, { stdio: 'ignore' })\n }\n catch {\n rmSync(pkgDir, { recursive: true, force: true })\n rmSync(tmpTarball, { force: true })\n return null\n }\n\n unlinkSync(tmpTarball)\n return pkgDir\n}\n\n/**\n * Fetch just the latest version string from npm (lightweight)\n */\nexport async function fetchLatestVersion(packageName: string): Promise<string | null> {\n const res = await fetch(`https://unpkg.com/${packageName}/package.json`, {\n headers: { 'User-Agent': 'skilld/1.0' },\n }).catch(() => null)\n if (!res?.ok)\n return null\n const data = await res.json() as { version?: string }\n return data.version || null\n}\n\n/**\n * Get installed skill version from SKILL.md\n */\nexport function getInstalledSkillVersion(skillDir: string): string | null {\n const skillPath = join(skillDir, 'SKILL.md')\n if (!existsSync(skillPath))\n return null\n\n const content = readFileSync(skillPath, 'utf-8')\n const match = content.match(/^version:\\s*\"?([^\"\\n]+)\"?/m)\n return match?.[1] || null\n}\n","/**\n * GitHub release notes fetching via gh CLI (preferred) with ungh.cc fallback\n */\n\nimport { execSync } from 'node:child_process'\nimport { isGhAvailable } from './issues'\n\nexport interface GitHubRelease {\n id: number\n tag: string\n name: string\n prerelease: boolean\n createdAt: string\n publishedAt: string\n markdown: string\n}\n\ninterface UnghReleasesResponse {\n releases: GitHubRelease[]\n}\n\ninterface CachedDoc {\n path: string\n content: string\n}\n\ninterface SemVer {\n major: number\n minor: number\n patch: number\n raw: string\n}\n\nfunction parseSemver(version: string): SemVer | null {\n const clean = version.replace(/^v/, '')\n const match = clean.match(/^(\\d+)\\.(\\d+)\\.(\\d+)/)\n if (!match)\n return null\n return { major: +match[1]!, minor: +match[2]!, patch: +match[3]!, raw: clean }\n}\n\n/**\n * Extract version from a release tag, handling monorepo formats:\n * - `pkg@1.2.3` → `1.2.3`\n * - `pkg-v1.2.3` → `1.2.3`\n * - `v1.2.3` → `1.2.3`\n * - `1.2.3` → `1.2.3`\n */\nfunction extractVersion(tag: string, packageName?: string): string | null {\n if (packageName) {\n // Monorepo: pkg@version or pkg-vversion\n const atMatch = tag.match(new RegExp(`^${escapeRegex(packageName)}@(.+)$`))\n if (atMatch)\n return atMatch[1]!\n const dashMatch = tag.match(new RegExp(`^${escapeRegex(packageName)}-v?(.+)$`))\n if (dashMatch)\n return dashMatch[1]!\n }\n // Standard: v1.2.3 or 1.2.3\n return tag.replace(/^v/, '')\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/**\n * Check if a release tag belongs to a specific package\n */\nfunction tagMatchesPackage(tag: string, packageName: string): boolean {\n // Exact match: pkg@version or pkg-vversion\n return tag.startsWith(`${packageName}@`) || tag.startsWith(`${packageName}-v`) || tag.startsWith(`${packageName}-`)\n}\n\nfunction compareSemver(a: SemVer, b: SemVer): number {\n if (a.major !== b.major)\n return a.major - b.major\n if (a.minor !== b.minor)\n return a.minor - b.minor\n return a.patch - b.patch\n}\n\n/**\n * Fetch releases via gh CLI (fast, authenticated, paginated)\n */\nfunction fetchReleasesViaGh(owner: string, repo: string): GitHubRelease[] {\n try {\n const json = execSync(\n `gh api \"repos/${owner}/${repo}/releases?per_page=100\" --jq '[.[] | {id: .id, tag: .tag_name, name: .name, prerelease: .prerelease, createdAt: .created_at, publishedAt: .published_at, markdown: .body}]'`,\n { encoding: 'utf-8', timeout: 15_000, stdio: ['ignore', 'pipe', 'ignore'] },\n )\n return JSON.parse(json) as GitHubRelease[]\n }\n catch {\n return []\n }\n}\n\n/**\n * Fetch all releases from a GitHub repo via ungh.cc (fallback)\n */\nasync function fetchReleasesViaUngh(owner: string, repo: string): Promise<GitHubRelease[]> {\n const res = await fetch(\n `https://ungh.cc/repos/${owner}/${repo}/releases`,\n { headers: { 'User-Agent': 'skilld/1.0' }, signal: AbortSignal.timeout(15_000) },\n ).catch(() => null)\n\n if (!res?.ok)\n return []\n\n const data = await res.json().catch(() => null) as UnghReleasesResponse | null\n return data?.releases ?? []\n}\n\n/**\n * Fetch all releases — gh CLI first, ungh.cc fallback\n */\nasync function fetchAllReleases(owner: string, repo: string): Promise<GitHubRelease[]> {\n if (isGhAvailable()) {\n const releases = fetchReleasesViaGh(owner, repo)\n if (releases.length > 0)\n return releases\n }\n return fetchReleasesViaUngh(owner, repo)\n}\n\n/**\n * Select last 20 stable releases for a package, sorted newest first.\n * For monorepos, filters to package-specific tags (pkg@version).\n * Falls back to generic tags (v1.2.3) only if no package-specific found.\n */\nexport function selectReleases(releases: GitHubRelease[], packageName?: string): GitHubRelease[] {\n // Check if this looks like a monorepo (has package-prefixed tags)\n const hasMonorepoTags = packageName && releases.some(r => tagMatchesPackage(r.tag, packageName))\n\n const filtered = releases.filter((r) => {\n if (r.prerelease)\n return false\n\n // Monorepo: only include tags for this package\n if (hasMonorepoTags && packageName) {\n if (!tagMatchesPackage(r.tag, packageName))\n return false\n const ver = extractVersion(r.tag, packageName)\n return ver && parseSemver(ver)\n }\n\n // Single-package repo: use generic version tags\n return parseSemver(r.tag)\n })\n\n return filtered\n .sort((a, b) => {\n const verA = extractVersion(a.tag, hasMonorepoTags ? packageName : undefined)\n const verB = extractVersion(b.tag, hasMonorepoTags ? packageName : undefined)\n if (!verA || !verB)\n return 0\n return compareSemver(parseSemver(verB)!, parseSemver(verA)!)\n })\n .slice(0, 20)\n}\n\n/**\n * Format a release as markdown\n */\nfunction formatRelease(release: GitHubRelease): string {\n const date = (release.publishedAt || release.createdAt).split('T')[0]\n return `# ${release.name || release.tag}\\n\\nTag: ${release.tag} | Published: ${date}\\n\\n${release.markdown}`\n}\n\n/**\n * Fetch CHANGELOG.md from a GitHub repo at a specific ref as fallback\n */\nasync function fetchChangelog(owner: string, repo: string, ref: string): Promise<string | null> {\n for (const filename of ['CHANGELOG.md', 'changelog.md', 'CHANGES.md']) {\n const url = `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${filename}`\n const res = await fetch(url, { headers: { 'User-Agent': 'skilld/1.0' }, signal: AbortSignal.timeout(10_000) }).catch(() => null)\n if (res?.ok)\n return res.text()\n }\n return null\n}\n\n/**\n * Fetch release notes for a package. Returns CachedDoc[] with releases/{tag}.md files.\n *\n * Strategy:\n * 1. Fetch GitHub releases, filter to package-specific tags for monorepos\n * 2. If no releases found, try CHANGELOG.md as fallback\n */\nexport async function fetchReleaseNotes(\n owner: string,\n repo: string,\n installedVersion: string,\n gitRef?: string,\n packageName?: string,\n): Promise<CachedDoc[]> {\n const releases = await fetchAllReleases(owner, repo)\n const selected = selectReleases(releases, packageName)\n\n if (selected.length > 0) {\n return selected.map((r) => {\n // For monorepo tags (pkg@version), use tag as-is\n // For standard tags (1.2.3), prefix with v\n const filename = r.tag.includes('@') || r.tag.startsWith('v')\n ? r.tag\n : `v${r.tag}`\n return {\n path: `releases/${filename}.md`,\n content: formatRelease(r),\n }\n })\n }\n\n // Fallback: CHANGELOG.md (indexed as single file)\n const ref = gitRef || 'main'\n const changelog = await fetchChangelog(owner, repo, ref)\n if (!changelog)\n return []\n\n return [{ path: 'CHANGELOG.md', content: changelog }]\n}\n"],"mappings":"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"}
@@ -0,0 +1,198 @@
1
+ import { a as getCacheDir, n as REFERENCES_DIR } from "./config.mjs";
2
+ import { basename, join } from "node:path";
3
+ import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
4
+ function isCached(name, version) {
5
+ return existsSync(getCacheDir(name, version));
6
+ }
7
+ function ensureCacheDir() {
8
+ mkdirSync(REFERENCES_DIR, { recursive: true });
9
+ }
10
+ function writeToCache(name, version, docs) {
11
+ const cacheDir = getCacheDir(name, version);
12
+ mkdirSync(cacheDir, { recursive: true });
13
+ cleanStaleCacheDirs(name, version);
14
+ for (const doc of docs) {
15
+ const filePath = join(cacheDir, doc.path);
16
+ mkdirSync(join(filePath, ".."), { recursive: true });
17
+ writeFileSync(filePath, doc.content);
18
+ }
19
+ return cacheDir;
20
+ }
21
+ function cleanStaleCacheDirs(name, version) {
22
+ const prefix = `${name}@`;
23
+ if (name.startsWith("@")) {
24
+ const [scope, pkg] = name.split("/");
25
+ const scopeDir = join(REFERENCES_DIR, scope);
26
+ if (!existsSync(scopeDir)) return;
27
+ const scopePrefix = `${pkg}@`;
28
+ const currentDirName = basename(getCacheDir(name, version));
29
+ for (const entry of readdirSync(scopeDir)) if (entry.startsWith(scopePrefix) && entry !== currentDirName) rmSync(join(scopeDir, entry), {
30
+ recursive: true,
31
+ force: true
32
+ });
33
+ } else {
34
+ if (!existsSync(REFERENCES_DIR)) return;
35
+ for (const entry of readdirSync(REFERENCES_DIR)) if (entry.startsWith(prefix) && entry !== basename(getCacheDir(name, version))) rmSync(join(REFERENCES_DIR, entry), {
36
+ recursive: true,
37
+ force: true
38
+ });
39
+ }
40
+ }
41
+ function linkReferences(skillDir, name, version) {
42
+ const cacheDir = getCacheDir(name, version);
43
+ const referencesDir = join(skillDir, ".skilld");
44
+ const docsLinkPath = join(referencesDir, "docs");
45
+ const cachedDocsPath = join(cacheDir, "docs");
46
+ mkdirSync(referencesDir, { recursive: true });
47
+ if (existsSync(docsLinkPath)) unlinkSync(docsLinkPath);
48
+ if (existsSync(cachedDocsPath)) symlinkSync(cachedDocsPath, docsLinkPath, "junction");
49
+ }
50
+ function linkGithub(skillDir, name, version) {
51
+ const cacheDir = getCacheDir(name, version);
52
+ const referencesDir = join(skillDir, ".skilld");
53
+ const githubLinkPath = join(referencesDir, "github");
54
+ const cachedGithubPath = join(cacheDir, "github");
55
+ mkdirSync(referencesDir, { recursive: true });
56
+ if (existsSync(githubLinkPath)) unlinkSync(githubLinkPath);
57
+ if (existsSync(cachedGithubPath)) symlinkSync(cachedGithubPath, githubLinkPath, "junction");
58
+ }
59
+ function resolvePkgDir(name, cwd, version) {
60
+ const nodeModulesPath = join(cwd, "node_modules", name);
61
+ if (existsSync(nodeModulesPath)) return nodeModulesPath;
62
+ if (version) {
63
+ const cachedPkgDir = join(getCacheDir(name, version), "pkg");
64
+ if (existsSync(join(cachedPkgDir, "package.json"))) return cachedPkgDir;
65
+ }
66
+ return null;
67
+ }
68
+ function linkPkg(skillDir, name, cwd, version) {
69
+ const pkgPath = resolvePkgDir(name, cwd, version);
70
+ if (!pkgPath) return;
71
+ const referencesDir = join(skillDir, ".skilld");
72
+ mkdirSync(referencesDir, { recursive: true });
73
+ const pkgLinkPath = join(referencesDir, "pkg");
74
+ if (existsSync(pkgLinkPath)) unlinkSync(pkgLinkPath);
75
+ symlinkSync(pkgPath, pkgLinkPath, "junction");
76
+ }
77
+ function getPkgKeyFiles(name, cwd, version) {
78
+ const pkgPath = resolvePkgDir(name, cwd, version);
79
+ if (!pkgPath) return [];
80
+ const files = [];
81
+ const pkgJsonPath = join(pkgPath, "package.json");
82
+ if (existsSync(pkgJsonPath)) {
83
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
84
+ if (pkg.main) files.push(basename(pkg.main));
85
+ if (pkg.module && pkg.module !== pkg.main) files.push(basename(pkg.module));
86
+ }
87
+ for (const f of [
88
+ "README.md",
89
+ "CHANGELOG.md",
90
+ "changelog.md"
91
+ ]) if (existsSync(join(pkgPath, f))) files.push(f);
92
+ return [...new Set(files)];
93
+ }
94
+ function getShippedSkills(name, cwd, version) {
95
+ const pkgPath = resolvePkgDir(name, cwd, version);
96
+ if (!pkgPath) return [];
97
+ const skillsPath = join(pkgPath, "skills");
98
+ if (!existsSync(skillsPath)) return [];
99
+ return readdirSync(skillsPath, { withFileTypes: true }).filter((d) => d.isDirectory() && existsSync(join(skillsPath, d.name, "_SKILL.md"))).map((d) => ({
100
+ skillName: d.name,
101
+ skillDir: join(skillsPath, d.name)
102
+ }));
103
+ }
104
+ function linkReleases(skillDir, name, version) {
105
+ const cacheDir = getCacheDir(name, version);
106
+ const referencesDir = join(skillDir, ".skilld");
107
+ const releasesLinkPath = join(referencesDir, "releases");
108
+ const cachedReleasesPath = join(cacheDir, "releases");
109
+ mkdirSync(referencesDir, { recursive: true });
110
+ if (existsSync(releasesLinkPath)) unlinkSync(releasesLinkPath);
111
+ if (existsSync(cachedReleasesPath)) symlinkSync(cachedReleasesPath, releasesLinkPath, "junction");
112
+ }
113
+ function linkShippedSkill(baseDir, skillName, targetDir) {
114
+ const linkPath = join(baseDir, skillName);
115
+ if (existsSync(linkPath)) if (lstatSync(linkPath).isSymbolicLink()) unlinkSync(linkPath);
116
+ else rmSync(linkPath, {
117
+ recursive: true,
118
+ force: true
119
+ });
120
+ symlinkSync(targetDir, linkPath);
121
+ }
122
+ function hasShippedDocs(name, cwd, version) {
123
+ const pkgPath = resolvePkgDir(name, cwd, version);
124
+ if (!pkgPath) return false;
125
+ for (const candidate of [
126
+ "docs",
127
+ "documentation",
128
+ "doc"
129
+ ]) if (existsSync(join(pkgPath, candidate))) return true;
130
+ return false;
131
+ }
132
+ function listCached() {
133
+ if (!existsSync(REFERENCES_DIR)) return [];
134
+ return readdirSync(REFERENCES_DIR).filter((name) => name.includes("@")).map((dir) => {
135
+ const [name, version] = dir.split("@");
136
+ return {
137
+ name,
138
+ version,
139
+ dir: join(REFERENCES_DIR, dir)
140
+ };
141
+ });
142
+ }
143
+ function readCachedDocs(name, version) {
144
+ const cacheDir = getCacheDir(name, version);
145
+ if (!existsSync(cacheDir)) return [];
146
+ const docs = [];
147
+ function walk(dir, prefix = "") {
148
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
149
+ const entryPath = join(dir, entry.name);
150
+ const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
151
+ if (entry.isDirectory()) walk(entryPath, relativePath);
152
+ else if (entry.name.endsWith(".md") || entry.name.endsWith(".mdx")) docs.push({
153
+ path: relativePath,
154
+ content: readFileSync(entryPath, "utf-8")
155
+ });
156
+ }
157
+ }
158
+ walk(cacheDir);
159
+ return docs;
160
+ }
161
+ function clearCache(name, version) {
162
+ const cacheDir = getCacheDir(name, version);
163
+ if (!existsSync(cacheDir)) return false;
164
+ rmSync(cacheDir, { recursive: true });
165
+ return true;
166
+ }
167
+ function clearAllCache() {
168
+ const packages = listCached();
169
+ for (const pkg of packages) clearCache(pkg.name, pkg.version);
170
+ return packages.length;
171
+ }
172
+ function listReferenceFiles(skillDir, maxDepth = 3) {
173
+ const referencesDir = join(skillDir, ".skilld");
174
+ if (!existsSync(referencesDir)) return [];
175
+ const files = [];
176
+ function walk(dir, depth) {
177
+ if (depth > maxDepth) return;
178
+ try {
179
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
180
+ const full = join(dir, entry.name);
181
+ if (entry.isDirectory() || entry.isSymbolicLink()) try {
182
+ if (statSync(full).isDirectory()) {
183
+ walk(full, depth + 1);
184
+ continue;
185
+ }
186
+ } catch {
187
+ continue;
188
+ }
189
+ if (entry.name.endsWith(".md")) files.push(full);
190
+ }
191
+ } catch {}
192
+ }
193
+ walk(referencesDir, 0);
194
+ return files;
195
+ }
196
+ export { writeToCache as _, getShippedSkills as a, linkGithub as c, linkReleases as d, linkShippedSkill as f, resolvePkgDir as g, readCachedDocs as h, getPkgKeyFiles as i, linkPkg as l, listReferenceFiles as m, clearCache as n, hasShippedDocs as o, listCached as p, ensureCacheDir as r, isCached as s, clearAllCache as t, linkReferences as u };
197
+
198
+ //# sourceMappingURL=storage.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.mjs","names":[],"sources":["../../src/cache/storage.ts"],"sourcesContent":["/**\n * Cache storage operations\n */\n\nimport type { CachedDoc, CachedPackage } from './types'\nimport { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { basename, join } from 'node:path'\nimport { REFERENCES_DIR } from './config'\nimport { getCacheDir } from './version'\n\n/**\n * Check if package is cached at given version\n */\nexport function isCached(name: string, version: string): boolean {\n return existsSync(getCacheDir(name, version))\n}\n\n/**\n * Ensure cache directories exist\n */\nexport function ensureCacheDir(): void {\n mkdirSync(REFERENCES_DIR, { recursive: true })\n}\n\n/**\n * Write docs to cache, cleaning stale version dirs for the same package\n */\nexport function writeToCache(\n name: string,\n version: string,\n docs: CachedDoc[],\n): string {\n const cacheDir = getCacheDir(name, version)\n mkdirSync(cacheDir, { recursive: true })\n\n // Clean stale cache dirs for same package with different version keys\n cleanStaleCacheDirs(name, version)\n\n for (const doc of docs) {\n const filePath = join(cacheDir, doc.path)\n mkdirSync(join(filePath, '..'), { recursive: true })\n writeFileSync(filePath, doc.content)\n }\n\n return cacheDir\n}\n\n/**\n * Remove stale cache dirs for same package but different version keys\n * e.g. @clack/prompts@1.0 vs @clack/prompts@1.0.0\n */\nfunction cleanStaleCacheDirs(name: string, version: string): void {\n const prefix = `${name}@`\n\n // For scoped packages, check inside the scope dir\n if (name.startsWith('@')) {\n const [scope, pkg] = name.split('/')\n const scopeDir = join(REFERENCES_DIR, scope!)\n if (!existsSync(scopeDir))\n return\n\n const scopePrefix = `${pkg}@`\n const currentDirName = basename(getCacheDir(name, version))\n\n for (const entry of readdirSync(scopeDir)) {\n if (entry.startsWith(scopePrefix) && entry !== currentDirName) {\n rmSync(join(scopeDir, entry), { recursive: true, force: true })\n }\n }\n }\n else {\n if (!existsSync(REFERENCES_DIR))\n return\n for (const entry of readdirSync(REFERENCES_DIR)) {\n if (entry.startsWith(prefix) && entry !== basename(getCacheDir(name, version))) {\n rmSync(join(REFERENCES_DIR, entry), { recursive: true, force: true })\n }\n }\n }\n}\n\n/**\n * Create .skilld directory with symlinked docs (only if external fetch needed)\n *\n * Structure:\n * .claude/skills/<skill>/.skilld/\n * pkg -> node_modules/<pkg> (always, has package.json, README.md, dist/)\n * docs -> ~/.skilld/references/<pkg>@<version>/docs (only if fetched externally)\n *\n * The .skilld/ dirs are gitignored. After clone, `skilld install` recreates from lockfile.\n */\nexport function linkReferences(skillDir: string, name: string, version: string): void {\n const cacheDir = getCacheDir(name, version)\n const referencesDir = join(skillDir, '.skilld')\n const docsLinkPath = join(referencesDir, 'docs')\n const cachedDocsPath = join(cacheDir, 'docs')\n\n // Create references dir if needed\n mkdirSync(referencesDir, { recursive: true })\n\n // Symlink docs from cache\n if (existsSync(docsLinkPath)) {\n unlinkSync(docsLinkPath)\n }\n if (existsSync(cachedDocsPath)) {\n symlinkSync(cachedDocsPath, docsLinkPath, 'junction')\n }\n}\n\n/**\n * Create symlink from .skilld dir to cached github data (issues + discussions)\n *\n * Structure:\n * .claude/skills/<skill>/.skilld/github -> ~/.skilld/references/<pkg>@<version>/github\n */\nexport function linkGithub(skillDir: string, name: string, version: string): void {\n const cacheDir = getCacheDir(name, version)\n const referencesDir = join(skillDir, '.skilld')\n const githubLinkPath = join(referencesDir, 'github')\n const cachedGithubPath = join(cacheDir, 'github')\n\n mkdirSync(referencesDir, { recursive: true })\n\n if (existsSync(githubLinkPath)) {\n unlinkSync(githubLinkPath)\n }\n if (existsSync(cachedGithubPath)) {\n symlinkSync(cachedGithubPath, githubLinkPath, 'junction')\n }\n}\n\n/**\n * Resolve the package directory: node_modules first, then cached dist fallback.\n * Returns the path if found, null otherwise.\n */\nexport function resolvePkgDir(name: string, cwd: string, version?: string): string | null {\n const nodeModulesPath = join(cwd, 'node_modules', name)\n if (existsSync(nodeModulesPath))\n return nodeModulesPath\n\n // Fallback: check cached npm dist\n if (version) {\n const cachedPkgDir = join(getCacheDir(name, version), 'pkg')\n if (existsSync(join(cachedPkgDir, 'package.json')))\n return cachedPkgDir\n }\n\n return null\n}\n\n/**\n * Create symlink from .skilld dir to package directory\n *\n * Structure:\n * .claude/skills/<skill>/.skilld/pkg -> node_modules/<pkg> OR ~/.skilld/references/<pkg>@<version>/pkg\n *\n * This gives access to package.json, README.md, dist/, and any shipped docs/\n */\nexport function linkPkg(skillDir: string, name: string, cwd: string, version?: string): void {\n const pkgPath = resolvePkgDir(name, cwd, version)\n if (!pkgPath)\n return\n\n const referencesDir = join(skillDir, '.skilld')\n mkdirSync(referencesDir, { recursive: true })\n\n const pkgLinkPath = join(referencesDir, 'pkg')\n if (existsSync(pkgLinkPath)) {\n unlinkSync(pkgLinkPath)\n }\n symlinkSync(pkgPath, pkgLinkPath, 'junction')\n}\n\n/**\n * Get key files from a package directory for display\n * Returns entry points + docs files\n */\nexport function getPkgKeyFiles(name: string, cwd: string, version?: string): string[] {\n const pkgPath = resolvePkgDir(name, cwd, version)\n if (!pkgPath)\n return []\n\n const files: string[] = []\n const pkgJsonPath = join(pkgPath, 'package.json')\n\n if (existsSync(pkgJsonPath)) {\n const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'))\n\n // Entry points\n if (pkg.main)\n files.push(basename(pkg.main))\n if (pkg.module && pkg.module !== pkg.main)\n files.push(basename(pkg.module))\n }\n\n // Check for common doc files\n for (const f of ['README.md', 'CHANGELOG.md', 'changelog.md']) {\n if (existsSync(join(pkgPath, f)))\n files.push(f)\n }\n\n return [...new Set(files)]\n}\n\n/**\n * Check if package ships its own docs folder\n */\nexport interface ShippedSkill {\n skillName: string\n skillDir: string\n}\n\n/**\n * Check if package ships a skills/ directory with _SKILL.md subdirs\n */\nexport function getShippedSkills(name: string, cwd: string, version?: string): ShippedSkill[] {\n const pkgPath = resolvePkgDir(name, cwd, version)\n if (!pkgPath)\n return []\n\n const skillsPath = join(pkgPath, 'skills')\n if (!existsSync(skillsPath))\n return []\n\n return readdirSync(skillsPath, { withFileTypes: true })\n .filter(d => d.isDirectory() && existsSync(join(skillsPath, d.name, '_SKILL.md')))\n .map(d => ({ skillName: d.name, skillDir: join(skillsPath, d.name) }))\n}\n\n/**\n * Create symlink from .skilld dir to cached releases\n *\n * Structure:\n * .claude/skills/<skill>/.skilld/releases -> ~/.skilld/references/<pkg>@<version>/releases\n */\nexport function linkReleases(skillDir: string, name: string, version: string): void {\n const cacheDir = getCacheDir(name, version)\n const referencesDir = join(skillDir, '.skilld')\n const releasesLinkPath = join(referencesDir, 'releases')\n const cachedReleasesPath = join(cacheDir, 'releases')\n\n mkdirSync(referencesDir, { recursive: true })\n\n if (existsSync(releasesLinkPath)) {\n unlinkSync(releasesLinkPath)\n }\n if (existsSync(cachedReleasesPath)) {\n symlinkSync(cachedReleasesPath, releasesLinkPath, 'junction')\n }\n}\n\n/**\n * Create symlink from skills dir to shipped skill dir\n */\nexport function linkShippedSkill(baseDir: string, skillName: string, targetDir: string): void {\n const linkPath = join(baseDir, skillName)\n if (existsSync(linkPath)) {\n const stat = lstatSync(linkPath)\n if (stat.isSymbolicLink())\n unlinkSync(linkPath)\n else rmSync(linkPath, { recursive: true, force: true })\n }\n symlinkSync(targetDir, linkPath)\n}\n\nexport function hasShippedDocs(name: string, cwd: string, version?: string): boolean {\n const pkgPath = resolvePkgDir(name, cwd, version)\n if (!pkgPath)\n return false\n\n const docsCandidates = ['docs', 'documentation', 'doc']\n for (const candidate of docsCandidates) {\n const docsPath = join(pkgPath, candidate)\n if (existsSync(docsPath))\n return true\n }\n return false\n}\n\n/**\n * List all cached packages\n */\nexport function listCached(): CachedPackage[] {\n if (!existsSync(REFERENCES_DIR))\n return []\n\n return readdirSync(REFERENCES_DIR)\n .filter(name => name.includes('@'))\n .map((dir) => {\n const [name, version] = dir.split('@')\n return { name: name!, version: version!, dir: join(REFERENCES_DIR, dir) }\n })\n}\n\n/**\n * Read cached docs for a package\n */\nexport function readCachedDocs(name: string, version: string): CachedDoc[] {\n const cacheDir = getCacheDir(name, version)\n if (!existsSync(cacheDir))\n return []\n\n const docs: CachedDoc[] = []\n\n function walk(dir: string, prefix = '') {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const entryPath = join(dir, entry.name)\n const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name\n\n if (entry.isDirectory()) {\n walk(entryPath, relativePath)\n }\n else if (entry.name.endsWith('.md') || entry.name.endsWith('.mdx')) {\n docs.push({\n path: relativePath,\n content: readFileSync(entryPath, 'utf-8'),\n })\n }\n }\n }\n\n walk(cacheDir)\n return docs\n}\n\n/**\n * Clear cache for a specific package\n */\nexport function clearCache(name: string, version: string): boolean {\n const cacheDir = getCacheDir(name, version)\n if (!existsSync(cacheDir))\n return false\n\n rmSync(cacheDir, { recursive: true })\n return true\n}\n\n/**\n * Clear all cache\n */\nexport function clearAllCache(): number {\n const packages = listCached()\n for (const pkg of packages) {\n clearCache(pkg.name, pkg.version)\n }\n return packages.length\n}\n\n/**\n * List files in .skilld directory (pkg + docs) as relative paths for prompt context\n * Returns paths like ./.skilld/pkg/README.md, ./.skilld/docs/api.md\n */\nexport function listReferenceFiles(skillDir: string, maxDepth = 3): string[] {\n const referencesDir = join(skillDir, '.skilld')\n if (!existsSync(referencesDir))\n return []\n\n const files: string[] = []\n\n function walk(dir: string, depth: number) {\n if (depth > maxDepth)\n return\n try {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory() || entry.isSymbolicLink()) {\n try {\n const stat = statSync(full)\n if (stat.isDirectory()) {\n walk(full, depth + 1)\n continue\n }\n }\n catch { continue }\n }\n if (entry.name.endsWith('.md')) {\n files.push(full)\n }\n }\n }\n catch {\n // Broken symlink or permission error\n }\n }\n\n walk(referencesDir, 0)\n return files\n}\n"],"mappings":"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"}