skilld 1.2.1 → 1.2.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"shared.mjs","names":["_semverGt"],"sources":["../../src/sources/package-registry.ts","../../src/core/shared.ts"],"sourcesContent":["/**\n * Unified package registry — single source of truth for package metadata.\n * Consolidates doc overrides, blog presets, and file patterns.\n * Keyed by GitHub 'owner/repo' (source code repo).\n */\n\nexport interface BlogRelease {\n version: string\n url: string\n date: string\n title?: string\n}\n\nexport interface PackageEntry {\n filePatterns?: string[]\n primary?: boolean\n /** Extra rules injected into skill generation prompts */\n rules?: string[]\n}\n\nexport interface RepoEntry {\n owner: string\n repo: string\n /** Separate docs repo name (e.g. 'docs' → owner/docs) */\n docsRepo?: string\n /** Path prefix to filter markdown files */\n docsPath?: string\n /** Branch/ref override */\n docsRef?: string\n /** Homepage URL */\n homepage?: string\n /** URL pattern to crawl for docs (glob, e.g. 'https://example.com/docs/**') */\n crawlUrl?: string\n /** Branch to fetch CHANGELOG.md from when installed version is a prerelease (e.g. 'minor' for Vue) */\n prereleaseChangelogRef?: string\n /** Packages in this repo */\n packages: Record<string, PackageEntry>\n /** Curated blog release posts */\n blogReleases?: BlogRelease[]\n}\n\n// Backwards-compatible types\nexport interface DocOverride {\n owner: string\n repo: string\n path: string\n ref?: string\n homepage?: string\n}\n\nexport interface BlogPreset {\n packageName: string\n releases: BlogRelease[]\n}\n\n// ── Registry ──\n\nconst REPO_REGISTRY: Record<string, RepoEntry> = {\n // ── Frameworks with doc overrides ──\n\n 'vuejs/core': {\n owner: 'vuejs',\n repo: 'core',\n docsRepo: 'docs',\n docsPath: 'src',\n homepage: 'https://vuejs.org',\n prereleaseChangelogRef: 'minor',\n packages: {\n 'vue': { primary: true, filePatterns: ['*.vue'], rules: ['ALWAYS use `<script setup lang=\"ts\">`', 'Use ```vue code fences for SFC examples containing `<script>` or `<template>` tags, ```ts for plain TypeScript'] },\n '@vue/compiler-core': {},\n '@vue/compiler-dom': {},\n '@vue/reactivity': {},\n '@vue/runtime-core': {},\n '@vue/runtime-dom': {},\n '@vue/shared': {},\n },\n blogReleases: [\n { version: '3.5', url: 'https://blog.vuejs.org/posts/vue-3-5', date: '2024-09-01' },\n { version: '3.4', url: 'https://blog.vuejs.org/posts/vue-3-4', date: '2023-12-28' },\n { version: '3.3', url: 'https://blog.vuejs.org/posts/vue-3-3', date: '2023-05-11' },\n { version: '3.2', url: 'https://blog.vuejs.org/posts/vue-3-2', date: '2021-08-05' },\n { version: '3.1', url: 'https://blog.vuejs.org/posts/vue-3-1', date: '2021-06-07' },\n { version: '3.0', url: 'https://blog.vuejs.org/posts/vue-3-0', date: '2020-09-18' },\n ],\n },\n\n 'tailwindlabs/tailwindcss': {\n owner: 'tailwindlabs',\n repo: 'tailwindcss',\n docsRepo: 'tailwindcss.com',\n docsPath: 'src/docs',\n homepage: 'https://tailwindcss.com',\n packages: {\n tailwindcss: { primary: true },\n },\n },\n\n 'withastro/astro': {\n owner: 'withastro',\n repo: 'astro',\n docsRepo: 'docs',\n docsPath: 'src/content/docs/en',\n homepage: 'https://docs.astro.build',\n packages: {\n astro: { primary: true, filePatterns: ['*.astro'] },\n },\n },\n\n 'vueuse/vueuse': {\n owner: 'vueuse',\n repo: 'vueuse',\n docsPath: 'packages',\n packages: {\n '@vueuse/core': { primary: true },\n },\n },\n\n // ── Frameworks (file patterns only) ──\n\n 'sveltejs/svelte': {\n owner: 'sveltejs',\n repo: 'svelte',\n packages: {\n svelte: { primary: true, filePatterns: ['*.svelte'], rules: ['ALWAYS use runes syntax ($state, $derived, $effect, $props)'] },\n },\n },\n\n 'solidjs/solid': {\n owner: 'solidjs',\n repo: 'solid',\n packages: {\n 'solid-js': { primary: true, filePatterns: ['*.jsx', '*.tsx'] },\n },\n },\n\n 'QwikDev/qwik': {\n owner: 'QwikDev',\n repo: 'qwik',\n packages: {\n qwik: { primary: true, filePatterns: ['*.tsx'] },\n },\n },\n\n 'marko-js/marko': {\n owner: 'marko-js',\n repo: 'marko',\n packages: {\n marko: { primary: true, filePatterns: ['*.marko'] },\n },\n },\n\n 'riot/riot': {\n owner: 'riot',\n repo: 'riot',\n packages: {\n riot: { primary: true, filePatterns: ['*.riot'] },\n },\n },\n\n // ── Languages/transpilers ──\n\n 'microsoft/TypeScript': {\n owner: 'microsoft',\n repo: 'TypeScript',\n packages: {\n typescript: { primary: true, filePatterns: ['*.ts', '*.tsx', '*.mts', '*.cts'] },\n },\n blogReleases: [\n { version: '6.0', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-beta/', date: '2026-02-11', title: 'Announcing TypeScript 6.0 Beta' },\n { version: '5.9', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-9/', date: '2025-08-01', title: 'Announcing TypeScript 5.9' },\n { version: '5.8', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-8/', date: '2025-02-28', title: 'Announcing TypeScript 5.8' },\n { version: '5.7', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-7/', date: '2024-11-22', title: 'Announcing TypeScript 5.7' },\n { version: '5.6', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-6/', date: '2024-09-09', title: 'Announcing TypeScript 5.6' },\n { version: '5.5', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/', date: '2024-06-20', title: 'Announcing TypeScript 5.5' },\n ],\n },\n\n 'jashkenas/coffeescript': {\n owner: 'jashkenas',\n repo: 'coffeescript',\n packages: {\n coffeescript: { primary: true, filePatterns: ['*.coffee'] },\n },\n },\n\n 'gkz/LiveScript': {\n owner: 'gkz',\n repo: 'LiveScript',\n packages: {\n livescript: { primary: true, filePatterns: ['*.ls'] },\n },\n },\n\n 'elm/compiler': {\n owner: 'elm',\n repo: 'compiler',\n packages: {\n elm: { primary: true, filePatterns: ['*.elm'] },\n },\n },\n\n // ── CSS preprocessors ──\n\n 'sass/dart-sass': {\n owner: 'sass',\n repo: 'dart-sass',\n packages: {\n sass: { primary: true, filePatterns: ['*.scss', '*.sass'] },\n },\n },\n\n 'less/less.js': {\n owner: 'less',\n repo: 'less.js',\n packages: {\n less: { primary: true, filePatterns: ['*.less'] },\n },\n },\n\n 'stylus/stylus': {\n owner: 'stylus',\n repo: 'stylus',\n packages: {\n stylus: { primary: true, filePatterns: ['*.styl'] },\n },\n },\n\n 'postcss/postcss': {\n owner: 'postcss',\n repo: 'postcss',\n packages: {\n postcss: { primary: true, filePatterns: ['*.css', '*.pcss'] },\n },\n },\n\n // ── Template engines ──\n\n 'pugjs/pug': {\n owner: 'pugjs',\n repo: 'pug',\n packages: {\n pug: { primary: true, filePatterns: ['*.pug'] },\n },\n },\n\n 'mde/ejs': {\n owner: 'mde',\n repo: 'ejs',\n packages: {\n ejs: { primary: true, filePatterns: ['*.ejs'] },\n },\n },\n\n 'handlebars-lang/handlebars.js': {\n owner: 'handlebars-lang',\n repo: 'handlebars.js',\n packages: {\n handlebars: { primary: true, filePatterns: ['*.hbs', '*.handlebars'] },\n },\n },\n\n 'janl/mustache.js': {\n owner: 'janl',\n repo: 'mustache.js',\n packages: {\n mustache: { primary: true, filePatterns: ['*.mustache'] },\n },\n },\n\n 'mozilla/nunjucks': {\n owner: 'mozilla',\n repo: 'nunjucks',\n packages: {\n nunjucks: { primary: true, filePatterns: ['*.njk'] },\n },\n },\n\n 'Shopify/liquid': {\n owner: 'Shopify',\n repo: 'liquid',\n packages: {\n liquid: { primary: true, filePatterns: ['*.liquid'] },\n },\n },\n\n // ── Data formats ──\n\n 'eemeli/yaml': {\n owner: 'eemeli',\n repo: 'yaml',\n packages: {\n yaml: { primary: true, filePatterns: ['*.yaml', '*.yml'] },\n },\n },\n\n 'nodeca/js-yaml': {\n owner: 'nodeca',\n repo: 'js-yaml',\n packages: {\n 'js-yaml': { primary: true, filePatterns: ['*.yaml', '*.yml'] },\n },\n },\n\n 'BinaryMuse/toml-node': {\n owner: 'BinaryMuse',\n repo: 'toml-node',\n packages: {\n 'toml': { primary: true, filePatterns: ['*.toml'] },\n '@iarna/toml': { filePatterns: ['*.toml'] },\n },\n },\n\n 'json5/json5': {\n owner: 'json5',\n repo: 'json5',\n packages: {\n json5: { primary: true, filePatterns: ['*.json5'] },\n },\n },\n\n 'microsoft/node-jsonc-parser': {\n owner: 'microsoft',\n repo: 'node-jsonc-parser',\n packages: {\n 'jsonc-parser': { primary: true, filePatterns: ['*.jsonc'] },\n },\n },\n\n // ── Markdown ──\n\n 'markdown-it/markdown-it': {\n owner: 'markdown-it',\n repo: 'markdown-it',\n packages: {\n 'markdown-it': { primary: true, filePatterns: ['*.md'] },\n },\n },\n\n 'markedjs/marked': {\n owner: 'markedjs',\n repo: 'marked',\n packages: {\n marked: { primary: true, filePatterns: ['*.md'] },\n },\n },\n\n 'remarkjs/remark': {\n owner: 'remarkjs',\n repo: 'remark',\n packages: {\n remark: { primary: true, filePatterns: ['*.md', '*.mdx'] },\n },\n },\n\n 'mdx-js/mdx': {\n owner: 'mdx-js',\n repo: 'mdx',\n packages: {\n '@mdx-js/mdx': { primary: true, filePatterns: ['*.mdx'] },\n },\n },\n\n // ── GraphQL ──\n\n 'graphql/graphql-js': {\n owner: 'graphql',\n repo: 'graphql-js',\n packages: {\n 'graphql': { primary: true, filePatterns: ['*.graphql', '*.gql'] },\n 'graphql-tag': { filePatterns: ['*.graphql', '*.gql'] },\n },\n },\n\n 'dotansimha/graphql-code-generator': {\n owner: 'dotansimha',\n repo: 'graphql-code-generator',\n packages: {\n '@graphql-codegen/cli': { primary: true, filePatterns: ['*.graphql', '*.gql'] },\n },\n },\n\n // ── UI Frameworks ──\n\n 'quasarframework/quasar': {\n owner: 'quasarframework',\n repo: 'quasar',\n docsPath: 'docs/src/pages',\n docsRef: 'dev',\n homepage: 'https://quasar.dev',\n packages: {\n quasar: { primary: true },\n },\n },\n\n // ── Animation ──\n\n 'motiondivision/motion-vue': {\n owner: 'motiondivision',\n repo: 'motion-vue',\n homepage: 'https://motion.dev',\n crawlUrl: 'https://motion.dev/docs/vue**',\n packages: {\n 'motion-v': { primary: true },\n },\n },\n\n // ── Other ──\n\n 'prisma/prisma': {\n owner: 'prisma',\n repo: 'prisma',\n packages: {\n 'prisma': { primary: true, filePatterns: ['*.prisma'] },\n '@prisma/client': { filePatterns: ['*.prisma'] },\n },\n },\n\n 'nicolo-ribaudo/tc39-proposal-wasm-esm-integration': {\n owner: 'nicolo-ribaudo',\n repo: 'tc39-proposal-wasm-esm-integration',\n packages: {\n 'wasm-pack': { primary: true, filePatterns: ['*.wasm'] },\n },\n },\n}\n\n// ── Reverse index (auto-generated) ──\n\nconst PACKAGE_TO_REPO_MAP: Record<string, string> = {}\n\nfor (const [repoKey, entry] of Object.entries(REPO_REGISTRY)) {\n for (const packageName of Object.keys(entry.packages)) {\n PACKAGE_TO_REPO_MAP[packageName] = repoKey\n }\n}\n\n// ── Backwards-compatible helpers ──\n\nexport function getDocOverride(packageName: string): DocOverride | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n const entry = REPO_REGISTRY[repoKey]\n if (!entry?.docsRepo && !entry?.docsPath)\n return undefined\n\n return {\n owner: entry.owner,\n repo: entry.docsRepo || entry.repo,\n path: entry.docsPath || '',\n ref: entry.docsRef,\n homepage: entry.homepage,\n }\n}\n\nexport function getBlogPreset(packageName: string): BlogPreset | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n const entry = REPO_REGISTRY[repoKey]\n if (!entry?.blogReleases)\n return undefined\n\n return {\n packageName,\n releases: entry.blogReleases,\n }\n}\n\nexport function getFilePatterns(packageName: string): string[] | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n return REPO_REGISTRY[repoKey]?.packages[packageName]?.filePatterns\n}\n\n// ── New APIs ──\n\nexport function getRepoEntry(repoKey: string): RepoEntry | undefined {\n return REPO_REGISTRY[repoKey]\n}\n\nexport function getRepoKeyForPackage(packageName: string): string | undefined {\n return PACKAGE_TO_REPO_MAP[packageName]\n}\n\nexport function getPackageRules(packageName: string): string[] {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return []\n return REPO_REGISTRY[repoKey]?.packages[packageName]?.rules ?? []\n}\n\nexport function getPrereleaseChangelogRef(packageName: string): string | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n return REPO_REGISTRY[repoKey]?.prereleaseChangelogRef\n}\n\nexport function getCrawlUrl(packageName: string): string | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n return REPO_REGISTRY[repoKey]?.crawlUrl\n}\n\nexport function getRelatedPackages(packageName: string): string[] {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return []\n const entry = REPO_REGISTRY[repoKey]\n if (!entry)\n return []\n return Object.keys(entry.packages)\n}\n","import { execSync } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { gt as _semverGt } from 'semver'\nimport { isWindows } from 'std-env'\n\n/** Get-or-create for Maps. Polyfill for Map.getOrInsertComputed (not yet in Node.js). */\nexport function mapInsert<K, V>(map: Map<K, V>, key: K, create: () => V): V {\n let val = map.get(key)\n if (val === undefined) {\n val = create()\n map.set(key, val)\n }\n return val\n}\n\n/** Compare two semver strings: returns true if a > b. Handles prereleases. */\nexport function semverGt(a: string, b: string): boolean {\n return _semverGt(a, b, true)\n}\n\nlet _skilldCommand: string | undefined\n\n/** Resolve the skilld CLI command — returns `skilld` if the binary is in PATH, otherwise `npx -y skilld` */\nexport function resolveSkilldCommand(): string {\n if (_skilldCommand !== undefined)\n return _skilldCommand\n try {\n const lookup = isWindows ? 'where' : 'which'\n execSync(`${lookup} skilld`, { stdio: 'ignore' })\n _skilldCommand = 'skilld'\n }\n catch {\n _skilldCommand = 'npx -y skilld'\n }\n return _skilldCommand\n}\n\nexport const SHARED_SKILLS_DIR = '.skills'\n\n/** Returns the shared skills directory path if `.skills/` exists at project root, else null */\nexport function getSharedSkillsDir(cwd: string = process.cwd()): string | null {\n const dir = join(cwd, SHARED_SKILLS_DIR)\n return existsSync(dir) ? dir : null\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared.mjs","names":["_semverValid","_semverGt"],"sources":["../../src/sources/package-registry.ts","../../src/core/shared.ts"],"sourcesContent":["/**\n * Unified package registry — single source of truth for package metadata.\n * Consolidates doc overrides, blog presets, and file patterns.\n * Keyed by GitHub 'owner/repo' (source code repo).\n */\n\nexport interface BlogRelease {\n version: string\n url: string\n date: string\n title?: string\n}\n\nexport interface PackageEntry {\n filePatterns?: string[]\n primary?: boolean\n /** Extra rules injected into skill generation prompts */\n rules?: string[]\n}\n\nexport interface RepoEntry {\n owner: string\n repo: string\n /** Separate docs repo name (e.g. 'docs' → owner/docs) */\n docsRepo?: string\n /** Path prefix to filter markdown files */\n docsPath?: string\n /** Branch/ref override */\n docsRef?: string\n /** Homepage URL */\n homepage?: string\n /** URL pattern to crawl for docs (glob, e.g. 'https://example.com/docs/**') */\n crawlUrl?: string\n /** Branch to fetch CHANGELOG.md from when installed version is a prerelease (e.g. 'minor' for Vue) */\n prereleaseChangelogRef?: string\n /** Packages in this repo */\n packages: Record<string, PackageEntry>\n /** Curated blog release posts */\n blogReleases?: BlogRelease[]\n}\n\n// Backwards-compatible types\nexport interface DocOverride {\n owner: string\n repo: string\n path: string\n ref?: string\n homepage?: string\n}\n\nexport interface BlogPreset {\n packageName: string\n releases: BlogRelease[]\n}\n\n// ── Registry ──\n\nconst REPO_REGISTRY: Record<string, RepoEntry> = {\n // ── Frameworks with doc overrides ──\n\n 'vuejs/core': {\n owner: 'vuejs',\n repo: 'core',\n docsRepo: 'docs',\n docsPath: 'src',\n homepage: 'https://vuejs.org',\n prereleaseChangelogRef: 'minor',\n packages: {\n 'vue': { primary: true, filePatterns: ['*.vue'], rules: ['ALWAYS use `<script setup lang=\"ts\">`', 'Use ```vue code fences for SFC examples containing `<script>` or `<template>` tags, ```ts for plain TypeScript'] },\n '@vue/compiler-core': {},\n '@vue/compiler-dom': {},\n '@vue/reactivity': {},\n '@vue/runtime-core': {},\n '@vue/runtime-dom': {},\n '@vue/shared': {},\n },\n blogReleases: [\n { version: '3.5', url: 'https://blog.vuejs.org/posts/vue-3-5', date: '2024-09-01' },\n { version: '3.4', url: 'https://blog.vuejs.org/posts/vue-3-4', date: '2023-12-28' },\n { version: '3.3', url: 'https://blog.vuejs.org/posts/vue-3-3', date: '2023-05-11' },\n { version: '3.2', url: 'https://blog.vuejs.org/posts/vue-3-2', date: '2021-08-05' },\n { version: '3.1', url: 'https://blog.vuejs.org/posts/vue-3-1', date: '2021-06-07' },\n { version: '3.0', url: 'https://blog.vuejs.org/posts/vue-3-0', date: '2020-09-18' },\n ],\n },\n\n 'tailwindlabs/tailwindcss': {\n owner: 'tailwindlabs',\n repo: 'tailwindcss',\n docsRepo: 'tailwindcss.com',\n docsPath: 'src/docs',\n homepage: 'https://tailwindcss.com',\n packages: {\n tailwindcss: { primary: true },\n },\n },\n\n 'withastro/astro': {\n owner: 'withastro',\n repo: 'astro',\n docsRepo: 'docs',\n docsPath: 'src/content/docs/en',\n homepage: 'https://docs.astro.build',\n packages: {\n astro: { primary: true, filePatterns: ['*.astro'] },\n },\n },\n\n 'vueuse/vueuse': {\n owner: 'vueuse',\n repo: 'vueuse',\n docsPath: 'packages',\n packages: {\n '@vueuse/core': { primary: true },\n },\n },\n\n // ── Frameworks (file patterns only) ──\n\n 'sveltejs/svelte': {\n owner: 'sveltejs',\n repo: 'svelte',\n packages: {\n svelte: { primary: true, filePatterns: ['*.svelte'], rules: ['ALWAYS use runes syntax ($state, $derived, $effect, $props)'] },\n },\n },\n\n 'solidjs/solid': {\n owner: 'solidjs',\n repo: 'solid',\n packages: {\n 'solid-js': { primary: true, filePatterns: ['*.jsx', '*.tsx'] },\n },\n },\n\n 'QwikDev/qwik': {\n owner: 'QwikDev',\n repo: 'qwik',\n packages: {\n qwik: { primary: true, filePatterns: ['*.tsx'] },\n },\n },\n\n 'marko-js/marko': {\n owner: 'marko-js',\n repo: 'marko',\n packages: {\n marko: { primary: true, filePatterns: ['*.marko'] },\n },\n },\n\n 'riot/riot': {\n owner: 'riot',\n repo: 'riot',\n packages: {\n riot: { primary: true, filePatterns: ['*.riot'] },\n },\n },\n\n // ── Languages/transpilers ──\n\n 'microsoft/TypeScript': {\n owner: 'microsoft',\n repo: 'TypeScript',\n packages: {\n typescript: { primary: true, filePatterns: ['*.ts', '*.tsx', '*.mts', '*.cts'] },\n },\n blogReleases: [\n { version: '6.0', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-beta/', date: '2026-02-11', title: 'Announcing TypeScript 6.0 Beta' },\n { version: '5.9', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-9/', date: '2025-08-01', title: 'Announcing TypeScript 5.9' },\n { version: '5.8', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-8/', date: '2025-02-28', title: 'Announcing TypeScript 5.8' },\n { version: '5.7', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-7/', date: '2024-11-22', title: 'Announcing TypeScript 5.7' },\n { version: '5.6', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-6/', date: '2024-09-09', title: 'Announcing TypeScript 5.6' },\n { version: '5.5', url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/', date: '2024-06-20', title: 'Announcing TypeScript 5.5' },\n ],\n },\n\n 'jashkenas/coffeescript': {\n owner: 'jashkenas',\n repo: 'coffeescript',\n packages: {\n coffeescript: { primary: true, filePatterns: ['*.coffee'] },\n },\n },\n\n 'gkz/LiveScript': {\n owner: 'gkz',\n repo: 'LiveScript',\n packages: {\n livescript: { primary: true, filePatterns: ['*.ls'] },\n },\n },\n\n 'elm/compiler': {\n owner: 'elm',\n repo: 'compiler',\n packages: {\n elm: { primary: true, filePatterns: ['*.elm'] },\n },\n },\n\n // ── CSS preprocessors ──\n\n 'sass/dart-sass': {\n owner: 'sass',\n repo: 'dart-sass',\n packages: {\n sass: { primary: true, filePatterns: ['*.scss', '*.sass'] },\n },\n },\n\n 'less/less.js': {\n owner: 'less',\n repo: 'less.js',\n packages: {\n less: { primary: true, filePatterns: ['*.less'] },\n },\n },\n\n 'stylus/stylus': {\n owner: 'stylus',\n repo: 'stylus',\n packages: {\n stylus: { primary: true, filePatterns: ['*.styl'] },\n },\n },\n\n 'postcss/postcss': {\n owner: 'postcss',\n repo: 'postcss',\n packages: {\n postcss: { primary: true, filePatterns: ['*.css', '*.pcss'] },\n },\n },\n\n // ── Template engines ──\n\n 'pugjs/pug': {\n owner: 'pugjs',\n repo: 'pug',\n packages: {\n pug: { primary: true, filePatterns: ['*.pug'] },\n },\n },\n\n 'mde/ejs': {\n owner: 'mde',\n repo: 'ejs',\n packages: {\n ejs: { primary: true, filePatterns: ['*.ejs'] },\n },\n },\n\n 'handlebars-lang/handlebars.js': {\n owner: 'handlebars-lang',\n repo: 'handlebars.js',\n packages: {\n handlebars: { primary: true, filePatterns: ['*.hbs', '*.handlebars'] },\n },\n },\n\n 'janl/mustache.js': {\n owner: 'janl',\n repo: 'mustache.js',\n packages: {\n mustache: { primary: true, filePatterns: ['*.mustache'] },\n },\n },\n\n 'mozilla/nunjucks': {\n owner: 'mozilla',\n repo: 'nunjucks',\n packages: {\n nunjucks: { primary: true, filePatterns: ['*.njk'] },\n },\n },\n\n 'Shopify/liquid': {\n owner: 'Shopify',\n repo: 'liquid',\n packages: {\n liquid: { primary: true, filePatterns: ['*.liquid'] },\n },\n },\n\n // ── Data formats ──\n\n 'eemeli/yaml': {\n owner: 'eemeli',\n repo: 'yaml',\n packages: {\n yaml: { primary: true, filePatterns: ['*.yaml', '*.yml'] },\n },\n },\n\n 'nodeca/js-yaml': {\n owner: 'nodeca',\n repo: 'js-yaml',\n packages: {\n 'js-yaml': { primary: true, filePatterns: ['*.yaml', '*.yml'] },\n },\n },\n\n 'BinaryMuse/toml-node': {\n owner: 'BinaryMuse',\n repo: 'toml-node',\n packages: {\n 'toml': { primary: true, filePatterns: ['*.toml'] },\n '@iarna/toml': { filePatterns: ['*.toml'] },\n },\n },\n\n 'json5/json5': {\n owner: 'json5',\n repo: 'json5',\n packages: {\n json5: { primary: true, filePatterns: ['*.json5'] },\n },\n },\n\n 'microsoft/node-jsonc-parser': {\n owner: 'microsoft',\n repo: 'node-jsonc-parser',\n packages: {\n 'jsonc-parser': { primary: true, filePatterns: ['*.jsonc'] },\n },\n },\n\n // ── Markdown ──\n\n 'markdown-it/markdown-it': {\n owner: 'markdown-it',\n repo: 'markdown-it',\n packages: {\n 'markdown-it': { primary: true, filePatterns: ['*.md'] },\n },\n },\n\n 'markedjs/marked': {\n owner: 'markedjs',\n repo: 'marked',\n packages: {\n marked: { primary: true, filePatterns: ['*.md'] },\n },\n },\n\n 'remarkjs/remark': {\n owner: 'remarkjs',\n repo: 'remark',\n packages: {\n remark: { primary: true, filePatterns: ['*.md', '*.mdx'] },\n },\n },\n\n 'mdx-js/mdx': {\n owner: 'mdx-js',\n repo: 'mdx',\n packages: {\n '@mdx-js/mdx': { primary: true, filePatterns: ['*.mdx'] },\n },\n },\n\n // ── GraphQL ──\n\n 'graphql/graphql-js': {\n owner: 'graphql',\n repo: 'graphql-js',\n packages: {\n 'graphql': { primary: true, filePatterns: ['*.graphql', '*.gql'] },\n 'graphql-tag': { filePatterns: ['*.graphql', '*.gql'] },\n },\n },\n\n 'dotansimha/graphql-code-generator': {\n owner: 'dotansimha',\n repo: 'graphql-code-generator',\n packages: {\n '@graphql-codegen/cli': { primary: true, filePatterns: ['*.graphql', '*.gql'] },\n },\n },\n\n // ── UI Frameworks ──\n\n 'quasarframework/quasar': {\n owner: 'quasarframework',\n repo: 'quasar',\n docsPath: 'docs/src/pages',\n docsRef: 'dev',\n homepage: 'https://quasar.dev',\n packages: {\n quasar: { primary: true },\n },\n },\n\n // ── Animation ──\n\n 'motiondivision/motion-vue': {\n owner: 'motiondivision',\n repo: 'motion-vue',\n homepage: 'https://motion.dev',\n crawlUrl: 'https://motion.dev/docs/vue**',\n packages: {\n 'motion-v': { primary: true },\n },\n },\n\n // ── Other ──\n\n 'prisma/prisma': {\n owner: 'prisma',\n repo: 'prisma',\n packages: {\n 'prisma': { primary: true, filePatterns: ['*.prisma'] },\n '@prisma/client': { filePatterns: ['*.prisma'] },\n },\n },\n\n 'nicolo-ribaudo/tc39-proposal-wasm-esm-integration': {\n owner: 'nicolo-ribaudo',\n repo: 'tc39-proposal-wasm-esm-integration',\n packages: {\n 'wasm-pack': { primary: true, filePatterns: ['*.wasm'] },\n },\n },\n}\n\n// ── Reverse index (auto-generated) ──\n\nconst PACKAGE_TO_REPO_MAP: Record<string, string> = {}\n\nfor (const [repoKey, entry] of Object.entries(REPO_REGISTRY)) {\n for (const packageName of Object.keys(entry.packages)) {\n PACKAGE_TO_REPO_MAP[packageName] = repoKey\n }\n}\n\n// ── Backwards-compatible helpers ──\n\nexport function getDocOverride(packageName: string): DocOverride | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n const entry = REPO_REGISTRY[repoKey]\n if (!entry?.docsRepo && !entry?.docsPath)\n return undefined\n\n return {\n owner: entry.owner,\n repo: entry.docsRepo || entry.repo,\n path: entry.docsPath || '',\n ref: entry.docsRef,\n homepage: entry.homepage,\n }\n}\n\nexport function getBlogPreset(packageName: string): BlogPreset | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n const entry = REPO_REGISTRY[repoKey]\n if (!entry?.blogReleases)\n return undefined\n\n return {\n packageName,\n releases: entry.blogReleases,\n }\n}\n\nexport function getFilePatterns(packageName: string): string[] | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n return REPO_REGISTRY[repoKey]?.packages[packageName]?.filePatterns\n}\n\n// ── New APIs ──\n\nexport function getRepoEntry(repoKey: string): RepoEntry | undefined {\n return REPO_REGISTRY[repoKey]\n}\n\nexport function getRepoKeyForPackage(packageName: string): string | undefined {\n return PACKAGE_TO_REPO_MAP[packageName]\n}\n\nexport function getPackageRules(packageName: string): string[] {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return []\n return REPO_REGISTRY[repoKey]?.packages[packageName]?.rules ?? []\n}\n\nexport function getPrereleaseChangelogRef(packageName: string): string | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n return REPO_REGISTRY[repoKey]?.prereleaseChangelogRef\n}\n\nexport function getCrawlUrl(packageName: string): string | undefined {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return undefined\n return REPO_REGISTRY[repoKey]?.crawlUrl\n}\n\nexport function getRelatedPackages(packageName: string): string[] {\n const repoKey = PACKAGE_TO_REPO_MAP[packageName]\n if (!repoKey)\n return []\n const entry = REPO_REGISTRY[repoKey]\n if (!entry)\n return []\n return Object.keys(entry.packages)\n}\n","import { execSync } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { gt as _semverGt, valid as _semverValid } from 'semver'\nimport { isWindows } from 'std-env'\n\n/** Get-or-create for Maps. Polyfill for Map.getOrInsertComputed (not yet in Node.js). */\nexport function mapInsert<K, V>(map: Map<K, V>, key: K, create: () => V): V {\n let val = map.get(key)\n if (val === undefined) {\n val = create()\n map.set(key, val)\n }\n return val\n}\n\n/** Returns the cleaned version if valid semver, null otherwise. */\nexport function semverValid(v: string): string | null {\n return _semverValid(v, true)\n}\n\n/** Compare two semver strings: returns true if a > b. Handles prereleases. */\nexport function semverGt(a: string, b: string): boolean {\n return _semverGt(a, b, true)\n}\n\nlet _skilldCommand: string | undefined\n\n/** Resolve the skilld CLI command — returns `skilld` if the binary is in PATH, otherwise `npx -y skilld` */\nexport function resolveSkilldCommand(): string {\n if (_skilldCommand !== undefined)\n return _skilldCommand\n try {\n const lookup = isWindows ? 'where' : 'which'\n execSync(`${lookup} skilld`, { stdio: 'ignore' })\n _skilldCommand = 'skilld'\n }\n catch {\n _skilldCommand = 'npx -y skilld'\n }\n return _skilldCommand\n}\n\nexport const SHARED_SKILLS_DIR = '.skills'\n\n/** Returns the shared skills directory path if `.skills/` exists at project root, else null */\nexport function getSharedSkillsDir(cwd: string = process.cwd()): string | null {\n const dir = join(cwd, SHARED_SKILLS_DIR)\n return existsSync(dir) ? dir : null\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { n as yamlParseKV, t as yamlEscape } from "./yaml.mjs";
2
2
  import { i as parseFrontmatter } from "./markdown.mjs";
3
- import { a as semverGt, n as getSharedSkillsDir } from "./shared.mjs";
3
+ import { a as semverGt, n as getSharedSkillsDir, o as semverValid } from "./shared.mjs";
4
4
  import { s as readLocalDependencies } from "./sources.mjs";
5
5
  import { a as targets } from "./detect.mjs";
6
6
  import { join } from "pathe";
@@ -230,7 +230,9 @@ function* iterateSkills(opts = {}) {
230
230
  }
231
231
  function isOutdated(skill, depVersion) {
232
232
  if (!skill.info?.version) return true;
233
- return semverGt(depVersion.replace(/^[\^~]/, ""), skill.info.version);
233
+ const depClean = depVersion.replace(/^[\^~>=<]+/, "");
234
+ if (!semverValid(depClean)) return false;
235
+ return semverGt(depClean, skill.info.version);
234
236
  }
235
237
  async function getProjectState(cwd = process.cwd()) {
236
238
  const skills = [...iterateSkills({
@@ -1 +1 @@
1
- {"version":3,"file":"skills.mjs","names":["agents"],"sources":["../../src/core/lockfile.ts","../../src/core/skills.ts"],"sourcesContent":["import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { parseFrontmatter } from './markdown.ts'\nimport { yamlEscape, yamlParseKV } from './yaml.ts'\n\nexport interface SkillInfo {\n packageName?: string\n version?: string\n /** All tracked packages as comma-separated \"name@version\" pairs (multi-package skills) */\n packages?: string\n repo?: string\n source?: string\n syncedAt?: string\n generator?: string\n /** Skill path within repo (git-sourced skills) */\n path?: string\n /** Git ref tracked for updates */\n ref?: string\n /** Git commit SHA at install time */\n commit?: string\n}\n\nexport function parsePackages(packages?: string): Array<{ name: string, version: string }> {\n if (!packages)\n return []\n return packages.split(',').map((s) => {\n const trimmed = s.trim()\n const atIdx = trimmed.lastIndexOf('@')\n if (atIdx <= 0)\n return { name: trimmed, version: '' }\n return { name: trimmed.slice(0, atIdx), version: trimmed.slice(atIdx + 1) }\n }).filter(p => p.name)\n}\n\nexport function serializePackages(pkgs: Array<{ name: string, version: string }>): string {\n return pkgs.map(p => `${p.name}@${p.version}`).join(', ')\n}\n\nexport interface SkilldLock {\n skills: Record<string, SkillInfo>\n}\n\nconst SKILL_FM_KEYS: (keyof SkillInfo)[] = ['packageName', 'version', 'packages', 'repo', 'source', 'syncedAt', 'generator', 'path', 'ref', 'commit']\n\nfunction isSkillInfoKey(key: string): key is keyof SkillInfo {\n return (SKILL_FM_KEYS as readonly string[]).includes(key)\n}\n\nexport function parseSkillFrontmatter(skillPath: string): SkillInfo | null {\n if (!existsSync(skillPath))\n return null\n const content = readFileSync(skillPath, 'utf-8')\n const fm = parseFrontmatter(content)\n if (Object.keys(fm).length === 0)\n return null\n\n const info: SkillInfo = {}\n for (const key of SKILL_FM_KEYS) {\n if (fm[key])\n info[key] = fm[key]\n }\n return info\n}\n\nexport function readLock(skillsDir: string): SkilldLock | null {\n const lockPath = join(skillsDir, 'skilld-lock.yaml')\n if (!existsSync(lockPath))\n return null\n const content = readFileSync(lockPath, 'utf-8')\n\n const skills: Record<string, SkillInfo> = {}\n let currentSkill: string | null = null\n\n for (const line of content.split('\\n')) {\n const skillMatch = line.match(/^ {2}(\\S+):$/)\n if (skillMatch) {\n currentSkill = skillMatch[1]\n skills[currentSkill] = {}\n continue\n }\n if (currentSkill && line.startsWith(' ')) {\n const kv = yamlParseKV(line)\n if (kv && isSkillInfoKey(kv[0]))\n skills[currentSkill]![kv[0]] = kv[1]\n }\n }\n return { skills }\n}\n\nfunction serializeLock(lock: SkilldLock): string {\n let yaml = 'skills:\\n'\n for (const [name, skill] of Object.entries(lock.skills)) {\n yaml += ` ${name}:\\n`\n for (const key of SKILL_FM_KEYS) {\n if (skill[key])\n yaml += ` ${key}: ${yamlEscape(skill[key])}\\n`\n }\n }\n return yaml\n}\n\nexport function writeLock(skillsDir: string, skillName: string, info: SkillInfo): void {\n const lockPath = join(skillsDir, 'skilld-lock.yaml')\n let lock: SkilldLock = { skills: {} }\n if (existsSync(lockPath)) {\n lock = readLock(skillsDir) || { skills: {} }\n }\n\n const existing = lock.skills[skillName]\n if (existing && info.packageName) {\n // Merge packages list\n const existingPkgs = parsePackages(existing.packages)\n // Also include existing primary if not yet in packages list\n if (existing.packageName && !existingPkgs.some(p => p.name === existing.packageName)) {\n existingPkgs.unshift({ name: existing.packageName, version: existing.version || '' })\n }\n // Add/update new package\n const idx = existingPkgs.findIndex(p => p.name === info.packageName)\n if (idx >= 0) {\n existingPkgs[idx]!.version = info.version || ''\n }\n else {\n existingPkgs.push({ name: info.packageName, version: info.version || '' })\n }\n info.packages = serializePackages(existingPkgs)\n // Keep primary as first package\n info.packageName = existingPkgs[0]!.name\n info.version = existingPkgs[0]!.version\n // Preserve fields from existing entry that aren't in new info\n if (!info.repo && existing.repo)\n info.repo = existing.repo\n if (!info.source && existing.source)\n info.source = existing.source\n if (!info.generator && existing.generator)\n info.generator = existing.generator\n }\n\n lock.skills[skillName] = info\n writeFileSync(lockPath, serializeLock(lock))\n}\n\n/**\n * Merge multiple lockfiles, preferring the most recently synced entry per skill.\n */\nexport function mergeLocks(locks: SkilldLock[]): SkilldLock {\n const merged: Record<string, SkillInfo> = {}\n for (const lock of locks) {\n for (const [name, info] of Object.entries(lock.skills)) {\n const existing = merged[name]\n if (!existing || (info.syncedAt && (!existing.syncedAt || info.syncedAt > existing.syncedAt)))\n merged[name] = info\n }\n }\n return { skills: merged }\n}\n\n/**\n * Sync a lockfile to all other dirs that already have a skilld-lock.yaml.\n * Only updates existing lockfiles — does not create new ones.\n */\nexport function syncLockfilesToDirs(sourceLock: SkilldLock, dirs: string[]): void {\n for (const dir of dirs) {\n const lockPath = join(dir, 'skilld-lock.yaml')\n if (!existsSync(lockPath))\n continue\n const existing = readLock(dir)\n if (!existing)\n continue\n // Merge source into existing\n const merged = mergeLocks([existing, sourceLock])\n writeFileSync(lockPath, serializeLock(merged))\n }\n}\n\nexport function removeLockEntry(skillsDir: string, skillName: string): void {\n const lockPath = join(skillsDir, 'skilld-lock.yaml')\n const lock = readLock(skillsDir)\n if (!lock)\n return\n\n delete lock.skills[skillName]\n\n if (Object.keys(lock.skills).length === 0) {\n unlinkSync(lockPath)\n return\n }\n\n writeFileSync(lockPath, serializeLock(lock))\n}\n","import type { AgentType } from '../agent/index.ts'\nimport type { SkillInfo } from './lockfile.ts'\nimport { existsSync, readdirSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { agents } from '../agent/index.ts'\nimport { readLocalDependencies } from '../sources/index.ts'\nimport { parsePackages, parseSkillFrontmatter, readLock } from './lockfile.ts'\nimport { getSharedSkillsDir, semverGt } from './shared.ts'\n\nexport interface SkillEntry {\n name: string\n dir: string\n agent: AgentType\n info: SkillInfo | null\n scope: 'local' | 'global'\n /** Original package name from package.json (e.g., @scope/pkg) */\n packageName?: string\n /** Latest version from package.json deps */\n latestVersion?: string\n}\n\nexport interface ProjectState {\n skills: SkillEntry[]\n deps: Map<string, string>\n missing: string[]\n outdated: SkillEntry[]\n synced: SkillEntry[]\n /** Skills in lockfile but not matched to any local dep */\n unmatched: SkillEntry[]\n}\n\nexport interface IterateSkillsOptions {\n scope?: 'local' | 'global' | 'all'\n agents?: AgentType[]\n cwd?: string\n}\n\nexport function* iterateSkills(opts: IterateSkillsOptions = {}): Generator<SkillEntry> {\n const { scope = 'all', cwd = process.cwd() } = opts\n const agentTypes = opts.agents ?? (Object.keys(agents) as AgentType[])\n\n // When shared dir exists, read local skills from there (avoid duplicates from agent symlinks)\n const sharedDir = getSharedSkillsDir(cwd)\n let yieldedLocal = false\n\n if (sharedDir && (scope === 'local' || scope === 'all')) {\n yieldedLocal = true\n const lock = readLock(sharedDir)\n const entries = readdirSync(sharedDir).filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml')\n // Use first detected agent as the representative\n const firstAgent = agentTypes[0] ?? (Object.keys(agents) as AgentType[])[0]!\n for (const name of entries) {\n const dir = join(sharedDir, name)\n if (lock?.skills[name]) {\n yield { name, dir, agent: firstAgent, info: lock.skills[name], scope: 'local' }\n }\n else {\n const info = parseSkillFrontmatter(join(dir, '.skilld', '_SKILL.md'))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: firstAgent, info, scope: 'local' }\n }\n }\n }\n }\n\n for (const agentType of agentTypes) {\n const agent = agents[agentType]\n\n // Local skills (skip if already yielded from shared dir)\n if (!yieldedLocal && (scope === 'local' || scope === 'all')) {\n const localDir = join(cwd, agent.skillsDir)\n if (existsSync(localDir)) {\n const lock = readLock(localDir)\n const entries = readdirSync(localDir).filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml')\n for (const name of entries) {\n const dir = join(localDir, name)\n // Only track skills in lockfile OR with generator: \"skilld\"\n if (lock?.skills[name]) {\n yield { name, dir, agent: agentType, info: lock.skills[name], scope: 'local' }\n }\n else {\n const info = parseSkillFrontmatter(join(dir, '.skilld', '_SKILL.md'))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: agentType, info, scope: 'local' }\n }\n }\n }\n }\n }\n\n // Global skills\n if ((scope === 'global' || scope === 'all') && agent.globalSkillsDir) {\n const globalDir = agent.globalSkillsDir\n if (existsSync(globalDir)) {\n const lock = readLock(globalDir)\n const entries = readdirSync(globalDir).filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml')\n for (const name of entries) {\n const dir = join(globalDir, name)\n // Only track skills in lockfile OR with generator: \"skilld\"\n if (lock?.skills[name]) {\n yield { name, dir, agent: agentType, info: lock.skills[name], scope: 'global' }\n }\n else {\n const info = parseSkillFrontmatter(join(dir, '.skilld', '_SKILL.md'))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: agentType, info, scope: 'global' }\n }\n }\n }\n }\n }\n }\n}\n\nexport function isOutdated(skill: SkillEntry, depVersion: string): boolean {\n if (!skill.info?.version)\n return true\n\n const depClean = depVersion.replace(/^[\\^~]/, '')\n\n return semverGt(depClean, skill.info.version)\n}\n\nexport async function getProjectState(cwd: string = process.cwd()): Promise<ProjectState> {\n const skills = [...iterateSkills({ scope: 'local', cwd })]\n\n // Get package.json deps\n const localDeps = await readLocalDependencies(cwd).catch(() => [])\n const deps = new Map(localDeps.map(d => [d.name, d.version]))\n\n // Build skill name -> entry map (for lookup by package name)\n const skillByName = new Map(skills.map(s => [s.name, s]))\n\n // Secondary lookup: packageName from lockfile (shipped skills have different names)\n // Also includes all packages from multi-package skills\n const skillByPkgName = new Map<string, SkillEntry>()\n for (const s of skills) {\n if (s.info?.packageName)\n skillByPkgName.set(s.info.packageName, s)\n for (const pkg of parsePackages(s.info?.packages))\n skillByPkgName.set(pkg.name, s)\n }\n\n const missing: string[] = []\n const outdated: SkillEntry[] = []\n const synced: SkillEntry[] = []\n const matchedSkillNames = new Set<string>()\n\n for (const [pkgName, version] of deps) {\n // Normalize package name (e.g., @scope/pkg -> scope-pkg)\n const normalizedName = pkgName.replace(/^@/, '').replace(/\\//g, '-')\n const skill = skillByName.get(`${normalizedName}-skilld`) || skillByName.get(normalizedName) || skillByName.get(pkgName) || skillByPkgName.get(pkgName)\n\n if (!skill) {\n missing.push(pkgName)\n }\n else {\n matchedSkillNames.add(skill.name)\n if (isOutdated(skill, version)) {\n outdated.push({ ...skill, packageName: pkgName, latestVersion: version })\n }\n else {\n synced.push({ ...skill, packageName: pkgName, latestVersion: version })\n }\n }\n }\n\n // Skills in lockfile but not matched to any local dep\n const unmatched = skills.filter(s => !matchedSkillNames.has(s.name))\n\n return { skills, deps, missing, outdated, synced, unmatched }\n}\n\nexport function getSkillsDir(agent: AgentType, scope: 'local' | 'global', cwd: string = process.cwd()): string {\n const agentConfig = agents[agent]\n if (scope === 'global') {\n if (!agentConfig.globalSkillsDir) {\n throw new Error(`Agent ${agent} does not support global skills`)\n }\n return agentConfig.globalSkillsDir\n }\n return getSharedSkillsDir(cwd) || join(cwd, agentConfig.skillsDir)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"skills.mjs","names":["agents"],"sources":["../../src/core/lockfile.ts","../../src/core/skills.ts"],"sourcesContent":["import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { parseFrontmatter } from './markdown.ts'\nimport { yamlEscape, yamlParseKV } from './yaml.ts'\n\nexport interface SkillInfo {\n packageName?: string\n version?: string\n /** All tracked packages as comma-separated \"name@version\" pairs (multi-package skills) */\n packages?: string\n repo?: string\n source?: string\n syncedAt?: string\n generator?: string\n /** Skill path within repo (git-sourced skills) */\n path?: string\n /** Git ref tracked for updates */\n ref?: string\n /** Git commit SHA at install time */\n commit?: string\n}\n\nexport function parsePackages(packages?: string): Array<{ name: string, version: string }> {\n if (!packages)\n return []\n return packages.split(',').map((s) => {\n const trimmed = s.trim()\n const atIdx = trimmed.lastIndexOf('@')\n if (atIdx <= 0)\n return { name: trimmed, version: '' }\n return { name: trimmed.slice(0, atIdx), version: trimmed.slice(atIdx + 1) }\n }).filter(p => p.name)\n}\n\nexport function serializePackages(pkgs: Array<{ name: string, version: string }>): string {\n return pkgs.map(p => `${p.name}@${p.version}`).join(', ')\n}\n\nexport interface SkilldLock {\n skills: Record<string, SkillInfo>\n}\n\nconst SKILL_FM_KEYS: (keyof SkillInfo)[] = ['packageName', 'version', 'packages', 'repo', 'source', 'syncedAt', 'generator', 'path', 'ref', 'commit']\n\nfunction isSkillInfoKey(key: string): key is keyof SkillInfo {\n return (SKILL_FM_KEYS as readonly string[]).includes(key)\n}\n\nexport function parseSkillFrontmatter(skillPath: string): SkillInfo | null {\n if (!existsSync(skillPath))\n return null\n const content = readFileSync(skillPath, 'utf-8')\n const fm = parseFrontmatter(content)\n if (Object.keys(fm).length === 0)\n return null\n\n const info: SkillInfo = {}\n for (const key of SKILL_FM_KEYS) {\n if (fm[key])\n info[key] = fm[key]\n }\n return info\n}\n\nexport function readLock(skillsDir: string): SkilldLock | null {\n const lockPath = join(skillsDir, 'skilld-lock.yaml')\n if (!existsSync(lockPath))\n return null\n const content = readFileSync(lockPath, 'utf-8')\n\n const skills: Record<string, SkillInfo> = {}\n let currentSkill: string | null = null\n\n for (const line of content.split('\\n')) {\n const skillMatch = line.match(/^ {2}(\\S+):$/)\n if (skillMatch) {\n currentSkill = skillMatch[1]\n skills[currentSkill] = {}\n continue\n }\n if (currentSkill && line.startsWith(' ')) {\n const kv = yamlParseKV(line)\n if (kv && isSkillInfoKey(kv[0]))\n skills[currentSkill]![kv[0]] = kv[1]\n }\n }\n return { skills }\n}\n\nfunction serializeLock(lock: SkilldLock): string {\n let yaml = 'skills:\\n'\n for (const [name, skill] of Object.entries(lock.skills)) {\n yaml += ` ${name}:\\n`\n for (const key of SKILL_FM_KEYS) {\n if (skill[key])\n yaml += ` ${key}: ${yamlEscape(skill[key])}\\n`\n }\n }\n return yaml\n}\n\nexport function writeLock(skillsDir: string, skillName: string, info: SkillInfo): void {\n const lockPath = join(skillsDir, 'skilld-lock.yaml')\n let lock: SkilldLock = { skills: {} }\n if (existsSync(lockPath)) {\n lock = readLock(skillsDir) || { skills: {} }\n }\n\n const existing = lock.skills[skillName]\n if (existing && info.packageName) {\n // Merge packages list\n const existingPkgs = parsePackages(existing.packages)\n // Also include existing primary if not yet in packages list\n if (existing.packageName && !existingPkgs.some(p => p.name === existing.packageName)) {\n existingPkgs.unshift({ name: existing.packageName, version: existing.version || '' })\n }\n // Add/update new package\n const idx = existingPkgs.findIndex(p => p.name === info.packageName)\n if (idx >= 0) {\n existingPkgs[idx]!.version = info.version || ''\n }\n else {\n existingPkgs.push({ name: info.packageName, version: info.version || '' })\n }\n info.packages = serializePackages(existingPkgs)\n // Keep primary as first package\n info.packageName = existingPkgs[0]!.name\n info.version = existingPkgs[0]!.version\n // Preserve fields from existing entry that aren't in new info\n if (!info.repo && existing.repo)\n info.repo = existing.repo\n if (!info.source && existing.source)\n info.source = existing.source\n if (!info.generator && existing.generator)\n info.generator = existing.generator\n }\n\n lock.skills[skillName] = info\n writeFileSync(lockPath, serializeLock(lock))\n}\n\n/**\n * Merge multiple lockfiles, preferring the most recently synced entry per skill.\n */\nexport function mergeLocks(locks: SkilldLock[]): SkilldLock {\n const merged: Record<string, SkillInfo> = {}\n for (const lock of locks) {\n for (const [name, info] of Object.entries(lock.skills)) {\n const existing = merged[name]\n if (!existing || (info.syncedAt && (!existing.syncedAt || info.syncedAt > existing.syncedAt)))\n merged[name] = info\n }\n }\n return { skills: merged }\n}\n\n/**\n * Sync a lockfile to all other dirs that already have a skilld-lock.yaml.\n * Only updates existing lockfiles — does not create new ones.\n */\nexport function syncLockfilesToDirs(sourceLock: SkilldLock, dirs: string[]): void {\n for (const dir of dirs) {\n const lockPath = join(dir, 'skilld-lock.yaml')\n if (!existsSync(lockPath))\n continue\n const existing = readLock(dir)\n if (!existing)\n continue\n // Merge source into existing\n const merged = mergeLocks([existing, sourceLock])\n writeFileSync(lockPath, serializeLock(merged))\n }\n}\n\nexport function removeLockEntry(skillsDir: string, skillName: string): void {\n const lockPath = join(skillsDir, 'skilld-lock.yaml')\n const lock = readLock(skillsDir)\n if (!lock)\n return\n\n delete lock.skills[skillName]\n\n if (Object.keys(lock.skills).length === 0) {\n unlinkSync(lockPath)\n return\n }\n\n writeFileSync(lockPath, serializeLock(lock))\n}\n","import type { AgentType } from '../agent/index.ts'\nimport type { SkillInfo } from './lockfile.ts'\nimport { existsSync, readdirSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { agents } from '../agent/index.ts'\nimport { readLocalDependencies } from '../sources/index.ts'\nimport { parsePackages, parseSkillFrontmatter, readLock } from './lockfile.ts'\nimport { getSharedSkillsDir, semverGt, semverValid } from './shared.ts'\n\nexport interface SkillEntry {\n name: string\n dir: string\n agent: AgentType\n info: SkillInfo | null\n scope: 'local' | 'global'\n /** Original package name from package.json (e.g., @scope/pkg) */\n packageName?: string\n /** Latest version from package.json deps */\n latestVersion?: string\n}\n\nexport interface ProjectState {\n skills: SkillEntry[]\n deps: Map<string, string>\n missing: string[]\n outdated: SkillEntry[]\n synced: SkillEntry[]\n /** Skills in lockfile but not matched to any local dep */\n unmatched: SkillEntry[]\n}\n\nexport interface IterateSkillsOptions {\n scope?: 'local' | 'global' | 'all'\n agents?: AgentType[]\n cwd?: string\n}\n\nexport function* iterateSkills(opts: IterateSkillsOptions = {}): Generator<SkillEntry> {\n const { scope = 'all', cwd = process.cwd() } = opts\n const agentTypes = opts.agents ?? (Object.keys(agents) as AgentType[])\n\n // When shared dir exists, read local skills from there (avoid duplicates from agent symlinks)\n const sharedDir = getSharedSkillsDir(cwd)\n let yieldedLocal = false\n\n if (sharedDir && (scope === 'local' || scope === 'all')) {\n yieldedLocal = true\n const lock = readLock(sharedDir)\n const entries = readdirSync(sharedDir).filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml')\n // Use first detected agent as the representative\n const firstAgent = agentTypes[0] ?? (Object.keys(agents) as AgentType[])[0]!\n for (const name of entries) {\n const dir = join(sharedDir, name)\n if (lock?.skills[name]) {\n yield { name, dir, agent: firstAgent, info: lock.skills[name], scope: 'local' }\n }\n else {\n const info = parseSkillFrontmatter(join(dir, '.skilld', '_SKILL.md'))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: firstAgent, info, scope: 'local' }\n }\n }\n }\n }\n\n for (const agentType of agentTypes) {\n const agent = agents[agentType]\n\n // Local skills (skip if already yielded from shared dir)\n if (!yieldedLocal && (scope === 'local' || scope === 'all')) {\n const localDir = join(cwd, agent.skillsDir)\n if (existsSync(localDir)) {\n const lock = readLock(localDir)\n const entries = readdirSync(localDir).filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml')\n for (const name of entries) {\n const dir = join(localDir, name)\n // Only track skills in lockfile OR with generator: \"skilld\"\n if (lock?.skills[name]) {\n yield { name, dir, agent: agentType, info: lock.skills[name], scope: 'local' }\n }\n else {\n const info = parseSkillFrontmatter(join(dir, '.skilld', '_SKILL.md'))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: agentType, info, scope: 'local' }\n }\n }\n }\n }\n }\n\n // Global skills\n if ((scope === 'global' || scope === 'all') && agent.globalSkillsDir) {\n const globalDir = agent.globalSkillsDir\n if (existsSync(globalDir)) {\n const lock = readLock(globalDir)\n const entries = readdirSync(globalDir).filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml')\n for (const name of entries) {\n const dir = join(globalDir, name)\n // Only track skills in lockfile OR with generator: \"skilld\"\n if (lock?.skills[name]) {\n yield { name, dir, agent: agentType, info: lock.skills[name], scope: 'global' }\n }\n else {\n const info = parseSkillFrontmatter(join(dir, '.skilld', '_SKILL.md'))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: agentType, info, scope: 'global' }\n }\n }\n }\n }\n }\n }\n}\n\nexport function isOutdated(skill: SkillEntry, depVersion: string): boolean {\n if (!skill.info?.version)\n return true\n\n const depClean = depVersion.replace(/^[\\^~>=<]+/, '')\n\n // Non-semver versions (e.g. '*' from catalog:/workspace: specifiers) can't be compared\n if (!semverValid(depClean))\n return false\n\n return semverGt(depClean, skill.info.version)\n}\n\nexport async function getProjectState(cwd: string = process.cwd()): Promise<ProjectState> {\n const skills = [...iterateSkills({ scope: 'local', cwd })]\n\n // Get package.json deps\n const localDeps = await readLocalDependencies(cwd).catch(() => [])\n const deps = new Map(localDeps.map(d => [d.name, d.version]))\n\n // Build skill name -> entry map (for lookup by package name)\n const skillByName = new Map(skills.map(s => [s.name, s]))\n\n // Secondary lookup: packageName from lockfile (shipped skills have different names)\n // Also includes all packages from multi-package skills\n const skillByPkgName = new Map<string, SkillEntry>()\n for (const s of skills) {\n if (s.info?.packageName)\n skillByPkgName.set(s.info.packageName, s)\n for (const pkg of parsePackages(s.info?.packages))\n skillByPkgName.set(pkg.name, s)\n }\n\n const missing: string[] = []\n const outdated: SkillEntry[] = []\n const synced: SkillEntry[] = []\n const matchedSkillNames = new Set<string>()\n\n for (const [pkgName, version] of deps) {\n // Normalize package name (e.g., @scope/pkg -> scope-pkg)\n const normalizedName = pkgName.replace(/^@/, '').replace(/\\//g, '-')\n const skill = skillByName.get(`${normalizedName}-skilld`) || skillByName.get(normalizedName) || skillByName.get(pkgName) || skillByPkgName.get(pkgName)\n\n if (!skill) {\n missing.push(pkgName)\n }\n else {\n matchedSkillNames.add(skill.name)\n if (isOutdated(skill, version)) {\n outdated.push({ ...skill, packageName: pkgName, latestVersion: version })\n }\n else {\n synced.push({ ...skill, packageName: pkgName, latestVersion: version })\n }\n }\n }\n\n // Skills in lockfile but not matched to any local dep\n const unmatched = skills.filter(s => !matchedSkillNames.has(s.name))\n\n return { skills, deps, missing, outdated, synced, unmatched }\n}\n\nexport function getSkillsDir(agent: AgentType, scope: 'local' | 'global', cwd: string = process.cwd()): string {\n const agentConfig = agents[agent]\n if (scope === 'global') {\n if (!agentConfig.globalSkillsDir) {\n throw new Error(`Agent ${agent} does not support global skills`)\n }\n return agentConfig.globalSkillsDir\n }\n return getSharedSkillsDir(cwd) || join(cwd, agentConfig.skillsDir)\n}\n"],"mappings":"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"}
@@ -1,9 +1,10 @@
1
1
  import { o as getCacheDir } from "./config.mjs";
2
+ import { t as yamlEscape } from "./yaml.mjs";
2
3
  import { i as parseFrontmatter, n as extractLinks, r as extractTitle, t as extractDescription } from "./markdown.mjs";
3
- import { c as getDocOverride, o as getBlogPreset, r as mapInsert, s as getCrawlUrl } from "./shared.mjs";
4
+ import { c as getCrawlUrl, l as getDocOverride, r as mapInsert, s as getBlogPreset } from "./shared.mjs";
4
5
  import { tmpdir } from "node:os";
5
6
  import { basename, dirname, join, resolve } from "pathe";
6
- import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, unlinkSync } from "node:fs";
7
+ import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "node:fs";
7
8
  import { htmlToMarkdown } from "mdream";
8
9
  import { spawnSync } from "node:child_process";
9
10
  import { ofetch } from "ofetch";
@@ -30,7 +31,7 @@ const isoDate = (iso) => iso.split("T")[0];
30
31
  /** Build YAML frontmatter from a key-value object, auto-quoting strings with special chars */
31
32
  function buildFrontmatter(fields) {
32
33
  const lines = ["---"];
33
- for (const [k, v] of Object.entries(fields)) if (v !== void 0) lines.push(`${k}: ${typeof v === "string" && /[:"[\]]/.test(v) ? `"${v.replace(/"/g, "\\\"")}"` : v}`);
34
+ for (const [k, v] of Object.entries(fields)) if (v !== void 0) lines.push(`${k}: ${typeof v === "string" ? yamlEscape(v) : v}`);
34
35
  lines.push("---");
35
36
  return lines.join("\n");
36
37
  }
@@ -1687,10 +1688,9 @@ function isSafeUrl(url) {
1687
1688
  const parsed = new URL(url);
1688
1689
  if (parsed.protocol !== "https:") return false;
1689
1690
  const host = parsed.hostname;
1690
- if (host === "localhost" || host === "127.0.0.1" || host === "::1") return false;
1691
- if (host === "169.254.169.254") return false;
1692
- if (/^(?:10\.|172\.(?:1[6-9]|2\d|3[01])\.|192\.168\.)/.test(host)) return false;
1693
- if (host.startsWith("[")) return false;
1691
+ if (host === "localhost" || host === "0.0.0.0" || host === "[::1]") return false;
1692
+ if (/^(?:127\.|10\.|172\.(?:1[6-9]|2\d|3[01])\.|192\.168\.|169\.254\.)/.test(host)) return false;
1693
+ if (/^\[(?:f[cd]|fe[89ab]|::ffff:)/i.test(host)) return false;
1694
1694
  return true;
1695
1695
  } catch {
1696
1696
  return false;
@@ -1702,8 +1702,8 @@ async function downloadLlmsDocs(llmsContent, baseUrl, onProgress) {
1702
1702
  return (await Promise.all(llmsContent.links.map((link) => limit(async () => {
1703
1703
  const url = link.url.startsWith("http") ? link.url : `${baseUrl.replace(/\/$/, "")}${link.url.startsWith("/") ? "" : "/"}${link.url}`;
1704
1704
  if (!isSafeUrl(url)) return null;
1705
- onProgress?.(link.url, completed++, llmsContent.links.length);
1706
1705
  const content = await fetchText(url);
1706
+ onProgress?.(link.url, ++completed, llmsContent.links.length);
1707
1707
  if (content && content.length > 100) return {
1708
1708
  url: link.url.startsWith("http") ? new URL(link.url).pathname : link.url,
1709
1709
  title: link.title,
@@ -2269,7 +2269,7 @@ async function fetchNpmPackage(packageName) {
2269
2269
  */
2270
2270
  async function fetchNpmRegistryMeta(packageName, version) {
2271
2271
  const { name: barePackageName } = parsePackageSpec(packageName);
2272
- const data = await $fetch(`https://registry.npmjs.org/${barePackageName}`).catch(() => null);
2272
+ const data = await $fetch(`https://registry.npmjs.org/${barePackageName}`, { headers: { Accept: "application/vnd.npm.install-v1+json" } }).catch(() => null);
2273
2273
  if (!data) return {};
2274
2274
  const distTags = data["dist-tags"] ? Object.fromEntries(Object.entries(data["dist-tags"]).map(([tag, ver]) => [tag, {
2275
2275
  version: ver,
@@ -2538,7 +2538,9 @@ function resolveInstalledVersion(name, cwd) {
2538
2538
  while (dir && basename(dir) !== "node_modules") {
2539
2539
  const pkgPath = join(dir, "package.json");
2540
2540
  if (existsSync(pkgPath)) return JSON.parse(readFileSync(pkgPath, "utf-8")).version || null;
2541
- dir = dirname(dir);
2541
+ const parent = dirname(dir);
2542
+ if (parent === dir) break;
2543
+ dir = parent;
2542
2544
  }
2543
2545
  } catch {}
2544
2546
  return null;
@@ -2632,51 +2634,67 @@ async function fetchPkgDist(name, version) {
2632
2634
  mkdirSync(pkgDir, { recursive: true });
2633
2635
  const tmpTarball = join(cacheDir, "_pkg.tgz");
2634
2636
  const fileStream = createWriteStream(tmpTarball);
2637
+ const fileClosed = new Promise((resolve) => fileStream.once("close", resolve));
2635
2638
  const reader = tarballRes.body.getReader();
2636
- await new Promise((res, reject) => {
2637
- const writable = new Writable({ write(chunk, _encoding, callback) {
2638
- fileStream.write(chunk, callback);
2639
- } });
2640
- writable.on("finish", () => {
2641
- fileStream.end();
2639
+ try {
2640
+ await new Promise((res, reject) => {
2641
+ const writable = new Writable({ write(chunk, _encoding, callback) {
2642
+ fileStream.write(chunk, callback);
2643
+ } });
2644
+ writable.on("finish", () => {
2645
+ fileStream.end();
2646
+ });
2647
+ fileStream.on("close", () => res());
2648
+ writable.on("error", reject);
2649
+ fileStream.on("error", reject);
2650
+ function pump() {
2651
+ reader.read().then(({ done, value }) => {
2652
+ if (done) {
2653
+ writable.end();
2654
+ return;
2655
+ }
2656
+ writable.write(value, () => pump());
2657
+ }).catch(reject);
2658
+ }
2659
+ pump();
2642
2660
  });
2643
- fileStream.on("close", () => res());
2644
- writable.on("error", reject);
2645
- fileStream.on("error", reject);
2646
- function pump() {
2647
- reader.read().then(({ done, value }) => {
2648
- if (done) {
2649
- writable.end();
2650
- return;
2651
- }
2652
- writable.write(value, () => pump());
2653
- }).catch(reject);
2661
+ const { status } = spawnSync("tar", [
2662
+ "xzf",
2663
+ tmpTarball,
2664
+ "--strip-components=1",
2665
+ "-C",
2666
+ pkgDir
2667
+ ], { stdio: "ignore" });
2668
+ if (status !== 0) {
2669
+ rmSync(pkgDir, {
2670
+ recursive: true,
2671
+ force: true
2672
+ });
2673
+ return null;
2654
2674
  }
2655
- pump();
2656
- });
2657
- const { status } = spawnSync("tar", [
2658
- "xzf",
2659
- tmpTarball,
2660
- "--strip-components=1",
2661
- "-C",
2662
- pkgDir
2663
- ], { stdio: "ignore" });
2664
- if (status !== 0) {
2675
+ return pkgDir;
2676
+ } catch {
2665
2677
  rmSync(pkgDir, {
2666
2678
  recursive: true,
2667
2679
  force: true
2668
2680
  });
2669
- rmSync(tmpTarball, { force: true });
2670
2681
  return null;
2682
+ } finally {
2683
+ reader.cancel().catch(() => {});
2684
+ fileStream.destroy();
2685
+ await fileClosed;
2686
+ try {
2687
+ rmSync(tmpTarball, { force: true });
2688
+ } catch {}
2671
2689
  }
2672
- unlinkSync(tmpTarball);
2673
- return pkgDir;
2674
2690
  }
2675
2691
  /**
2676
2692
  * Fetch just the latest version string from npm (lightweight)
2677
2693
  */
2678
2694
  async function fetchLatestVersion(packageName) {
2679
- return (await $fetch(`https://unpkg.com/${packageName}/package.json`).catch(() => null))?.version || null;
2695
+ const data = await $fetch(`https://unpkg.com/${packageName}/package.json`).catch(() => null);
2696
+ if (data?.version) return data.version;
2697
+ return (await $fetch(`https://registry.npmjs.org/${packageName}`, { headers: { Accept: "application/vnd.npm.install-v1+json" } }).catch(() => null))?.["dist-tags"]?.latest || null;
2680
2698
  }
2681
2699
  /**
2682
2700
  * Get installed skill version from SKILL.md