skilld 1.5.5 → 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.
Files changed (120) hide show
  1. package/README.md +32 -23
  2. package/dist/_chunks/agent.mjs +2 -78
  3. package/dist/_chunks/agent.mjs.map +1 -1
  4. package/dist/_chunks/assemble.mjs +1 -18
  5. package/dist/_chunks/assemble.mjs.map +1 -1
  6. package/dist/_chunks/author-group.mjs +17 -0
  7. package/dist/_chunks/author-group.mjs.map +1 -0
  8. package/dist/_chunks/author.mjs +8 -24
  9. package/dist/_chunks/author.mjs.map +1 -1
  10. package/dist/_chunks/cache.mjs +1 -73
  11. package/dist/_chunks/cache.mjs.map +1 -1
  12. package/dist/_chunks/cache2.mjs +84 -17
  13. package/dist/_chunks/cache2.mjs.map +1 -1
  14. package/dist/_chunks/cli-helpers.mjs +3 -166
  15. package/dist/_chunks/cli-helpers.mjs.map +1 -1
  16. package/dist/_chunks/cli-helpers2.mjs +0 -11
  17. package/dist/_chunks/config.mjs +119 -54
  18. package/dist/_chunks/config.mjs.map +1 -1
  19. package/dist/_chunks/core.mjs +9 -0
  20. package/dist/_chunks/detect.mjs +29 -226
  21. package/dist/_chunks/detect.mjs.map +1 -1
  22. package/dist/_chunks/embedding-cache.mjs +0 -5
  23. package/dist/_chunks/embedding-cache2.mjs +2 -3
  24. package/dist/_chunks/formatting.mjs +0 -6
  25. package/dist/_chunks/formatting.mjs.map +1 -1
  26. package/dist/_chunks/index.d.mts +0 -10
  27. package/dist/_chunks/index.d.mts.map +1 -1
  28. package/dist/_chunks/index2.d.mts +3 -6
  29. package/dist/_chunks/index2.d.mts.map +1 -1
  30. package/dist/_chunks/index3.d.mts +81 -109
  31. package/dist/_chunks/index3.d.mts.map +1 -1
  32. package/dist/_chunks/install.mjs +85 -550
  33. package/dist/_chunks/install.mjs.map +1 -1
  34. package/dist/_chunks/install2.mjs +554 -0
  35. package/dist/_chunks/install2.mjs.map +1 -0
  36. package/dist/_chunks/libs/@sinclair/typebox.mjs +0 -444
  37. package/dist/_chunks/libs/@sinclair/typebox.mjs.map +1 -1
  38. package/dist/_chunks/list.mjs +0 -16
  39. package/dist/_chunks/list.mjs.map +1 -1
  40. package/dist/_chunks/lockfile.mjs +2 -10
  41. package/dist/_chunks/lockfile.mjs.map +1 -1
  42. package/dist/_chunks/markdown.mjs +0 -9
  43. package/dist/_chunks/markdown.mjs.map +1 -1
  44. package/dist/_chunks/package-json.mjs +0 -25
  45. package/dist/_chunks/package-json.mjs.map +1 -1
  46. package/dist/_chunks/package-registry.mjs +465 -0
  47. package/dist/_chunks/package-registry.mjs.map +1 -0
  48. package/dist/_chunks/pool2.mjs +0 -2
  49. package/dist/_chunks/pool2.mjs.map +1 -1
  50. package/dist/_chunks/prefix.mjs +108 -0
  51. package/dist/_chunks/prefix.mjs.map +1 -0
  52. package/dist/_chunks/prepare.mjs +14 -9
  53. package/dist/_chunks/prepare.mjs.map +1 -1
  54. package/dist/_chunks/prepare2.mjs +1 -19
  55. package/dist/_chunks/prepare2.mjs.map +1 -1
  56. package/dist/_chunks/prompts.mjs +6 -201
  57. package/dist/_chunks/prompts.mjs.map +1 -1
  58. package/dist/_chunks/retriv.mjs +23 -24
  59. package/dist/_chunks/retriv.mjs.map +1 -1
  60. package/dist/_chunks/rolldown-runtime.mjs +0 -2
  61. package/dist/_chunks/sanitize.mjs +0 -78
  62. package/dist/_chunks/sanitize.mjs.map +1 -1
  63. package/dist/_chunks/search-helpers.mjs +99 -0
  64. package/dist/_chunks/search-helpers.mjs.map +1 -0
  65. package/dist/_chunks/search-interactive.mjs +1 -18
  66. package/dist/_chunks/search-interactive.mjs.map +1 -1
  67. package/dist/_chunks/search.mjs +218 -19
  68. package/dist/_chunks/search.mjs.map +1 -0
  69. package/dist/_chunks/setup.mjs +0 -13
  70. package/dist/_chunks/setup.mjs.map +1 -1
  71. package/dist/_chunks/shared.mjs +1 -473
  72. package/dist/_chunks/shared.mjs.map +1 -1
  73. package/dist/_chunks/skills.mjs +3 -3
  74. package/dist/_chunks/skills.mjs.map +1 -1
  75. package/dist/_chunks/sources.mjs +1179 -1440
  76. package/dist/_chunks/sources.mjs.map +1 -1
  77. package/dist/_chunks/sync-registry.mjs +59 -0
  78. package/dist/_chunks/sync-registry.mjs.map +1 -0
  79. package/dist/_chunks/sync-shared.mjs +0 -16
  80. package/dist/_chunks/sync-shared2.mjs +10 -49
  81. package/dist/_chunks/sync-shared2.mjs.map +1 -1
  82. package/dist/_chunks/sync.mjs +209 -120
  83. package/dist/_chunks/sync.mjs.map +1 -1
  84. package/dist/_chunks/sync2.mjs +1 -21
  85. package/dist/_chunks/types.d.mts +0 -2
  86. package/dist/_chunks/types.d.mts.map +1 -1
  87. package/dist/_chunks/uninstall.mjs +3 -27
  88. package/dist/_chunks/uninstall.mjs.map +1 -1
  89. package/dist/_chunks/upload.mjs +152 -0
  90. package/dist/_chunks/upload.mjs.map +1 -0
  91. package/dist/_chunks/validate.mjs +1 -8
  92. package/dist/_chunks/validate.mjs.map +1 -1
  93. package/dist/_chunks/version.mjs +30 -0
  94. package/dist/_chunks/version.mjs.map +1 -0
  95. package/dist/_chunks/wizard.mjs +2 -3
  96. package/dist/_chunks/yaml.mjs +0 -21
  97. package/dist/_chunks/yaml.mjs.map +1 -1
  98. package/dist/agent/index.d.mts +0 -24
  99. package/dist/agent/index.d.mts.map +1 -1
  100. package/dist/agent/index.mjs +2 -9
  101. package/dist/cache/index.mjs +1 -3
  102. package/dist/cli-entry.mjs +0 -6
  103. package/dist/cli-entry.mjs.map +1 -1
  104. package/dist/cli.mjs +48 -33
  105. package/dist/cli.mjs.map +1 -1
  106. package/dist/index.d.mts +1 -1
  107. package/dist/index.mjs +2 -8
  108. package/dist/prepare.mjs +0 -12
  109. package/dist/prepare.mjs.map +1 -1
  110. package/dist/retriv/index.mjs +0 -2
  111. package/dist/retriv/worker.d.mts +0 -3
  112. package/dist/retriv/worker.d.mts.map +1 -1
  113. package/dist/retriv/worker.mjs +0 -2
  114. package/dist/retriv/worker.mjs.map +1 -1
  115. package/dist/sources/index.d.mts +2 -2
  116. package/dist/sources/index.mjs +3 -7
  117. package/dist/types.d.mts +1 -1
  118. package/package.json +20 -21
  119. package/dist/_chunks/search2.mjs +0 -319
  120. package/dist/_chunks/search2.mjs.map +0 -1
