skilld 1.7.2 → 1.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/agent.mjs +693 -599
- package/dist/_chunks/agent.mjs.map +1 -1
- package/dist/_chunks/assemble.mjs +3 -3
- package/dist/_chunks/assemble.mjs.map +1 -1
- package/dist/_chunks/author-group.mjs.map +1 -1
- package/dist/_chunks/author.mjs +51 -121
- package/dist/_chunks/author.mjs.map +1 -1
- package/dist/_chunks/cache.mjs +315 -9
- package/dist/_chunks/cache.mjs.map +1 -1
- package/dist/_chunks/cache2.mjs +2 -2
- package/dist/_chunks/cache2.mjs.map +1 -1
- package/dist/_chunks/cli-helpers.mjs +3 -3
- package/dist/_chunks/cli-helpers.mjs.map +1 -1
- package/dist/_chunks/core.mjs +7 -4
- package/dist/_chunks/detect.mjs +1 -1
- package/dist/_chunks/detect.mjs.map +1 -1
- package/dist/_chunks/embedding-cache2.mjs +2 -2
- package/dist/_chunks/embedding-cache2.mjs.map +1 -1
- package/dist/_chunks/index.d.mts +305 -112
- package/dist/_chunks/index.d.mts.map +1 -1
- package/dist/_chunks/index2.d.mts +267 -32
- package/dist/_chunks/index2.d.mts.map +1 -1
- package/dist/_chunks/index3.d.mts +32 -577
- package/dist/_chunks/index3.d.mts.map +1 -1
- package/dist/_chunks/index4.d.mts +553 -0
- package/dist/_chunks/index4.d.mts.map +1 -0
- package/dist/_chunks/install.mjs +48 -88
- package/dist/_chunks/install.mjs.map +1 -1
- package/dist/_chunks/list.mjs +1 -1
- package/dist/_chunks/list.mjs.map +1 -1
- package/dist/_chunks/lockfile.mjs +29 -6
- package/dist/_chunks/lockfile.mjs.map +1 -1
- package/dist/_chunks/markdown.mjs.map +1 -1
- package/dist/_chunks/monorepo.mjs +71 -0
- package/dist/_chunks/monorepo.mjs.map +1 -0
- package/dist/_chunks/package-json.mjs.map +1 -1
- package/dist/_chunks/{shared.mjs → package-registry.mjs} +2 -40
- package/dist/_chunks/package-registry.mjs.map +1 -0
- package/dist/_chunks/paths.mjs +49 -0
- package/dist/_chunks/paths.mjs.map +1 -0
- package/dist/_chunks/pool2.mjs +7 -2
- package/dist/_chunks/pool2.mjs.map +1 -1
- package/dist/_chunks/prepare.mjs +1 -1
- package/dist/_chunks/prepare.mjs.map +1 -1
- package/dist/_chunks/prepare2.mjs +5 -5
- package/dist/_chunks/prepare2.mjs.map +1 -1
- package/dist/_chunks/prompts.mjs +366 -18
- package/dist/_chunks/prompts.mjs.map +1 -1
- package/dist/_chunks/retriv.mjs.map +1 -1
- package/dist/_chunks/sanitize.mjs.map +1 -1
- package/dist/_chunks/search-helpers.mjs +5 -6
- package/dist/_chunks/search-helpers.mjs.map +1 -1
- package/dist/_chunks/search-interactive.mjs +1 -1
- package/dist/_chunks/search-interactive.mjs.map +1 -1
- package/dist/_chunks/search.mjs +1 -2
- package/dist/_chunks/search.mjs.map +1 -1
- package/dist/_chunks/semver.mjs +13 -0
- package/dist/_chunks/semver.mjs.map +1 -0
- package/dist/_chunks/setup.mjs.map +1 -1
- package/dist/_chunks/skill-installer.mjs +2 -0
- package/dist/_chunks/skill-installer2.mjs +155 -0
- package/dist/_chunks/skill-installer2.mjs.map +1 -0
- package/dist/_chunks/skills.mjs +10 -9
- package/dist/_chunks/skills.mjs.map +1 -1
- package/dist/_chunks/sources.mjs +575 -386
- package/dist/_chunks/sources.mjs.map +1 -1
- package/dist/_chunks/sync-pipeline.mjs +952 -0
- package/dist/_chunks/sync-pipeline.mjs.map +1 -0
- package/dist/_chunks/sync-registry.mjs +19 -13
- package/dist/_chunks/sync-registry.mjs.map +1 -1
- package/dist/_chunks/sync.mjs +797 -886
- package/dist/_chunks/sync.mjs.map +1 -1
- package/dist/_chunks/sync2.mjs +4 -2
- package/dist/_chunks/types.d.mts +65 -77
- package/dist/_chunks/types.d.mts.map +1 -1
- package/dist/_chunks/types2.d.mts +88 -0
- package/dist/_chunks/types2.d.mts.map +1 -0
- package/dist/_chunks/uninstall.mjs +7 -8
- package/dist/_chunks/uninstall.mjs.map +1 -1
- package/dist/_chunks/upload.mjs +2 -2
- package/dist/_chunks/upload.mjs.map +1 -1
- package/dist/_chunks/validate.mjs +1 -1
- package/dist/_chunks/validate.mjs.map +1 -1
- package/dist/_chunks/version.mjs +3 -13
- package/dist/_chunks/version.mjs.map +1 -1
- package/dist/_chunks/wizard.mjs +2 -2
- package/dist/_chunks/wizard.mjs.map +1 -1
- package/dist/_chunks/yaml.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -346
- package/dist/agent/index.mjs +2 -3
- package/dist/cache/index.d.mts +2 -2
- package/dist/cache/index.mjs +4 -3
- package/dist/cli.mjs +12 -13
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +5 -4
- package/dist/index.mjs +4 -3
- package/dist/prepare.mjs +2 -2
- package/dist/prepare.mjs.map +1 -1
- package/dist/retriv/index.d.mts +2 -2
- package/dist/retriv/worker.d.mts +2 -1
- package/dist/retriv/worker.d.mts.map +1 -1
- package/dist/retriv/worker.mjs +2 -1
- package/dist/retriv/worker.mjs.map +1 -1
- package/dist/sources/index.d.mts +3 -2
- package/dist/sources/index.mjs +3 -3
- package/dist/types.d.mts +3 -3
- package/package.json +5 -5
- package/dist/_chunks/config.mjs +0 -122
- package/dist/_chunks/config.mjs.map +0 -1
- package/dist/_chunks/prefix.mjs +0 -108
- package/dist/_chunks/prefix.mjs.map +0 -1
- package/dist/_chunks/shared.mjs.map +0 -1
- package/dist/_chunks/skill.mjs +0 -329
- package/dist/_chunks/skill.mjs.map +0 -1
- package/dist/_chunks/sync-shared.mjs +0 -2
- package/dist/_chunks/sync-shared2.mjs +0 -1020
- package/dist/_chunks/sync-shared2.mjs.map +0 -1
- package/dist/agent/index.d.mts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-pipeline.mjs","names":[],"sources":["../../src/agent/skill-builder.ts","../../src/commands/llm-prompts.ts","../../src/retriv/index-pipeline.ts","../../src/sources/content-resolver.ts","../../src/sources/timeline-resolver.ts","../../src/commands/sync-pipeline.ts"],"sourcesContent":["/**\n * SkillBuilder: turns cached references into a generated SKILL.md.\n *\n * Two entry points, both consume a `SkillContext` (skill-state) plus a\n * per-call `RunOptions` (invocation intent):\n * - `enhanceSkillWithLLM` — runs the LLM optimization pipeline and writes\n * the final SKILL.md (with cost/warning surfacing).\n * - `writePromptFiles` — emits PROMPT_*.md per section into `.skilld/`\n * for manual LLM usage (no LLM call).\n *\n * Both share the same per-section prompt assembly via `buildAllSectionPrompts`.\n */\n\nimport type { FeaturesConfig } from '../core/config.ts'\nimport type { CustomPrompt, OptimizeModel, OptimizeResult, SkillSection, StreamProgress } from './index.ts'\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { join, relative } from 'pathe'\nimport { createReferenceCache, listReferenceFiles } from '../cache/index.ts'\nimport { skillInternalDir } from '../core/paths.ts'\nimport {\n buildAllSectionPrompts,\n createToolProgress,\n getModelLabel,\n optimizeDocs,\n SECTION_MERGE_ORDER,\n SECTION_OUTPUT_FILES,\n wrapSection,\n writeGeneratedSkillMd,\n} from './index.ts'\n\nconst RATE_LIMIT_RE = /\\b429\\b|rate.?limit|exhausted.*capacity|quota.*reset/i\n\n/** Upstream metadata gathered during URL/content resolution. */\nexport interface ResolvedSkillMeta {\n repoUrl?: string\n llmsUrl?: string\n releasedAt?: string\n description?: string\n docsUrl?: string\n gitRef?: string\n dependencies?: Record<string, string>\n distTags?: Record<string, { version: string, releasedAt?: string }>\n}\n\n/** Reference-cache state for a skill: what's been linked under `.skilld/`. */\nexport interface SkillReferences {\n docsType: 'llms.txt' | 'readme' | 'docs'\n hasShippedDocs: boolean\n pkgFiles: string[]\n hasIssues: boolean\n hasDiscussions: boolean\n hasReleases: boolean\n hasChangelog: string | false\n}\n\n/**\n * Everything a SkillBuilder needs to know about a skill *before* deciding how\n * to invoke the LLM. Constructed by the orchestrator after fetch+link finish.\n */\nexport interface SkillContext {\n /** Identity for display/frontmatter; storage key defaults to name. */\n packageName: string\n cachePackageName?: string\n version: string\n skillDir: string\n dirName?: string\n references: SkillReferences\n resolved: ResolvedSkillMeta\n relatedSkills: string[]\n packages?: Array<{ name: string }>\n features?: FeaturesConfig\n /** Lines consumed by SKILL.md overhead (frontmatter, etc.) */\n overheadLines?: number\n}\n\n/** Per-call flags for `enhanceSkillWithLLM`. */\nexport interface EnhanceRunOptions {\n model: OptimizeModel\n force?: boolean\n debug?: boolean\n eject?: boolean\n sections?: SkillSection[]\n customPrompt?: CustomPrompt\n}\n\n/** Per-call flags for `writePromptFiles`. */\nexport interface PromptRunOptions {\n sections: SkillSection[]\n customPrompt?: CustomPrompt\n}\n\n/**\n * Pure entry: invoke the LLM, write SKILL.md on success, return the result.\n * No clack UI, no error handling — caller decides how to surface progress and\n * what to do on failure (warn vs throw).\n */\nexport async function runSkillEnhancement(\n ctx: SkillContext,\n run: EnhanceRunOptions,\n onProgress: (progress: StreamProgress) => void,\n): Promise<OptimizeResult> {\n const { packageName, cachePackageName, version, skillDir, dirName, resolved, relatedSkills, references, packages, features, overheadLines } = ctx\n const { docsType, hasShippedDocs: shippedDocs, pkgFiles, hasIssues, hasDiscussions, hasReleases, hasChangelog } = references\n const { model, force, debug, sections, customPrompt, eject } = run\n const cacheKey = cachePackageName || packageName\n\n const docFiles = listReferenceFiles(skillDir)\n const hasGithub = hasIssues || hasDiscussions\n const result = await optimizeDocs({\n packageName: cacheKey,\n skillDir,\n model,\n version,\n hasGithub,\n hasReleases,\n hasChangelog,\n docFiles,\n docsType,\n hasShippedDocs: shippedDocs,\n noCache: force,\n debug,\n sections,\n customPrompt,\n features,\n pkgFiles,\n overheadLines,\n onProgress,\n })\n\n if (result.wasOptimized) {\n writeGeneratedSkillMd(skillDir, {\n name: packageName,\n version,\n releasedAt: resolved.releasedAt,\n distTags: resolved.distTags,\n body: result.optimized,\n relatedSkills,\n hasIssues,\n hasDiscussions,\n hasReleases,\n hasChangelog,\n docsType,\n hasShippedDocs: shippedDocs,\n pkgFiles,\n generatedBy: getModelLabel(model),\n dirName,\n packages,\n repoUrl: resolved.repoUrl,\n features,\n eject,\n })\n }\n\n return result\n}\n\n/**\n * Interactive entry: wraps `runSkillEnhancement` with a clack `taskLog` and\n * surfaces cost/warnings/errors. Use this from interactive sync flows.\n */\nexport async function enhanceSkillWithLLM(ctx: SkillContext, run: EnhanceRunOptions): Promise<void> {\n const llmLog = p.taskLog({ title: `Agent exploring ${ctx.packageName}`, limit: 3 })\n const { wasOptimized, usage, cost, warnings, error, debugLogsDir } = await runSkillEnhancement(ctx, run, createToolProgress(llmLog))\n\n if (wasOptimized) {\n const costParts: string[] = []\n if (usage) {\n const totalK = Math.round(usage.totalTokens / 1000)\n costParts.push(`${totalK}k tokens`)\n }\n if (cost)\n costParts.push(`$${cost.toFixed(2)}`)\n const costSuffix = costParts.length > 0 ? ` (${costParts.join(', ')})` : ''\n llmLog.success(`Generated best practices${costSuffix}`)\n if (debugLogsDir)\n p.log.info(`Debug logs: ${relative(process.cwd(), debugLogsDir)}`)\n if (error)\n p.log.warn(`\\x1B[33mPartial failure: ${error}\\x1B[0m`)\n if (warnings?.length) {\n for (const w of warnings)\n p.log.warn(`\\x1B[33m${w}\\x1B[0m`)\n }\n }\n else {\n if (error && RATE_LIMIT_RE.test(error))\n llmLog.error(`Rate limited by LLM provider. Try again shortly or use a different model via \\`skilld config\\``)\n else\n llmLog.error(`Enhancement failed${error ? `: ${error}` : ''}`)\n }\n}\n\n/** Per-call options for `writeBaseSkill`. */\nexport interface BaseSkillOptions {\n /** LLM-generated body inserted between header and footer. */\n body?: string\n /** Label written into frontmatter (`generated_by`); use 'cached' for replays. */\n generatedBy?: string\n /** Eject mode: portable layout with `./references/` paths. */\n eject?: boolean\n}\n\n/**\n * Write the SKILL.md for `ctx`. Single seam for the writeGeneratedSkillMd\n * field-bag every sync command was assembling inline.\n *\n * Returns the written content (callers use it for line-count diagnostics).\n */\nexport function writeBaseSkill(ctx: SkillContext, opts: BaseSkillOptions = {}): string {\n const { packageName, version, skillDir, dirName, references, resolved, relatedSkills, packages, features } = ctx\n return writeGeneratedSkillMd(skillDir, {\n name: packageName,\n version,\n releasedAt: resolved.releasedAt,\n description: resolved.description,\n distTags: resolved.distTags,\n body: opts.body,\n relatedSkills,\n hasIssues: references.hasIssues,\n hasDiscussions: references.hasDiscussions,\n hasReleases: references.hasReleases,\n hasChangelog: references.hasChangelog,\n docsType: references.docsType,\n hasShippedDocs: references.hasShippedDocs,\n pkgFiles: references.pkgFiles,\n generatedBy: opts.generatedBy,\n dirName,\n packages,\n repoUrl: resolved.repoUrl,\n features,\n eject: opts.eject,\n })\n}\n\n/**\n * If every section in `sections` has cached LLM output for this package,\n * assemble the body and write SKILL.md with `generated_by: cached`.\n *\n * Returns true when applied. Caller passes `DEFAULT_SECTIONS` (or an override)\n * so the agent layer doesn't need to know about command-layer defaults.\n */\nexport function applyCachedSections(ctx: SkillContext, sections: SkillSection[], opts: { eject?: boolean } = {}): boolean {\n const cache = createReferenceCache(ctx.cachePackageName || ctx.packageName, ctx.version)\n const allCached = sections.every(s => cache.readSection(SECTION_OUTPUT_FILES[s]) !== null)\n if (!allCached)\n return false\n\n const parts: string[] = []\n for (const s of SECTION_MERGE_ORDER) {\n if (!sections.includes(s))\n continue\n const content = cache.readSection(SECTION_OUTPUT_FILES[s])\n if (content)\n parts.push(wrapSection(s, content))\n }\n writeBaseSkill(ctx, { body: parts.join('\\n\\n'), generatedBy: 'cached', eject: opts.eject })\n return true\n}\n\n/**\n * Build and write PROMPT_*.md files for manual LLM use.\n * Returns the list of sections that had prompts written.\n */\nexport function writePromptFiles(ctx: SkillContext, run: PromptRunOptions): SkillSection[] {\n const { packageName, version, skillDir, references, features, overheadLines } = ctx\n const { sections, customPrompt } = run\n const docFiles = listReferenceFiles(skillDir)\n const prompts = buildAllSectionPrompts({\n packageName,\n skillDir,\n version,\n hasIssues: references.hasIssues,\n hasDiscussions: references.hasDiscussions,\n hasReleases: references.hasReleases,\n hasChangelog: references.hasChangelog,\n docFiles,\n docsType: references.docsType,\n hasShippedDocs: references.hasShippedDocs,\n pkgFiles: references.pkgFiles,\n customPrompt,\n features,\n overheadLines,\n sections,\n })\n\n const skilldDir = skillInternalDir(skillDir)\n mkdirSync(skilldDir, { recursive: true })\n\n for (const [section, prompt] of prompts)\n writeFileSync(join(skilldDir, `PROMPT_${section}.md`), prompt)\n\n const written = [...prompts.keys()]\n if (written.length > 0) {\n const relDir = relative(process.cwd(), skillDir)\n const promptFiles = written.map(s => `PROMPT_${s}.md`).join(', ')\n const outputFileList = written.map(s => SECTION_OUTPUT_FILES[s]).join(', ')\n p.log.info(`Prompt files written to ${relDir}/.skilld/\\n\\x1B[2m\\x1B[3m Read each prompt file (${promptFiles}) in ${relDir}/.skilld/, read the\\n referenced files, then write your output to the matching file (${outputFileList}).\\n When done, run: skilld assemble\\x1B[0m`)\n }\n\n return written\n}\n","/**\n * Interactive @clack/prompts UI for LLM model + section selection.\n *\n * Three entry points:\n * - `selectModel` — pick a model (or use configured) for one-off use\n * - `selectSkillSections` — pick which SKILL.md sections to enhance\n * - `selectLlmConfig` — combined flow: model + sections + prompt-only\n * option, with update-context hints\n *\n * All return `null` on cancel/no-models/no-tty. Updates `config.model` when\n * the user picks a non-default model.\n */\n\nimport type { CustomPrompt, OptimizeModel, SkillSection } from '../agent/index.ts'\nimport * as p from '@clack/prompts'\nimport { getAvailableModels, getModelName } from '../agent/index.ts'\nimport { maxItems, maxLines } from '../agent/prompts/optional/budget.ts'\nimport { isInteractive, NO_MODELS_MESSAGE, pickModel } from '../cli-helpers.ts'\nimport { readConfig, updateConfig } from '../core/config.ts'\nimport { semverDiff } from '../core/semver.ts'\n\n/** Default sections when model is pre-set (non-interactive) */\nexport const DEFAULT_SECTIONS: SkillSection[] = ['best-practices', 'api-changes']\n\n/**\n * Resolve the model to use when `-y` is passed without `-m`. Returns the\n * caller-supplied override, then the configured model, then the recommended\n * available model. Returns `undefined` when no model can be auto-picked\n * (skipLlm set, no `-y`, or no models installed).\n */\nexport async function resolveAutoModel(\n override: OptimizeModel | undefined,\n yes: boolean | undefined,\n): Promise<OptimizeModel | undefined> {\n if (override)\n return override\n const config = readConfig()\n if (!yes || config.skipLlm)\n return undefined\n if (config.model)\n return config.model\n const available = await getAvailableModels()\n const auto = available.find(m => m.recommended)?.id ?? available[0]?.id\n return auto as OptimizeModel | undefined\n}\n\n/** Select LLM model for SKILL.md generation (independent of target agent) */\nexport async function selectModel(skipPrompt: boolean): Promise<OptimizeModel | null> {\n const config = readConfig()\n const available = await getAvailableModels()\n\n if (available.length === 0) {\n p.log.warn(NO_MODELS_MESSAGE)\n return null\n }\n\n if (skipPrompt) {\n if (config.model && available.some(m => m.id === config.model))\n return config.model\n if (config.model)\n p.log.warn(`Configured model \\x1B[36m${config.model}\\x1B[0m is unavailable — using auto-selected fallback`)\n return available.find(m => m.recommended)?.id ?? available[0]!.id\n }\n\n const choice = await pickModel(available)\n if (!choice)\n return null\n\n updateConfig({ model: choice as OptimizeModel })\n return choice as OptimizeModel\n}\n\nexport async function selectSkillSections(message = 'Enhance SKILL.md'): Promise<{ sections: SkillSection[], customPrompt?: CustomPrompt, cancelled: boolean }> {\n p.log.info('Budgets adapt to package release density.')\n const selected = await p.multiselect({\n message,\n options: [\n { label: 'API changes', value: 'api-changes' as SkillSection, hint: 'new/deprecated APIs from version history' },\n { label: 'Best practices', value: 'best-practices' as SkillSection, hint: 'gotchas, pitfalls, patterns' },\n { label: 'Custom section', value: 'custom' as SkillSection, hint: 'add your own section' },\n ],\n initialValues: DEFAULT_SECTIONS,\n required: false,\n })\n\n if (p.isCancel(selected))\n return { sections: [], cancelled: true }\n\n const sections = selected as SkillSection[]\n if (sections.length === 0)\n return { sections: [], cancelled: false }\n\n if (sections.length > 1) {\n const n = sections.length\n const budgetLines: string[] = []\n for (const s of sections) {\n switch (s) {\n case 'api-changes':\n budgetLines.push(` API changes ${maxItems(6, 12, n)}–${maxItems(6, Math.round(12 * 1.6), n)} items (adapts to release churn)`)\n break\n case 'best-practices':\n budgetLines.push(` Best practices ${maxItems(4, 10, n)}–${maxItems(4, Math.round(10 * 1.3), n)} items`)\n break\n case 'custom':\n budgetLines.push(` Custom ≤${maxLines(50, 80, n)} lines`)\n break\n }\n }\n p.log.info(`Budget (${n} sections):\\n${budgetLines.join('\\n')}`)\n }\n\n let customPrompt: CustomPrompt | undefined\n if (sections.includes('custom')) {\n const heading = await p.text({\n message: 'Section heading',\n placeholder: 'e.g. \"Migration from v2\" or \"SSR Patterns\"',\n })\n if (p.isCancel(heading))\n return { sections: [], cancelled: true }\n\n const body = await p.text({\n message: 'Instructions for this section',\n placeholder: 'e.g. \"Document breaking changes and migration steps from v2 to v3\"',\n })\n if (p.isCancel(body))\n return { sections: [], cancelled: true }\n\n customPrompt = { heading: heading as string, body: body as string }\n }\n\n return { sections, customPrompt, cancelled: false }\n}\n\nexport interface LlmConfig {\n model: OptimizeModel\n sections: SkillSection[]\n customPrompt?: CustomPrompt\n promptOnly?: boolean\n}\n\n/** Context about the existing skill when running an update (not a fresh add). */\nexport interface UpdateContext {\n oldVersion?: string\n newVersion?: string\n syncedAt?: string\n /** Whether the existing SKILL.md was LLM-enhanced (has generated_by in frontmatter). */\n wasEnhanced: boolean\n /** Pre-computed bump type (used by parallel sync to pass the max across packages). */\n bumpType?: string\n}\n\n/**\n * Resolve sections + model for LLM enhancement.\n * If presetModel is provided, uses DEFAULT_SECTIONS without prompting.\n * Returns null if cancelled or no sections/model selected.\n */\nexport async function selectLlmConfig(presetModel?: OptimizeModel, message?: string, updateCtx?: UpdateContext): Promise<LlmConfig | null> {\n if (presetModel) {\n const available = await getAvailableModels()\n if (available.some(m => m.id === presetModel))\n return { model: presetModel, sections: DEFAULT_SECTIONS }\n if (!isInteractive())\n return null\n }\n\n if (!isInteractive())\n return null\n\n const config = readConfig()\n const available = await getAvailableModels()\n\n if (available.length === 0) {\n p.log.warn(NO_MODELS_MESSAGE)\n return null\n }\n\n let defaultModel: OptimizeModel\n if (config.model && available.some(m => m.id === config.model)) {\n defaultModel = config.model\n }\n else {\n if (config.model)\n p.log.warn(`Configured model \\x1B[36m${config.model}\\x1B[0m is unavailable — using auto-selected fallback`)\n defaultModel = (available.find(m => m.recommended)?.id ?? available[0]!.id) as OptimizeModel\n }\n\n const defaultModelName = getModelName(defaultModel)\n const defaultModelInfo = available.find(m => m.id === defaultModel)\n const providerHint = defaultModelInfo?.providerName ?? ''\n const sourceHint = config.model === defaultModel ? 'configured' : 'recommended'\n const defaultHint = providerHint ? `${providerHint} · ${sourceHint}` : sourceHint\n\n let enhanceMessage = message ? `${message}?` : 'Enhance SKILL.md?'\n let defaultToSkip = false\n if (updateCtx) {\n const diff = updateCtx.bumpType\n ?? (updateCtx.oldVersion && updateCtx.newVersion ? semverDiff(updateCtx.oldVersion, updateCtx.newVersion) : null)\n const isSmallBump = diff === 'patch' || diff === 'prerelease' || diff === 'prepatch' || diff === 'preminor' || diff === 'premajor'\n\n const ageParts: string[] = []\n if (diff)\n ageParts.push(diff)\n if (updateCtx.syncedAt) {\n const syncedAtMs = new Date(updateCtx.syncedAt).getTime()\n if (Number.isFinite(syncedAtMs)) {\n const days = Math.floor((Date.now() - syncedAtMs) / 86_400_000)\n ageParts.push(days === 0 ? 'today' : days === 1 ? '1d ago' : `${days}d ago`)\n }\n }\n if (updateCtx.wasEnhanced)\n ageParts.push('LLM-enhanced')\n\n const versionHint = updateCtx.oldVersion && updateCtx.newVersion\n ? `${updateCtx.oldVersion} → ${updateCtx.newVersion}`\n : null\n const hint = [versionHint, ...ageParts].filter(Boolean).join(' · ')\n if (hint)\n enhanceMessage = `Enhance SKILL.md? \\x1B[90m(${hint})\\x1B[0m`\n\n if (updateCtx.wasEnhanced && isSmallBump)\n defaultToSkip = true\n }\n\n const choice = await p.select({\n message: enhanceMessage,\n options: [\n { label: defaultModelName, value: 'default' as const, hint: defaultHint },\n { label: 'Different model', value: 'pick' as const, hint: 'choose another enhancement model' },\n { label: 'Prompt only', value: 'prompt' as const, hint: 'write prompts for manual use' },\n { label: 'Skip', value: 'skip' as const, hint: 'base skill with docs, issues, and types' },\n ],\n ...(defaultToSkip ? { initialValue: 'skip' as const } : {}),\n })\n\n if (p.isCancel(choice))\n return null\n\n if (choice === 'skip')\n return null\n\n if (choice === 'prompt') {\n const { sections, customPrompt, cancelled } = await selectSkillSections(\n message ? `${message} (prompt only)` : 'Select sections for prompt generation',\n )\n if (cancelled || sections.length === 0)\n return null\n return { model: defaultModel, sections, customPrompt, promptOnly: true }\n }\n\n let model: OptimizeModel\n if (choice === 'pick') {\n const picked = await pickModel(available)\n if (!picked)\n return null\n updateConfig({ model: picked as OptimizeModel })\n model = picked as OptimizeModel\n }\n else {\n model = defaultModel\n }\n if (!model)\n return null\n\n const modelName = getModelName(model)\n const { sections, customPrompt, cancelled } = await selectSkillSections(\n message ? `${message} (${modelName})` : `Enhance SKILL.md with ${modelName}`,\n )\n\n if (cancelled || sections.length === 0)\n return null\n\n return { model, sections, customPrompt }\n}\n","/**\n * Search-index pipeline: builds and incrementally updates the per-package\n * sqlite-vec database.\n *\n * Wraps `createIndex` / `listIndexIds` with the higher-level concerns of\n * adding `pkg/` entry files, capping doc count, computing diffs against the\n * existing index, and gracefully degrading when native deps are unavailable.\n */\n\nimport type { FeaturesConfig } from '../core/config.ts'\nimport type { IndexDoc } from '../sources/content-resolver.ts'\nimport { existsSync } from 'node:fs'\nimport { getPackageDbPath, resolvePkgDir } from '../cache/index.ts'\nimport { defaultFeatures, readConfig } from '../core/config.ts'\nimport { resolveEntryFiles } from '../sources/index.ts'\nimport { createIndex, listIndexIds, SearchDepsUnavailableError } from './index.ts'\n\n/** Max docs sent to the embedding pipeline to prevent oversized indexes */\nexport const MAX_INDEX_DOCS = 250\n\n/**\n * Extract the parent document ID from a chunk ID.\n * Chunk IDs have the form \"docId#chunk-N\"; non-chunk IDs return as-is.\n */\nfunction parentDocId(id: string): string {\n const idx = id.indexOf('#chunk-')\n return idx === -1 ? id : id.slice(0, idx)\n}\n\n/** Cap and sort docs by type priority, mutates and truncates allDocs in place */\nfunction capDocs(allDocs: IndexDoc[], max: number, onProgress: (msg: string) => void): void {\n if (allDocs.length <= max)\n return\n const TYPE_PRIORITY: Record<string, number> = { doc: 0, issue: 1, discussion: 2, release: 3, source: 4, types: 5 }\n allDocs.sort((a, b) => {\n const ta = TYPE_PRIORITY[a.metadata?.type || 'doc'] ?? 3\n const tb = TYPE_PRIORITY[b.metadata?.type || 'doc'] ?? 3\n if (ta !== tb)\n return ta - tb\n return a.id.localeCompare(b.id)\n })\n onProgress(`Indexing capped at ${max}/${allDocs.length} docs (prioritized by type)`)\n allDocs.length = max\n}\n\nexport interface IndexResourcesOptions {\n packageName: string\n version: string\n cwd: string\n docsToIndex: IndexDoc[]\n features?: FeaturesConfig\n onProgress: (message: string) => void\n}\n\n/** Index all resources into the search database, with incremental support */\nexport async function indexResources(opts: IndexResourcesOptions): Promise<void> {\n const { packageName, version, cwd, onProgress } = opts\n const features = opts.features ?? readConfig().features ?? defaultFeatures\n\n if (!features.search)\n return\n\n const dbPath = getPackageDbPath(packageName, version)\n const dbExists = existsSync(dbPath)\n\n const allDocs = [...opts.docsToIndex]\n\n // Add entry files\n const pkgDir = resolvePkgDir(packageName, cwd, version)\n if (features.search && pkgDir) {\n onProgress('Scanning exports')\n const entryFiles = await resolveEntryFiles(pkgDir)\n for (const e of entryFiles) {\n allDocs.push({\n id: e.path,\n content: e.content,\n metadata: { package: packageName, source: `pkg/${e.path}`, type: e.type },\n })\n }\n }\n\n if (allDocs.length === 0)\n return\n\n capDocs(allDocs, MAX_INDEX_DOCS, onProgress)\n\n // Full build when no existing DB\n if (!dbExists) {\n onProgress(`Building search index (${allDocs.length} docs)`)\n try {\n await createIndex(allDocs, {\n dbPath,\n onProgress: ({ phase, current, total }) => {\n if (phase === 'storing') {\n const d = allDocs[current - 1]\n const type = d?.metadata?.type === 'source' || d?.metadata?.type === 'types' ? 'code' : (d?.metadata?.type || 'doc')\n onProgress(`Storing ${type} (${current}/${total})`)\n }\n else if (phase === 'embedding') {\n onProgress(`Creating embeddings (${current}/${total})`)\n }\n },\n })\n }\n catch (err) {\n if (err instanceof SearchDepsUnavailableError)\n onProgress('Search indexing skipped (native deps unavailable)')\n else\n throw err\n }\n return\n }\n\n // Incremental update: diff incoming docs against existing index\n let existingIds: string[]\n try {\n existingIds = await listIndexIds({ dbPath })\n }\n catch (err) {\n if (err instanceof SearchDepsUnavailableError) {\n onProgress('Search indexing skipped (native deps unavailable)')\n return\n }\n throw err\n }\n\n const existingParentIds = new Set(existingIds.map(parentDocId))\n const incomingIds = new Set(allDocs.map(d => d.id))\n\n const newDocs = allDocs.filter(d => !existingParentIds.has(d.id))\n const removeIds = existingIds.filter(id => !incomingIds.has(parentDocId(id)))\n\n if (newDocs.length === 0 && removeIds.length === 0) {\n onProgress('Search index up to date')\n return\n }\n\n const parts: string[] = []\n if (newDocs.length > 0)\n parts.push(`+${newDocs.length} new`)\n if (removeIds.length > 0)\n parts.push(`-${removeIds.length} stale`)\n onProgress(`Updating search index (${parts.join(', ')})`)\n\n try {\n await createIndex(newDocs, {\n dbPath,\n removeIds,\n onProgress: ({ phase, current, total }) => {\n if (phase === 'storing') {\n const d = newDocs[current - 1]\n const type = d?.metadata?.type === 'source' || d?.metadata?.type === 'types' ? 'code' : (d?.metadata?.type || 'doc')\n onProgress(`Storing ${type} (${current}/${total})`)\n }\n else if (phase === 'embedding') {\n onProgress(`Creating embeddings (${current}/${total})`)\n }\n },\n })\n }\n catch (err) {\n if (err instanceof SearchDepsUnavailableError)\n onProgress('Search indexing skipped (native deps unavailable)')\n else\n throw err\n }\n}\n","/**\n * Content cascade: resolve in-memory docs for a package given its already-resolved URLs.\n *\n * Pure: no fs writes. The caller owns persistence; a single cache-write seam.\n *\n * Cascade order (first-wins for primary, with one supplementary case):\n * 1. Versioned git docs (docs/**\\/*.md at the package's git tag)\n * - if \"shallow\" and an llms.txt exists, discard and fall through\n * - on success, also fetch llms.txt as supplementary reference\n * 2. Crawl URL (registry-configured, e.g. motion-v)\n * 3. llms.txt (linked docs, with optional docsUrl crawl augmentation)\n * 4. Crawl docsUrl as a fallback when no doc files found above\n * 5. README\n */\n\nimport type { ResolvedPackage } from './types.ts'\nimport { join } from 'pathe'\nimport {\n downloadLlmsDocs,\n fetchCrawledDocs,\n fetchGitDocs,\n fetchGitHubRaw,\n fetchLlmsTxt,\n fetchReadmeContent,\n filterFrameworkDocs,\n isShallowGitDocs,\n normalizeLlmsLinks,\n parseGitHubUrl,\n toCrawlPattern,\n} from './index.ts'\n\nexport interface CachedDoc {\n path: string\n content: string\n}\n\nexport interface IndexDoc {\n id: string\n content: string\n metadata: Record<string, any>\n}\n\nexport interface ResolvedContent {\n /** Docs to persist to the package cache (path is cache-relative) */\n docs: CachedDoc[]\n /** Docs to feed the embedding index (id may differ from cache path) */\n docsToIndex: IndexDoc[]\n /** Human-readable origin (URL or label) of the chosen primary source */\n docSource: string\n /** Which kind of source won */\n docsType: 'docs' | 'llms.txt' | 'readme'\n /** Non-fatal issues to surface to the user */\n warnings: string[]\n}\n\nexport interface ResolveContentOptions {\n packageName: string\n resolved: ResolvedPackage\n version: string\n onProgress: (message: string) => void\n}\n\nexport async function resolveContentDocs(opts: ResolveContentOptions): Promise<ResolvedContent> {\n const { packageName, resolved, version, onProgress } = opts\n\n const docs: CachedDoc[] = []\n const docsToIndex: IndexDoc[] = []\n const warnings: string[] = []\n let docSource: string = resolved.readmeUrl || 'readme'\n let docsType: 'docs' | 'llms.txt' | 'readme' = 'readme'\n\n const isFrameworkDoc = (path: string) => filterFrameworkDocs([path], packageName).length > 0\n\n // 1. Versioned git docs\n if (resolved.gitDocsUrl && resolved.repoUrl) {\n const gh = parseGitHubUrl(resolved.repoUrl)\n if (gh) {\n const r = await tryGitDocs({\n owner: gh.owner,\n repo: gh.repo,\n version,\n packageName,\n repoUrl: resolved.repoUrl,\n llmsUrl: resolved.llmsUrl,\n docsUrl: resolved.docsUrl,\n onProgress,\n })\n if (r) {\n docs.push(...r.docs)\n docsToIndex.push(...r.docsToIndex)\n if (r.warning)\n warnings.push(r.warning)\n if (r.kind === 'git') {\n docSource = r.docSource\n docsType = 'docs'\n }\n }\n }\n }\n\n // 2. Registry-configured crawl URL\n if (resolved.crawlUrl && docs.length === 0) {\n onProgress('Crawling website')\n const crawled = await fetchCrawledDocs(resolved.crawlUrl, onProgress).catch((err) => {\n warnings.push(`Crawl failed for ${resolved.crawlUrl}: ${err?.message || err}`)\n return []\n })\n if (crawled.length === 0) {\n warnings.push(`Crawl returned 0 docs from ${resolved.crawlUrl}`)\n }\n for (const doc of crawled) {\n if (!isFrameworkDoc(doc.path))\n continue\n docs.push(doc)\n docsToIndex.push({\n id: doc.path,\n content: doc.content,\n metadata: { package: packageName, source: doc.path, type: 'doc' },\n })\n }\n if (docs.length > 0) {\n docSource = resolved.crawlUrl\n docsType = 'docs'\n }\n }\n\n // 3. llms.txt\n if (resolved.llmsUrl && docs.length === 0) {\n onProgress('Fetching llms.txt')\n const llmsContent = await fetchLlmsTxt(resolved.llmsUrl)\n if (llmsContent) {\n docSource = resolved.llmsUrl\n docsType = 'llms.txt'\n const baseUrl = resolved.docsUrl || new URL(resolved.llmsUrl).origin\n docs.push({ path: 'llms.txt', content: normalizeLlmsLinks(llmsContent.raw, baseUrl) })\n\n if (llmsContent.links.length > 0) {\n onProgress(`Downloading ${llmsContent.links.length} linked docs`)\n const linked = await downloadLlmsDocs(llmsContent, baseUrl, (_url, done, total) => {\n onProgress(`Downloading linked doc ${done + 1}/${total}`)\n })\n for (const doc of linked) {\n if (!isFrameworkDoc(doc.url))\n continue\n const localPath = doc.url.startsWith('/') ? doc.url.slice(1) : doc.url\n const cachePath = join('docs', ...localPath.split('/'))\n docs.push({ path: cachePath, content: doc.content })\n docsToIndex.push({\n id: doc.url,\n content: doc.content,\n metadata: { package: packageName, source: cachePath, type: 'doc' },\n })\n }\n if (linked.length > 0)\n docsType = 'docs'\n }\n }\n }\n\n // 4. Crawl docsUrl as fallback when no actual doc files found yet\n if (resolved.docsUrl && !docs.some(d => d.path.startsWith('docs/'))) {\n const crawlPattern = resolved.crawlUrl || toCrawlPattern(resolved.docsUrl)\n onProgress('Crawling docs site')\n const maxPages = resolved.crawlUrl ? 200 : 400\n const crawled = await fetchCrawledDocs(crawlPattern, onProgress, maxPages).catch((err) => {\n warnings.push(`Crawl failed for ${crawlPattern}: ${err?.message || err}`)\n return []\n })\n let added = 0\n for (const doc of crawled) {\n if (!isFrameworkDoc(doc.path))\n continue\n docs.push(doc)\n docsToIndex.push({\n id: doc.path,\n content: doc.content,\n metadata: { package: packageName, source: doc.path, type: 'doc' },\n })\n added++\n }\n if (added > 0) {\n docSource = crawlPattern\n docsType = 'docs'\n }\n }\n\n // 5. README fallback\n if (resolved.readmeUrl && docs.length === 0) {\n onProgress('Fetching README')\n const content = await fetchReadmeContent(resolved.readmeUrl)\n if (content) {\n docs.push({ path: 'docs/README.md', content })\n docsToIndex.push({\n id: 'README.md',\n content,\n metadata: { package: packageName, source: 'docs/README.md', type: 'doc' },\n })\n }\n }\n\n return { docs, docsToIndex, docSource, docsType, warnings }\n}\n\ninterface GitDocsAttempt {\n kind: 'git' | 'discarded'\n docs: CachedDoc[]\n docsToIndex: IndexDoc[]\n docSource: string\n warning?: string\n}\n\nasync function tryGitDocs(opts: {\n owner: string\n repo: string\n version: string\n packageName: string\n repoUrl: string\n llmsUrl?: string\n docsUrl?: string\n onProgress: (msg: string) => void\n}): Promise<GitDocsAttempt | null> {\n const { owner, repo, version, packageName, repoUrl, llmsUrl, docsUrl, onProgress } = opts\n onProgress('Fetching git docs')\n const gitDocs = await fetchGitDocs(owner, repo, version, packageName)\n if (!gitDocs || gitDocs.files.length === 0)\n return null\n\n let warning: string | undefined\n if (gitDocs.fallback)\n warning = `Docs fetched from ${gitDocs.ref} branch (no tag found for v${version})`\n\n const BATCH_SIZE = 20\n const results: Array<{ file: string, content: string } | null> = []\n for (let i = 0; i < gitDocs.files.length; i += BATCH_SIZE) {\n const batch = gitDocs.files.slice(i, i + BATCH_SIZE)\n onProgress(`Downloading docs ${Math.min(i + BATCH_SIZE, gitDocs.files.length)}/${gitDocs.files.length} from ${gitDocs.ref}`)\n const batchResults = await Promise.all(\n batch.map(async (file) => {\n const url = `${gitDocs.baseUrl}/${file}`\n const content = await fetchGitHubRaw(url)\n return content ? { file, content } : null\n }),\n )\n results.push(...batchResults)\n }\n\n const docs: CachedDoc[] = []\n const docsToIndex: IndexDoc[] = []\n for (const r of results) {\n if (!r)\n continue\n const stripped = gitDocs.docsPrefix ? r.file.replace(gitDocs.docsPrefix, '') : r.file\n const cachePath = stripped.startsWith('docs/') ? stripped : `docs/${stripped}`\n docs.push({ path: cachePath, content: r.content })\n docsToIndex.push({\n id: cachePath,\n content: r.content,\n metadata: { package: packageName, source: cachePath, type: 'doc' },\n })\n }\n\n // Shallow git-docs: if too few docs and llms.txt exists, discard and fall through\n if (isShallowGitDocs(docs.length) && llmsUrl) {\n onProgress(`Shallow git-docs (${docs.length} files), trying llms.txt`)\n return { kind: 'discarded', docs: [], docsToIndex: [], docSource: '', warning }\n }\n\n // Always cache llms.txt alongside good git-docs as supplementary reference\n if (llmsUrl) {\n onProgress('Caching supplementary llms.txt')\n const llmsContent = await fetchLlmsTxt(llmsUrl)\n if (llmsContent) {\n const baseUrl = docsUrl || new URL(llmsUrl).origin\n docs.push({ path: 'llms.txt', content: normalizeLlmsLinks(llmsContent.raw, baseUrl) })\n if (llmsContent.links.length > 0) {\n onProgress(`Downloading ${llmsContent.links.length} supplementary docs`)\n const supplementary = await downloadLlmsDocs(llmsContent, baseUrl, (_url, done, total) => {\n onProgress(`Downloading supplementary doc ${done + 1}/${total}`)\n })\n for (const doc of supplementary) {\n if (filterFrameworkDocs([doc.url], packageName).length === 0)\n continue\n const localPath = doc.url.startsWith('/') ? doc.url.slice(1) : doc.url\n docs.push({ path: join('llms-docs', ...localPath.split('/')), content: doc.content })\n }\n }\n }\n }\n\n return {\n kind: 'git',\n docs,\n docsToIndex,\n docSource: `${repoUrl}/tree/${gitDocs.ref}/docs`,\n warning,\n }\n}\n","/**\n * Timeline-references cascade: GitHub issues, discussions, and releases.\n *\n * Distinct from `resolveContentDocs` (which handles primary docs). Each\n * timeline source has its own existsSync guard, so this module owns its cache\n * writes (idempotent — won't refetch if data exists).\n *\n * Repo-level data lives at `~/.skilld/references/<owner>/<repo>/{issues,\n * discussions,releases}/` when a GitHub repo is known; otherwise it falls\n * back to the per-package cache.\n */\n\nimport type { FeaturesConfig } from '../core/config.ts'\nimport type { IndexDoc } from './content-resolver.ts'\nimport type { ResolvedPackage } from './types.ts'\nimport { existsSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { getCacheDir, getRepoCacheDir, writeToCache, writeToRepoCache } from '../cache/index.ts'\nimport { parseFrontmatter } from '../core/markdown.ts'\nimport { sanitizeMarkdown } from '../core/sanitize.ts'\nimport {\n fetchBlogReleases,\n fetchGitHubDiscussions,\n fetchGitHubIssues,\n fetchReleaseNotes,\n formatDiscussionAsMarkdown,\n formatIssueAsMarkdown,\n generateDiscussionIndex,\n generateIssueIndex,\n generateReleaseIndex,\n getBlogPreset,\n getPrereleaseChangelogRef,\n isGhAvailable,\n isPrerelease,\n parseGitHubUrl,\n} from './index.ts'\n\nexport interface TimelineReferences {\n docsToIndex: IndexDoc[]\n hasIssues: boolean\n hasDiscussions: boolean\n hasReleases: boolean\n repoInfo?: { owner: string, repo: string }\n}\n\nexport interface ResolveTimelineOptions {\n packageName: string\n resolved: ResolvedPackage\n version: string\n features: FeaturesConfig\n /** Lower-bound date for release/issue/discussion collection (ISO date) */\n from?: string\n onProgress: (message: string) => void\n}\n\nexport async function resolveTimelineReferences(opts: ResolveTimelineOptions): Promise<TimelineReferences> {\n const { packageName, resolved, version, features, from, onProgress } = opts\n const docsToIndex: IndexDoc[] = []\n\n const gh = resolved.repoUrl ? parseGitHubUrl(resolved.repoUrl) : null\n const repoInfo = gh ? { owner: gh.owner, repo: gh.repo } : undefined\n\n const repoCacheDir = repoInfo ? getRepoCacheDir(repoInfo.owner, repoInfo.repo) : null\n const cacheDir = getCacheDir(packageName, version)\n const issuesDir = repoCacheDir ? join(repoCacheDir, 'issues') : join(cacheDir, 'issues')\n const discussionsDir = repoCacheDir ? join(repoCacheDir, 'discussions') : join(cacheDir, 'discussions')\n const releasesPath = repoCacheDir ? join(repoCacheDir, 'releases') : join(cacheDir, 'releases')\n\n // Issues\n if (features.issues && gh && isGhAvailable() && !existsSync(issuesDir)) {\n onProgress('Fetching issues via GitHub API')\n const issues = await fetchGitHubIssues(gh.owner, gh.repo, 30, resolved.releasedAt, from).catch(() => [])\n if (issues.length > 0) {\n onProgress(`Caching ${issues.length} issues`)\n const issueDocs = [\n ...issues.map(issue => ({\n path: `issues/issue-${issue.number}.md`,\n content: formatIssueAsMarkdown(issue),\n })),\n {\n path: 'issues/_INDEX.md',\n content: generateIssueIndex(issues),\n },\n ]\n if (repoInfo)\n writeToRepoCache(repoInfo.owner, repoInfo.repo, issueDocs)\n else\n writeToCache(packageName, version, issueDocs)\n for (const issue of issues) {\n docsToIndex.push({\n id: `issue-${issue.number}`,\n content: sanitizeMarkdown(`#${issue.number}: ${issue.title}\\n\\n${issue.body || ''}`),\n metadata: { package: packageName, source: `issues/issue-${issue.number}.md`, type: 'issue', number: issue.number },\n })\n }\n }\n }\n\n // Discussions\n if (features.discussions && gh && isGhAvailable() && !existsSync(discussionsDir)) {\n onProgress('Fetching discussions via GitHub API')\n const discussions = await fetchGitHubDiscussions(gh.owner, gh.repo, 20, resolved.releasedAt, from).catch(() => [])\n if (discussions.length > 0) {\n onProgress(`Caching ${discussions.length} discussions`)\n const discussionDocs = [\n ...discussions.map(d => ({\n path: `discussions/discussion-${d.number}.md`,\n content: formatDiscussionAsMarkdown(d),\n })),\n {\n path: 'discussions/_INDEX.md',\n content: generateDiscussionIndex(discussions),\n },\n ]\n if (repoInfo)\n writeToRepoCache(repoInfo.owner, repoInfo.repo, discussionDocs)\n else\n writeToCache(packageName, version, discussionDocs)\n for (const d of discussions) {\n docsToIndex.push({\n id: `discussion-${d.number}`,\n content: sanitizeMarkdown(`#${d.number}: ${d.title}\\n\\n${d.body || ''}`),\n metadata: { package: packageName, source: `discussions/discussion-${d.number}.md`, type: 'discussion', number: d.number },\n })\n }\n }\n }\n\n // Releases (GitHub releases + blog releases + CHANGELOG into a unified releases/ dir)\n if (features.releases && gh && isGhAvailable() && !existsSync(releasesPath)) {\n onProgress('Fetching releases via GitHub API')\n const changelogRef = isPrerelease(version) ? getPrereleaseChangelogRef(packageName) : undefined\n const releaseDocs = await fetchReleaseNotes(gh.owner, gh.repo, version, resolved.gitRef, packageName, from, changelogRef).catch(() => [])\n\n let blogDocs: Array<{ path: string, content: string }> = []\n if (getBlogPreset(packageName)) {\n onProgress('Fetching blog release notes')\n blogDocs = await fetchBlogReleases(packageName, version).catch(() => [])\n }\n\n const allDocs = [...releaseDocs, ...blogDocs]\n\n const blogEntries = blogDocs\n .filter(d => !d.path.endsWith('_INDEX.md'))\n .map((d) => {\n const versionMatch = d.path.match(/blog-(.+)\\.md$/)\n const fm = parseFrontmatter(d.content)\n return {\n version: versionMatch?.[1] ?? '',\n title: fm.title ?? `Release ${versionMatch?.[1]}`,\n date: fm.date ?? '',\n }\n })\n .filter(b => b.version)\n\n const ghReleases = releaseDocs\n .filter(d => d.path.startsWith('releases/') && !d.path.endsWith('CHANGELOG.md'))\n .map((d) => {\n const fm = parseFrontmatter(d.content)\n const tag = fm.tag ?? ''\n const name = fm.name ?? tag\n const published = fm.published ?? ''\n return { id: 0, tag, name, prerelease: false, createdAt: published, publishedAt: published, markdown: '' }\n })\n .filter(r => r.tag)\n\n const hasChangelog = allDocs.some(d => d.path === 'releases/CHANGELOG.md')\n\n if (ghReleases.length > 0 || blogEntries.length > 0) {\n allDocs.push({\n path: 'releases/_INDEX.md',\n content: generateReleaseIndex({ releases: ghReleases, packageName, blogReleases: blogEntries, hasChangelog }),\n })\n }\n\n if (allDocs.length > 0) {\n onProgress(`Caching ${allDocs.length} releases`)\n if (repoInfo)\n writeToRepoCache(repoInfo.owner, repoInfo.repo, allDocs)\n else\n writeToCache(packageName, version, allDocs)\n for (const doc of allDocs) {\n docsToIndex.push({\n id: doc.path,\n content: doc.content,\n metadata: { package: packageName, source: doc.path, type: 'release' },\n })\n }\n }\n }\n\n return {\n docsToIndex,\n hasIssues: features.issues && existsSync(issuesDir),\n hasDiscussions: features.discussions && existsSync(discussionsDir),\n hasReleases: features.releases && existsSync(releasesPath),\n repoInfo,\n }\n}\n","import type { SkillContext } from '../agent/skill-builder.ts'\nimport type { FeaturesConfig } from '../core/config.ts'\nimport type { IndexDoc } from '../sources/content-resolver.ts'\nimport type { ResolvedPackage, ResolveStep } from '../sources/index.ts'\nimport { existsSync, readdirSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { createReferenceCache } from '../cache/index.ts'\nimport { defaultFeatures, readConfig } from '../core/config.ts'\nimport { buildPackageDirMap, readLock } from '../core/lockfile.ts'\nimport { indexResources } from '../retriv/index-pipeline.ts'\nimport { resolveContentDocs } from '../sources/content-resolver.ts'\nimport {\n fetchNpmPackage,\n generateDocsIndex,\n} from '../sources/index.ts'\nimport { resolveTimelineReferences } from '../sources/timeline-resolver.ts'\n\nexport type { IndexDoc } from '../sources/content-resolver.ts'\n\nexport const RESOLVE_STEP_LABELS: Record<ResolveStep, string> = {\n 'npm': 'npm registry',\n 'github-docs': 'GitHub docs',\n 'github-meta': 'GitHub meta',\n 'github-search': 'GitHub search',\n 'readme': 'README',\n 'llms.txt': 'llms.txt',\n 'crawl': 'website crawl',\n 'local': 'node_modules',\n}\n\nexport async function findRelatedSkills(packageName: string, skillsDir: string): Promise<string[]> {\n const related: string[] = []\n\n const npmInfo = await fetchNpmPackage(packageName)\n if (!npmInfo?.dependencies)\n return related\n\n const deps = new Set(Object.keys(npmInfo.dependencies))\n\n if (!existsSync(skillsDir))\n return related\n\n const lock = readLock(skillsDir)\n const pkgToDirName = lock ? buildPackageDirMap(lock) : new Map<string, string>()\n const installedSet = new Set(readdirSync(skillsDir))\n\n for (const dep of deps) {\n const dirName = pkgToDirName.get(dep)\n if (dirName && installedSet.has(dirName))\n related.push(dirName)\n }\n\n return related.slice(0, 5)\n}\n\n/** Detect CHANGELOG.md in a package directory or cached releases */\nexport function detectChangelog(pkgDir: string | null, cacheDir?: string): string | false {\n if (pkgDir) {\n const found = ['CHANGELOG.md', 'changelog.md'].find(f => existsSync(join(pkgDir, f)))\n if (found)\n return `pkg/${found}`\n }\n // Also check cached releases/CHANGELOG.md (fetched from GitHub)\n if (cacheDir && existsSync(join(cacheDir, 'releases', 'CHANGELOG.md')))\n return 'releases/CHANGELOG.md'\n return false\n}\n\n// ── Shared pipeline functions ──\n\nexport interface FetchResult {\n docSource: string\n docsType: 'llms.txt' | 'readme' | 'docs'\n docsToIndex: IndexDoc[]\n hasIssues: boolean\n hasDiscussions: boolean\n hasReleases: boolean\n warnings: string[]\n /** Parsed GitHub owner/repo for repo-level cache */\n repoInfo?: { owner: string, repo: string }\n /** Whether this result was served from cache (no fresh fetches) */\n usedCache: boolean\n}\n\n/** Fetch and cache all resources for a package (docs cascade + issues + discussions + releases) */\nexport async function fetchAndCacheResources(opts: {\n packageName: string\n resolved: ResolvedPackage\n version: string\n useCache: boolean\n features?: FeaturesConfig\n /** Lower-bound date for release/issue/discussion collection (ISO date) */\n from?: string\n onProgress: (message: string) => void\n}): Promise<FetchResult> {\n const { packageName, resolved, version, onProgress } = opts\n const features = opts.features ?? readConfig().features ?? defaultFeatures\n const cache = createReferenceCache(packageName, version)\n\n // Retry fetch if cache is README-only but richer sources exist (likely transient failure)\n const cacheInvalidated = opts.useCache\n && resolved.crawlUrl\n && cache.detectDocs(resolved.repoUrl, resolved.llmsUrl).docsType === 'readme'\n const useCache = opts.useCache && !cacheInvalidated\n let docSource: string = resolved.readmeUrl || 'readme'\n let docsType: 'llms.txt' | 'readme' | 'docs' = 'readme'\n const docsToIndex: IndexDoc[] = []\n const warnings: string[] = []\n if (cacheInvalidated)\n warnings.push(`Retrying crawl for ${resolved.crawlUrl} (previous attempt only cached README)`)\n\n if (!useCache) {\n const content = await resolveContentDocs({ packageName, resolved, version, onProgress })\n docSource = content.docSource\n docsType = content.docsType\n docsToIndex.push(...content.docsToIndex)\n warnings.push(...content.warnings)\n if (content.docs.length > 0) {\n cache.write(content.docs)\n // Generate docs index when we have multiple markdown files under docs/\n if (docsType !== 'readme' && content.docs.filter(d => d.path.startsWith('docs/') && d.path.endsWith('.md')).length > 1) {\n const docsIndex = generateDocsIndex(content.docs)\n if (docsIndex)\n cache.write([{ path: 'docs/_INDEX.md', content: docsIndex }])\n }\n }\n }\n else {\n const cached = cache.load({\n repoUrl: resolved.repoUrl,\n llmsUrl: resolved.llmsUrl,\n readmeUrl: resolved.readmeUrl,\n onProgress,\n generateDocsIndex,\n })\n docsType = cached.docsType\n docSource = cached.docSource\n docsToIndex.push(...cached.docsToIndex)\n if (cached.backfillIndex)\n cache.write([cached.backfillIndex])\n }\n\n const timeline = await resolveTimelineReferences({\n packageName,\n resolved,\n version,\n features,\n from: opts.from,\n onProgress,\n })\n docsToIndex.push(...timeline.docsToIndex)\n\n return {\n docSource,\n docsType,\n docsToIndex,\n hasIssues: timeline.hasIssues,\n hasDiscussions: timeline.hasDiscussions,\n hasReleases: timeline.hasReleases,\n warnings,\n repoInfo: timeline.repoInfo,\n usedCache: useCache,\n }\n}\n\nexport interface PreparedSkill {\n hasChangelog: string | false\n shippedDocs: boolean\n pkgFiles: string[]\n relatedSkills: string[]\n}\n\n/**\n * Post-fetch preparation: link references under `.skilld/`, build the search\n * index (when enabled), and gather the local facts the SkillBuilder needs\n * (changelog, shipped-docs flag, key files, related skills).\n *\n * Pass `baseDir` to populate `relatedSkills`; omit to skip the lookup\n * (e.g. for sync-git where the base dir has no installed skills to relate to).\n */\nexport async function prepareSkillReferences(opts: {\n packageName: string\n version: string\n cwd: string\n skillDir: string\n resources: FetchResult\n features: FeaturesConfig\n /** When provided, run `findRelatedSkills` against this directory. */\n baseDir?: string\n onIndexProgress?: (msg: string) => void\n}): Promise<PreparedSkill> {\n const { packageName, version, cwd, skillDir, resources, features, baseDir, onIndexProgress } = opts\n const cache = createReferenceCache(packageName, version)\n\n cache.linkInto(skillDir, cwd, resources.docsType, { features, repoInfo: resources.repoInfo })\n\n if (features.search) {\n await indexResources({\n packageName,\n version,\n cwd,\n docsToIndex: resources.docsToIndex,\n features,\n onProgress: onIndexProgress ?? (() => {}),\n })\n }\n\n const pkgDir = cache.pkgDir(cwd)\n const hasChangelog = detectChangelog(pkgDir, cache.dir)\n const shippedDocs = cache.hasShipped(cwd)\n const pkgFiles = cache.keyFiles(cwd)\n const relatedSkills = baseDir ? await findRelatedSkills(packageName, baseDir) : []\n\n return { hasChangelog, shippedDocs, pkgFiles, relatedSkills }\n}\n\nexport { resolveLocalDep } from '../sources/local-dep.ts'\n\nexport interface BuildSkillContextOpts {\n /** Display/frontmatter identity (e.g. `@scope/pkg`) */\n packageName: string\n /** Storage key for cache lookups; defaults to `packageName` */\n cachePackageName?: string\n version: string\n skillDir: string\n skillDirName: string\n resources: FetchResult\n prepared: PreparedSkill\n resolved: ResolvedPackage\n /** Multi-package merge list (only carried when length > 1) */\n packages?: Array<{ name: string }>\n features: FeaturesConfig\n overheadLines?: number\n}\n\n/**\n * Assemble a `SkillContext` from the pipeline outputs. Single seam so the\n * SkillContext shape isn't reconstructed inline in every sync orchestrator.\n */\nexport function buildSkillContext(opts: BuildSkillContextOpts): SkillContext {\n const { packages, cachePackageName, packageName } = opts\n return {\n packageName,\n ...(cachePackageName && cachePackageName !== packageName ? { cachePackageName } : {}),\n version: opts.version,\n skillDir: opts.skillDir,\n dirName: opts.skillDirName,\n references: {\n docsType: opts.resources.docsType,\n hasShippedDocs: opts.prepared.shippedDocs,\n pkgFiles: opts.prepared.pkgFiles,\n hasIssues: opts.resources.hasIssues,\n hasDiscussions: opts.resources.hasDiscussions,\n hasReleases: opts.resources.hasReleases,\n hasChangelog: opts.prepared.hasChangelog,\n },\n resolved: opts.resolved,\n relatedSkills: opts.prepared.relatedSkills,\n packages: packages && packages.length > 1 ? packages : undefined,\n features: opts.features,\n overheadLines: opts.overheadLines,\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { join } from "pathe";
|
|
1
|
+
import { c as SHARED_SKILLS_DIR } from "./paths.mjs";
|
|
2
|
+
import { r as writeSkillMd } from "./prompts.mjs";
|
|
3
|
+
import { l as resolveBaseDir, s as installSkill } from "./skill-installer2.mjs";
|
|
5
4
|
import { mkdirSync } from "node:fs";
|
|
5
|
+
import { join } from "pathe";
|
|
6
6
|
import { ofetch } from "ofetch";
|
|
7
7
|
const DEFAULT_REGISTRY_URL = "https://skilld.dev/api";
|
|
8
8
|
function getRegistryBase() {
|
|
@@ -41,17 +41,23 @@ async function syncRegistrySkill(opts) {
|
|
|
41
41
|
const skillDir = join(join(cwd, SHARED_SKILLS_DIR), skill.name);
|
|
42
42
|
mkdirSync(skillDir, { recursive: true });
|
|
43
43
|
writeSkillMd(skillDir, skill.content);
|
|
44
|
-
const baseDir =
|
|
44
|
+
const baseDir = resolveBaseDir(cwd, agent, false);
|
|
45
45
|
mkdirSync(baseDir, { recursive: true });
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
installSkill({
|
|
47
|
+
cwd,
|
|
48
|
+
agent,
|
|
49
|
+
global: false,
|
|
50
|
+
baseDir,
|
|
51
|
+
skillDirName: skill.name,
|
|
52
|
+
lock: {
|
|
53
|
+
packageName: skill.packageName,
|
|
54
|
+
version: skill.updatedAt,
|
|
55
|
+
repo: skill.repo,
|
|
56
|
+
source: "registry",
|
|
57
|
+
syncedAt: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
58
|
+
generator: "curator"
|
|
59
|
+
}
|
|
53
60
|
});
|
|
54
|
-
linkSkillToAgents(skill.name, skillDir, cwd, agent);
|
|
55
61
|
return skill;
|
|
56
62
|
}
|
|
57
63
|
export { syncRegistrySkill };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-registry.mjs","names":[],"sources":["../../src/registry/client.ts","../../src/commands/sync-registry.ts"],"sourcesContent":["/**\n * Registry client for skilld.dev\n *\n * Talks to the public skilld.dev JSON API: resolves an npm package name to a\n * curated skill's owner/repo, then fetches the detail payload which includes\n * the raw SKILL.md. For local development, set SKILLD_REGISTRY_URL (e.g.\n * http://localhost:3000/api) to point at a running Nuxt dev server.\n *\n * Returns null when a skill isn't curated, the API is unreachable, or the\n * skill has no resolvable SKILL.md, so callers fall through to the\n * doc-generation pipeline.\n */\n\nimport { ofetch } from 'ofetch'\n\nconst DEFAULT_REGISTRY_URL = 'https://skilld.dev/api'\n\nexport function getRegistryBase(): string {\n return (process.env.SKILLD_REGISTRY_URL || DEFAULT_REGISTRY_URL).replace(/\\/$/, '')\n}\n\nexport interface RegistrySkill {\n /** Skill directory name (matches what lands in .claude/skills/<name>/) */\n name: string\n /** npm package name used to look up this skill */\n packageName: string\n /** Raw SKILL.md content (frontmatter + body) */\n content: string\n /** Source repo owner */\n owner: string\n /** Full \"owner/repo\" identifier */\n repo: string\n /** Human-readable display name from the registry */\n displayName?: string\n /** Install count reported by the registry */\n installs?: number\n /** True when the source repo is on the official owners list */\n official?: boolean\n /** Default branch the SKILL.md was fetched from */\n branch?: string\n /** Path to SKILL.md within the source repo */\n skillPath?: string\n /** ISO timestamp of the source repo's last push — used for staleness */\n updatedAt?: string\n}\n\nexport interface RegistrySearchHit {\n name: string\n packageName: string\n displayName?: string\n owner: string\n repo: string\n installs?: number\n official?: boolean\n}\n\nexport interface RegistrySearchResult {\n skills: RegistrySearchHit[]\n total: number\n}\n\ninterface ResolveResponseEntry {\n owner: string\n repo: string\n official: boolean\n}\n\ninterface SkillDetailResponse {\n owner: string\n repo: string\n name: string\n displayName: string\n installs: number\n branch?: string\n skillPath?: string | null\n raw?: string | null\n pushedAt?: string | null\n}\n\ninterface SkillListItem {\n name: string\n owner: string\n repo: string\n displayName: string\n installs: number\n official: boolean\n}\n\ninterface SkillListResponse {\n items: SkillListItem[]\n total: number\n}\n\nexport interface FetchRegistrySkillOptions {\n /** Narrow the resolve to a specific owner when multiple skills share a name */\n owner?: string\n}\n\n/**\n * Fetch a curated package skill from the registry.\n * Returns null if no curated skill exists, the SKILL.md can't be loaded, or the API is unreachable.\n */\nexport async function fetchRegistrySkill(\n packageName: string,\n opts: FetchRegistrySkillOptions = {},\n): Promise<RegistrySkill | null> {\n const base = getRegistryBase()\n\n const resolved = await ofetch<Record<string, ResolveResponseEntry>>(`${base}/skills/resolve`, {\n method: 'POST',\n body: { items: [{ packageName, owner: opts.owner }] },\n }).catch(() => null)\n\n const hit = resolved?.[packageName]\n if (!hit)\n return null\n\n const slug = `${hit.owner}/${hit.repo}/${packageName}`\n const detail = await ofetch<SkillDetailResponse>(`${base}/skills/${slug}`).catch(() => null)\n\n if (!detail?.raw)\n return null\n\n return {\n name: detail.name,\n packageName,\n content: detail.raw,\n owner: detail.owner,\n repo: `${detail.owner}/${detail.repo}`,\n displayName: detail.displayName,\n installs: detail.installs,\n official: hit.official,\n branch: detail.branch,\n skillPath: detail.skillPath ?? undefined,\n updatedAt: detail.pushedAt ?? undefined,\n }\n}\n\n/**\n * Search the registry for skills matching a query.\n */\nexport async function searchRegistry(\n query: string,\n opts: { limit?: number, owner?: string, official?: boolean } = {},\n): Promise<RegistrySearchResult> {\n const base = getRegistryBase()\n const result = await ofetch<SkillListResponse>(`${base}/skills`, {\n query: {\n q: query,\n limit: opts.limit ?? 20,\n owner: opts.owner,\n official: opts.official ? 'true' : undefined,\n },\n }).catch(() => null)\n\n if (!result)\n return { skills: [], total: 0 }\n\n return {\n skills: result.items.map(i => ({\n name: i.name,\n packageName: i.name,\n displayName: i.displayName,\n owner: i.owner,\n repo: `${i.owner}/${i.repo}`,\n installs: i.installs,\n official: i.official,\n })),\n total: result.total,\n }\n}\n\n/**\n * Check whether the registry has a newer SKILL.md than the local copy.\n * Returns the new `updatedAt` timestamp if newer, else null.\n */\nexport async function checkRegistryUpdate(\n packageName: string,\n currentUpdatedAt: string | undefined,\n): Promise<string | null> {\n const skill = await fetchRegistrySkill(packageName)\n if (!skill?.updatedAt)\n return null\n if (!currentUpdatedAt || skill.updatedAt > currentUpdatedAt)\n return skill.updatedAt\n return null\n}\n","/**\n * Registry-based skill installation\n *\n * Simplified install flow for curated skills from skilld.dev:\n * fetch SKILL.md → write to disk → update lockfile → link to agents.\n *\n * No doc resolution, no LLM, no caching. Fast path.\n */\n\nimport type { AgentType } from '../agent/index.ts'\nimport type { RegistrySkill } from '../registry/client.ts'\nimport { mkdirSync } from 'node:fs'\nimport { join } from 'pathe'\nimport {
|
|
1
|
+
{"version":3,"file":"sync-registry.mjs","names":[],"sources":["../../src/registry/client.ts","../../src/commands/sync-registry.ts"],"sourcesContent":["/**\n * Registry client for skilld.dev\n *\n * Talks to the public skilld.dev JSON API: resolves an npm package name to a\n * curated skill's owner/repo, then fetches the detail payload which includes\n * the raw SKILL.md. For local development, set SKILLD_REGISTRY_URL (e.g.\n * http://localhost:3000/api) to point at a running Nuxt dev server.\n *\n * Returns null when a skill isn't curated, the API is unreachable, or the\n * skill has no resolvable SKILL.md, so callers fall through to the\n * doc-generation pipeline.\n */\n\nimport { ofetch } from 'ofetch'\n\nconst DEFAULT_REGISTRY_URL = 'https://skilld.dev/api'\n\nexport function getRegistryBase(): string {\n return (process.env.SKILLD_REGISTRY_URL || DEFAULT_REGISTRY_URL).replace(/\\/$/, '')\n}\n\nexport interface RegistrySkill {\n /** Skill directory name (matches what lands in .claude/skills/<name>/) */\n name: string\n /** npm package name used to look up this skill */\n packageName: string\n /** Raw SKILL.md content (frontmatter + body) */\n content: string\n /** Source repo owner */\n owner: string\n /** Full \"owner/repo\" identifier */\n repo: string\n /** Human-readable display name from the registry */\n displayName?: string\n /** Install count reported by the registry */\n installs?: number\n /** True when the source repo is on the official owners list */\n official?: boolean\n /** Default branch the SKILL.md was fetched from */\n branch?: string\n /** Path to SKILL.md within the source repo */\n skillPath?: string\n /** ISO timestamp of the source repo's last push — used for staleness */\n updatedAt?: string\n}\n\nexport interface RegistrySearchHit {\n name: string\n packageName: string\n displayName?: string\n owner: string\n repo: string\n installs?: number\n official?: boolean\n}\n\nexport interface RegistrySearchResult {\n skills: RegistrySearchHit[]\n total: number\n}\n\ninterface ResolveResponseEntry {\n owner: string\n repo: string\n official: boolean\n}\n\ninterface SkillDetailResponse {\n owner: string\n repo: string\n name: string\n displayName: string\n installs: number\n branch?: string\n skillPath?: string | null\n raw?: string | null\n pushedAt?: string | null\n}\n\ninterface SkillListItem {\n name: string\n owner: string\n repo: string\n displayName: string\n installs: number\n official: boolean\n}\n\ninterface SkillListResponse {\n items: SkillListItem[]\n total: number\n}\n\nexport interface FetchRegistrySkillOptions {\n /** Narrow the resolve to a specific owner when multiple skills share a name */\n owner?: string\n}\n\n/**\n * Fetch a curated package skill from the registry.\n * Returns null if no curated skill exists, the SKILL.md can't be loaded, or the API is unreachable.\n */\nexport async function fetchRegistrySkill(\n packageName: string,\n opts: FetchRegistrySkillOptions = {},\n): Promise<RegistrySkill | null> {\n const base = getRegistryBase()\n\n const resolved = await ofetch<Record<string, ResolveResponseEntry>>(`${base}/skills/resolve`, {\n method: 'POST',\n body: { items: [{ packageName, owner: opts.owner }] },\n }).catch(() => null)\n\n const hit = resolved?.[packageName]\n if (!hit)\n return null\n\n const slug = `${hit.owner}/${hit.repo}/${packageName}`\n const detail = await ofetch<SkillDetailResponse>(`${base}/skills/${slug}`).catch(() => null)\n\n if (!detail?.raw)\n return null\n\n return {\n name: detail.name,\n packageName,\n content: detail.raw,\n owner: detail.owner,\n repo: `${detail.owner}/${detail.repo}`,\n displayName: detail.displayName,\n installs: detail.installs,\n official: hit.official,\n branch: detail.branch,\n skillPath: detail.skillPath ?? undefined,\n updatedAt: detail.pushedAt ?? undefined,\n }\n}\n\n/**\n * Search the registry for skills matching a query.\n */\nexport async function searchRegistry(\n query: string,\n opts: { limit?: number, owner?: string, official?: boolean } = {},\n): Promise<RegistrySearchResult> {\n const base = getRegistryBase()\n const result = await ofetch<SkillListResponse>(`${base}/skills`, {\n query: {\n q: query,\n limit: opts.limit ?? 20,\n owner: opts.owner,\n official: opts.official ? 'true' : undefined,\n },\n }).catch(() => null)\n\n if (!result)\n return { skills: [], total: 0 }\n\n return {\n skills: result.items.map(i => ({\n name: i.name,\n packageName: i.name,\n displayName: i.displayName,\n owner: i.owner,\n repo: `${i.owner}/${i.repo}`,\n installs: i.installs,\n official: i.official,\n })),\n total: result.total,\n }\n}\n\n/**\n * Check whether the registry has a newer SKILL.md than the local copy.\n * Returns the new `updatedAt` timestamp if newer, else null.\n */\nexport async function checkRegistryUpdate(\n packageName: string,\n currentUpdatedAt: string | undefined,\n): Promise<string | null> {\n const skill = await fetchRegistrySkill(packageName)\n if (!skill?.updatedAt)\n return null\n if (!currentUpdatedAt || skill.updatedAt > currentUpdatedAt)\n return skill.updatedAt\n return null\n}\n","/**\n * Registry-based skill installation\n *\n * Simplified install flow for curated skills from skilld.dev:\n * fetch SKILL.md → write to disk → update lockfile → link to agents.\n *\n * No doc resolution, no LLM, no caching. Fast path.\n */\n\nimport type { AgentType } from '../agent/index.ts'\nimport type { RegistrySkill } from '../registry/client.ts'\nimport { mkdirSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { writeSkillMd } from '../agent/prompts/skill.ts'\nimport { installSkill, resolveBaseDir } from '../agent/skill-installer.ts'\nimport { SHARED_SKILLS_DIR } from '../core/paths.ts'\nimport { fetchRegistrySkill } from '../registry/client.ts'\n\nexport interface SyncRegistryOptions {\n packageName: string\n agent: AgentType\n global?: boolean\n cwd?: string\n}\n\n/**\n * Install a package skill from the skilld.dev registry.\n * Returns the installed skill, or null if no curated skill exists.\n */\nexport async function syncRegistrySkill(opts: SyncRegistryOptions): Promise<RegistrySkill | null> {\n const { packageName, agent, cwd = process.cwd() } = opts\n\n const skill = await fetchRegistrySkill(packageName)\n if (!skill)\n return null\n\n // Write SKILL.md to shared skills dir\n const sharedDir = join(cwd, SHARED_SKILLS_DIR)\n const skillDir = join(sharedDir, skill.name)\n mkdirSync(skillDir, { recursive: true })\n writeSkillMd(skillDir, skill.content)\n\n const baseDir = resolveBaseDir(cwd, agent, false)\n mkdirSync(baseDir, { recursive: true })\n installSkill({\n cwd,\n agent,\n global: false,\n baseDir,\n skillDirName: skill.name,\n lock: {\n packageName: skill.packageName,\n version: skill.updatedAt,\n repo: skill.repo,\n source: 'registry',\n syncedAt: new Date().toISOString().slice(0, 10),\n generator: 'curator',\n },\n })\n\n return skill\n}\n"],"mappings":";;;;;;;;;;;;;;;;GAeA,OAAM,KAAA;GAEN,CAAA,EAAA;EACE,CAAA,CAAA,YAAQ,KAAY,IAAA;;;;;;EAoFtB;EAIE,SAAM,OAAO;EAOb,OAAM,OAAM;EAJV,MAAA,GAAQ,OAAA,MAAA,GAAA,OAAA;EACR,aAAQ,OAAQ;YAAE,OAAA;YAAoB,IAAK;UAAU,OAAA;EACtD,WAAE,OAAY,aAEQ,KAAA;EACvB,WACE,OAAO,YAAA,KAAA;EAGT;;eAOE,kBAAA,MAAA;OACA,EAAA,aAAgB,OAAA,MAAA,QAAA,KAAA,KAAA;OAChB,QAAO,MAAO,mBAAA,YAAA;KACd,CAAA,OAAS,OAAO;OAChB,WAAa,KAAO,KAAA,KAAA,kBAAA,EAAA,MAAA,KAAA;WACpB,UAAiB,EAAA,WAAA,MAAA,CAAA;cACP,UAAI,MAAA,QAAA;OACd,UAAe,eAAA,KAAA,OAAA,MAAA;WACf,SAAkB,EAAA,WAAa,MAAA,CAAA;cACpB;EACZ;;;;;;;GC1GH,SAAA,MAAsB;GACpB,MAAQ,MAAA;GAER,QAAM;GACN,2BACS,IAAA,MAAA,EAAA,aAAA,CAAA,MAAA,GAAA,GAAA;GAIT,WAAM;GACN;EACA,CAAA;CAEA,OAAM;;SAGJ"}
|