skilld 1.6.2 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -20
- package/dist/_chunks/agent.mjs +2 -1
- package/dist/_chunks/agent.mjs.map +1 -1
- package/dist/_chunks/assemble.mjs +1 -1
- package/dist/_chunks/author-group.mjs +17 -0
- package/dist/_chunks/author-group.mjs.map +1 -0
- package/dist/_chunks/author.mjs +8 -6
- package/dist/_chunks/author.mjs.map +1 -1
- package/dist/_chunks/cache.mjs +1 -1
- package/dist/_chunks/cache2.mjs +1 -1
- package/dist/_chunks/cli-helpers.mjs +3 -119
- package/dist/_chunks/cli-helpers.mjs.map +1 -1
- package/dist/_chunks/config.mjs +119 -27
- package/dist/_chunks/config.mjs.map +1 -1
- package/dist/_chunks/core.mjs +1 -1
- package/dist/_chunks/embedding-cache2.mjs +1 -1
- package/dist/_chunks/index.d.mts.map +1 -1
- package/dist/_chunks/index3.d.mts +79 -78
- package/dist/_chunks/index3.d.mts.map +1 -1
- package/dist/_chunks/install.mjs +85 -535
- package/dist/_chunks/install.mjs.map +1 -1
- package/dist/_chunks/install2.mjs +554 -0
- package/dist/_chunks/install2.mjs.map +1 -0
- package/dist/_chunks/lockfile.mjs +1 -0
- package/dist/_chunks/lockfile.mjs.map +1 -1
- package/dist/_chunks/package-registry.mjs +465 -0
- package/dist/_chunks/package-registry.mjs.map +1 -0
- package/dist/_chunks/prefix.mjs +108 -0
- package/dist/_chunks/prefix.mjs.map +1 -0
- package/dist/_chunks/prepare.mjs +6 -2
- package/dist/_chunks/prepare.mjs.map +1 -1
- package/dist/_chunks/prepare2.mjs +1 -1
- package/dist/_chunks/prompts.mjs +5 -99
- package/dist/_chunks/prompts.mjs.map +1 -1
- package/dist/_chunks/search-helpers.mjs +99 -0
- package/dist/_chunks/search-helpers.mjs.map +1 -0
- package/dist/_chunks/search-interactive.mjs +1 -1
- package/dist/_chunks/search-interactive.mjs.map +1 -1
- package/dist/_chunks/search.mjs +219 -1
- package/dist/_chunks/search.mjs.map +1 -0
- package/dist/_chunks/shared.mjs +1 -463
- package/dist/_chunks/shared.mjs.map +1 -1
- package/dist/_chunks/skills.mjs +1 -1
- package/dist/_chunks/sources.mjs +1177 -988
- package/dist/_chunks/sources.mjs.map +1 -1
- package/dist/_chunks/sync-registry.mjs +59 -0
- package/dist/_chunks/sync-registry.mjs.map +1 -0
- package/dist/_chunks/sync-shared2.mjs +10 -7
- package/dist/_chunks/sync-shared2.mjs.map +1 -1
- package/dist/_chunks/sync.mjs +208 -99
- package/dist/_chunks/sync.mjs.map +1 -1
- package/dist/_chunks/sync2.mjs +1 -1
- package/dist/_chunks/uninstall.mjs +3 -2
- package/dist/_chunks/uninstall.mjs.map +1 -1
- package/dist/_chunks/upload.mjs +152 -0
- package/dist/_chunks/upload.mjs.map +1 -0
- package/dist/_chunks/validate.mjs +1 -1
- package/dist/_chunks/version.mjs +30 -0
- package/dist/_chunks/version.mjs.map +1 -0
- package/dist/_chunks/wizard.mjs +2 -1
- package/dist/_chunks/wizard.mjs.map +1 -1
- package/dist/agent/index.mjs +2 -1
- package/dist/cache/index.mjs +1 -1
- package/dist/cli.mjs +48 -20
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/sources/index.d.mts +2 -2
- package/dist/sources/index.mjs +3 -3
- package/dist/types.d.mts +1 -1
- package/package.json +11 -12
- package/dist/_chunks/search2.mjs +0 -310
- package/dist/_chunks/search2.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uninstall.mjs","names":["agents"],"sources":["../../src/commands/uninstall.ts"],"sourcesContent":["import type { AgentType } from '../agent/index.ts'\nimport { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { join } from 'pathe'\nimport { agents } from '../agent/index.ts'\nimport { CACHE_DIR } from '../cache/index.ts'\nimport { isInteractive, sharedArgs } from '../cli-helpers.ts'\nimport { getRegisteredProjects, unregisterProject } from '../core/config.ts'\nimport { readLock } from '../core/lockfile.ts'\nimport { mapInsert, SHARED_SKILLS_DIR } from '../core/shared.ts'\nimport { SKILLD_MARKER_END, SKILLD_MARKER_START } from './sync.ts'\n\n/**\n * Remove the skilld marker block from an agent's instruction file.\n * For .mdc files (dedicated skilld files), delete the entire file.\n * Also cleans up legacy .cursorrules markers for backwards compat.\n */\nfunction removeAgentInstructions(agent: AgentType, projectPath: string): boolean {\n const agentConfig = agents[agent]\n if (!agentConfig.instructionFile)\n return false\n\n let removed = false\n\n // Handle current instruction file\n const filePath = join(projectPath, agentConfig.instructionFile)\n if (agentConfig.instructionFile.endsWith('.mdc')) {\n // MDC files are dedicated skilld files - just delete\n if (existsSync(filePath)) {\n rmSync(filePath)\n removed = true\n }\n // Also clean up legacy .cursorrules markers (cursor-specific)\n if (agent === 'cursor')\n removed = removeMarkerBlock(join(projectPath, '.cursorrules')) || removed\n }\n else if (existsSync(filePath)) {\n removed = removeMarkerBlock(filePath)\n }\n\n return removed\n}\n\nfunction removeMarkerBlock(filePath: string): boolean {\n if (!existsSync(filePath))\n return false\n\n const content = readFileSync(filePath, 'utf-8')\n const startIdx = content.indexOf(SKILLD_MARKER_START)\n if (startIdx === -1)\n return false\n\n const endIdx = content.indexOf(SKILLD_MARKER_END, startIdx)\n if (endIdx === -1)\n return false\n\n // Remove marker block plus surrounding blank lines\n const before = content.slice(0, startIdx).replace(/\\n+$/, '')\n const after = content.slice(endIdx + SKILLD_MARKER_END.length).replace(/^\\n+/, '')\n const updated = before + (before && after ? '\\n' : '') + after\n\n if (updated.trim() === '') {\n rmSync(filePath)\n }\n else {\n writeFileSync(filePath, updated.endsWith('\\n') ? updated : `${updated}\\n`)\n }\n return true\n}\n\nexport interface UninstallOptions {\n scope?: 'project' | 'all'\n agent?: AgentType\n yes: boolean\n}\n\n/**\n * Uninstall skilld skills by scope:\n * - project: Remove project skills (cwd)\n * - all: All registered projects + global skills + cache\n */\nexport async function uninstallCommand(opts: UninstallOptions): Promise<void> {\n let scope = opts.scope\n const registeredProjects = getRegisteredProjects()\n\n // Prompt for scope if not provided\n if (!scope) {\n if (!isInteractive()) {\n scope = 'project'\n }\n else {\n const allHint = registeredProjects.length > 0\n ? `${registeredProjects.length} projects + global + cache`\n : 'global skills + cache'\n\n const selected = await p.select({\n message: 'What do you want to uninstall?',\n options: [\n { label: 'This project', value: 'project', hint: 'current project only' },\n { label: 'Everything', value: 'all', hint: allHint },\n ],\n })\n\n if (p.isCancel(selected)) {\n p.cancel('Cancelled')\n return\n }\n scope = selected as 'project' | 'all'\n }\n }\n\n interface RemoveItem { label: string, path: string, version?: string }\n const toRemove: RemoveItem[] = []\n const seenPaths = new Set<string>()\n const projectsToUnregister: string[] = []\n const agentFilter = opts.agent ? [opts.agent] : undefined\n\n const addToRemove = (label: string, path: string, version?: string) => {\n if (seenPaths.has(path))\n return\n seenPaths.add(path)\n toRemove.push({ label, path, version })\n }\n\n // Helper to add skills from a lockfile\n const addSkillsFromLock = (skillsDir: string, label: string): string[] => {\n const trackedNames: string[] = []\n const lock = readLock(skillsDir)\n\n if (lock?.skills) {\n for (const [skillName, info] of Object.entries(lock.skills)) {\n trackedNames.push(skillName)\n const skillDir = join(skillsDir, skillName)\n if (existsSync(skillDir)) {\n const version = info.version ? `${info.version.split('.').slice(0, 2).join('.')}.x` : undefined\n addToRemove(`${label}: ${skillName}`, skillDir, version)\n }\n }\n\n // Also add the lockfile itself\n const lockPath = join(skillsDir, 'skilld-lock.yaml')\n if (existsSync(lockPath)) {\n addToRemove(`${label}: skilld-lock.yaml`, lockPath)\n }\n }\n\n return trackedNames\n }\n\n // Helper to find untracked skills in a directory\n const findUntrackedSkills = (skillsDir: string, trackedNames: string[]): string[] => {\n if (!existsSync(skillsDir))\n return []\n const tracked = new Set(trackedNames)\n return readdirSync(skillsDir)\n .filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml' && !tracked.has(f))\n }\n\n // Track untracked skills per directory (dedupe by path)\n const untrackedByDir = new Map<string, { label: string, skills: string[] }>()\n const processedDirs = new Set<string>()\n\n // Helper to process a skills directory (with deduping)\n const processSkillsDir = (skillsDir: string, label: string) => {\n if (processedDirs.has(skillsDir))\n return\n processedDirs.add(skillsDir)\n\n const tracked = addSkillsFromLock(skillsDir, label)\n const untracked = findUntrackedSkills(skillsDir, tracked)\n if (untracked.length > 0) {\n untrackedByDir.set(skillsDir, { label, skills: untracked })\n }\n }\n\n // Project skills\n if (scope === 'project') {\n // Shared dir\n const sharedDir = join(process.cwd(), SHARED_SKILLS_DIR)\n if (existsSync(sharedDir))\n processSkillsDir(sharedDir, 'project (.skills)')\n for (const [name, agent] of Object.entries(agents)) {\n if (agentFilter && !agentFilter.includes(name as AgentType))\n continue\n processSkillsDir(join(process.cwd(), agent.skillsDir), 'project')\n }\n projectsToUnregister.push(process.cwd())\n }\n\n // All registered projects + global\n if (scope === 'all') {\n const projectPaths = registeredProjects.length > 0 ? registeredProjects : [process.cwd()]\n\n // Show which projects will be affected\n if (registeredProjects.length > 0) {\n p.log.info('Projects to uninstall from:')\n for (const proj of projectPaths) {\n p.log.message(` ${proj}`)\n }\n }\n\n // Project skills from lockfiles\n for (const projectPath of projectPaths) {\n if (!existsSync(projectPath))\n continue\n\n const shortPath = projectPath.replace(process.env.HOME || '', '~')\n\n // Shared dir\n const sharedDir = join(projectPath, SHARED_SKILLS_DIR)\n if (existsSync(sharedDir))\n processSkillsDir(sharedDir, `${shortPath} (.skills)`)\n\n for (const [name, agent] of Object.entries(agents)) {\n if (agentFilter && !agentFilter.includes(name as AgentType))\n continue\n processSkillsDir(join(projectPath, agent.skillsDir), shortPath)\n }\n\n projectsToUnregister.push(projectPath)\n }\n\n // Global skills from lockfiles\n for (const [name, agent] of Object.entries(agents)) {\n if (agentFilter && !agentFilter.includes(name as AgentType))\n continue\n if (!agent.globalSkillsDir)\n continue\n processSkillsDir(agent.globalSkillsDir, 'user')\n }\n\n // Cache directory\n if (existsSync(CACHE_DIR)) {\n addToRemove('~/.skilld cache', CACHE_DIR)\n }\n }\n\n // Warn about untracked skills that will remain (grouped by label, deduped)\n if (untrackedByDir.size > 0) {\n const groupedUntracked = new Map<string, Set<string>>()\n for (const [_dir, { label, skills }] of untrackedByDir) {\n const set = mapInsert(groupedUntracked, label, () => new Set())\n for (const s of skills) set.add(s)\n }\n\n const totalUntracked = [...groupedUntracked.values()].reduce((sum, s) => sum + s.size, 0)\n p.log.warn(`${totalUntracked} untracked skill(s) will remain (not managed by skilld):`)\n for (const [label, skills] of groupedUntracked) {\n p.log.message(` ${label}: ${[...skills].join(', ')}`)\n }\n }\n\n if (toRemove.length === 0) {\n p.log.info('Nothing to uninstall')\n return\n }\n\n // Group by prefix for display\n const groups = new Map<string, Array<{ name: string, version?: string }>>()\n for (const item of toRemove) {\n const [prefix, name] = item.label.includes(': ')\n ? item.label.split(': ', 2)\n : ['other', item.label]\n mapInsert(groups, prefix!, () => []).push({ name: name!, version: item.version })\n }\n\n const formatGroup = (items: Array<{ name: string, version?: string }>) =>\n items.map(i => i.version ? `${i.name}@${i.version}` : i.name).join(', ')\n\n p.log.info(`Will remove ${toRemove.length} items:`)\n for (const [prefix, items] of groups) {\n p.log.message(` ${prefix}: ${formatGroup(items)}`)\n }\n\n if (!opts.yes && isInteractive()) {\n const confirmed = await p.confirm({\n message: 'Proceed with uninstall?',\n })\n\n if (p.isCancel(confirmed) || !confirmed) {\n p.cancel('Cancelled')\n return\n }\n }\n\n // Remove all items\n for (const item of toRemove) {\n rmSync(item.path, { recursive: true, force: true })\n }\n\n // Show grouped removal summary\n for (const [prefix, items] of groups) {\n p.log.success(`Removed ${prefix}: ${formatGroup(items)}`)\n }\n\n // Remove skilld instructions from agent instruction files\n const agentTypes = agentFilter || (Object.keys(agents) as AgentType[])\n for (const proj of projectsToUnregister) {\n for (const agent of agentTypes) {\n if (removeAgentInstructions(agent, proj)) {\n const file = agents[agent].instructionFile!\n p.log.success(`Cleaned ${file}`)\n }\n }\n }\n\n // Unregister projects from config (skip if cache dir was removed — config is gone)\n if (scope !== 'all') {\n for (const proj of projectsToUnregister) {\n unregisterProject(proj)\n }\n }\n\n p.outro('skilld uninstalled')\n}\n\nexport const uninstallCommandDef = defineCommand({\n meta: { name: 'uninstall', description: 'Remove skilld data' },\n args: {\n ...sharedArgs,\n },\n async run({ args }) {\n p.intro(`\\x1B[1m\\x1B[35mskilld\\x1B[0m uninstall`)\n return uninstallCommand({\n scope: args.global ? 'all' : undefined,\n agent: args.agent as AgentType | undefined,\n yes: args.yes,\n })\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"uninstall.mjs","names":["agents"],"sources":["../../src/commands/uninstall.ts"],"sourcesContent":["import type { AgentType } from '../agent/index.ts'\nimport { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { join } from 'pathe'\nimport { agents } from '../agent/index.ts'\nimport { CACHE_DIR } from '../cache/index.ts'\nimport { isInteractive, sharedArgs } from '../cli-helpers.ts'\nimport { getRegisteredProjects, unregisterProject } from '../core/config.ts'\nimport { readLock } from '../core/lockfile.ts'\nimport { mapInsert, SHARED_SKILLS_DIR } from '../core/shared.ts'\nimport { SKILLD_MARKER_END, SKILLD_MARKER_START } from './sync.ts'\n\n/**\n * Remove the skilld marker block from an agent's instruction file.\n * For .mdc files (dedicated skilld files), delete the entire file.\n * Also cleans up legacy .cursorrules markers for backwards compat.\n */\nfunction removeAgentInstructions(agent: AgentType, projectPath: string): boolean {\n const agentConfig = agents[agent]\n if (!agentConfig.instructionFile)\n return false\n\n let removed = false\n\n // Handle current instruction file\n const filePath = join(projectPath, agentConfig.instructionFile)\n if (agentConfig.instructionFile.endsWith('.mdc')) {\n // MDC files are dedicated skilld files - just delete\n if (existsSync(filePath)) {\n rmSync(filePath)\n removed = true\n }\n // Also clean up legacy .cursorrules markers (cursor-specific)\n if (agent === 'cursor')\n removed = removeMarkerBlock(join(projectPath, '.cursorrules')) || removed\n }\n else if (existsSync(filePath)) {\n removed = removeMarkerBlock(filePath)\n }\n\n return removed\n}\n\nfunction removeMarkerBlock(filePath: string): boolean {\n if (!existsSync(filePath))\n return false\n\n const content = readFileSync(filePath, 'utf-8')\n const startIdx = content.indexOf(SKILLD_MARKER_START)\n if (startIdx === -1)\n return false\n\n const endIdx = content.indexOf(SKILLD_MARKER_END, startIdx)\n if (endIdx === -1)\n return false\n\n // Remove marker block plus surrounding blank lines\n const before = content.slice(0, startIdx).replace(/\\n+$/, '')\n const after = content.slice(endIdx + SKILLD_MARKER_END.length).replace(/^\\n+/, '')\n const updated = before + (before && after ? '\\n' : '') + after\n\n if (updated.trim() === '') {\n rmSync(filePath)\n }\n else {\n writeFileSync(filePath, updated.endsWith('\\n') ? updated : `${updated}\\n`)\n }\n return true\n}\n\nexport interface UninstallOptions {\n scope?: 'project' | 'all'\n agent?: AgentType\n yes: boolean\n}\n\n/**\n * Uninstall skilld skills by scope:\n * - project: Remove project skills (cwd)\n * - all: All registered projects + global skills + cache\n */\nexport async function uninstallCommand(opts: UninstallOptions): Promise<void> {\n let scope = opts.scope\n const registeredProjects = getRegisteredProjects()\n\n // Prompt for scope if not provided\n if (!scope) {\n if (!isInteractive()) {\n scope = 'project'\n }\n else {\n const allHint = registeredProjects.length > 0\n ? `${registeredProjects.length} projects + global + cache`\n : 'global skills + cache'\n\n const selected = await p.select({\n message: 'What do you want to uninstall?',\n options: [\n { label: 'This project', value: 'project', hint: 'current project only' },\n { label: 'Everything', value: 'all', hint: allHint },\n ],\n })\n\n if (p.isCancel(selected)) {\n p.cancel('Cancelled')\n return\n }\n scope = selected as 'project' | 'all'\n }\n }\n\n interface RemoveItem { label: string, path: string, version?: string }\n const toRemove: RemoveItem[] = []\n const seenPaths = new Set<string>()\n const projectsToUnregister: string[] = []\n const agentFilter = opts.agent ? [opts.agent] : undefined\n\n const addToRemove = (label: string, path: string, version?: string) => {\n if (seenPaths.has(path))\n return\n seenPaths.add(path)\n toRemove.push({ label, path, version })\n }\n\n // Helper to add skills from a lockfile\n const addSkillsFromLock = (skillsDir: string, label: string): string[] => {\n const trackedNames: string[] = []\n const lock = readLock(skillsDir)\n\n if (lock?.skills) {\n for (const [skillName, info] of Object.entries(lock.skills)) {\n trackedNames.push(skillName)\n const skillDir = join(skillsDir, skillName)\n if (existsSync(skillDir)) {\n const version = info.version ? `${info.version.split('.').slice(0, 2).join('.')}.x` : undefined\n addToRemove(`${label}: ${skillName}`, skillDir, version)\n }\n }\n\n // Also add the lockfile itself\n const lockPath = join(skillsDir, 'skilld-lock.yaml')\n if (existsSync(lockPath)) {\n addToRemove(`${label}: skilld-lock.yaml`, lockPath)\n }\n }\n\n return trackedNames\n }\n\n // Helper to find untracked skills in a directory\n const findUntrackedSkills = (skillsDir: string, trackedNames: string[]): string[] => {\n if (!existsSync(skillsDir))\n return []\n const tracked = new Set(trackedNames)\n return readdirSync(skillsDir)\n .filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml' && !tracked.has(f))\n }\n\n // Track untracked skills per directory (dedupe by path)\n const untrackedByDir = new Map<string, { label: string, skills: string[] }>()\n const processedDirs = new Set<string>()\n\n // Helper to process a skills directory (with deduping)\n const processSkillsDir = (skillsDir: string, label: string) => {\n if (processedDirs.has(skillsDir))\n return\n processedDirs.add(skillsDir)\n\n const tracked = addSkillsFromLock(skillsDir, label)\n const untracked = findUntrackedSkills(skillsDir, tracked)\n if (untracked.length > 0) {\n untrackedByDir.set(skillsDir, { label, skills: untracked })\n }\n }\n\n // Project skills\n if (scope === 'project') {\n // Shared dir\n const sharedDir = join(process.cwd(), SHARED_SKILLS_DIR)\n if (existsSync(sharedDir))\n processSkillsDir(sharedDir, 'project (.skills)')\n for (const [name, agent] of Object.entries(agents)) {\n if (agentFilter && !agentFilter.includes(name as AgentType))\n continue\n processSkillsDir(join(process.cwd(), agent.skillsDir), 'project')\n }\n projectsToUnregister.push(process.cwd())\n }\n\n // All registered projects + global\n if (scope === 'all') {\n const projectPaths = registeredProjects.length > 0 ? registeredProjects : [process.cwd()]\n\n // Show which projects will be affected\n if (registeredProjects.length > 0) {\n p.log.info('Projects to uninstall from:')\n for (const proj of projectPaths) {\n p.log.message(` ${proj}`)\n }\n }\n\n // Project skills from lockfiles\n for (const projectPath of projectPaths) {\n if (!existsSync(projectPath))\n continue\n\n const shortPath = projectPath.replace(process.env.HOME || '', '~')\n\n // Shared dir\n const sharedDir = join(projectPath, SHARED_SKILLS_DIR)\n if (existsSync(sharedDir))\n processSkillsDir(sharedDir, `${shortPath} (.skills)`)\n\n for (const [name, agent] of Object.entries(agents)) {\n if (agentFilter && !agentFilter.includes(name as AgentType))\n continue\n processSkillsDir(join(projectPath, agent.skillsDir), shortPath)\n }\n\n projectsToUnregister.push(projectPath)\n }\n\n // Global skills from lockfiles\n for (const [name, agent] of Object.entries(agents)) {\n if (agentFilter && !agentFilter.includes(name as AgentType))\n continue\n if (!agent.globalSkillsDir)\n continue\n processSkillsDir(agent.globalSkillsDir, 'user')\n }\n\n // Cache directory\n if (existsSync(CACHE_DIR)) {\n addToRemove('~/.skilld cache', CACHE_DIR)\n }\n }\n\n // Warn about untracked skills that will remain (grouped by label, deduped)\n if (untrackedByDir.size > 0) {\n const groupedUntracked = new Map<string, Set<string>>()\n for (const [_dir, { label, skills }] of untrackedByDir) {\n const set = mapInsert(groupedUntracked, label, () => new Set())\n for (const s of skills) set.add(s)\n }\n\n const totalUntracked = [...groupedUntracked.values()].reduce((sum, s) => sum + s.size, 0)\n p.log.warn(`${totalUntracked} untracked skill(s) will remain (not managed by skilld):`)\n for (const [label, skills] of groupedUntracked) {\n p.log.message(` ${label}: ${[...skills].join(', ')}`)\n }\n }\n\n if (toRemove.length === 0) {\n p.log.info('Nothing to uninstall')\n return\n }\n\n // Group by prefix for display\n const groups = new Map<string, Array<{ name: string, version?: string }>>()\n for (const item of toRemove) {\n const [prefix, name] = item.label.includes(': ')\n ? item.label.split(': ', 2)\n : ['other', item.label]\n mapInsert(groups, prefix!, () => []).push({ name: name!, version: item.version })\n }\n\n const formatGroup = (items: Array<{ name: string, version?: string }>) =>\n items.map(i => i.version ? `${i.name}@${i.version}` : i.name).join(', ')\n\n p.log.info(`Will remove ${toRemove.length} items:`)\n for (const [prefix, items] of groups) {\n p.log.message(` ${prefix}: ${formatGroup(items)}`)\n }\n\n if (!opts.yes && isInteractive()) {\n const confirmed = await p.confirm({\n message: 'Proceed with uninstall?',\n })\n\n if (p.isCancel(confirmed) || !confirmed) {\n p.cancel('Cancelled')\n return\n }\n }\n\n // Remove all items\n for (const item of toRemove) {\n rmSync(item.path, { recursive: true, force: true })\n }\n\n // Show grouped removal summary\n for (const [prefix, items] of groups) {\n p.log.success(`Removed ${prefix}: ${formatGroup(items)}`)\n }\n\n // Remove skilld instructions from agent instruction files\n const agentTypes = agentFilter || (Object.keys(agents) as AgentType[])\n for (const proj of projectsToUnregister) {\n for (const agent of agentTypes) {\n if (removeAgentInstructions(agent, proj)) {\n const file = agents[agent].instructionFile!\n p.log.success(`Cleaned ${file}`)\n }\n }\n }\n\n // Unregister projects from config (skip if cache dir was removed — config is gone)\n if (scope !== 'all') {\n for (const proj of projectsToUnregister) {\n unregisterProject(proj)\n }\n }\n\n p.outro('skilld uninstalled')\n}\n\nexport const uninstallCommandDef = defineCommand({\n meta: { name: 'uninstall', description: 'Remove skilld data' },\n args: {\n ...sharedArgs,\n },\n async run({ args }) {\n p.intro(`\\x1B[1m\\x1B[35mskilld\\x1B[0m uninstall`)\n return uninstallCommand({\n scope: args.global ? 'all' : undefined,\n agent: args.agent as AgentType | undefined,\n yes: args.yes,\n })\n },\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { i as parseFrontmatter } from "./markdown.mjs";
|
|
2
|
+
import { i as readLock } from "./lockfile.mjs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "pathe";
|
|
5
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
6
|
+
import { ofetch } from "ofetch";
|
|
7
|
+
import { multiselect } from "@clack/prompts";
|
|
8
|
+
import { defineCommand } from "citty";
|
|
9
|
+
import { colorize } from "consola/utils";
|
|
10
|
+
const UPLOAD_URL = "https://skilld.dev/api/collections/import";
|
|
11
|
+
function readSkillsFromDir(dir, source, extraLockDirs) {
|
|
12
|
+
if (!existsSync(dir)) return [];
|
|
13
|
+
let lock = readLock(dir);
|
|
14
|
+
if (!lock && extraLockDirs) for (const d of extraLockDirs) {
|
|
15
|
+
lock = readLock(d);
|
|
16
|
+
if (lock) break;
|
|
17
|
+
}
|
|
18
|
+
const entries = readdirSync(dir).filter((f) => {
|
|
19
|
+
if (f.startsWith(".") || f.endsWith(".yaml") || f.endsWith(".yml")) return false;
|
|
20
|
+
return statSync(join(dir, f)).isDirectory();
|
|
21
|
+
});
|
|
22
|
+
const skills = [];
|
|
23
|
+
for (const dirName of entries) {
|
|
24
|
+
const skillMd = join(dir, dirName, "SKILL.md");
|
|
25
|
+
if (!existsSync(skillMd)) continue;
|
|
26
|
+
const fm = parseFrontmatter(readFileSync(skillMd, "utf-8"));
|
|
27
|
+
const lockInfo = lock?.skills[dirName];
|
|
28
|
+
skills.push({
|
|
29
|
+
name: fm.name || dirName,
|
|
30
|
+
description: fm.description,
|
|
31
|
+
version: fm.version || lockInfo?.version,
|
|
32
|
+
repo: lockInfo?.repo,
|
|
33
|
+
generator: lockInfo?.generator,
|
|
34
|
+
source
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return skills;
|
|
38
|
+
}
|
|
39
|
+
function readPlugins(configDir) {
|
|
40
|
+
const settingsPath = join(configDir, "settings.json");
|
|
41
|
+
if (!existsSync(settingsPath)) return [];
|
|
42
|
+
const enabledPlugins = JSON.parse(readFileSync(settingsPath, "utf-8")).enabledPlugins;
|
|
43
|
+
if (!enabledPlugins) return [];
|
|
44
|
+
const marketplacesPath = join(configDir, "plugins", "known_marketplaces.json");
|
|
45
|
+
const marketplaces = existsSync(marketplacesPath) ? JSON.parse(readFileSync(marketplacesPath, "utf-8")) : {};
|
|
46
|
+
const installedPath = join(configDir, "plugins", "installed_plugins.json");
|
|
47
|
+
const installed = existsSync(installedPath) ? JSON.parse(readFileSync(installedPath, "utf-8")) : { plugins: {} };
|
|
48
|
+
return Object.entries(enabledPlugins).filter(([, enabled]) => enabled).map(([id]) => {
|
|
49
|
+
const marketplace = id.split("@")[1];
|
|
50
|
+
const pluginName = id.split("@")[0];
|
|
51
|
+
const repo = (marketplace ? marketplaces[marketplace] : void 0)?.source?.repo;
|
|
52
|
+
const versions = installed.plugins[id];
|
|
53
|
+
return {
|
|
54
|
+
name: pluginName,
|
|
55
|
+
version: versions?.[0]?.version !== "unknown" ? versions?.[0]?.version : void 0,
|
|
56
|
+
repo: repo ? `${repo}` : void 0,
|
|
57
|
+
source: "plugin"
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function discoverAllSkills(cwd) {
|
|
62
|
+
const claudeHome = process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
|
|
63
|
+
const localDir = join(cwd, ".claude", "skills");
|
|
64
|
+
const globalDir = join(claudeHome, "skills");
|
|
65
|
+
const skilldGlobalDir = join(homedir(), ".skilld", "skills");
|
|
66
|
+
const local = readSkillsFromDir(localDir, "local");
|
|
67
|
+
const global = readSkillsFromDir(globalDir, "global", [skilldGlobalDir]);
|
|
68
|
+
const plugins = readPlugins(claudeHome);
|
|
69
|
+
const seenRepos = /* @__PURE__ */ new Set();
|
|
70
|
+
const all = [];
|
|
71
|
+
for (const skill of [
|
|
72
|
+
...local,
|
|
73
|
+
...global,
|
|
74
|
+
...plugins
|
|
75
|
+
]) {
|
|
76
|
+
if (!skill.repo || seenRepos.has(skill.repo)) continue;
|
|
77
|
+
if (skill.generator === "skilld") continue;
|
|
78
|
+
seenRepos.add(skill.repo);
|
|
79
|
+
all.push(skill);
|
|
80
|
+
}
|
|
81
|
+
return all;
|
|
82
|
+
}
|
|
83
|
+
const SOURCE_COLORS = {
|
|
84
|
+
local: "green",
|
|
85
|
+
global: "blue",
|
|
86
|
+
plugin: "magenta"
|
|
87
|
+
};
|
|
88
|
+
async function uploadCommand(options) {
|
|
89
|
+
const skills = discoverAllSkills(process.cwd());
|
|
90
|
+
if (skills.length === 0) {
|
|
91
|
+
process.stdout.write("No skills found.\n");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (options?.dryRun) {
|
|
95
|
+
process.stdout.write(`Found ${colorize("bold", String(skills.length))} skill${skills.length === 1 ? "" : "s"}:\n\n`);
|
|
96
|
+
for (const skill of skills) {
|
|
97
|
+
const version = skill.version ? colorize("dim", ` v${skill.version}`) : "";
|
|
98
|
+
const tag = colorize(SOURCE_COLORS[skill.source] || "dim", skill.source);
|
|
99
|
+
const repo = skill.repo ? colorize("dim", ` github.com/${skill.repo}`) : "";
|
|
100
|
+
process.stdout.write(` ${colorize("cyan", skill.name)}${version} ${tag}${repo}\n`);
|
|
101
|
+
}
|
|
102
|
+
process.stdout.write(`\n${colorize("dim", "Dry run complete. No requests were made.")}\n`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const selected = await multiselect({
|
|
106
|
+
message: `Select skills to upload (${skills.length} found)`,
|
|
107
|
+
options: skills.map((s) => {
|
|
108
|
+
const version = s.version ? ` v${s.version}` : "";
|
|
109
|
+
const repo = s.repo ? ` github.com/${s.repo}` : "";
|
|
110
|
+
return {
|
|
111
|
+
value: s.name,
|
|
112
|
+
label: `${s.name}${version}`,
|
|
113
|
+
hint: `${s.source}${repo}`
|
|
114
|
+
};
|
|
115
|
+
}),
|
|
116
|
+
initialValues: []
|
|
117
|
+
});
|
|
118
|
+
if (typeof selected === "symbol" || selected.length === 0) return;
|
|
119
|
+
const selectedSet = new Set(selected);
|
|
120
|
+
const { token, expires } = await ofetch(UPLOAD_URL, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
body: { skills: skills.filter((s) => selectedSet.has(s.name)).map((s) => ({
|
|
123
|
+
name: s.name,
|
|
124
|
+
version: s.version,
|
|
125
|
+
repo: s.repo,
|
|
126
|
+
source: s.source
|
|
127
|
+
})) }
|
|
128
|
+
});
|
|
129
|
+
const expiresDate = new Date(expires);
|
|
130
|
+
const minutesLeft = Math.round((expiresDate.getTime() - Date.now()) / 6e4);
|
|
131
|
+
process.stdout.write(`\nToken: ${colorize("green", token)}\n\n`);
|
|
132
|
+
process.stdout.write(`Paste this token at: ${colorize("cyan", "https://skilld.dev/people/YOUR_HANDLE/edit-skills")}\n`);
|
|
133
|
+
process.stdout.write(colorize("dim", `Token expires in ${minutesLeft} minutes.\n`));
|
|
134
|
+
}
|
|
135
|
+
const uploadCommandDef = defineCommand({
|
|
136
|
+
meta: {
|
|
137
|
+
name: "publish",
|
|
138
|
+
description: "Publish your skill list to skilld.dev"
|
|
139
|
+
},
|
|
140
|
+
args: { dryRun: {
|
|
141
|
+
type: "boolean",
|
|
142
|
+
alias: "d",
|
|
143
|
+
description: "Show what would be uploaded without making any requests",
|
|
144
|
+
default: false
|
|
145
|
+
} },
|
|
146
|
+
run({ args }) {
|
|
147
|
+
return uploadCommand({ dryRun: args.dryRun });
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
export { uploadCommandDef };
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=upload.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.mjs","names":[],"sources":["../../src/commands/upload.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { multiselect } from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { colorize } from 'consola/utils'\nimport { ofetch } from 'ofetch'\nimport { join } from 'pathe'\nimport { readLock } from '../core/lockfile.ts'\nimport { parseFrontmatter } from '../core/markdown.ts'\n\nconst UPLOAD_URL = 'https://skilld.dev/api/collections/import'\n\ninterface DiscoveredSkill {\n name: string\n description?: string\n version?: string\n repo?: string\n generator?: string\n source: 'local' | 'global' | 'plugin'\n}\n\nfunction readSkillsFromDir(dir: string, source: 'local' | 'global', extraLockDirs?: string[]): DiscoveredSkill[] {\n if (!existsSync(dir))\n return []\n\n // Merge lockfiles: primary dir + any extra dirs (e.g. ~/.skilld/skills/ for global)\n let lock = readLock(dir)\n if (!lock && extraLockDirs) {\n for (const d of extraLockDirs) {\n lock = readLock(d)\n if (lock)\n break\n }\n }\n const entries = readdirSync(dir).filter((f) => {\n if (f.startsWith('.') || f.endsWith('.yaml') || f.endsWith('.yml'))\n return false\n const full = join(dir, f)\n return statSync(full).isDirectory()\n })\n\n const skills: DiscoveredSkill[] = []\n for (const dirName of entries) {\n const skillMd = join(dir, dirName, 'SKILL.md')\n if (!existsSync(skillMd))\n continue\n const content = readFileSync(skillMd, 'utf-8')\n const fm = parseFrontmatter(content)\n const lockInfo = lock?.skills[dirName]\n skills.push({\n name: fm.name || dirName,\n description: fm.description,\n version: fm.version || lockInfo?.version,\n repo: lockInfo?.repo,\n generator: lockInfo?.generator,\n source,\n })\n }\n\n return skills\n}\n\ninterface MarketplaceInfo {\n source: { source: string, repo: string }\n}\n\nfunction readPlugins(configDir: string): DiscoveredSkill[] {\n const settingsPath = join(configDir, 'settings.json')\n if (!existsSync(settingsPath))\n return []\n\n const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'))\n const enabledPlugins = settings.enabledPlugins as Record<string, boolean> | undefined\n if (!enabledPlugins)\n return []\n\n // Load marketplace repos for GitHub links\n const marketplacesPath = join(configDir, 'plugins', 'known_marketplaces.json')\n const marketplaces: Record<string, MarketplaceInfo> = existsSync(marketplacesPath)\n ? JSON.parse(readFileSync(marketplacesPath, 'utf-8'))\n : {}\n\n // Load installed plugins for version info\n const installedPath = join(configDir, 'plugins', 'installed_plugins.json')\n const installed: { plugins: Record<string, Array<{ version?: string }>> } = existsSync(installedPath)\n ? JSON.parse(readFileSync(installedPath, 'utf-8'))\n : { plugins: {} }\n\n return Object.entries(enabledPlugins)\n .filter(([, enabled]) => enabled)\n .map(([id]) => {\n const marketplace = id.split('@')[1]\n const pluginName = id.split('@')[0]\n const marketplaceInfo = marketplace ? marketplaces[marketplace] : undefined\n const repo = marketplaceInfo?.source?.repo\n const versions = installed.plugins[id]\n const version = versions?.[0]?.version !== 'unknown' ? versions?.[0]?.version : undefined\n\n return {\n name: pluginName!,\n version,\n repo: repo ? `${repo}` : undefined,\n source: 'plugin' as const,\n }\n })\n}\n\nfunction discoverAllSkills(cwd: string): DiscoveredSkill[] {\n const claudeHome = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude')\n const localDir = join(cwd, '.claude', 'skills')\n const globalDir = join(claudeHome, 'skills')\n\n const skilldGlobalDir = join(homedir(), '.skilld', 'skills')\n const local = readSkillsFromDir(localDir, 'local')\n const global = readSkillsFromDir(globalDir, 'global', [skilldGlobalDir])\n const plugins = readPlugins(claudeHome)\n\n // Filter out skilld-generated skills, deduplicate by repo\n const seenRepos = new Set<string>()\n const all: DiscoveredSkill[] = []\n for (const skill of [...local, ...global, ...plugins]) {\n if (!skill.repo || seenRepos.has(skill.repo))\n continue\n if (skill.generator === 'skilld')\n continue\n seenRepos.add(skill.repo)\n all.push(skill)\n }\n return all\n}\n\nconst SOURCE_COLORS: Record<string, string> = {\n local: 'green',\n global: 'blue',\n plugin: 'magenta',\n}\n\nexport async function uploadCommand(options?: { dryRun?: boolean }): Promise<void> {\n const skills = discoverAllSkills(process.cwd())\n\n if (skills.length === 0) {\n process.stdout.write('No skills found.\\n')\n return\n }\n\n if (options?.dryRun) {\n process.stdout.write(`Found ${colorize('bold', String(skills.length))} skill${skills.length === 1 ? '' : 's'}:\\n\\n`)\n for (const skill of skills) {\n const version = skill.version ? colorize('dim', ` v${skill.version}`) : ''\n const tag = colorize((SOURCE_COLORS[skill.source] || 'dim') as 'dim', skill.source)\n const repo = skill.repo ? colorize('dim', ` github.com/${skill.repo}`) : ''\n process.stdout.write(` ${colorize('cyan', skill.name)}${version} ${tag}${repo}\\n`)\n }\n process.stdout.write(`\\n${colorize('dim', 'Dry run complete. No requests were made.')}\\n`)\n return\n }\n\n const selected = await multiselect({\n message: `Select skills to upload (${skills.length} found)`,\n options: skills.map((s) => {\n const version = s.version ? ` v${s.version}` : ''\n const repo = s.repo ? ` github.com/${s.repo}` : ''\n return {\n value: s.name,\n label: `${s.name}${version}`,\n hint: `${s.source}${repo}`,\n }\n }),\n initialValues: [],\n })\n\n if (typeof selected === 'symbol' || selected.length === 0)\n return\n\n const selectedSet = new Set(selected)\n const payload = skills\n .filter(s => selectedSet.has(s.name))\n .map(s => ({\n name: s.name,\n version: s.version,\n repo: s.repo,\n source: s.source,\n }))\n\n const { token, expires } = await ofetch<{ token: string, expires: string }>(UPLOAD_URL, {\n method: 'POST',\n body: { skills: payload },\n })\n\n const expiresDate = new Date(expires)\n const minutesLeft = Math.round((expiresDate.getTime() - Date.now()) / 60000)\n\n process.stdout.write(`\\nToken: ${colorize('green', token)}\\n\\n`)\n process.stdout.write(`Paste this token at: ${colorize('cyan', 'https://skilld.dev/people/YOUR_HANDLE/edit-skills')}\\n`)\n process.stdout.write(colorize('dim', `Token expires in ${minutesLeft} minutes.\\n`))\n}\n\nexport const uploadCommandDef = defineCommand({\n meta: { name: 'publish', description: 'Publish your skill list to skilld.dev' },\n args: {\n dryRun: {\n type: 'boolean',\n alias: 'd',\n description: 'Show what would be uploaded without making any requests',\n default: false,\n },\n },\n run({ args }) {\n return uploadCommand({ dryRun: args.dryRun })\n },\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join, resolve } from "pathe";
|
|
3
|
+
const CACHE_DIR = join(homedir(), ".skilld");
|
|
4
|
+
const REFERENCES_DIR = join(CACHE_DIR, "references");
|
|
5
|
+
const REPOS_DIR = join(CACHE_DIR, "repos");
|
|
6
|
+
function getRepoCacheDir(owner, repo) {
|
|
7
|
+
if (owner.includes("..") || repo.includes("..") || owner.includes("/") || repo.includes("/")) throw new Error(`Invalid repo path: ${owner}/${repo}`);
|
|
8
|
+
return join(REPOS_DIR, owner, repo);
|
|
9
|
+
}
|
|
10
|
+
function getPackageDbPath(name, version) {
|
|
11
|
+
return join(REFERENCES_DIR, `${name}@${version}`, "search.db");
|
|
12
|
+
}
|
|
13
|
+
const VALID_PKG_NAME = /^(?:@[a-z0-9][-a-z0-9._]*\/)?[a-z0-9][-a-z0-9._]*$/;
|
|
14
|
+
const VALID_VERSION = /^[a-z0-9][-\w.+]*$/i;
|
|
15
|
+
function getVersionKey(version) {
|
|
16
|
+
return version;
|
|
17
|
+
}
|
|
18
|
+
function getCacheKey(name, version) {
|
|
19
|
+
return `${name}@${getVersionKey(version)}`;
|
|
20
|
+
}
|
|
21
|
+
function getCacheDir(name, version) {
|
|
22
|
+
if (!VALID_PKG_NAME.test(name)) throw new Error(`Invalid package name: ${name}`);
|
|
23
|
+
if (!VALID_VERSION.test(version)) throw new Error(`Invalid version: ${version}`);
|
|
24
|
+
const dir = resolve(REFERENCES_DIR, getCacheKey(name, version));
|
|
25
|
+
if (!dir.startsWith(REFERENCES_DIR)) throw new Error(`Path traversal detected: ${dir}`);
|
|
26
|
+
return dir;
|
|
27
|
+
}
|
|
28
|
+
export { REFERENCES_DIR as a, getRepoCacheDir as c, CACHE_DIR as i, getCacheKey as n, REPOS_DIR as o, getVersionKey as r, getPackageDbPath as s, getCacheDir as t };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=version.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.mjs","names":[],"sources":["../../src/cache/config.ts","../../src/cache/version.ts"],"sourcesContent":["/**\n * Cache configuration\n */\n\nimport { homedir } from 'node:os'\nimport { join } from 'pathe'\n\n/** Global cache directory */\nexport const CACHE_DIR = join(homedir(), '.skilld')\n\n/** References subdirectory */\nexport const REFERENCES_DIR = join(CACHE_DIR, 'references')\n\n/** Repo-level cache (issues, discussions, releases shared across monorepo packages) */\nexport const REPOS_DIR = join(CACHE_DIR, 'repos')\n\n/** Get repo cache dir for owner/repo with path traversal validation */\nexport function getRepoCacheDir(owner: string, repo: string): string {\n if (owner.includes('..') || repo.includes('..') || owner.includes('/') || repo.includes('/'))\n throw new Error(`Invalid repo path: ${owner}/${repo}`)\n return join(REPOS_DIR, owner, repo)\n}\n\n/** Get search DB path for a specific package@version */\nexport function getPackageDbPath(name: string, version: string): string {\n return join(REFERENCES_DIR, `${name}@${version}`, 'search.db')\n}\n","/**\n * Version utilities\n */\n\nimport { resolve } from 'pathe'\nimport { REFERENCES_DIR } from './config.ts'\n\n/** Validate npm package name (scoped or unscoped) */\nconst VALID_PKG_NAME = /^(?:@[a-z0-9][-a-z0-9._]*\\/)?[a-z0-9][-a-z0-9._]*$/\n\n/** Validate version string (semver-ish, no path separators) */\nconst VALID_VERSION = /^[a-z0-9][-\\w.+]*$/i\n\n/**\n * Get exact version key for cache keying\n */\nexport function getVersionKey(version: string): string {\n return version\n}\n\n/**\n * Get cache key for a package: name@version\n */\nexport function getCacheKey(name: string, version: string): string {\n return `${name}@${getVersionKey(version)}`\n}\n\n/**\n * Get path to cached package references.\n * Validates name/version to prevent path traversal.\n */\nexport function getCacheDir(name: string, version: string): string {\n if (!VALID_PKG_NAME.test(name))\n throw new Error(`Invalid package name: ${name}`)\n if (!VALID_VERSION.test(version))\n throw new Error(`Invalid version: ${version}`)\n\n const dir = resolve(REFERENCES_DIR, getCacheKey(name, version))\n if (!dir.startsWith(REFERENCES_DIR))\n throw new Error(`Path traversal detected: ${dir}`)\n return dir\n}\n"],"mappings":";;;AAQA,MAAa,iBAAiB,KAAA,WAAW,aAAU;AAGnD,MAAa,YAAA,KAAiB,WAAK,QAAW;AAG9C,SAAa,gBAAiB,OAAA,MAAW;;AAGzC,QAAA,KAAgB,WAAA,OAAgB,KAAe;;;;;;;ACT/C,SAAM,cAAiB,SAAA;;;;;;;;;;AAevB,KAAA,CAAA,IAAgB,WAAY,eAAuC,CAAA,OAAA,IAAA,MAAA,4BAAA,MAAA;AACjE,QAAO"}
|
package/dist/_chunks/wizard.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { a as targets } from "./detect.mjs";
|
|
2
2
|
import { c as getOAuthProviderList, i as getAvailableModels, l as loginOAuthProvider, o as getModelName } from "./agent.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { c as updateConfig, t as defaultFeatures } from "./config.mjs";
|
|
4
|
+
import { g as pickModel, n as NO_MODELS_MESSAGE, p as isInteractive, r as OAUTH_NOTE } from "./cli-helpers.mjs";
|
|
4
5
|
import { execSync } from "node:child_process";
|
|
5
6
|
import * as p from "@clack/prompts";
|
|
6
7
|
function hasGhCli() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wizard.mjs","names":["agents"],"sources":["../../src/commands/wizard.ts"],"sourcesContent":["import type { AgentType, OptimizeModel } from '../agent/index.ts'\nimport type { FeaturesConfig } from '../core/config.ts'\nimport { execSync } from 'node:child_process'\nimport * as p from '@clack/prompts'\nimport { getOAuthProviderList, loginOAuthProvider } from '../agent/clis/pi-ai.ts'\nimport { agents, getAvailableModels, getModelName } from '../agent/index.ts'\nimport { isInteractive, NO_MODELS_MESSAGE, OAUTH_NOTE, pickModel } from '../cli-helpers.ts'\nimport { defaultFeatures, updateConfig } from '../core/config.ts'\n\nfunction hasGhCli(): boolean {\n if (process.env.SKILLD_NO_GH)\n return false\n try {\n execSync('gh --version', { stdio: 'ignore' })\n return true\n }\n catch {\n return false\n }\n}\n\nexport interface WizardOptions {\n /** Resolved target agent, if known */\n agent?: AgentType\n /** Show next-steps outro when done (default: true) */\n showOutro?: boolean\n}\n\nexport async function runWizard(opts: WizardOptions = {}): Promise<boolean> {\n if (!isInteractive())\n return false\n\n const agentLabel = opts.agent ? agents[opts.agent].displayName : null\n const skillsDir = opts.agent ? agents[opts.agent].skillsDir : '.claude/skills'\n const agentLine = agentLabel\n ? `\\n\\x1B[90mTarget agent: ${agentLabel}\\x1B[0m`\n : ''\n\n p.note(\n `Your AI agent reads docs from its training data - but APIs change,\\n`\n + `versions drift, and patterns go stale. Skilld fixes this.\\n`\n + `\\n`\n + `It generates a \\x1B[1mSKILL.md\\x1B[0m - a markdown reference card built from\\n`\n + `the \\x1B[1mactual docs, issues, and release notes\\x1B[0m for the exact\\n`\n + `package versions in your project. Your agent reads this file\\n`\n + `every session - no hallucinated APIs.\\n`\n + `\\n`\n + `\\x1B[1mHow it works:\\x1B[0m\\n`\n + ` 1. Fetch docs, issues, and types for your packages\\n`\n + ` 2. Optionally compress with an LLM into a concise cheat sheet\\n`\n + `\\n`\n + `\\x1B[90mExample: \\`skilld add vue\\` creates ${skillsDir}/vue-skilld/SKILL.md\\n`\n + `Your agent then knows the right APIs, gotchas, and patterns\\n`\n + `for your exact version.\\x1B[0m${\n agentLine}`,\n 'Welcome to skilld',\n )\n\n const ghInstalled = hasGhCli()\n\n if (ghInstalled) {\n p.log.success(\n 'GitHub CLI detected — will use it to pull issues and discussions.',\n )\n }\n else {\n p.log.info(\n '\\x1B[90mGitHub CLI not installed — issues and discussions disabled.\\n'\n + ' Install later to enable: \\x1B[36mhttps://cli.github.com\\x1B[0m',\n )\n }\n\n // Feature toggles\n const selected = await p.multiselect({\n message: 'What data sources should skills include?',\n options: [\n { label: 'Local search', value: 'search' as const, hint: 'query engine for `skilld search` across all skill docs' },\n { label: 'Release notes', value: 'releases' as const, hint: 'changelogs and migration notes per version' },\n { label: 'GitHub issues', value: 'issues' as const, hint: 'common bugs, workarounds, and solutions', disabled: !ghInstalled },\n { label: 'GitHub discussions', value: 'discussions' as const, hint: 'community Q&A and usage examples', disabled: !ghInstalled },\n ],\n initialValues: [\n ...Object.entries(defaultFeatures)\n .filter(([, v]) => v)\n .map(([k]) => k),\n ...(ghInstalled ? ['issues', 'discussions'] as const : []),\n ] as Array<keyof FeaturesConfig>,\n required: false,\n })\n\n if (p.isCancel(selected)) {\n p.cancel('Setup cancelled')\n return false\n }\n\n const features: FeaturesConfig = {\n search: selected.includes('search'),\n issues: selected.includes('issues'),\n discussions: selected.includes('discussions'),\n releases: selected.includes('releases'),\n }\n\n // Enhancement model - optional, independent of target agent\n p.note(\n 'An LLM can optionally summarize raw docs into a focused reference\\n'\n + 'highlighting best practices, gotchas, and migrations.\\n'\n + '\\n'\n + '\\x1B[1mWithout LLM:\\x1B[0m ~2 KB skill with package metadata, types, and links\\n'\n + '\\x1B[1mWith LLM:\\x1B[0m ~5 KB skill with curated gotchas, patterns, and migration notes\\n'\n + '\\n'\n + '\\x1B[1mThis is a one-time build step\\x1B[0m - it generates the SKILL.md, then your\\n'\n + 'coding agent reads the result every session. Can be a different model.\\n'\n + '\\n'\n + '\\x1B[90mWorks with API keys (ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY)\\n'\n + 'or CLI tools (claude, gemini, codex).\\x1B[0m',\n 'Enhancement model (optional)',\n )\n\n let modelId: OptimizeModel | undefined\n let skippedEnhancement = false\n let oauthJustConnected = false\n\n // Loop so user can connect OAuth then come back to pick a model\n while (true) {\n const allModels = process.env.SKILLD_NO_AGENTS ? [] : await getAvailableModels()\n\n if (allModels.length === 0) {\n p.log.warn(NO_MODELS_MESSAGE)\n }\n else if (oauthJustConnected) {\n p.log.step(`${allModels.length} models now available. Select one below.`)\n }\n else {\n // Show which providers were found by name (e.g. \"Anthropic via CLI, OpenAI via API key\")\n const providers = new Set<string>()\n for (const m of allModels) {\n const vendor = m.vendorGroup ?? m.providerName\n if (!m.id.startsWith('pi:'))\n providers.add(`${vendor} via CLI`)\n else if (m.hint?.includes('API key'))\n providers.add(`${vendor} via API key`)\n else if (m.hint?.includes('OAuth'))\n providers.add(`${vendor} via OAuth`)\n }\n if (providers.size > 0)\n p.log.success(`Found: ${[...providers].join(', ')}`)\n }\n\n const oauthProviders = getOAuthProviderList()\n const afterOptions = oauthProviders.length > 0\n ? [\n { label: '⚠ Connect OAuth provider...', value: '_connect', hint: 'may violate provider ToS' },\n { label: 'Skip enhancement', value: '_skip', hint: 'base skill with docs, issues, and types, add LLM later via `skilld config`' },\n ]\n : [\n { label: 'Skip enhancement', value: '_skip', hint: 'base skill with docs, issues, and types, add LLM later via `skilld config`' },\n ]\n\n const choice = await pickModel(allModels, {\n before: allModels.length > 0\n ? [{ label: 'Auto', value: '_auto', hint: 'picks best available model from connected providers' }]\n : [],\n after: afterOptions,\n })\n\n if (choice === null) {\n p.cancel('Setup cancelled')\n return false\n }\n\n if (choice === '_connect') {\n await wizardConnectProvider()\n oauthJustConnected = true\n continue\n }\n\n if (choice === '_skip') {\n skippedEnhancement = true\n break\n }\n if (choice === '_auto')\n break\n\n modelId = choice as OptimizeModel\n break\n }\n\n updateConfig({\n features,\n ...(modelId\n ? { model: modelId, skipLlm: false }\n : { model: undefined, skipLlm: skippedEnhancement }),\n })\n\n // Summary of what was saved\n const modelSummary = modelId\n ? getModelName(modelId)\n : skippedEnhancement\n ? 'none (raw docs)'\n : 'auto'\n const featureList = Object.entries(features).filter(([, v]) => v).map(([k]) => k).join(', ') || 'none'\n p.log.success(`Model: ${modelSummary} · Features: ${featureList}`)\n\n if (opts.showOutro !== false) {\n p.note(\n 'Run \\x1B[36mskilld add <pkg>\\x1B[0m to generate skills for specific packages\\n'\n + 'Run \\x1B[36mskilld\\x1B[0m to scan your project and pick packages interactively\\n'\n + 'Run \\x1B[36mskilld config\\x1B[0m to change settings later',\n 'Setup complete',\n )\n }\n return true\n}\n\nasync function wizardConnectProvider(): Promise<void> {\n p.note(OAUTH_NOTE, 'How OAuth works')\n\n const providers = getOAuthProviderList()\n const provider = await p.select({\n message: 'Connect provider',\n options: providers.map(pr => ({\n label: pr.name,\n value: pr.id,\n hint: pr.loggedIn ? 'connected' : undefined,\n })),\n })\n\n if (p.isCancel(provider))\n return\n\n const spinner = p.spinner()\n spinner.start('Connecting...')\n\n const success = await loginOAuthProvider(provider as string, {\n onAuth: (url, instructions) => {\n spinner.stop('Open this URL in your browser:')\n p.log.info(` \\x1B[36m${url}\\x1B[0m`)\n if (instructions)\n p.log.info(` \\x1B[90m${instructions}\\x1B[0m`)\n spinner.start('Waiting for authentication...')\n },\n onPrompt: async (message, placeholder) => {\n const value = await p.text({ message, placeholder })\n if (p.isCancel(value))\n return ''\n return value as string\n },\n onProgress: msg => p.log.step(msg),\n }).catch((err: Error) => {\n spinner.stop(`Login failed: ${err.message}`)\n return false\n })\n\n spinner.stop()\n\n if (success) {\n const name = providers.find(pr => pr.id === provider)?.name ?? provider\n p.log.success(`Connected to ${name}`)\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"wizard.mjs","names":["agents"],"sources":["../../src/commands/wizard.ts"],"sourcesContent":["import type { AgentType, OptimizeModel } from '../agent/index.ts'\nimport type { FeaturesConfig } from '../core/config.ts'\nimport { execSync } from 'node:child_process'\nimport * as p from '@clack/prompts'\nimport { getOAuthProviderList, loginOAuthProvider } from '../agent/clis/pi-ai.ts'\nimport { agents, getAvailableModels, getModelName } from '../agent/index.ts'\nimport { isInteractive, NO_MODELS_MESSAGE, OAUTH_NOTE, pickModel } from '../cli-helpers.ts'\nimport { defaultFeatures, updateConfig } from '../core/config.ts'\n\nfunction hasGhCli(): boolean {\n if (process.env.SKILLD_NO_GH)\n return false\n try {\n execSync('gh --version', { stdio: 'ignore' })\n return true\n }\n catch {\n return false\n }\n}\n\nexport interface WizardOptions {\n /** Resolved target agent, if known */\n agent?: AgentType\n /** Show next-steps outro when done (default: true) */\n showOutro?: boolean\n}\n\nexport async function runWizard(opts: WizardOptions = {}): Promise<boolean> {\n if (!isInteractive())\n return false\n\n const agentLabel = opts.agent ? agents[opts.agent].displayName : null\n const skillsDir = opts.agent ? agents[opts.agent].skillsDir : '.claude/skills'\n const agentLine = agentLabel\n ? `\\n\\x1B[90mTarget agent: ${agentLabel}\\x1B[0m`\n : ''\n\n p.note(\n `Your AI agent reads docs from its training data - but APIs change,\\n`\n + `versions drift, and patterns go stale. Skilld fixes this.\\n`\n + `\\n`\n + `It generates a \\x1B[1mSKILL.md\\x1B[0m - a markdown reference card built from\\n`\n + `the \\x1B[1mactual docs, issues, and release notes\\x1B[0m for the exact\\n`\n + `package versions in your project. Your agent reads this file\\n`\n + `every session - no hallucinated APIs.\\n`\n + `\\n`\n + `\\x1B[1mHow it works:\\x1B[0m\\n`\n + ` 1. Fetch docs, issues, and types for your packages\\n`\n + ` 2. Optionally compress with an LLM into a concise cheat sheet\\n`\n + `\\n`\n + `\\x1B[90mExample: \\`skilld add vue\\` creates ${skillsDir}/vue-skilld/SKILL.md\\n`\n + `Your agent then knows the right APIs, gotchas, and patterns\\n`\n + `for your exact version.\\x1B[0m${\n agentLine}`,\n 'Welcome to skilld',\n )\n\n const ghInstalled = hasGhCli()\n\n if (ghInstalled) {\n p.log.success(\n 'GitHub CLI detected — will use it to pull issues and discussions.',\n )\n }\n else {\n p.log.info(\n '\\x1B[90mGitHub CLI not installed — issues and discussions disabled.\\n'\n + ' Install later to enable: \\x1B[36mhttps://cli.github.com\\x1B[0m',\n )\n }\n\n // Feature toggles\n const selected = await p.multiselect({\n message: 'What data sources should skills include?',\n options: [\n { label: 'Local search', value: 'search' as const, hint: 'query engine for `skilld search` across all skill docs' },\n { label: 'Release notes', value: 'releases' as const, hint: 'changelogs and migration notes per version' },\n { label: 'GitHub issues', value: 'issues' as const, hint: 'common bugs, workarounds, and solutions', disabled: !ghInstalled },\n { label: 'GitHub discussions', value: 'discussions' as const, hint: 'community Q&A and usage examples', disabled: !ghInstalled },\n ],\n initialValues: [\n ...Object.entries(defaultFeatures)\n .filter(([, v]) => v)\n .map(([k]) => k),\n ...(ghInstalled ? ['issues', 'discussions'] as const : []),\n ] as Array<keyof FeaturesConfig>,\n required: false,\n })\n\n if (p.isCancel(selected)) {\n p.cancel('Setup cancelled')\n return false\n }\n\n const features: FeaturesConfig = {\n search: selected.includes('search'),\n issues: selected.includes('issues'),\n discussions: selected.includes('discussions'),\n releases: selected.includes('releases'),\n }\n\n // Enhancement model - optional, independent of target agent\n p.note(\n 'An LLM can optionally summarize raw docs into a focused reference\\n'\n + 'highlighting best practices, gotchas, and migrations.\\n'\n + '\\n'\n + '\\x1B[1mWithout LLM:\\x1B[0m ~2 KB skill with package metadata, types, and links\\n'\n + '\\x1B[1mWith LLM:\\x1B[0m ~5 KB skill with curated gotchas, patterns, and migration notes\\n'\n + '\\n'\n + '\\x1B[1mThis is a one-time build step\\x1B[0m - it generates the SKILL.md, then your\\n'\n + 'coding agent reads the result every session. Can be a different model.\\n'\n + '\\n'\n + '\\x1B[90mWorks with API keys (ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY)\\n'\n + 'or CLI tools (claude, gemini, codex).\\x1B[0m',\n 'Enhancement model (optional)',\n )\n\n let modelId: OptimizeModel | undefined\n let skippedEnhancement = false\n let oauthJustConnected = false\n\n // Loop so user can connect OAuth then come back to pick a model\n while (true) {\n const allModels = process.env.SKILLD_NO_AGENTS ? [] : await getAvailableModels()\n\n if (allModels.length === 0) {\n p.log.warn(NO_MODELS_MESSAGE)\n }\n else if (oauthJustConnected) {\n p.log.step(`${allModels.length} models now available. Select one below.`)\n }\n else {\n // Show which providers were found by name (e.g. \"Anthropic via CLI, OpenAI via API key\")\n const providers = new Set<string>()\n for (const m of allModels) {\n const vendor = m.vendorGroup ?? m.providerName\n if (!m.id.startsWith('pi:'))\n providers.add(`${vendor} via CLI`)\n else if (m.hint?.includes('API key'))\n providers.add(`${vendor} via API key`)\n else if (m.hint?.includes('OAuth'))\n providers.add(`${vendor} via OAuth`)\n }\n if (providers.size > 0)\n p.log.success(`Found: ${[...providers].join(', ')}`)\n }\n\n const oauthProviders = getOAuthProviderList()\n const afterOptions = oauthProviders.length > 0\n ? [\n { label: '⚠ Connect OAuth provider...', value: '_connect', hint: 'may violate provider ToS' },\n { label: 'Skip enhancement', value: '_skip', hint: 'base skill with docs, issues, and types, add LLM later via `skilld config`' },\n ]\n : [\n { label: 'Skip enhancement', value: '_skip', hint: 'base skill with docs, issues, and types, add LLM later via `skilld config`' },\n ]\n\n const choice = await pickModel(allModels, {\n before: allModels.length > 0\n ? [{ label: 'Auto', value: '_auto', hint: 'picks best available model from connected providers' }]\n : [],\n after: afterOptions,\n })\n\n if (choice === null) {\n p.cancel('Setup cancelled')\n return false\n }\n\n if (choice === '_connect') {\n await wizardConnectProvider()\n oauthJustConnected = true\n continue\n }\n\n if (choice === '_skip') {\n skippedEnhancement = true\n break\n }\n if (choice === '_auto')\n break\n\n modelId = choice as OptimizeModel\n break\n }\n\n updateConfig({\n features,\n ...(modelId\n ? { model: modelId, skipLlm: false }\n : { model: undefined, skipLlm: skippedEnhancement }),\n })\n\n // Summary of what was saved\n const modelSummary = modelId\n ? getModelName(modelId)\n : skippedEnhancement\n ? 'none (raw docs)'\n : 'auto'\n const featureList = Object.entries(features).filter(([, v]) => v).map(([k]) => k).join(', ') || 'none'\n p.log.success(`Model: ${modelSummary} · Features: ${featureList}`)\n\n if (opts.showOutro !== false) {\n p.note(\n 'Run \\x1B[36mskilld add <pkg>\\x1B[0m to generate skills for specific packages\\n'\n + 'Run \\x1B[36mskilld\\x1B[0m to scan your project and pick packages interactively\\n'\n + 'Run \\x1B[36mskilld config\\x1B[0m to change settings later',\n 'Setup complete',\n )\n }\n return true\n}\n\nasync function wizardConnectProvider(): Promise<void> {\n p.note(OAUTH_NOTE, 'How OAuth works')\n\n const providers = getOAuthProviderList()\n const provider = await p.select({\n message: 'Connect provider',\n options: providers.map(pr => ({\n label: pr.name,\n value: pr.id,\n hint: pr.loggedIn ? 'connected' : undefined,\n })),\n })\n\n if (p.isCancel(provider))\n return\n\n const spinner = p.spinner()\n spinner.start('Connecting...')\n\n const success = await loginOAuthProvider(provider as string, {\n onAuth: (url, instructions) => {\n spinner.stop('Open this URL in your browser:')\n p.log.info(` \\x1B[36m${url}\\x1B[0m`)\n if (instructions)\n p.log.info(` \\x1B[90m${instructions}\\x1B[0m`)\n spinner.start('Waiting for authentication...')\n },\n onPrompt: async (message, placeholder) => {\n const value = await p.text({ message, placeholder })\n if (p.isCancel(value))\n return ''\n return value as string\n },\n onProgress: msg => p.log.step(msg),\n }).catch((err: Error) => {\n spinner.stop(`Login failed: ${err.message}`)\n return false\n })\n\n spinner.stop()\n\n if (success) {\n const name = providers.find(pr => pr.id === provider)?.name ?? provider\n p.log.success(`Connected to ${name}`)\n }\n}\n"],"mappings":"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"}
|
package/dist/agent/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { a as targets, i as getAgentVersion, n as detectProjectAgents, r as detectTargetAgent, t as detectInstalledAgents } from "../_chunks/detect.mjs";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as buildSectionPrompt, c as portabilizePrompt, i as buildAllSectionPrompts, l as wrapSection, n as SECTION_MERGE_ORDER, o as extractMarkedSections, r as SECTION_OUTPUT_FILES, s as getSectionValidator, t as generateSkillMd } from "../_chunks/prompts.mjs";
|
|
3
|
+
import { a as unlinkSkillFromAgents, i as sanitizeName, n as installSkillForAgents, r as linkSkillToAgents, t as computeSkillDirName } from "../_chunks/install.mjs";
|
|
3
4
|
import { a as getModelLabel, i as getAvailableModels, o as getModelName, r as createToolProgress, s as optimizeDocs, t as detectImportedPackages } from "../_chunks/agent.mjs";
|
|
4
5
|
export { SECTION_MERGE_ORDER, SECTION_OUTPUT_FILES, targets as agents, buildAllSectionPrompts, buildSectionPrompt, computeSkillDirName, createToolProgress, detectImportedPackages, detectInstalledAgents, detectProjectAgents, detectTargetAgent, extractMarkedSections, generateSkillMd, getAgentVersion, getAvailableModels, getModelLabel, getModelName, getSectionValidator, installSkillForAgents, linkSkillToAgents, optimizeDocs, portabilizePrompt, sanitizeName, unlinkSkillFromAgents, wrapSection };
|
package/dist/cache/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as REFERENCES_DIR, c as getRepoCacheDir, i as CACHE_DIR, n as getCacheKey, o as REPOS_DIR, r as getVersionKey, s as getPackageDbPath, t as getCacheDir } from "../_chunks/version.mjs";
|
|
2
2
|
import { n as linkShippedSkill, r as resolvePkgDir, t as getShippedSkills } from "../_chunks/prepare.mjs";
|
|
3
3
|
import { _ as writeToRepoCache, a as hasShippedDocs, c as linkPkg, d as listCached, f as listReferenceFiles, g as writeToCache, h as writeSections, i as getPkgKeyFiles, l as linkPkgNamed, m as readCachedSection, n as clearCache, o as isCached, p as readCachedDocs, r as ensureCacheDir, s as linkCachedDir, t as clearAllCache, u as linkRepoCachedDir } from "../_chunks/cache.mjs";
|
|
4
4
|
export { CACHE_DIR, REFERENCES_DIR, REPOS_DIR, clearAllCache, clearCache, ensureCacheDir, getCacheDir, getCacheKey, getPackageDbPath, getPkgKeyFiles, getRepoCacheDir, getShippedSkills, getVersionKey, hasShippedDocs, isCached, linkCachedDir, linkPkg, linkPkgNamed, linkRepoCachedDir, linkShippedSkill, listCached, listReferenceFiles, readCachedDocs, readCachedSection, resolvePkgDir, writeSections, writeToCache, writeToRepoCache };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as
|
|
2
|
+
import { i as CACHE_DIR, s as getPackageDbPath, t as getCacheDir } from "./_chunks/version.mjs";
|
|
3
3
|
import { i as readPackageJsonSafe } from "./_chunks/package-json.mjs";
|
|
4
4
|
import "./_chunks/cache.mjs";
|
|
5
|
-
import { n as getSharedSkillsDir, o as semverGt, r as mapInsert } from "./_chunks/shared.mjs";
|
|
6
5
|
import { r as fetchNpmRegistryMeta, t as fetchLatestVersion } from "./_chunks/sources.mjs";
|
|
6
|
+
import { n as getSharedSkillsDir, o as semverGt, r as mapInsert } from "./_chunks/shared.mjs";
|
|
7
7
|
import { a as targets, i as getAgentVersion, r as detectTargetAgent, t as detectInstalledAgents } from "./_chunks/detect.mjs";
|
|
8
|
-
import {
|
|
8
|
+
import { a as unlinkSkillFromAgents } from "./_chunks/install.mjs";
|
|
9
9
|
import { c as getOAuthProviderList, i as getAvailableModels, l as loginOAuthProvider, o as getModelName, t as detectImportedPackages, u as logoutOAuthProvider } from "./_chunks/agent.mjs";
|
|
10
|
-
import {
|
|
10
|
+
import { a as readConfig, c as updateConfig, i as hasConfig, r as hasCompletedWizard, t as defaultFeatures } from "./_chunks/config.mjs";
|
|
11
|
+
import { C as version, S as suggestPrepareHook, _ as promptForAgent, a as formatStatus, b as resolveAgent, c as getRepoHint, d as hasPrepareHook, f as introLine, g as pickModel, h as menuLoop, m as isRunningInsideAgent, n as NO_MODELS_MESSAGE, o as getInstalledGenerators, p as isInteractive, r as OAUTH_NOTE, u as guard, v as relativeTime, x as sharedArgs, y as requireInteractive } from "./_chunks/cli-helpers.mjs";
|
|
11
12
|
import { a as removeLockEntry, n as parsePackages } from "./_chunks/lockfile.mjs";
|
|
12
13
|
import { i as iterateSkills, n as getSkillsDir, r as isOutdated, t as getProjectState } from "./_chunks/skills.mjs";
|
|
14
|
+
import { n as resolveSkillName, r as toStoragePackageName } from "./_chunks/prefix.mjs";
|
|
13
15
|
import { c as timeAgo, i as formatSource, l as timedSpinner } from "./_chunks/formatting.mjs";
|
|
14
16
|
import { t as runWizard } from "./_chunks/wizard.mjs";
|
|
15
17
|
import "./_chunks/core.mjs";
|
|
@@ -18,6 +20,7 @@ import { existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync }
|
|
|
18
20
|
import pLimit from "p-limit";
|
|
19
21
|
import * as p from "@clack/prompts";
|
|
20
22
|
import { defineCommand, runMain } from "citty";
|
|
23
|
+
import logUpdate from "log-update";
|
|
21
24
|
async function configCommand() {
|
|
22
25
|
const initConfig = readConfig();
|
|
23
26
|
const agentId = initConfig.agent || detectTargetAgent() || void 0;
|
|
@@ -339,7 +342,14 @@ const removeCommandDef = defineCommand({
|
|
|
339
342
|
};
|
|
340
343
|
p.intro(`${introLine(intro)} · remove (${scope})`);
|
|
341
344
|
return removeCommand(state, {
|
|
342
|
-
packages: args.package ? [...new Set([args.package, ...args._ || []].map((s) => s.trim()).filter(Boolean))
|
|
345
|
+
packages: args.package ? [...new Set([args.package, ...args._ || []].map((s) => s.trim()).filter(Boolean).map((s) => {
|
|
346
|
+
const name = resolveSkillName(s);
|
|
347
|
+
if (!name) {
|
|
348
|
+
p.log.warn(`Cannot remove \x1B[36m${s}\x1B[0m: curator/collection inputs are not addressable here.`);
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
return name;
|
|
352
|
+
}).filter((s) => s !== null))] : void 0,
|
|
343
353
|
global: args.global,
|
|
344
354
|
agent,
|
|
345
355
|
yes: args.yes
|
|
@@ -472,7 +482,7 @@ async function statusCommand(opts = {}) {
|
|
|
472
482
|
if (source && source !== "shipped") parts.push(dim(source));
|
|
473
483
|
lines.push(parts.join(" "));
|
|
474
484
|
const meta = [];
|
|
475
|
-
const pkgName = info.packageName || pkg.name;
|
|
485
|
+
const pkgName = toStoragePackageName(info.packageName || pkg.name);
|
|
476
486
|
const docs = countDocs(pkgName, info.version) || countRefDocs(join(pkg.scope === "global" ? targets[pkg.agents.values().next().value].globalSkillsDir : join(process.cwd(), targets[pkg.agents.values().next().value].skillsDir), pkg.name));
|
|
477
487
|
if (docs > 0) meta.push(`${docs} docs`);
|
|
478
488
|
const embeddings = await countEmbeddings(pkgName, info.version);
|
|
@@ -512,8 +522,6 @@ const infoCommandDef = defineCommand({
|
|
|
512
522
|
return statusCommand({ global: args.global });
|
|
513
523
|
}
|
|
514
524
|
});
|
|
515
|
-
const _emit = process.emit;
|
|
516
|
-
process.emit = (event, ...args) => event === "warning" && args[0]?.name === "ExperimentalWarning" && args[0]?.message?.includes("SQLite") ? false : _emit.apply(process, [event, ...args]);
|
|
517
525
|
const NOISE_CHARS = "⣿⡿⣷⣾⣽⣻⢿⡷⣯⣟⡾⣵⣳⢾⡽⣞⡷⣝⢯";
|
|
518
526
|
function djb2(s) {
|
|
519
527
|
let h = 5381;
|
|
@@ -549,6 +557,7 @@ function noiseLine(len, brightnessFn, density = 0) {
|
|
|
549
557
|
for (let i = 0; i < len; i++) s += noiseChar(brightnessFn(i), density);
|
|
550
558
|
return `${s}\x1B[0m`;
|
|
551
559
|
}
|
|
560
|
+
const BRAND_MARK = "\x1B[38;2;251;113;133m⏶\x1B[0m";
|
|
552
561
|
function brandFrame(t, floor = 0, density = 0) {
|
|
553
562
|
const cx = 5;
|
|
554
563
|
const cy = 1;
|
|
@@ -567,18 +576,17 @@ function brandFrame(t, floor = 0, density = 0) {
|
|
|
567
576
|
};
|
|
568
577
|
return [
|
|
569
578
|
noiseLine(10, (x) => brightness(x, 0), density),
|
|
570
|
-
`${noiseLine(2, (x) => brightness(x, 1), density)} %NAME% ${noiseLine(2, (x) => brightness(x + 8, 1), density)} %VER%`,
|
|
579
|
+
`${noiseLine(2, (x) => brightness(x, 1), density)} ${BRAND_MARK} %NAME% ${noiseLine(2, (x) => brightness(x + 8, 1), density)} %VER%`,
|
|
571
580
|
noiseLine(10, (x) => brightness(x, 2), density)
|
|
572
581
|
].join("\n");
|
|
573
582
|
}
|
|
574
583
|
async function brandLoader(work, minMs = 1500) {
|
|
575
584
|
if (process.env.SKILLD_EFFECT === "none") return work();
|
|
576
|
-
const logUpdate = (await import("log-update")).default;
|
|
577
585
|
const name = "\x1B[1m\x1B[38;2;255;255;255mskilld\x1B[0m";
|
|
578
|
-
const
|
|
586
|
+
const verStr = `\x1B[2mv${version}\x1B[0m`;
|
|
579
587
|
const status = "\x1B[2mSetting up your environment\x1B[0m";
|
|
580
588
|
const start = Date.now();
|
|
581
|
-
const sub = (raw) => raw.replace("%NAME%", name).replace("%VER%",
|
|
589
|
+
const sub = (raw) => raw.replace("%NAME%", name).replace("%VER%", verStr);
|
|
582
590
|
let done = false;
|
|
583
591
|
const result = Promise.all([work(), new Promise((r) => setTimeout(r, minMs))]).then(([v]) => {
|
|
584
592
|
done = true;
|
|
@@ -601,6 +609,24 @@ async function brandLoader(work, minMs = 1500) {
|
|
|
601
609
|
logUpdate.done();
|
|
602
610
|
return result;
|
|
603
611
|
}
|
|
612
|
+
const _emit = process.emit;
|
|
613
|
+
process.emit = (event, ...args) => event === "warning" && args[0]?.name === "ExperimentalWarning" && args[0]?.message?.includes("SQLite") ? false : _emit.apply(process, [event, ...args]);
|
|
614
|
+
function deprecatedForwarder(oldName, newName, loader) {
|
|
615
|
+
return () => loader().then((cmd) => {
|
|
616
|
+
const original = cmd.run;
|
|
617
|
+
return defineCommand({
|
|
618
|
+
...cmd,
|
|
619
|
+
meta: {
|
|
620
|
+
...cmd.meta,
|
|
621
|
+
name: oldName
|
|
622
|
+
},
|
|
623
|
+
async run(ctx) {
|
|
624
|
+
console.warn(`\x1B[33m⚠ \`skilld ${oldName}\` is deprecated. Use \`skilld ${newName}\` instead.\x1B[0m`);
|
|
625
|
+
return original(ctx);
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
}
|
|
604
630
|
const SUBCOMMAND_NAMES = [
|
|
605
631
|
"add",
|
|
606
632
|
"eject",
|
|
@@ -618,33 +644,35 @@ const SUBCOMMAND_NAMES = [
|
|
|
618
644
|
"setup",
|
|
619
645
|
"prepare",
|
|
620
646
|
"author",
|
|
621
|
-
"publish"
|
|
647
|
+
"publish",
|
|
648
|
+
"upload"
|
|
622
649
|
];
|
|
623
650
|
runMain(defineCommand({
|
|
624
651
|
meta: {
|
|
625
652
|
name: "skilld",
|
|
626
653
|
version,
|
|
627
|
-
description: "
|
|
654
|
+
description: "Curated agent skills for your projects"
|
|
628
655
|
},
|
|
629
656
|
args: { agent: sharedArgs.agent },
|
|
630
657
|
subCommands: {
|
|
631
658
|
add: () => import("./_chunks/sync2.mjs").then((m) => m.addCommandDef),
|
|
632
|
-
eject: () => import("./_chunks/sync2.mjs").then((m) => m.ejectCommandDef),
|
|
633
659
|
update: () => import("./_chunks/sync2.mjs").then((m) => m.updateCommandDef),
|
|
634
660
|
info: () => infoCommandDef,
|
|
635
661
|
list: () => import("./_chunks/list.mjs").then((m) => m.listCommandDef),
|
|
636
662
|
config: () => configCommandDef,
|
|
637
663
|
remove: () => removeCommandDef,
|
|
638
|
-
install: () => import("./_chunks/
|
|
664
|
+
install: () => import("./_chunks/install2.mjs").then((m) => m.installCommandDef),
|
|
639
665
|
prepare: () => import("./_chunks/prepare2.mjs").then((m) => m.prepareCommandDef),
|
|
640
666
|
uninstall: () => import("./_chunks/uninstall.mjs").then((m) => m.uninstallCommandDef),
|
|
641
667
|
search: () => import("./_chunks/search.mjs").then((m) => m.searchCommandDef),
|
|
642
668
|
cache: () => import("./_chunks/cache2.mjs").then((m) => m.cacheCommandDef),
|
|
643
|
-
validate: () => import("./_chunks/validate.mjs").then((m) => m.validateCommandDef),
|
|
644
|
-
assemble: () => import("./_chunks/assemble.mjs").then((m) => m.assembleCommandDef),
|
|
645
669
|
setup: () => import("./_chunks/setup.mjs").then((m) => m.setupCommandDef),
|
|
646
|
-
author: () => import("./_chunks/author.mjs").then((m) => m.
|
|
647
|
-
|
|
670
|
+
author: () => import("./_chunks/author-group.mjs").then((m) => m.authorGroupDef),
|
|
671
|
+
eject: deprecatedForwarder("eject", "author eject", () => import("./_chunks/sync2.mjs").then((m) => m.ejectCommandDef)),
|
|
672
|
+
validate: deprecatedForwarder("validate", "author validate", () => import("./_chunks/validate.mjs").then((m) => m.validateCommandDef)),
|
|
673
|
+
assemble: deprecatedForwarder("assemble", "author assemble", () => import("./_chunks/assemble.mjs").then((m) => m.assembleCommandDef)),
|
|
674
|
+
publish: deprecatedForwarder("publish", "author publish", () => import("./_chunks/upload.mjs").then((m) => m.uploadCommandDef)),
|
|
675
|
+
upload: deprecatedForwarder("upload", "author publish", () => import("./_chunks/upload.mjs").then((m) => m.uploadCommandDef))
|
|
648
676
|
},
|
|
649
677
|
async run({ args }) {
|
|
650
678
|
const firstArg = process.argv[2];
|