skilld 1.7.3 → 2.0.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.
Files changed (161) hide show
  1. package/dist/_chunks/add.mjs +66 -0
  2. package/dist/_chunks/add.mjs.map +1 -0
  3. package/dist/_chunks/agent-prompt.mjs +88 -0
  4. package/dist/_chunks/agent-prompt.mjs.map +1 -0
  5. package/dist/_chunks/agent.mjs +737 -619
  6. package/dist/_chunks/agent.mjs.map +1 -1
  7. package/dist/_chunks/args.mjs +42 -0
  8. package/dist/_chunks/args.mjs.map +1 -0
  9. package/dist/_chunks/assemble.mjs +11 -8
  10. package/dist/_chunks/assemble.mjs.map +1 -1
  11. package/dist/_chunks/author.mjs +77 -131
  12. package/dist/_chunks/author.mjs.map +1 -1
  13. package/dist/_chunks/cache.mjs +320 -54
  14. package/dist/_chunks/cache.mjs.map +1 -1
  15. package/dist/_chunks/cache2.mjs +7 -6
  16. package/dist/_chunks/cache2.mjs.map +1 -1
  17. package/dist/_chunks/client.mjs +117 -0
  18. package/dist/_chunks/client.mjs.map +1 -0
  19. package/dist/_chunks/core.mjs +7 -4
  20. package/dist/_chunks/detect.mjs +54 -44
  21. package/dist/_chunks/detect.mjs.map +1 -1
  22. package/dist/_chunks/eject.mjs +69 -0
  23. package/dist/_chunks/eject.mjs.map +1 -0
  24. package/dist/_chunks/embedding-cache2.mjs +2 -2
  25. package/dist/_chunks/env.mjs +19 -0
  26. package/dist/_chunks/env.mjs.map +1 -0
  27. package/dist/_chunks/install-many.mjs +376 -0
  28. package/dist/_chunks/install-many.mjs.map +1 -0
  29. package/dist/_chunks/install.mjs +86 -371
  30. package/dist/_chunks/install.mjs.map +1 -1
  31. package/dist/_chunks/intro.mjs +63 -0
  32. package/dist/_chunks/intro.mjs.map +1 -0
  33. package/dist/_chunks/list.mjs +2 -2
  34. package/dist/_chunks/list.mjs.map +1 -1
  35. package/dist/_chunks/lockfile.mjs +31 -7
  36. package/dist/_chunks/lockfile.mjs.map +1 -1
  37. package/dist/_chunks/login.mjs +233 -0
  38. package/dist/_chunks/login.mjs.map +1 -0
  39. package/dist/_chunks/logout.mjs +27 -0
  40. package/dist/_chunks/logout.mjs.map +1 -0
  41. package/dist/_chunks/map.mjs +11 -0
  42. package/dist/_chunks/map.mjs.map +1 -0
  43. package/dist/_chunks/markdown.mjs +79 -54
  44. package/dist/_chunks/markdown.mjs.map +1 -1
  45. package/dist/_chunks/menu.mjs +33 -0
  46. package/dist/_chunks/menu.mjs.map +1 -0
  47. package/dist/_chunks/model-picker.mjs +61 -0
  48. package/dist/_chunks/model-picker.mjs.map +1 -0
  49. package/dist/_chunks/monorepo.mjs +73 -0
  50. package/dist/_chunks/monorepo.mjs.map +1 -0
  51. package/dist/_chunks/package-json.mjs.map +1 -1
  52. package/dist/_chunks/paths.mjs +47 -0
  53. package/dist/_chunks/paths.mjs.map +1 -0
  54. package/dist/_chunks/pipeline.mjs +985 -0
  55. package/dist/_chunks/pipeline.mjs.map +1 -0
  56. package/dist/_chunks/pool2.mjs +2 -2
  57. package/dist/_chunks/portable.mjs +151 -0
  58. package/dist/_chunks/portable.mjs.map +1 -0
  59. package/dist/_chunks/prepare-hook.mjs +2 -0
  60. package/dist/_chunks/prepare-hook2.mjs +61 -0
  61. package/dist/_chunks/prepare-hook2.mjs.map +1 -0
  62. package/dist/_chunks/prepare.mjs +47 -3
  63. package/dist/_chunks/prepare.mjs.map +1 -1
  64. package/dist/_chunks/prepare2.mjs +9 -8
  65. package/dist/_chunks/prepare2.mjs.map +1 -1
  66. package/dist/_chunks/prompts.mjs +784 -26
  67. package/dist/_chunks/prompts.mjs.map +1 -1
  68. package/dist/_chunks/pull.mjs +219 -0
  69. package/dist/_chunks/pull.mjs.map +1 -0
  70. package/dist/_chunks/regex.mjs +19 -0
  71. package/dist/_chunks/regex.mjs.map +1 -0
  72. package/dist/_chunks/retriv.mjs +2 -171
  73. package/dist/_chunks/retriv2.mjs +159 -0
  74. package/dist/_chunks/retriv2.mjs.map +1 -0
  75. package/dist/_chunks/sanitize.mjs +12 -9
  76. package/dist/_chunks/sanitize.mjs.map +1 -1
  77. package/dist/_chunks/search-helpers.mjs +9 -8
  78. package/dist/_chunks/search-helpers.mjs.map +1 -1
  79. package/dist/_chunks/search-interactive.mjs +23 -20
  80. package/dist/_chunks/search-interactive.mjs.map +1 -1
  81. package/dist/_chunks/search.mjs +3 -4
  82. package/dist/_chunks/search.mjs.map +1 -1
  83. package/dist/_chunks/{sources.mjs → semver.mjs} +1128 -838
  84. package/dist/_chunks/semver.mjs.map +1 -0
  85. package/dist/_chunks/skill-installer.mjs +2 -0
  86. package/dist/_chunks/skill-installer2.mjs +154 -0
  87. package/dist/_chunks/skill-installer2.mjs.map +1 -0
  88. package/dist/_chunks/skills.mjs +12 -12
  89. package/dist/_chunks/skills.mjs.map +1 -1
  90. package/dist/_chunks/store.mjs +107 -0
  91. package/dist/_chunks/store.mjs.map +1 -0
  92. package/dist/_chunks/sync.mjs +761 -1349
  93. package/dist/_chunks/sync.mjs.map +1 -1
  94. package/dist/_chunks/sync2.mjs +2 -3
  95. package/dist/_chunks/telemetry.mjs +26 -0
  96. package/dist/_chunks/telemetry.mjs.map +1 -0
  97. package/dist/_chunks/uninstall.mjs +15 -13
  98. package/dist/_chunks/uninstall.mjs.map +1 -1
  99. package/dist/_chunks/update.mjs +171 -0
  100. package/dist/_chunks/update.mjs.map +1 -0
  101. package/dist/_chunks/upload.mjs +4 -4
  102. package/dist/_chunks/validate.mjs +1 -1
  103. package/dist/_chunks/version.mjs +16 -27
  104. package/dist/_chunks/version.mjs.map +1 -1
  105. package/dist/_chunks/whoami.mjs +21 -0
  106. package/dist/_chunks/whoami.mjs.map +1 -0
  107. package/dist/_chunks/wizard.mjs +2 -190
  108. package/dist/_chunks/wizard2.mjs +200 -0
  109. package/dist/_chunks/wizard2.mjs.map +1 -0
  110. package/dist/cli.mjs +77 -59
  111. package/dist/cli.mjs.map +1 -1
  112. package/dist/prepare.mjs +5 -4
  113. package/dist/prepare.mjs.map +1 -1
  114. package/dist/retriv/worker.d.mts +5 -1
  115. package/dist/retriv/worker.d.mts.map +1 -1
  116. package/dist/retriv/worker.mjs +1 -1
  117. package/package.json +20 -29
  118. package/dist/_chunks/author-group.mjs +0 -17
  119. package/dist/_chunks/author-group.mjs.map +0 -1
  120. package/dist/_chunks/cli-helpers.mjs +0 -335
  121. package/dist/_chunks/cli-helpers.mjs.map +0 -1
  122. package/dist/_chunks/cli-helpers2.mjs +0 -2
  123. package/dist/_chunks/config.mjs +0 -122
  124. package/dist/_chunks/config.mjs.map +0 -1
  125. package/dist/_chunks/index.d.mts +0 -151
  126. package/dist/_chunks/index.d.mts.map +0 -1
  127. package/dist/_chunks/index2.d.mts +0 -44
  128. package/dist/_chunks/index2.d.mts.map +0 -1
  129. package/dist/_chunks/index3.d.mts +0 -589
  130. package/dist/_chunks/index3.d.mts.map +0 -1
  131. package/dist/_chunks/prefix.mjs +0 -108
  132. package/dist/_chunks/prefix.mjs.map +0 -1
  133. package/dist/_chunks/retriv.mjs.map +0 -1
  134. package/dist/_chunks/setup.mjs +0 -17
  135. package/dist/_chunks/setup.mjs.map +0 -1
  136. package/dist/_chunks/shared.mjs +0 -503
  137. package/dist/_chunks/shared.mjs.map +0 -1
  138. package/dist/_chunks/skill.mjs +0 -329
  139. package/dist/_chunks/skill.mjs.map +0 -1
  140. package/dist/_chunks/sources.mjs.map +0 -1
  141. package/dist/_chunks/sync-registry.mjs +0 -59
  142. package/dist/_chunks/sync-registry.mjs.map +0 -1
  143. package/dist/_chunks/sync-shared.mjs +0 -2
  144. package/dist/_chunks/sync-shared2.mjs +0 -1020
  145. package/dist/_chunks/sync-shared2.mjs.map +0 -1
  146. package/dist/_chunks/types.d.mts +0 -88
  147. package/dist/_chunks/types.d.mts.map +0 -1
  148. package/dist/_chunks/wizard.mjs.map +0 -1
  149. package/dist/agent/index.d.mts +0 -346
  150. package/dist/agent/index.d.mts.map +0 -1
  151. package/dist/agent/index.mjs +0 -5
  152. package/dist/cache/index.d.mts +0 -2
  153. package/dist/cache/index.mjs +0 -4
  154. package/dist/index.d.mts +0 -5
  155. package/dist/index.mjs +0 -5
  156. package/dist/retriv/index.d.mts +0 -3
  157. package/dist/retriv/index.mjs +0 -2
  158. package/dist/sources/index.d.mts +0 -2
  159. package/dist/sources/index.mjs +0 -3
  160. package/dist/types.d.mts +0 -4
  161. package/dist/types.mjs +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 { styleText } from 'node:util'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { join } from 'pathe'\nimport { agents } from '../agent/index.ts'\nimport { SKILLD_MARKER_END, SKILLD_MARKER_START } from '../agent/skill-installer.ts'\nimport { CACHE_DIR } from '../cache/index.ts'\nimport { sharedArgs } from '../cli/args.ts'\nimport { isInteractive } from '../cli/env.ts'\nimport { getRegisteredProjects, unregisterProject } from '../core/config.ts'\nimport { readLock } from '../core/lockfile.ts'\nimport { mapInsert } from '../core/map.ts'\nimport { SHARED_SKILLS_DIR } from '../core/paths.ts'\n\nconst STATIC_REGEX_1 = /\\n+$/\nconst STATIC_REGEX_2 = /^\\n+/\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(STATIC_REGEX_1, '')\n const after = content.slice(endIdx + SKILLD_MARKER_END.length).replace(STATIC_REGEX_2, '')\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(`${styleText(['bold', 'magenta'], 'skilld')} 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,171 @@
1
+ import { n as COMMA_OR_WHITESPACE_RE } from "./regex.mjs";
2
+ import { d as readConfig } from "./cache.mjs";
3
+ import { t as isInteractive } from "./env.mjs";
4
+ import { t as autoResolveAgent } from "./agent-prompt.mjs";
5
+ import { t as sharedArgs } from "./args.mjs";
6
+ import { i as introLine, n as getInstalledGenerators } from "./intro.mjs";
7
+ import { c as resolveSkillName } from "./semver.mjs";
8
+ import { t as getProjectState } from "./skills.mjs";
9
+ import { a as updateMarker, n as loadSession, r as peekMarker } from "./store.mjs";
10
+ import { t as createRegistryClient } from "./client.mjs";
11
+ import { t as syncCommand } from "./sync.mjs";
12
+ import { t as exportPortablePrompts } from "./portable.mjs";
13
+ import { styleText } from "node:util";
14
+ import * as p from "@clack/prompts";
15
+ import { defineCommand } from "citty";
16
+ const RELATIVE_FORMATTER = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
17
+ function relative(iso, now = Date.now()) {
18
+ const minutes = (new Date(iso).getTime() - now) / 1e3 / 60;
19
+ if (Math.abs(minutes) < 60) return RELATIVE_FORMATTER.format(Math.round(minutes), "minute");
20
+ const hours = minutes / 60;
21
+ if (Math.abs(hours) < 24) return RELATIVE_FORMATTER.format(Math.round(hours), "hour");
22
+ return RELATIVE_FORMATTER.format(Math.round(hours / 24), "day");
23
+ }
24
+ function renderDigest(entries) {
25
+ if (entries.length === 0) {
26
+ p.log.success("No new updates since last digest.");
27
+ return;
28
+ }
29
+ const byRepo = /* @__PURE__ */ new Map();
30
+ for (const entry of entries) {
31
+ const list = byRepo.get(entry.repo) ?? [];
32
+ list.push(entry);
33
+ byRepo.set(entry.repo, list);
34
+ }
35
+ const lines = [];
36
+ for (const [repo, items] of byRepo) {
37
+ lines.push(styleText("cyan", repo));
38
+ for (const item of items) {
39
+ const when = styleText("gray", relative(item.at));
40
+ lines.push(` ${styleText("green", "•")} ${item.skill} ${when}`);
41
+ if (item.summary) lines.push(` ${styleText("gray", item.summary)}`);
42
+ }
43
+ }
44
+ lines.push("");
45
+ lines.push(styleText("gray", "See full activity at https://skilld.dev/me/activity"));
46
+ p.log.message(lines.join("\n"));
47
+ }
48
+ async function renderChangesDigest() {
49
+ const session = await loadSession();
50
+ if (!session || session.scheme === "env") return;
51
+ const marker = peekMarker();
52
+ const digest = await createRegistryClient({ session }).my.changes({ since: marker?.lastDigestAt }).catch(() => null);
53
+ if (!digest || digest.entries.length === 0) return;
54
+ renderDigest(digest.entries);
55
+ updateMarker({ lastDigestAt: digest.windowEnd });
56
+ }
57
+ const updateCommandDef = defineCommand({
58
+ meta: {
59
+ name: "update",
60
+ description: "Update outdated skills"
61
+ },
62
+ args: {
63
+ package: {
64
+ type: "positional",
65
+ description: "Package(s) to update (space or comma-separated). Without args, syncs all outdated.",
66
+ required: false
67
+ },
68
+ background: {
69
+ type: "boolean",
70
+ alias: "b",
71
+ description: "Run in background (detached process, non-interactive)",
72
+ default: false
73
+ },
74
+ ...sharedArgs
75
+ },
76
+ async run({ args }) {
77
+ const cwd = process.cwd();
78
+ if (args.background) {
79
+ const { spawn } = await import("node:child_process");
80
+ const updateArgs = [
81
+ "update",
82
+ ...args.package ? [args.package] : [],
83
+ ...args.agent ? ["--agent", args.agent] : [],
84
+ ...args.model ? ["--model", args.model] : []
85
+ ];
86
+ spawn(process.execPath, [process.argv[1], ...updateArgs], {
87
+ cwd,
88
+ detached: true,
89
+ stdio: "ignore"
90
+ }).unref();
91
+ return;
92
+ }
93
+ const silent = !isInteractive();
94
+ let agent;
95
+ if (args.agent === "none") agent = "none";
96
+ else {
97
+ agent = autoResolveAgent(args.agent);
98
+ if (!agent) {
99
+ p.log.error("No target agent detected.\n Pass --agent <name> (claude-code, cursor, codex, …) or run `skilld config` to set a default.");
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+ }
104
+ if (agent === "none") {
105
+ const state = await getProjectState(cwd);
106
+ const packages = args.package ? Array.from(new Set([args.package, ...args._ || []].flatMap((s) => s.split(COMMA_OR_WHITESPACE_RE)).map((s) => s.trim()).filter(Boolean)), (s) => resolveSkillName(s)).filter((s) => s !== null) : state.outdated.map((s) => s.packageName || s.name);
107
+ if (packages.length === 0) {
108
+ if (!silent) p.log.success("All skills up to date");
109
+ return;
110
+ }
111
+ for (const pkg of packages) await exportPortablePrompts(pkg, {
112
+ force: args.force,
113
+ agent: "none"
114
+ });
115
+ return;
116
+ }
117
+ const config = readConfig();
118
+ const state = await getProjectState(cwd);
119
+ if (!silent) {
120
+ const generators = getInstalledGenerators();
121
+ p.intro(introLine({
122
+ state,
123
+ generators,
124
+ modelId: config.model,
125
+ agentId: config.agent || agent || void 0
126
+ }));
127
+ }
128
+ if (args.package) {
129
+ const raw = [...new Set([args.package, ...args._ || []].flatMap((s) => s.split(COMMA_OR_WHITESPACE_RE)).map((s) => s.trim()).filter(Boolean))];
130
+ const packages = [];
131
+ for (const r of raw) {
132
+ const name = resolveSkillName(r);
133
+ if (!name) {
134
+ p.log.warn(`Cannot update ${styleText("cyan", r)}: curator/collection inputs are not addressable here.`);
135
+ continue;
136
+ }
137
+ packages.push(name);
138
+ }
139
+ if (packages.length === 0) return;
140
+ return syncCommand(state, {
141
+ packages,
142
+ global: args.global,
143
+ agent,
144
+ model: args.model || (silent ? config.model : void 0),
145
+ yes: args.yes || silent,
146
+ force: args.force,
147
+ debug: args.debug,
148
+ mode: "update"
149
+ });
150
+ }
151
+ const crateSpecs = state.skills.map((s) => s.info?.packageName).filter((name) => !!name && name.startsWith("crate:"));
152
+ if (state.outdated.length === 0 && crateSpecs.length === 0) {
153
+ p.log.success("All skills up to date");
154
+ return;
155
+ }
156
+ await syncCommand(state, {
157
+ packages: [...state.outdated.map((s) => s.packageName || s.name), ...crateSpecs],
158
+ global: args.global,
159
+ agent,
160
+ model: args.model || (silent ? config.model : void 0),
161
+ yes: args.yes || silent,
162
+ force: args.force,
163
+ debug: args.debug,
164
+ mode: "update"
165
+ });
166
+ if (!silent) await renderChangesDigest();
167
+ }
168
+ });
169
+ export { updateCommandDef };
170
+
171
+ //# sourceMappingURL=update.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.mjs","names":[],"sources":["../../src/cli/digest-render.ts","../../src/commands/sync/update.ts"],"sourcesContent":["/**\n * Render the in-terminal change digest from `/api/cli/changes`.\n *\n * Groups entries by repo, shows the skill name + AI summary if present, and\n * prints a link back to the web view. Format mirrors the email digest minus\n * the HTML wrapper.\n */\n\nimport type { ChangeEntry } from '../registry/client.ts'\nimport { styleText } from 'node:util'\nimport * as p from '@clack/prompts'\n\nconst RELATIVE_FORMATTER = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })\n\nfunction relative(iso: string, now = Date.now()): string {\n const delta = (new Date(iso).getTime() - now) / 1000\n const minutes = delta / 60\n if (Math.abs(minutes) < 60)\n return RELATIVE_FORMATTER.format(Math.round(minutes), 'minute')\n const hours = minutes / 60\n if (Math.abs(hours) < 24)\n return RELATIVE_FORMATTER.format(Math.round(hours), 'hour')\n return RELATIVE_FORMATTER.format(Math.round(hours / 24), 'day')\n}\n\nexport function renderDigest(entries: ChangeEntry[]): void {\n if (entries.length === 0) {\n p.log.success('No new updates since last digest.')\n return\n }\n\n const byRepo = new Map<string, ChangeEntry[]>()\n for (const entry of entries) {\n const list = byRepo.get(entry.repo) ?? []\n list.push(entry)\n byRepo.set(entry.repo, list)\n }\n\n const lines: string[] = []\n for (const [repo, items] of byRepo) {\n lines.push(styleText('cyan', repo))\n for (const item of items) {\n const when = styleText('gray', relative(item.at))\n lines.push(` ${styleText('green', '•')} ${item.skill} ${when}`)\n if (item.summary)\n lines.push(` ${styleText('gray', item.summary)}`)\n }\n }\n lines.push('')\n lines.push(styleText('gray', 'See full activity at https://skilld.dev/me/activity'))\n\n p.log.message(lines.join('\\n'))\n}\n","import type { AgentType, OptimizeModel } from '../../agent/index.ts'\nimport { styleText } from 'node:util'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { loadSession, peekMarker, updateMarker } from '../../auth/store.ts'\nimport { autoResolveAgent } from '../../cli/agent-prompt.ts'\nimport { sharedArgs } from '../../cli/args.ts'\nimport { renderDigest } from '../../cli/digest-render.ts'\nimport { isInteractive } from '../../cli/env.ts'\nimport { getInstalledGenerators, introLine } from '../../cli/intro.ts'\nimport { readConfig } from '../../core/config.ts'\nimport { resolveSkillName } from '../../core/prefix.ts'\nimport { COMMA_OR_WHITESPACE_RE } from '../../core/regex.ts'\nimport { getProjectState } from '../../core/skills.ts'\nimport { createRegistryClient } from '../../registry/client.ts'\nimport { syncCommand } from '../sync.ts'\nimport { exportPortablePrompts } from './portable.ts'\n\nasync function renderChangesDigest(): Promise<void> {\n const session = await loadSession()\n if (!session || session.scheme === 'env')\n return\n const marker = peekMarker()\n const client = createRegistryClient({ session })\n const digest = await client.my.changes({ since: marker?.lastDigestAt }).catch(() => null)\n if (!digest || digest.entries.length === 0)\n return\n renderDigest(digest.entries)\n // Use the server's windowEnd as the canonical watermark — round-trippable\n // and aligns with skilld.dev's email digest cron.\n updateMarker({ lastDigestAt: digest.windowEnd })\n}\n\nexport const updateCommandDef = defineCommand({\n meta: { name: 'update', description: 'Update outdated skills' },\n args: {\n package: {\n type: 'positional',\n description: 'Package(s) to update (space or comma-separated). Without args, syncs all outdated.',\n required: false,\n },\n background: {\n type: 'boolean',\n alias: 'b',\n description: 'Run in background (detached process, non-interactive)',\n default: false,\n },\n ...sharedArgs,\n },\n async run({ args }) {\n const cwd = process.cwd()\n\n if (args.background) {\n const { spawn } = await import('node:child_process')\n const updateArgs = ['update', ...(args.package ? [args.package] : []), ...(args.agent ? ['--agent', args.agent] : []), ...(args.model ? ['--model', args.model as string] : [])]\n const child = spawn(process.execPath, [process.argv[1]!, ...updateArgs], {\n cwd,\n detached: true,\n stdio: 'ignore',\n }) as import('node:child_process').ChildProcess\n child.unref()\n return\n }\n\n const silent = !isInteractive()\n\n // `update --agent none` exports portable prompts; otherwise auto-detect or error.\n let agent: AgentType | 'none' | null\n if (args.agent === 'none') {\n agent = 'none'\n }\n else {\n agent = autoResolveAgent(args.agent)\n if (!agent) {\n p.log.error('No target agent detected.\\n Pass --agent <name> (claude-code, cursor, codex, …) or run `skilld config` to set a default.')\n process.exitCode = 1\n return\n }\n }\n\n if (agent === 'none') {\n const state = await getProjectState(cwd)\n const packages = args.package\n ? Array.from(\n new Set([args.package, ...((args as any)._ || [])].flatMap(s => s.split(COMMA_OR_WHITESPACE_RE)).map(s => s.trim()).filter(Boolean)),\n s => resolveSkillName(s),\n ).filter((s): s is string => s !== null)\n : state.outdated.map(s => s.packageName || s.name)\n if (packages.length === 0) {\n if (!silent)\n p.log.success('All skills up to date')\n return\n }\n for (const pkg of packages)\n await exportPortablePrompts(pkg, { force: args.force, agent: 'none' })\n return\n }\n\n const config = readConfig()\n const state = await getProjectState(cwd)\n\n if (!silent) {\n const generators = getInstalledGenerators()\n p.intro(introLine({ state, generators, modelId: config.model, agentId: config.agent || agent || undefined }))\n }\n\n if (args.package) {\n const raw = [...new Set([args.package, ...((args as any)._ || [])].flatMap(s => s.split(COMMA_OR_WHITESPACE_RE)).map(s => s.trim()).filter(Boolean))]\n const packages: string[] = []\n for (const r of raw) {\n const name = resolveSkillName(r)\n if (!name) {\n p.log.warn(`Cannot update ${styleText('cyan', r)}: curator/collection inputs are not addressable here.`)\n continue\n }\n packages.push(name)\n }\n if (packages.length === 0)\n return\n return syncCommand(state, {\n packages,\n global: args.global,\n agent,\n model: (args.model as OptimizeModel | undefined) || (silent ? config.model : undefined),\n yes: args.yes || silent,\n force: args.force,\n debug: args.debug,\n mode: 'update',\n })\n }\n\n const crateSpecs = state.skills\n .map(s => s.info?.packageName)\n .filter((name): name is string => !!name && name.startsWith('crate:'))\n if (state.outdated.length === 0 && crateSpecs.length === 0) {\n p.log.success('All skills up to date')\n return\n }\n\n const packages = [\n ...state.outdated.map(s => s.packageName || s.name),\n ...crateSpecs,\n ]\n await syncCommand(state, {\n packages,\n global: args.global,\n agent,\n model: (args.model as OptimizeModel | undefined) || (silent ? config.model : undefined),\n yes: args.yes || silent,\n force: args.force,\n debug: args.debug,\n mode: 'update',\n })\n\n if (!silent)\n await renderChangesDigest()\n },\n})\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { i as parseFrontmatter } from "./markdown.mjs";
2
- import { a as readLock } from "./lockfile.mjs";
3
- import { homedir } from "node:os";
4
- import { join } from "pathe";
2
+ import { c as readLock } from "./lockfile.mjs";
5
3
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
6
- import { ofetch } from "ofetch";
7
4
  import { multiselect } from "@clack/prompts";
8
5
  import { defineCommand } from "citty";
6
+ import { join } from "pathe";
7
+ import { homedir } from "node:os";
8
+ import { ofetch } from "ofetch";
9
9
  import { colorize } from "consola/utils";
10
10
  const UPLOAD_URL = "https://skilld.dev/api/collections/import";
11
11
  function readSkillsFromDir(dir, source, extraLockDirs) {
@@ -1,4 +1,4 @@
1
- import { o as getSectionValidator } from "./prompts.mjs";
1
+ import { y as getSectionValidator } from "./prompts.mjs";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { defineCommand } from "citty";
4
4
  const SECTION_HEADINGS = {
@@ -1,30 +1,19 @@
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);
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ function findPackageJson() {
5
+ let dir = dirname(fileURLToPath(import.meta.url));
6
+ for (let i = 0; i < 5; i++) {
7
+ const candidate = resolve(dir, "package.json");
8
+ try {
9
+ const content = readFileSync(candidate, "utf8");
10
+ if (content) return content;
11
+ } catch {}
12
+ dir = resolve(dir, "..");
13
+ }
14
+ return "{\"version\":\"0.0.0\"}";
9
15
  }
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 };
16
+ const version = JSON.parse(findPackageJson()).version;
17
+ export { version as t };
29
18
 
30
19
  //# sourceMappingURL=version.mjs.map
@@ -1 +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;;CAGzC,OAAA,KAAgB,WAAA,OAAgB,KAAe;;;;;;;ACT/C,SAAM,cAAiB,SAAA;;;;;;;;;;CAevB,IAAA,CAAA,IAAgB,WAAY,eAAuC,EAAA,MAAA,IAAA,MAAA,4BAAA,MAAA;CACjE,OAAO"}
1
+ {"version":3,"file":"version.mjs","names":[],"sources":["../../src/version.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n// Walk up from current file to find package.json (works in both src/ and dist/_chunks/)\nfunction findPackageJson(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (let i = 0; i < 5; i++) {\n const candidate = resolve(dir, 'package.json')\n try {\n const content = readFileSync(candidate, 'utf8')\n if (content)\n return content\n }\n catch {}\n dir = resolve(dir, '..')\n }\n return '{\"version\":\"0.0.0\"}'\n}\n\nexport const version: string = JSON.parse(findPackageJson()).version\n"],"mappings":";;;AAKA,SAAS,kBAA0B;CACjC,IAAI,MAAM,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;CACjD,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC1B,MAAM,YAAY,QAAQ,KAAK,eAAe;EAC9C,IAAI;GACF,MAAM,UAAU,aAAa,WAAW,OAAO;GAC/C,IAAI,SACF,OAAO;UAEL;EACN,MAAM,QAAQ,KAAK,KAAK;;CAE1B,OAAO;;AAGT,MAAa,UAAkB,KAAK,MAAM,iBAAiB,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { n as loadSession } from "./store.mjs";
2
+ import { styleText } from "node:util";
3
+ import * as p from "@clack/prompts";
4
+ import { defineCommand } from "citty";
5
+ const whoamiCommandDef = defineCommand({
6
+ meta: {
7
+ name: "whoami",
8
+ description: "Show the active skilld.dev session"
9
+ },
10
+ async run() {
11
+ const session = await loadSession();
12
+ if (!session) {
13
+ p.log.info("Not logged in. Run `skilld login` to authenticate.");
14
+ return;
15
+ }
16
+ p.log.message(`Logged in as ${styleText("cyan", `@${session.login}`)} ${styleText("gray", `(${session.scheme})`)}`);
17
+ }
18
+ });
19
+ export { whoamiCommandDef };
20
+
21
+ //# sourceMappingURL=whoami.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.mjs","names":[],"sources":["../../src/commands/whoami.ts"],"sourcesContent":["/**\n * `skilld whoami` — print the active login + storage scheme.\n */\n\nimport { styleText } from 'node:util'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { loadSession } from '../auth/store.ts'\n\nexport const whoamiCommandDef = defineCommand({\n meta: { name: 'whoami', description: 'Show the active skilld.dev session' },\n async run() {\n const session = await loadSession()\n if (!session) {\n p.log.info('Not logged in. Run `skilld login` to authenticate.')\n return\n }\n p.log.message(`Logged in as ${styleText('cyan', `@${session.login}`)} ${styleText('gray', `(${session.scheme})`)}`)\n },\n})\n"],"mappings":";;;;;;EASA,MAAa;EACX,aAAM;EAAE;OAAgB,MAAA;EAAmD,MAAA,UAAA,MAAA,aAAA;EAC3E,IAAA,CAAM,SAAM;GACV,EAAA,IAAM,KAAA,qDAA6B;GACnC;;IAEE,IAAA,QAAA,gBAAA,UAAA,QAAA,IAAA,QAAA,QAAA,CAAA,GAAA,UAAA,QAAA,IAAA,QAAA,OAAA,GAAA,GAAA;;EAEF;SAEF"}
@@ -1,190 +1,2 @@
1
- import { c as getOAuthProviderList, i as getAvailableModels, l as loginOAuthProvider, o as getModelName } from "./agent.mjs";
2
- import { a as targets } from "./detect.mjs";
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";
5
- import { execSync } from "node:child_process";
6
- import * as p from "@clack/prompts";
7
- function hasGhCli() {
8
- if (process.env.SKILLD_NO_GH) return false;
9
- try {
10
- execSync("gh --version", { stdio: "ignore" });
11
- return true;
12
- } catch {
13
- return false;
14
- }
15
- }
16
- async function runWizard(opts = {}) {
17
- if (!isInteractive()) return false;
18
- const agentLabel = opts.agent ? targets[opts.agent].displayName : null;
19
- const skillsDir = opts.agent ? targets[opts.agent].skillsDir : ".claude/skills";
20
- const agentLine = agentLabel ? `\n\x1B[90mTarget agent: ${agentLabel}\x1B[0m` : "";
21
- p.note(`Your AI agent reads docs from its training data - but APIs change,
22
- versions drift, and patterns go stale. Skilld fixes this.
23
-
24
- It generates a SKILL.md - a markdown reference card built from
25
- the actual docs, issues, and release notes for the exact
26
- package versions in your project. Your agent reads this file
27
- every session - no hallucinated APIs.
28
-
29
- How it works:
30
- 1. Fetch docs, issues, and types for your packages
31
- 2. Optionally compress with an LLM into a concise cheat sheet
32
-
33
- \x1B[90mExample: \`skilld add vue\` creates ${skillsDir}/vue-skilld/SKILL.md\nYour agent then knows the right APIs, gotchas, and patterns\nfor your exact version.\x1B[0m${agentLine}`, "Welcome to skilld");
34
- const ghInstalled = hasGhCli();
35
- if (ghInstalled) p.log.success("GitHub CLI detected — will use it to pull issues and discussions.");
36
- else p.log.info("\x1B[90mGitHub CLI not installed — issues and discussions disabled.\n Install later to enable: \x1B[36mhttps://cli.github.com\x1B[0m");
37
- const selected = await p.multiselect({
38
- message: "What data sources should skills include?",
39
- options: [
40
- {
41
- label: "Local search",
42
- value: "search",
43
- hint: "query engine for `skilld search` across all skill docs"
44
- },
45
- {
46
- label: "Release notes",
47
- value: "releases",
48
- hint: "changelogs and migration notes per version"
49
- },
50
- {
51
- label: "GitHub issues",
52
- value: "issues",
53
- hint: "common bugs, workarounds, and solutions",
54
- disabled: !ghInstalled
55
- },
56
- {
57
- label: "GitHub discussions",
58
- value: "discussions",
59
- hint: "community Q&A and usage examples",
60
- disabled: !ghInstalled
61
- }
62
- ],
63
- initialValues: [...Object.entries(defaultFeatures).filter(([, v]) => v).map(([k]) => k), ...ghInstalled ? ["issues", "discussions"] : []],
64
- required: false
65
- });
66
- if (p.isCancel(selected)) {
67
- p.cancel("Setup cancelled");
68
- return false;
69
- }
70
- const features = {
71
- search: selected.includes("search"),
72
- issues: selected.includes("issues"),
73
- discussions: selected.includes("discussions"),
74
- releases: selected.includes("releases")
75
- };
76
- p.note("An LLM can optionally summarize raw docs into a focused reference\nhighlighting best practices, gotchas, and migrations.\n\n\x1B[1mWithout LLM:\x1B[0m ~2 KB skill with package metadata, types, and links\n\x1B[1mWith LLM:\x1B[0m ~5 KB skill with curated gotchas, patterns, and migration notes\n\n\x1B[1mThis is a one-time build step\x1B[0m - it generates the SKILL.md, then your\ncoding agent reads the result every session. Can be a different model.\n\n\x1B[90mWorks with API keys (ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY)\nor CLI tools (claude, gemini, codex).\x1B[0m", "Enhancement model (optional)");
77
- let modelId;
78
- let skippedEnhancement = false;
79
- let oauthJustConnected = false;
80
- while (true) {
81
- const allModels = process.env.SKILLD_NO_AGENTS ? [] : await getAvailableModels();
82
- if (allModels.length === 0) p.log.warn(NO_MODELS_MESSAGE);
83
- else if (oauthJustConnected) p.log.step(`${allModels.length} models now available. Select one below.`);
84
- else {
85
- const providers = /* @__PURE__ */ new Set();
86
- for (const m of allModels) {
87
- const vendor = m.vendorGroup ?? m.providerName;
88
- if (!m.id.startsWith("pi:")) providers.add(`${vendor} via CLI`);
89
- else if (m.hint?.includes("API key")) providers.add(`${vendor} via API key`);
90
- else if (m.hint?.includes("OAuth")) providers.add(`${vendor} via OAuth`);
91
- }
92
- if (providers.size > 0) p.log.success(`Found: ${[...providers].join(", ")}`);
93
- }
94
- const afterOptions = getOAuthProviderList().length > 0 ? [{
95
- label: "⚠ Connect OAuth provider...",
96
- value: "_connect",
97
- hint: "may violate provider ToS"
98
- }, {
99
- label: "Skip enhancement",
100
- value: "_skip",
101
- hint: "base skill with docs, issues, and types, add LLM later via `skilld config`"
102
- }] : [{
103
- label: "Skip enhancement",
104
- value: "_skip",
105
- hint: "base skill with docs, issues, and types, add LLM later via `skilld config`"
106
- }];
107
- const choice = await pickModel(allModels, {
108
- before: allModels.length > 0 ? [{
109
- label: "Auto",
110
- value: "_auto",
111
- hint: "picks best available model from connected providers"
112
- }] : [],
113
- after: afterOptions
114
- });
115
- if (choice === null) {
116
- p.cancel("Setup cancelled");
117
- return false;
118
- }
119
- if (choice === "_connect") {
120
- await wizardConnectProvider();
121
- oauthJustConnected = true;
122
- continue;
123
- }
124
- if (choice === "_skip") {
125
- skippedEnhancement = true;
126
- break;
127
- }
128
- if (choice === "_auto") break;
129
- modelId = choice;
130
- break;
131
- }
132
- updateConfig({
133
- features,
134
- ...modelId ? {
135
- model: modelId,
136
- skipLlm: false
137
- } : {
138
- model: void 0,
139
- skipLlm: skippedEnhancement
140
- }
141
- });
142
- const modelSummary = modelId ? getModelName(modelId) : skippedEnhancement ? "none (raw docs)" : "auto";
143
- const featureList = Object.entries(features).filter(([, v]) => v).map(([k]) => k).join(", ") || "none";
144
- p.log.success(`Model: ${modelSummary} · Features: ${featureList}`);
145
- if (opts.showOutro !== false) p.note("Run \x1B[36mskilld add <pkg>\x1B[0m to generate skills for specific packages\nRun \x1B[36mskilld\x1B[0m to scan your project and pick packages interactively\nRun \x1B[36mskilld config\x1B[0m to change settings later", "Setup complete");
146
- return true;
147
- }
148
- async function wizardConnectProvider() {
149
- p.note(OAUTH_NOTE, "How OAuth works");
150
- const providers = getOAuthProviderList();
151
- const provider = await p.select({
152
- message: "Connect provider",
153
- options: providers.map((pr) => ({
154
- label: pr.name,
155
- value: pr.id,
156
- hint: pr.loggedIn ? "connected" : void 0
157
- }))
158
- });
159
- if (p.isCancel(provider)) return;
160
- const spinner = p.spinner();
161
- spinner.start("Connecting...");
162
- const success = await loginOAuthProvider(provider, {
163
- onAuth: (url, instructions) => {
164
- spinner.stop("Open this URL in your browser:");
165
- p.log.info(` \x1B[36m${url}\x1B[0m`);
166
- if (instructions) p.log.info(` \x1B[90m${instructions}\x1B[0m`);
167
- spinner.start("Waiting for authentication...");
168
- },
169
- onPrompt: async (message, placeholder) => {
170
- const value = await p.text({
171
- message,
172
- placeholder
173
- });
174
- if (p.isCancel(value)) return "";
175
- return value;
176
- },
177
- onProgress: (msg) => p.log.step(msg)
178
- }).catch((err) => {
179
- spinner.stop(`Login failed: ${err.message}`);
180
- return false;
181
- });
182
- spinner.stop();
183
- if (success) {
184
- const name = providers.find((pr) => pr.id === provider)?.name ?? provider;
185
- p.log.success(`Connected to ${name}`);
186
- }
187
- }
188
- export { runWizard as t };
189
-
190
- //# sourceMappingURL=wizard.mjs.map
1
+ import { n as setupCommandDef } from "./wizard2.mjs";
2
+ export { setupCommandDef };