@@ -1,4 +1,3 @@
1
- //#region src/retriv/types.d.ts
2
1
  interface ChunkEntity {
3
2
  name: string;
4
3
  type: string;
@@ -85,6 +84,5 @@ interface SearchSnippet {
85
84
  /** Containing scope chain */
86
85
  scope?: ChunkEntity[];
87
86
  }
88
- //#endregion
89
87
  export { IndexProgress as a, SearchResult as c, IndexPhase as i, SearchSnippet as l, Document as n, SearchFilter as o, IndexConfig as r, SearchOptions as s, ChunkEntity as t };
90
88
  //# sourceMappingURL=types.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../src/retriv/types.ts"],"mappings":";UAAiB,WAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,QAAA;EACf,EAAA;EACA,OAAA;EACA,QAAA,GAAW,MAAA;AAAA;AAAA,UAGI,eAAA;EACf,SAAA;EACA,YAAA;AAAA;AAAA,KAGU,UAAA;AAAA,UAEK,aAAA;EACf,KAAA,EAAO,UAAA;EACP,OAAA;EACA,KAAA;AAAA;AAAA,UAGe,WAAA;EACf,MAAA;EACA,KAAA;EACA,QAAA,GAAW,eAAA;EAdC;EAgBZ,UAAA,IAAc,QAAA,EAAU,aAAA;AAAA;AAAA,UAGT,YAAA;EACf,EAAA;EACA,OAAA;EACA,KAAA;EACA,QAAA,EAAU,MAAA;EACV,UAAA;EAlBiB;EAoBjB,SAAA;EACA,QAAA,GAAW,WAAA;EACX,KAAA,GAAQ,WAAA;AAAA;AAAA,KAGE,cAAA;EACJ,GAAA;AAAA;EACA,GAAA;AAAA;EACA,GAAA;AAAA;EACA,IAAA;AAAA;EACA,GAAA;AAAA;EACA,IAAA;AAAA;EACA,GAAA;AAAA;EACA,OAAA;AAAA;EACA,OAAA;AAAA;AAAA,KAEI,WAAA,+BAA0C,cAAA;AAAA,KAC1C,YAAA,GAAe,MAAA,SAAe,WAAA;AAAA,UAEzB,aAAA;EAjBI;EAmBnB,KAAA;EA1BA;EA4BA,MAAA,GAAS,YAAA;AAAA;AAAA,UAGM,aAAA;EA5Bf;EA8BA,OAAA;EA3BA;EA6BA,MAAA;EA5BA;EA8BA,SAAA;;EAEA,OAAA;EA7BU;EA+BV,OAAA;;EAEA,KAAA;EAhCM;EAkCN,UAAA;EAhCM;EAkCN,QAAA,GAAW,WAAA;EAhCL;EAkCN,KAAA,GAAQ,WAAA;AAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../src/retriv/types.ts"],"mappings":"UAAiB,WAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,QAAA;EACf,EAAA;EACA,OAAA;EACA,QAAA,GAAW,MAAA;AAAA;AAAA,UAGI,eAAA;EACf,SAAA;EACA,YAAA;AAAA;AAAA,KAGU,UAAA;AAAA,UAEK,aAAA;EACf,KAAA,EAAO,UAAA;EACP,OAAA;EACA,KAAA;AAAA;AAAA,UAGe,WAAA;EACf,MAAA;EACA,KAAA;EACA,QAAA,GAAW,eAAA;EAXD;EAaV,UAAA,IAAc,QAAA,EAAU,aAAA;AAAA;AAAA,UAGT,YAAA;EACf,EAAA;EACA,OAAA;EACA,KAAA;EACA,QAAA,EAAU,MAAA;EACV,UAAA;EAlBA;EAoBA,SAAA;EACA,QAAA,GAAW,WAAA;EACX,KAAA,GAAQ,WAAA;AAAA;AAAA,KAGE,cAAA;EACJ,GAAA;AAAA;EACA,GAAA;AAAA;EACA,GAAA;AAAA;EACA,IAAA;AAAA;EACA,GAAA;AAAA;EACA,IAAA;AAAA;EACA,GAAA;AAAA;EACA,OAAA;AAAA;EACA,OAAA;AAAA;AAAA,KAEI,WAAA,+BAA0C,cAAA;AAAA,KAC1C,YAAA,GAAe,MAAA,SAAe,WAAA;AAAA,UAEzB,aAAA;EAzBf;EA2BA,KAAA;EAzBA;EA2BA,MAAA,GAAS,YAAA;AAAA;AAAA,UAGM,aAAA;EA1Bf;EA4BA,OAAA;EA3BW;EA6BX,MAAA;EA5BQ;EA8BR,SAAA;EA9BmB;EAgCnB,OAAA;EA7BwB;EA+BxB,OAAA;EA/BwB;EAiCxB,KAAA;EA/BM;EAiCN,UAAA;EA/BM;EAiCN,QAAA,GAAW,WAAA;EA/BL;EAiCN,KAAA,GAAQ,WAAA;AAAA"}
@@ -1,35 +1,17 @@
1
- import { t as CACHE_DIR } from "./config.mjs";
2
- import "./package-json.mjs";
3
- import "./prepare.mjs";
4
- import "./sanitize.mjs";
1
+ import { i as CACHE_DIR } from "./version.mjs";
5
2
  import "./cache.mjs";
6
- import "./yaml.mjs";
7
- import "./markdown.mjs";
8
- import "./retriv.mjs";
9
3
  import { r as mapInsert, t as SHARED_SKILLS_DIR } from "./shared.mjs";
10
- import "./sources.mjs";
11
4
  import { a as targets } from "./detect.mjs";
12
- import "./prompts.mjs";
13
5
  import "./agent.mjs";
14
- import "./libs/@sinclair/typebox.mjs";
15
- import { A as unregisterProject, T as getRegisteredProjects, p as isInteractive, x as sharedArgs } from "./cli-helpers.mjs";
6
+ import { n as getRegisteredProjects, s as unregisterProject } from "./config.mjs";
7
+ import { p as isInteractive, x as sharedArgs } from "./cli-helpers.mjs";
16
8
  import { i as readLock } from "./lockfile.mjs";
17
- import "./skills.mjs";
18
- import "./formatting.mjs";
19
- import "./wizard.mjs";
20
9
  import { i as SKILLD_MARKER_START, r as SKILLD_MARKER_END } from "./sync-shared2.mjs";
21
- import "./pool2.mjs";
22
10
  import "./sync.mjs";
23
11
  import { join } from "pathe";
24
12
  import { existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
25
13
  import * as p from "@clack/prompts";
26
14
  import { defineCommand } from "citty";
27
- //#region src/commands/uninstall.ts
28
- /**
29
- * Remove the skilld marker block from an agent's instruction file.
30
- * For .mdc files (dedicated skilld files), delete the entire file.
31
- * Also cleans up legacy .cursorrules markers for backwards compat.
32
- */
33
15
  function removeAgentInstructions(agent, projectPath) {
34
16
  const agentConfig = targets[agent];
35
17
  if (!agentConfig.instructionFile) return false;
@@ -58,11 +40,6 @@ function removeMarkerBlock(filePath) {
58
40
  else writeFileSync(filePath, updated.endsWith("\n") ? updated : `${updated}\n`);
59
41
  return true;
60
42
  }
61
- /**
62
- * Uninstall skilld skills by scope:
63
- * - project: Remove project skills (cwd)
64
- * - all: All registered projects + global skills + cache
65
- */
66
43
  async function uninstallCommand(opts) {
67
44
  let scope = opts.scope;
68
45
  const registeredProjects = getRegisteredProjects();
@@ -226,7 +203,6 @@ const uninstallCommandDef = defineCommand({
226
203
  });
227
204
  }
228
205
  });
229
- //#endregion
230
206
  export { uninstallCommandDef };
231
207
 
232
208
  //# sourceMappingURL=uninstall.mjs.map
@@ -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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"}
@@ -1,16 +1,10 @@
1
- import "./sanitize.mjs";
2
- import "./yaml.mjs";
3
- import "./shared.mjs";
4
- import "./detect.mjs";
5
- import { f as getSectionValidator } from "./prompts.mjs";
1
+ import { s as getSectionValidator } from "./prompts.mjs";
6
2
  import { existsSync, readFileSync } from "node:fs";
7
3
  import { defineCommand } from "citty";
8
- //#region src/commands/validate.ts
9
4
  const SECTION_HEADINGS = {
10
5
  "## API Changes": "api-changes",
11
6
  "## Best Practices": "best-practices"
12
7
  };
13
- /** Infer section type from content headings */
14
8
  function inferSection(content) {
15
9
  for (const [heading, section] of Object.entries(SECTION_HEADINGS)) if (content.includes(heading)) return section;
16
10
  return "custom";
@@ -61,7 +55,6 @@ const validateCommandDef = defineCommand({
61
55
  process.exit(1);
62
56
  }
63
57
  });
64
- //#endregion
65
58
  export { validateCommandDef };
66
59
 
67
60
  //# sourceMappingURL=validate.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.mjs","names":[],"sources":["../../src/commands/validate.ts"],"sourcesContent":["/**\n * skilld validate <file> [--section <type>]\n *\n * Validates a generated skill section file against quality heuristics.\n * Exits non-zero on warnings so the LLM agent detects issues via exit code.\n */\n\nimport type { SkillSection } from '../agent/prompts/index.ts'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { defineCommand } from 'citty'\nimport { getSectionValidator } from '../agent/prompts/index.ts'\n\nconst SECTION_HEADINGS: Record<string, SkillSection> = {\n '## API Changes': 'api-changes',\n '## Best Practices': 'best-practices',\n}\n\n/** Infer section type from content headings */\nfunction inferSection(content: string): SkillSection | null {\n for (const [heading, section] of Object.entries(SECTION_HEADINGS)) {\n if (content.includes(heading))\n return section\n }\n // Custom sections don't have a fixed heading — fall back\n return 'custom'\n}\n\nexport const validateCommandDef = defineCommand({\n meta: { name: 'validate', description: 'Validate a generated skill section' },\n args: {\n file: {\n type: 'positional',\n description: 'Path to the section file to validate',\n required: true,\n },\n section: {\n type: 'string',\n description: 'Section type (api-changes, best-practices, custom). Auto-detected from heading if omitted.',\n },\n },\n\n /* eslint-disable no-console */\n async run({ args }) {\n const filePath = args.file as string\n if (!existsSync(filePath)) {\n console.error(`File not found: ${filePath}`)\n process.exit(1)\n }\n\n const content = readFileSync(filePath, 'utf-8').trim()\n if (!content) {\n console.error('File is empty')\n process.exit(1)\n }\n\n const section = (args.section as SkillSection) || inferSection(content)\n if (!section) {\n console.error('Could not infer section type — use --section flag')\n process.exit(1)\n }\n\n const validator = getSectionValidator(section)\n if (!validator) {\n console.log('OK: No validator for section type:', section)\n process.exit(0)\n }\n\n const warnings = validator(content)\n\n if (warnings.length === 0) {\n console.log('OK: No validation warnings')\n process.exit(0)\n }\n\n for (const w of warnings)\n console.log(`WARNING: ${w.warning}`)\n\n process.exit(1)\n },\n})\n"],"mappings":";;;;;;;;AAYA,MAAM,mBAAiD;CACrD,kBAAkB;CAClB,qBAAqB;CACtB;;AAGD,SAAS,aAAa,SAAsC;AAC1D,MAAK,MAAM,CAAC,SAAS,YAAY,OAAO,QAAQ,iBAAiB,CAC/D,KAAI,QAAQ,SAAS,QAAQ,CAC3B,QAAO;AAGX,QAAO;;AAGT,MAAa,qBAAqB,cAAc;CAC9C,MAAM;EAAE,MAAM;EAAY,aAAa;EAAsC;CAC7E,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,SAAS;GACP,MAAM;GACN,aAAa;;EAEhB;CAGD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,WAAW,KAAK;AACtB,MAAI,CAAC,WAAW,SAAS,EAAE;AACzB,WAAQ,MAAM,mBAAmB,WAAW;AAC5C,WAAQ,KAAK,EAAE;;EAGjB,MAAM,UAAU,aAAa,UAAU,QAAQ,CAAC,MAAM;AACtD,MAAI,CAAC,SAAS;AACZ,WAAQ,MAAM,gBAAgB;AAC9B,WAAQ,KAAK,EAAE;;EAGjB,MAAM,UAAW,KAAK,WAA4B,aAAa,QAAQ;AACvE,MAAI,CAAC,SAAS;AACZ,WAAQ,MAAM,oDAAoD;AAClE,WAAQ,KAAK,EAAE;;EAGjB,MAAM,YAAY,oBAAoB,QAAQ;AAC9C,MAAI,CAAC,WAAW;AACd,WAAQ,IAAI,sCAAsC,QAAQ;AAC1D,WAAQ,KAAK,EAAE;;EAGjB,MAAM,WAAW,UAAU,QAAQ;AAEnC,MAAI,SAAS,WAAW,GAAG;AACzB,WAAQ,IAAI,6BAA6B;AACzC,WAAQ,KAAK,EAAE;;AAGjB,OAAK,MAAM,KAAK,SACd,SAAQ,IAAI,YAAY,EAAE,UAAU;AAEtC,UAAQ,KAAK,EAAE;;CAElB,CAAC"}
1
+ {"version":3,"file":"validate.mjs","names":[],"sources":["../../src/commands/validate.ts"],"sourcesContent":["/**\n * skilld validate <file> [--section <type>]\n *\n * Validates a generated skill section file against quality heuristics.\n * Exits non-zero on warnings so the LLM agent detects issues via exit code.\n */\n\nimport type { SkillSection } from '../agent/prompts/index.ts'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { defineCommand } from 'citty'\nimport { getSectionValidator } from '../agent/prompts/index.ts'\n\nconst SECTION_HEADINGS: Record<string, SkillSection> = {\n '## API Changes': 'api-changes',\n '## Best Practices': 'best-practices',\n}\n\n/** Infer section type from content headings */\nfunction inferSection(content: string): SkillSection | null {\n for (const [heading, section] of Object.entries(SECTION_HEADINGS)) {\n if (content.includes(heading))\n return section\n }\n // Custom sections don't have a fixed heading — fall back\n return 'custom'\n}\n\nexport const validateCommandDef = defineCommand({\n meta: { name: 'validate', description: 'Validate a generated skill section' },\n args: {\n file: {\n type: 'positional',\n description: 'Path to the section file to validate',\n required: true,\n },\n section: {\n type: 'string',\n description: 'Section type (api-changes, best-practices, custom). Auto-detected from heading if omitted.',\n },\n },\n\n /* eslint-disable no-console */\n async run({ args }) {\n const filePath = args.file as string\n if (!existsSync(filePath)) {\n console.error(`File not found: ${filePath}`)\n process.exit(1)\n }\n\n const content = readFileSync(filePath, 'utf-8').trim()\n if (!content) {\n console.error('File is empty')\n process.exit(1)\n }\n\n const section = (args.section as SkillSection) || inferSection(content)\n if (!section) {\n console.error('Could not infer section type — use --section flag')\n process.exit(1)\n }\n\n const validator = getSectionValidator(section)\n if (!validator) {\n console.log('OK: No validator for section type:', section)\n process.exit(0)\n }\n\n const warnings = validator(content)\n\n if (warnings.length === 0) {\n console.log('OK: No validation warnings')\n process.exit(0)\n }\n\n for (const w of warnings)\n console.log(`WARNING: ${w.warning}`)\n\n process.exit(1)\n },\n})\n"],"mappings":";;;AAYA,MAAM,mBAAiD;CACrD,kBAAkB;CAClB,qBAAqB;CACtB;AAGD,SAAS,aAAa,SAAsC;AAC1D,MAAK,MAAM,CAAC,SAAS,YAAY,OAAO,QAAQ,iBAAiB,CAC/D,KAAI,QAAQ,SAAS,QAAQ,CAC3B,QAAO;AAGX,QAAO;;AAGT,MAAa,qBAAqB,cAAc;CAC9C,MAAM;EAAE,MAAM;EAAY,aAAa;EAAsC;CAC7E,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,SAAS;GACP,MAAM;GACN,aAAa;;EAEhB;CAGD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,WAAW,KAAK;AACtB,MAAI,CAAC,WAAW,SAAS,EAAE;AACzB,WAAQ,MAAM,mBAAmB,WAAW;AAC5C,WAAQ,KAAK,EAAE;;EAGjB,MAAM,UAAU,aAAa,UAAU,QAAQ,CAAC,MAAM;AACtD,MAAI,CAAC,SAAS;AACZ,WAAQ,MAAM,gBAAgB;AAC9B,WAAQ,KAAK,EAAE;;EAGjB,MAAM,UAAW,KAAK,WAA4B,aAAa,QAAQ;AACvE,MAAI,CAAC,SAAS;AACZ,WAAQ,MAAM,oDAAoD;AAClE,WAAQ,KAAK,EAAE;;EAGjB,MAAM,YAAY,oBAAoB,QAAQ;AAC9C,MAAI,CAAC,WAAW;AACd,WAAQ,IAAI,sCAAsC,QAAQ;AAC1D,WAAQ,KAAK,EAAE;;EAGjB,MAAM,WAAW,UAAU,QAAQ;AAEnC,MAAI,SAAS,WAAW,GAAG;AACzB,WAAQ,IAAI,6BAA6B;AACzC,WAAQ,KAAK,EAAE;;AAGjB,OAAK,MAAM,KAAK,SACd,SAAQ,IAAI,YAAY,EAAE,UAAU;AAEtC,UAAQ,KAAK,EAAE;;CAElB,CAAC"}
@@ -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"}
@@ -1,9 +1,9 @@
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 { g as pickModel, j as updateConfig, n as NO_MODELS_MESSAGE, p as isInteractive, r as OAUTH_NOTE, w as defaultFeatures } from "./cli-helpers.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";
4
5
  import { execSync } from "node:child_process";
5
6
  import * as p from "@clack/prompts";
6
- //#region src/commands/wizard.ts
7
7
  function hasGhCli() {
8
8
  if (process.env.SKILLD_NO_GH) return false;
9
9
  try {
@@ -185,7 +185,6 @@ async function wizardConnectProvider() {
185
185
  p.log.success(`Connected to ${name}`);
186
186
  }
187
187
  }
188
- //#endregion
189
188
  export { runWizard as t };
190
189
 
191
190
  //# sourceMappingURL=wizard.mjs.map
@@ -1,24 +1,8 @@
1
- //#region src/core/yaml.ts
2
- /**
3
- * Minimal YAML value escaping/unescaping for our hand-rolled parsers.
4
- *
5
- * Handles the characters that break naive `:` splitting and quote stripping:
6
- * colons, quotes, newlines, backslashes.
7
- */
8
- /** Characters that require double-quoting in YAML values */
9
1
  const NEEDS_QUOTING = /[:"'\\\n\r\t#{}[\],&*!|>%@`]/;
10
- /**
11
- * Escape a value for safe YAML emission. Always double-quotes if the value
12
- * contains any special characters; returns unquoted for simple values.
13
- */
14
2
  function yamlEscape(value) {
15
3
  if (!NEEDS_QUOTING.test(value)) return value;
16
4
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
17
5
  }
18
- /**
19
- * Parse a raw YAML value string back to its actual value.
20
- * Handles double-quoted (with escapes), single-quoted, and unquoted values.
21
- */
22
6
  function yamlUnescape(raw) {
23
7
  const trimmed = raw.trim();
24
8
  if (!trimmed) return "";
@@ -35,10 +19,6 @@ function yamlUnescape(raw) {
35
19
  if (trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
36
20
  return trimmed;
37
21
  }
38
- /**
39
- * Parse a YAML `key: value` line, correctly handling colons inside quoted values.
40
- * Returns [key, value] or null if not a valid KV line.
41
- */
42
22
  function yamlParseKV(line) {
43
23
  const trimmed = line.trim();
44
24
  const colonIdx = trimmed.indexOf(":");
@@ -48,7 +28,6 @@ function yamlParseKV(line) {
48
28
  if (!key) return null;
49
29
  return [key, yamlUnescape(rawValue)];
50
30
  }
51
- //#endregion
52
31
  export { yamlParseKV as n, yamlUnescape as r, yamlEscape as t };
53
32
 
54
33
  //# sourceMappingURL=yaml.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"yaml.mjs","names":[],"sources":["../../src/core/yaml.ts"],"sourcesContent":["/**\n * Minimal YAML value escaping/unescaping for our hand-rolled parsers.\n *\n * Handles the characters that break naive `:` splitting and quote stripping:\n * colons, quotes, newlines, backslashes.\n */\n\n/** Characters that require double-quoting in YAML values */\nconst NEEDS_QUOTING = /[:\"'\\\\\\n\\r\\t#{}[\\],&*!|>%@`]/\n\n/**\n * Escape a value for safe YAML emission. Always double-quotes if the value\n * contains any special characters; returns unquoted for simple values.\n */\nexport function yamlEscape(value: string): string {\n if (!NEEDS_QUOTING.test(value))\n return value\n // Escape backslashes first, then double quotes, then control chars\n const escaped = value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t')\n return `\"${escaped}\"`\n}\n\n/**\n * Parse a raw YAML value string back to its actual value.\n * Handles double-quoted (with escapes), single-quoted, and unquoted values.\n */\nexport function yamlUnescape(raw: string): string {\n const trimmed = raw.trim()\n if (!trimmed)\n return ''\n\n // Double-quoted: single-pass escape processing to handle backslashes correctly\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"')) {\n return trimmed.slice(1, -1)\n .replace(/\\\\([\\\\nrt\"])/g, (_, c) => {\n switch (c) {\n case '\\\\': return '\\\\'\n case 'n': return '\\n'\n case 'r': return '\\r'\n case 't': return '\\t'\n case '\"': return '\"'\n default: return c\n }\n })\n }\n\n // Single-quoted: no escape processing, just strip quotes\n if (trimmed.startsWith('\\'') && trimmed.endsWith('\\''))\n return trimmed.slice(1, -1)\n\n return trimmed\n}\n\n/**\n * Parse a YAML `key: value` line, correctly handling colons inside quoted values.\n * Returns [key, value] or null if not a valid KV line.\n */\nexport function yamlParseKV(line: string): [string, string] | null {\n const trimmed = line.trim()\n // Find the first `: ` or `:\\n` or `:$` — the YAML key-value separator\n const colonIdx = trimmed.indexOf(':')\n if (colonIdx === -1)\n return null\n const key = trimmed.slice(0, colonIdx).trim()\n const rawValue = trimmed.slice(colonIdx + 1)\n if (!key)\n return null\n return [key, yamlUnescape(rawValue)]\n}\n"],"mappings":";;;;;;;;AAQA,MAAM,gBAAgB;;;;;AAMtB,SAAgB,WAAW,OAAuB;AAChD,KAAI,CAAC,cAAc,KAAK,MAAM,CAC5B,QAAO;AAQT,QAAO,IANS,MACb,QAAQ,OAAO,OAAO,CACtB,QAAQ,MAAM,OAAM,CACpB,QAAQ,OAAO,MAAM,CACrB,QAAQ,OAAO,MAAM,CACrB,QAAQ,OAAO,MAAM,CACL;;;;;;AAOrB,SAAgB,aAAa,KAAqB;CAChD,MAAM,UAAU,IAAI,MAAM;AAC1B,KAAI,CAAC,QACH,QAAO;AAGT,KAAI,QAAQ,WAAW,KAAI,IAAI,QAAQ,SAAS,KAAI,CAClD,QAAO,QAAQ,MAAM,GAAG,GAAG,CACxB,QAAQ,kBAAkB,GAAG,MAAM;AAClC,UAAQ,GAAR;GACE,KAAK,KAAM,QAAO;GAClB,KAAK,IAAK,QAAO;GACjB,KAAK,IAAK,QAAO;GACjB,KAAK,IAAK,QAAO;GACjB,KAAK,KAAK,QAAO;GACjB,QAAS,QAAO;;GAElB;AAIN,KAAI,QAAQ,WAAW,IAAK,IAAI,QAAQ,SAAS,IAAK,CACpD,QAAO,QAAQ,MAAM,GAAG,GAAG;AAE7B,QAAO;;;;;;AAOT,SAAgB,YAAY,MAAuC;CACjE,MAAM,UAAU,KAAK,MAAM;CAE3B,MAAM,WAAW,QAAQ,QAAQ,IAAI;AACrC,KAAI,aAAa,GACf,QAAO;CACT,MAAM,MAAM,QAAQ,MAAM,GAAG,SAAS,CAAC,MAAM;CAC7C,MAAM,WAAW,QAAQ,MAAM,WAAW,EAAE;AAC5C,KAAI,CAAC,IACH,QAAO;AACT,QAAO,CAAC,KAAK,aAAa,SAAS,CAAC"}
1
+ {"version":3,"file":"yaml.mjs","names":[],"sources":["../../src/core/yaml.ts"],"sourcesContent":["/**\n * Minimal YAML value escaping/unescaping for our hand-rolled parsers.\n *\n * Handles the characters that break naive `:` splitting and quote stripping:\n * colons, quotes, newlines, backslashes.\n */\n\n/** Characters that require double-quoting in YAML values */\nconst NEEDS_QUOTING = /[:\"'\\\\\\n\\r\\t#{}[\\],&*!|>%@`]/\n\n/**\n * Escape a value for safe YAML emission. Always double-quotes if the value\n * contains any special characters; returns unquoted for simple values.\n */\nexport function yamlEscape(value: string): string {\n if (!NEEDS_QUOTING.test(value))\n return value\n // Escape backslashes first, then double quotes, then control chars\n const escaped = value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t')\n return `\"${escaped}\"`\n}\n\n/**\n * Parse a raw YAML value string back to its actual value.\n * Handles double-quoted (with escapes), single-quoted, and unquoted values.\n */\nexport function yamlUnescape(raw: string): string {\n const trimmed = raw.trim()\n if (!trimmed)\n return ''\n\n // Double-quoted: single-pass escape processing to handle backslashes correctly\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"')) {\n return trimmed.slice(1, -1)\n .replace(/\\\\([\\\\nrt\"])/g, (_, c) => {\n switch (c) {\n case '\\\\': return '\\\\'\n case 'n': return '\\n'\n case 'r': return '\\r'\n case 't': return '\\t'\n case '\"': return '\"'\n default: return c\n }\n })\n }\n\n // Single-quoted: no escape processing, just strip quotes\n if (trimmed.startsWith('\\'') && trimmed.endsWith('\\''))\n return trimmed.slice(1, -1)\n\n return trimmed\n}\n\n/**\n * Parse a YAML `key: value` line, correctly handling colons inside quoted values.\n * Returns [key, value] or null if not a valid KV line.\n */\nexport function yamlParseKV(line: string): [string, string] | null {\n const trimmed = line.trim()\n // Find the first `: ` or `:\\n` or `:$` — the YAML key-value separator\n const colonIdx = trimmed.indexOf(':')\n if (colonIdx === -1)\n return null\n const key = trimmed.slice(0, colonIdx).trim()\n const rawValue = trimmed.slice(colonIdx + 1)\n if (!key)\n return null\n return [key, yamlUnescape(rawValue)]\n}\n"],"mappings":";;;;;;;;AAcA,KAAA,QAAgB,WAAW,KAAuB,IAAA,QAAA,SAAA,KAAA,CAAA,QAAA,QAAA,MAAA,GAAA,GAAA,CAAA,QAAA,kBAAA,GAAA,MAAA;AAChD,UAAK,GAAL;GASA,KAAO,KANS,QACb;;;;;;;GAaH;AACA,KAAI,QAAC,WACI,IAAA,IAAA,QAAA,SAAA,IAAA,CAAA,QAAA,QAAA,MAAA,GAAA,GAAA;AAGT,QAAI;;SAKS,YAAY,MAAA;OACjB,UAAU,KAAO,MAAA;OACjB,WAAU,QAAO,QAAA,IAAA;KACjB,aAAU,GAAO,QAAA;OACjB,MAAS,QAAO,MAAA,GAAA,SAAA,CAAA,MAAA;;KAElB,CAAA,IAAA,QAAA;AAIN,QAAI,CAAA,KAAQ,aAAW,SAAS,CAAA"}
@@ -1,12 +1,9 @@
1
- //#region src/core/config.d.ts
2
1
  interface FeaturesConfig {
3
2
  search: boolean;
4
3
  issues: boolean;
5
4
  discussions: boolean;
6
5
  releases: boolean;
7
6
  }
8
- //#endregion
9
- //#region src/agent/prompts/optional/types.d.ts
10
7
  interface SectionValidationWarning {
11
8
  warning: string;
12
9
  }
@@ -14,8 +11,6 @@ interface CustomPrompt {
14
11
  heading: string;
15
12
  body: string;
16
13
  }
17
- //#endregion
18
- //#region src/agent/prompts/prompt.d.ts
19
14
  type SkillSection = 'api-changes' | 'best-practices' | 'custom';
20
15
  /** Output file per section (inside .skilld/) */
21
16
  declare const SECTION_OUTPUT_FILES: Record<SkillSection, string>;
@@ -85,8 +80,6 @@ declare function buildAllSectionPrompts(opts: BuildSkillPromptOptions & {
85
80
  * - Strips agent-specific rules
86
81
  */
87
82
  declare function portabilizePrompt(prompt: string, section?: SkillSection): string;
88
- //#endregion
89
- //#region src/agent/prompts/skill.d.ts
90
83
  interface SkillOptions {
91
84
  name: string;
92
85
  version?: string;
@@ -125,8 +118,6 @@ interface SkillOptions {
125
118
  eject?: boolean;
126
119
  }
127
120
  declare function generateSkillMd(opts: SkillOptions): string;
128
- //#endregion
129
- //#region src/agent/types.d.ts
130
121
  /**
131
122
  * Agent types and interfaces
132
123
  */
@@ -138,8 +129,6 @@ interface SkillMetadata {
138
129
  releasedAt?: string;
139
130
  description?: string;
140
131
  }
141
- //#endregion
142
- //#region src/agent/clis/types.d.ts
143
132
  type OptimizeModel = 'opus' | 'sonnet' | 'haiku' | 'gemini-3.1-pro' | 'gemini-3-flash' | 'gpt-5.3-codex' | 'gpt-5.3-codex-spark' | 'gpt-5.2-codex' | `pi:${string}`;
144
133
  interface ModelInfo {
145
134
  id: OptimizeModel;
@@ -204,8 +193,6 @@ interface OptimizeResult {
204
193
  cost?: number;
205
194
  debugLogsDir?: string;
206
195
  }
207
- //#endregion
208
- //#region src/agent/clis/index.d.ts
209
196
  interface ToolProgressLog {
210
197
  message: (msg: string) => void;
211
198
  }
@@ -215,8 +202,6 @@ declare function getModelName(id: OptimizeModel): string;
215
202
  declare function getModelLabel(id: OptimizeModel): string;
216
203
  declare function getAvailableModels(): Promise<ModelInfo[]>;
217
204
  declare function optimizeDocs(opts: OptimizeDocsOptions): Promise<OptimizeResult>;
218
- //#endregion
219
- //#region src/agent/detect-imports.d.ts
220
205
  /**
221
206
  * Detect directly-used npm packages by scanning source files
222
207
  * Uses mlly for proper ES module parsing + tinyglobby for file discovery
@@ -235,8 +220,6 @@ interface DetectResult {
235
220
  * Async with gitignore support for proper spinner animation
236
221
  */
237
222
  declare function detectImportedPackages(cwd?: string): Promise<DetectResult>;
238
- //#endregion
239
- //#region src/agent/detect.d.ts
240
223
  /**
241
224
  * Detect which agents are installed on the system
242
225
  */
@@ -259,8 +242,6 @@ declare function detectProjectAgents(): AgentType[];
259
242
  * Get the version of an agent's CLI (if available)
260
243
  */
261
244
  declare function getAgentVersion(agentType: AgentType): string | null;
262
- //#endregion
263
- //#region src/agent/install.d.ts
264
245
  /**
265
246
  * Sanitize skill name for filesystem
266
247
  */
@@ -304,8 +285,6 @@ declare function linkSkillToAgents(skillName: string, sharedDir: string, cwd: st
304
285
  * Remove per-agent symlinks for a skill when removing from shared dir.
305
286
  */
306
287
  declare function unlinkSkillFromAgents(skillName: string, cwd: string, agentType?: AgentType): void;
307
- //#endregion
308
- //#region src/agent/targets/types.d.ts
309
288
  interface FrontmatterField {
310
289
  /** Field name in YAML frontmatter */
311
290
  name: string;
@@ -360,9 +339,6 @@ interface AgentTarget {
360
339
  /** Agent-specific instruction text for skill activation (written to instructionFile) */
361
340
  skillActivationHint?: string;
362
341
  }
363
- //#endregion
364
- //#region src/agent/targets/registry.d.ts
365
342
  declare const targets: Record<AgentType, AgentTarget>;
366
- //#endregion
367
343
  export { type AgentTarget, type AgentType, type CustomPrompt, type FrontmatterField, type ModelInfo, type OptimizeDocsOptions, type OptimizeModel, type OptimizeResult, SECTION_MERGE_ORDER, SECTION_OUTPUT_FILES, type SkillMetadata, type SkillOptions, type SkillSection, type StreamProgress, 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 };
368
344
  //# sourceMappingURL=index.d.mts.map