skilld 1.7.3 → 1.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/_chunks/agent.mjs +693 -599
  2. package/dist/_chunks/agent.mjs.map +1 -1
  3. package/dist/_chunks/assemble.mjs +3 -3
  4. package/dist/_chunks/author.mjs +51 -121
  5. package/dist/_chunks/author.mjs.map +1 -1
  6. package/dist/_chunks/cache.mjs +315 -9
  7. package/dist/_chunks/cache.mjs.map +1 -1
  8. package/dist/_chunks/cache2.mjs +2 -2
  9. package/dist/_chunks/cli-helpers.mjs +3 -3
  10. package/dist/_chunks/core.mjs +7 -4
  11. package/dist/_chunks/detect.mjs +1 -1
  12. package/dist/_chunks/embedding-cache2.mjs +2 -2
  13. package/dist/_chunks/index.d.mts +305 -112
  14. package/dist/_chunks/index.d.mts.map +1 -1
  15. package/dist/_chunks/index2.d.mts +267 -32
  16. package/dist/_chunks/index2.d.mts.map +1 -1
  17. package/dist/_chunks/index3.d.mts +32 -577
  18. package/dist/_chunks/index3.d.mts.map +1 -1
  19. package/dist/_chunks/index4.d.mts +553 -0
  20. package/dist/_chunks/index4.d.mts.map +1 -0
  21. package/dist/_chunks/install.mjs +48 -88
  22. package/dist/_chunks/install.mjs.map +1 -1
  23. package/dist/_chunks/list.mjs +1 -1
  24. package/dist/_chunks/lockfile.mjs +29 -6
  25. package/dist/_chunks/lockfile.mjs.map +1 -1
  26. package/dist/_chunks/monorepo.mjs +71 -0
  27. package/dist/_chunks/monorepo.mjs.map +1 -0
  28. package/dist/_chunks/{shared.mjs → package-registry.mjs} +2 -40
  29. package/dist/_chunks/package-registry.mjs.map +1 -0
  30. package/dist/_chunks/paths.mjs +49 -0
  31. package/dist/_chunks/paths.mjs.map +1 -0
  32. package/dist/_chunks/pool2.mjs +1 -1
  33. package/dist/_chunks/prepare.mjs +1 -1
  34. package/dist/_chunks/prepare2.mjs +5 -5
  35. package/dist/_chunks/prepare2.mjs.map +1 -1
  36. package/dist/_chunks/prompts.mjs +366 -18
  37. package/dist/_chunks/prompts.mjs.map +1 -1
  38. package/dist/_chunks/search-helpers.mjs +5 -6
  39. package/dist/_chunks/search-helpers.mjs.map +1 -1
  40. package/dist/_chunks/search-interactive.mjs +1 -1
  41. package/dist/_chunks/search.mjs +1 -2
  42. package/dist/_chunks/search.mjs.map +1 -1
  43. package/dist/_chunks/semver.mjs +13 -0
  44. package/dist/_chunks/semver.mjs.map +1 -0
  45. package/dist/_chunks/skill-installer.mjs +2 -0
  46. package/dist/_chunks/skill-installer2.mjs +155 -0
  47. package/dist/_chunks/skill-installer2.mjs.map +1 -0
  48. package/dist/_chunks/skills.mjs +10 -9
  49. package/dist/_chunks/skills.mjs.map +1 -1
  50. package/dist/_chunks/sources.mjs +549 -372
  51. package/dist/_chunks/sources.mjs.map +1 -1
  52. package/dist/_chunks/sync-pipeline.mjs +952 -0
  53. package/dist/_chunks/sync-pipeline.mjs.map +1 -0
  54. package/dist/_chunks/sync-registry.mjs +19 -13
  55. package/dist/_chunks/sync-registry.mjs.map +1 -1
  56. package/dist/_chunks/sync.mjs +797 -886
  57. package/dist/_chunks/sync.mjs.map +1 -1
  58. package/dist/_chunks/sync2.mjs +4 -2
  59. package/dist/_chunks/types.d.mts +65 -77
  60. package/dist/_chunks/types.d.mts.map +1 -1
  61. package/dist/_chunks/types2.d.mts +88 -0
  62. package/dist/_chunks/types2.d.mts.map +1 -0
  63. package/dist/_chunks/uninstall.mjs +7 -8
  64. package/dist/_chunks/uninstall.mjs.map +1 -1
  65. package/dist/_chunks/upload.mjs +2 -2
  66. package/dist/_chunks/validate.mjs +1 -1
  67. package/dist/_chunks/version.mjs +3 -13
  68. package/dist/_chunks/version.mjs.map +1 -1
  69. package/dist/_chunks/wizard.mjs +2 -2
  70. package/dist/agent/index.d.mts +2 -346
  71. package/dist/agent/index.mjs +2 -3
  72. package/dist/cache/index.d.mts +2 -2
  73. package/dist/cache/index.mjs +4 -3
  74. package/dist/cli.mjs +12 -13
  75. package/dist/cli.mjs.map +1 -1
  76. package/dist/index.d.mts +5 -4
  77. package/dist/index.mjs +4 -3
  78. package/dist/prepare.mjs +2 -2
  79. package/dist/prepare.mjs.map +1 -1
  80. package/dist/retriv/index.d.mts +2 -2
  81. package/dist/retriv/worker.d.mts +1 -1
  82. package/dist/sources/index.d.mts +3 -2
  83. package/dist/sources/index.mjs +3 -3
  84. package/dist/types.d.mts +3 -3
  85. package/package.json +2 -2
  86. package/dist/_chunks/config.mjs +0 -122
  87. package/dist/_chunks/config.mjs.map +0 -1
  88. package/dist/_chunks/prefix.mjs +0 -108
  89. package/dist/_chunks/prefix.mjs.map +0 -1
  90. package/dist/_chunks/shared.mjs.map +0 -1
  91. package/dist/_chunks/skill.mjs +0 -329
  92. package/dist/_chunks/skill.mjs.map +0 -1
  93. package/dist/_chunks/sync-shared.mjs +0 -2
  94. package/dist/_chunks/sync-shared2.mjs +0 -1020
  95. package/dist/_chunks/sync-shared2.mjs.map +0 -1
  96. package/dist/agent/index.d.mts.map +0 -1
@@ -1,1020 +0,0 @@
1
- import { a as getModelLabel, i as getAvailableModels, o as getModelName, r as createToolProgress, s as optimizeDocs } from "./agent.mjs";
2
- import { c as getRepoCacheDir, s as getPackageDbPath, t as getCacheDir } from "./version.mjs";
3
- import { i as readPackageJsonSafe } from "./package-json.mjs";
4
- import { n as linkShippedSkill, r as resolvePkgDir, t as getShippedSkills } from "./prepare.mjs";
5
- import { n as sanitizeMarkdown } from "./sanitize.mjs";
6
- import { a as hasShippedDocs, d as linkPkgNamed, f as linkRepoCachedDir, h as readCachedDocs, l as linkCachedDir, m as listReferenceFiles, n as clearCache, u as linkPkg, v as writeToCache, y as writeToRepoCache } from "./cache.mjs";
7
- import { i as parseFrontmatter } from "./markdown.mjs";
8
- import { o as listIndexIds, r as createIndex, t as SearchDepsUnavailableError } from "./retriv.mjs";
9
- import { a as semverDiff, c as getBlogPreset, n as getSharedSkillsDir, p as getPrereleaseChangelogRef } from "./shared.mjs";
10
- import { A as filterFrameworkDocs, B as fetchGitHubIssues, C as toCrawlPattern, E as fetchGitDocs, H as generateIssueIndex, I as fetchLlmsTxt, J as isPrerelease, K as fetchReleaseNotes, P as downloadLlmsDocs, Q as fetchGitHubRaw, R as normalizeLlmsLinks, S as fetchCrawledDocs, U as isGhAvailable, V as formatIssueAsMarkdown, W as fetchBlogReleases, _ as resolveEntryFiles, b as formatDiscussionAsMarkdown, j as isShallowGitDocs, k as fetchReadmeContent, n as fetchNpmPackage, q as generateReleaseIndex, rt as parseGitHubUrl, u as resolveLocalPackageDocs, v as generateDocsIndex, x as generateDiscussionIndex, y as fetchGitHubDiscussions } from "./sources.mjs";
11
- import { a as targets } from "./detect.mjs";
12
- import { l as maxItems, n as SECTION_OUTPUT_FILES, r as buildAllSectionPrompts, u as maxLines } from "./prompts.mjs";
13
- import { n as writeGeneratedSkillMd, v as todayIsoDate } from "./skill.mjs";
14
- import { a as readConfig, c as updateConfig, o as registerProject, t as defaultFeatures } from "./config.mjs";
15
- import { g as pickModel, n as NO_MODELS_MESSAGE, p as isInteractive } from "./cli-helpers.mjs";
16
- import { a as readLock, c as writeLock, r as parsePackages } from "./lockfile.mjs";
17
- import { join, relative, resolve } from "pathe";
18
- import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
19
- import * as p from "@clack/prompts";
20
- const MAX_INDEX_DOCS = 250;
21
- const RESOLVE_STEP_LABELS = {
22
- "npm": "npm registry",
23
- "github-docs": "GitHub docs",
24
- "github-meta": "GitHub meta",
25
- "github-search": "GitHub search",
26
- "readme": "README",
27
- "llms.txt": "llms.txt",
28
- "crawl": "website crawl",
29
- "local": "node_modules"
30
- };
31
- function classifyCachedDoc(path) {
32
- const issueMatch = path.match(/^issues\/issue-(\d+)\.md$/);
33
- if (issueMatch) return {
34
- type: "issue",
35
- number: Number(issueMatch[1])
36
- };
37
- const discussionMatch = path.match(/^discussions\/discussion-(\d+)\.md$/);
38
- if (discussionMatch) return {
39
- type: "discussion",
40
- number: Number(discussionMatch[1])
41
- };
42
- if (path.startsWith("releases/")) return { type: "release" };
43
- return { type: "doc" };
44
- }
45
- async function findRelatedSkills(packageName, skillsDir) {
46
- const related = [];
47
- const npmInfo = await fetchNpmPackage(packageName);
48
- if (!npmInfo?.dependencies) return related;
49
- const deps = new Set(Object.keys(npmInfo.dependencies));
50
- if (!existsSync(skillsDir)) return related;
51
- const lock = readLock(skillsDir);
52
- const pkgToDirName = /* @__PURE__ */ new Map();
53
- if (lock) for (const [dirName, info] of Object.entries(lock.skills)) {
54
- if (info.packageName) pkgToDirName.set(info.packageName, dirName);
55
- for (const pkg of parsePackages(info.packages)) pkgToDirName.set(pkg.name, dirName);
56
- }
57
- const installedSkills = readdirSync(skillsDir);
58
- const installedSet = new Set(installedSkills);
59
- for (const dep of deps) {
60
- const dirName = pkgToDirName.get(dep);
61
- if (dirName && installedSet.has(dirName)) related.push(dirName);
62
- }
63
- return related.slice(0, 5);
64
- }
65
- function forceClearCache(packageName, version, repoInfo) {
66
- clearCache(packageName, version);
67
- const forcedDbPath = getPackageDbPath(packageName, version);
68
- if (existsSync(forcedDbPath)) rmSync(forcedDbPath, {
69
- recursive: true,
70
- force: true
71
- });
72
- if (repoInfo) {
73
- const repoDir = getRepoCacheDir(repoInfo.owner, repoInfo.repo);
74
- if (existsSync(repoDir)) rmSync(repoDir, {
75
- recursive: true,
76
- force: true
77
- });
78
- }
79
- }
80
- function linkAllReferences(skillDir, packageName, cwd, version, docsType, extraPackages, features, repoInfo) {
81
- const f = features ?? readConfig().features ?? defaultFeatures;
82
- try {
83
- linkPkg(skillDir, packageName, cwd, version);
84
- linkPkgNamed(skillDir, packageName, cwd, version);
85
- if (!hasShippedDocs(packageName, cwd, version) && docsType !== "readme") linkCachedDir(skillDir, packageName, version, "docs");
86
- if (f.issues) if (repoInfo) linkRepoCachedDir(skillDir, repoInfo.owner, repoInfo.repo, "issues");
87
- else linkCachedDir(skillDir, packageName, version, "issues");
88
- if (f.discussions) if (repoInfo) linkRepoCachedDir(skillDir, repoInfo.owner, repoInfo.repo, "discussions");
89
- else linkCachedDir(skillDir, packageName, version, "discussions");
90
- if (f.releases) if (repoInfo) linkRepoCachedDir(skillDir, repoInfo.owner, repoInfo.repo, "releases");
91
- else linkCachedDir(skillDir, packageName, version, "releases");
92
- linkCachedDir(skillDir, packageName, version, "sections");
93
- if (extraPackages) {
94
- for (const pkg of extraPackages) if (pkg.name !== packageName) linkPkgNamed(skillDir, pkg.name, cwd, pkg.version);
95
- }
96
- } catch {}
97
- }
98
- function detectDocsType(packageName, version, repoUrl, llmsUrl) {
99
- const cacheDir = getCacheDir(packageName, version);
100
- if (existsSync(join(cacheDir, "docs", "index.md")) || existsSync(join(cacheDir, "docs", "guide"))) return {
101
- docsType: "docs",
102
- docSource: repoUrl ? `${repoUrl}/tree/v${version}/docs` : "git"
103
- };
104
- if (existsSync(join(cacheDir, "llms.txt"))) return {
105
- docsType: "llms.txt",
106
- docSource: llmsUrl || "llms.txt"
107
- };
108
- if (existsSync(join(cacheDir, "docs", "README.md"))) return { docsType: "readme" };
109
- return { docsType: "readme" };
110
- }
111
- function handleShippedSkills(packageName, version, cwd, agent, global) {
112
- const shippedSkills = getShippedSkills(packageName, cwd, version);
113
- if (shippedSkills.length === 0) return null;
114
- const baseDir = resolveBaseDir(cwd, agent, global);
115
- mkdirSync(baseDir, { recursive: true });
116
- for (const shipped of shippedSkills) {
117
- linkShippedSkill(baseDir, shipped.skillName, shipped.skillDir);
118
- writeLock(baseDir, shipped.skillName, {
119
- packageName,
120
- version,
121
- source: "shipped",
122
- syncedAt: todayIsoDate(),
123
- generator: "skilld"
124
- });
125
- }
126
- if (!global) registerProject(cwd);
127
- return {
128
- shipped: shippedSkills,
129
- baseDir
130
- };
131
- }
132
- function resolveBaseDir(cwd, agent, global) {
133
- if (global) return targets[agent].globalSkillsDir;
134
- const shared = getSharedSkillsDir(cwd);
135
- if (shared) return shared;
136
- const agentConfig = targets[agent];
137
- return join(cwd, agentConfig.skillsDir);
138
- }
139
- async function resolveLocalDep(packageName, cwd) {
140
- const result = readPackageJsonSafe(join(cwd, "package.json"));
141
- if (!result) return null;
142
- const pkg = result.parsed;
143
- const depVersion = {
144
- ...pkg.dependencies,
145
- ...pkg.devDependencies
146
- }[packageName];
147
- if (!depVersion?.startsWith("link:")) return null;
148
- return resolveLocalPackageDocs(resolve(cwd, depVersion.slice(5)));
149
- }
150
- function detectChangelog(pkgDir, cacheDir) {
151
- if (pkgDir) {
152
- const found = ["CHANGELOG.md", "changelog.md"].find((f) => existsSync(join(pkgDir, f)));
153
- if (found) return `pkg/${found}`;
154
- }
155
- if (cacheDir && existsSync(join(cacheDir, "releases", "CHANGELOG.md"))) return "releases/CHANGELOG.md";
156
- return false;
157
- }
158
- async function fetchAndCacheResources(opts) {
159
- const { packageName, resolved, version, onProgress } = opts;
160
- const features = opts.features ?? readConfig().features ?? defaultFeatures;
161
- const cacheInvalidated = opts.useCache && resolved.crawlUrl && detectDocsType(packageName, version, resolved.repoUrl, resolved.llmsUrl).docsType === "readme";
162
- const useCache = opts.useCache && !cacheInvalidated;
163
- let docSource = resolved.readmeUrl || "readme";
164
- let docsType = "readme";
165
- const docsToIndex = [];
166
- const warnings = [];
167
- if (cacheInvalidated) warnings.push(`Retrying crawl for ${resolved.crawlUrl} (previous attempt only cached README)`);
168
- if (!useCache) {
169
- const cachedDocs = [];
170
- const isFrameworkDoc = (path) => filterFrameworkDocs([path], packageName).length > 0;
171
- if (resolved.gitDocsUrl && resolved.repoUrl) {
172
- const gh = parseGitHubUrl(resolved.repoUrl);
173
- if (gh) {
174
- onProgress("Fetching git docs");
175
- const gitDocs = await fetchGitDocs(gh.owner, gh.repo, version, packageName);
176
- if (gitDocs?.fallback) warnings.push(`Docs fetched from ${gitDocs.ref} branch (no tag found for v${version})`);
177
- if (gitDocs && gitDocs.files.length > 0) {
178
- const BATCH_SIZE = 20;
179
- const results = [];
180
- for (let i = 0; i < gitDocs.files.length; i += BATCH_SIZE) {
181
- const batch = gitDocs.files.slice(i, i + BATCH_SIZE);
182
- onProgress(`Downloading docs ${Math.min(i + BATCH_SIZE, gitDocs.files.length)}/${gitDocs.files.length} from ${gitDocs.ref}`);
183
- const batchResults = await Promise.all(batch.map(async (file) => {
184
- const content = await fetchGitHubRaw(`${gitDocs.baseUrl}/${file}`);
185
- if (!content) return null;
186
- return {
187
- file,
188
- content
189
- };
190
- }));
191
- results.push(...batchResults);
192
- }
193
- for (const r of results) if (r) {
194
- const stripped = gitDocs.docsPrefix ? r.file.replace(gitDocs.docsPrefix, "") : r.file;
195
- const cachePath = stripped.startsWith("docs/") ? stripped : `docs/${stripped}`;
196
- cachedDocs.push({
197
- path: cachePath,
198
- content: r.content
199
- });
200
- docsToIndex.push({
201
- id: cachePath,
202
- content: r.content,
203
- metadata: {
204
- package: packageName,
205
- source: cachePath,
206
- type: "doc"
207
- }
208
- });
209
- }
210
- const downloaded = results.filter(Boolean).length;
211
- if (downloaded > 0) if (isShallowGitDocs(downloaded) && resolved.llmsUrl) {
212
- onProgress(`Shallow git-docs (${downloaded} files), trying llms.txt`);
213
- cachedDocs.length = 0;
214
- docsToIndex.length = 0;
215
- } else {
216
- docSource = `${resolved.repoUrl}/tree/${gitDocs.ref}/docs`;
217
- docsType = "docs";
218
- writeToCache(packageName, version, cachedDocs);
219
- if (resolved.llmsUrl) {
220
- onProgress("Caching supplementary llms.txt");
221
- const llmsContent = await fetchLlmsTxt(resolved.llmsUrl);
222
- if (llmsContent) {
223
- const baseUrl = resolved.docsUrl || new URL(resolved.llmsUrl).origin;
224
- const supplementary = [{
225
- path: "llms.txt",
226
- content: normalizeLlmsLinks(llmsContent.raw, baseUrl)
227
- }];
228
- if (llmsContent.links.length > 0) {
229
- onProgress(`Downloading ${llmsContent.links.length} supplementary docs`);
230
- const docs = await downloadLlmsDocs(llmsContent, baseUrl, (url, done, total) => {
231
- onProgress(`Downloading supplementary doc ${done + 1}/${total}`);
232
- });
233
- for (const doc of docs) {
234
- if (!isFrameworkDoc(doc.url)) continue;
235
- const localPath = doc.url.startsWith("/") ? doc.url.slice(1) : doc.url;
236
- supplementary.push({
237
- path: join("llms-docs", ...localPath.split("/")),
238
- content: doc.content
239
- });
240
- }
241
- }
242
- writeToCache(packageName, version, supplementary);
243
- }
244
- }
245
- }
246
- }
247
- }
248
- }
249
- if (resolved.crawlUrl && cachedDocs.length === 0) {
250
- onProgress("Crawling website");
251
- const crawledDocs = await fetchCrawledDocs(resolved.crawlUrl, onProgress).catch((err) => {
252
- warnings.push(`Crawl failed for ${resolved.crawlUrl}: ${err?.message || err}`);
253
- return [];
254
- });
255
- if (crawledDocs.length === 0 && resolved.crawlUrl) warnings.push(`Crawl returned 0 docs from ${resolved.crawlUrl}`);
256
- if (crawledDocs.length > 0) {
257
- for (const doc of crawledDocs) {
258
- if (!isFrameworkDoc(doc.path)) continue;
259
- cachedDocs.push(doc);
260
- docsToIndex.push({
261
- id: doc.path,
262
- content: doc.content,
263
- metadata: {
264
- package: packageName,
265
- source: doc.path,
266
- type: "doc"
267
- }
268
- });
269
- }
270
- docSource = resolved.crawlUrl;
271
- docsType = "docs";
272
- writeToCache(packageName, version, cachedDocs);
273
- }
274
- }
275
- if (resolved.llmsUrl && cachedDocs.length === 0) {
276
- onProgress("Fetching llms.txt");
277
- const llmsContent = await fetchLlmsTxt(resolved.llmsUrl);
278
- if (llmsContent) {
279
- docSource = resolved.llmsUrl;
280
- docsType = "llms.txt";
281
- const baseUrl = resolved.docsUrl || new URL(resolved.llmsUrl).origin;
282
- cachedDocs.push({
283
- path: "llms.txt",
284
- content: normalizeLlmsLinks(llmsContent.raw, baseUrl)
285
- });
286
- if (llmsContent.links.length > 0) {
287
- onProgress(`Downloading ${llmsContent.links.length} linked docs`);
288
- const docs = await downloadLlmsDocs(llmsContent, baseUrl, (url, done, total) => {
289
- onProgress(`Downloading linked doc ${done + 1}/${total}`);
290
- });
291
- for (const doc of docs) {
292
- if (!isFrameworkDoc(doc.url)) continue;
293
- const cachePath = join("docs", ...(doc.url.startsWith("/") ? doc.url.slice(1) : doc.url).split("/"));
294
- cachedDocs.push({
295
- path: cachePath,
296
- content: doc.content
297
- });
298
- docsToIndex.push({
299
- id: doc.url,
300
- content: doc.content,
301
- metadata: {
302
- package: packageName,
303
- source: cachePath,
304
- type: "doc"
305
- }
306
- });
307
- }
308
- if (docs.length > 0) docsType = "docs";
309
- }
310
- writeToCache(packageName, version, cachedDocs);
311
- }
312
- }
313
- if (resolved.docsUrl && !cachedDocs.some((d) => d.path.startsWith("docs/"))) {
314
- const crawlPattern = resolved.crawlUrl || toCrawlPattern(resolved.docsUrl);
315
- onProgress("Crawling docs site");
316
- const crawledDocs = await fetchCrawledDocs(crawlPattern, onProgress, resolved.crawlUrl ? 200 : 400).catch((err) => {
317
- warnings.push(`Crawl failed for ${crawlPattern}: ${err?.message || err}`);
318
- return [];
319
- });
320
- if (crawledDocs.length > 0) {
321
- for (const doc of crawledDocs) {
322
- if (!isFrameworkDoc(doc.path)) continue;
323
- cachedDocs.push(doc);
324
- docsToIndex.push({
325
- id: doc.path,
326
- content: doc.content,
327
- metadata: {
328
- package: packageName,
329
- source: doc.path,
330
- type: "doc"
331
- }
332
- });
333
- }
334
- docSource = crawlPattern;
335
- docsType = "docs";
336
- writeToCache(packageName, version, cachedDocs);
337
- }
338
- }
339
- if (resolved.readmeUrl && cachedDocs.length === 0) {
340
- onProgress("Fetching README");
341
- const content = await fetchReadmeContent(resolved.readmeUrl);
342
- if (content) {
343
- cachedDocs.push({
344
- path: "docs/README.md",
345
- content
346
- });
347
- docsToIndex.push({
348
- id: "README.md",
349
- content,
350
- metadata: {
351
- package: packageName,
352
- source: "docs/README.md",
353
- type: "doc"
354
- }
355
- });
356
- writeToCache(packageName, version, cachedDocs);
357
- }
358
- }
359
- if (docsType !== "readme" && cachedDocs.filter((d) => d.path.startsWith("docs/") && d.path.endsWith(".md")).length > 1) {
360
- const docsIndex = generateDocsIndex(cachedDocs);
361
- if (docsIndex) writeToCache(packageName, version, [{
362
- path: "docs/_INDEX.md",
363
- content: docsIndex
364
- }]);
365
- }
366
- } else {
367
- onProgress("Loading cached docs");
368
- const detected = detectDocsType(packageName, version, resolved.repoUrl, resolved.llmsUrl);
369
- docsType = detected.docsType;
370
- if (detected.docSource) docSource = detected.docSource;
371
- if (!existsSync(getPackageDbPath(packageName, version))) {
372
- onProgress("Reading cached docs for indexing");
373
- const cached = readCachedDocs(packageName, version);
374
- for (const doc of cached) docsToIndex.push({
375
- id: doc.path,
376
- content: doc.content,
377
- metadata: {
378
- package: packageName,
379
- source: doc.path,
380
- ...classifyCachedDoc(doc.path)
381
- }
382
- });
383
- }
384
- if (docsType !== "readme" && !existsSync(join(getCacheDir(packageName, version), "docs", "_INDEX.md"))) {
385
- onProgress("Generating docs index");
386
- const cached = readCachedDocs(packageName, version);
387
- if (cached.filter((d) => d.path.startsWith("docs/") && d.path.endsWith(".md")).length > 1) {
388
- const docsIndex = generateDocsIndex(cached);
389
- if (docsIndex) writeToCache(packageName, version, [{
390
- path: "docs/_INDEX.md",
391
- content: docsIndex
392
- }]);
393
- }
394
- }
395
- }
396
- const gh = resolved.repoUrl ? parseGitHubUrl(resolved.repoUrl) : null;
397
- const repoInfo = gh ? {
398
- owner: gh.owner,
399
- repo: gh.repo
400
- } : void 0;
401
- const repoCacheDir = repoInfo ? getRepoCacheDir(repoInfo.owner, repoInfo.repo) : null;
402
- const cacheDir = getCacheDir(packageName, version);
403
- const issuesDir = repoCacheDir ? join(repoCacheDir, "issues") : join(cacheDir, "issues");
404
- const discussionsDir = repoCacheDir ? join(repoCacheDir, "discussions") : join(cacheDir, "discussions");
405
- const releasesPath = repoCacheDir ? join(repoCacheDir, "releases") : join(cacheDir, "releases");
406
- if (features.issues && gh && isGhAvailable() && !existsSync(issuesDir)) {
407
- onProgress("Fetching issues via GitHub API");
408
- const issues = await fetchGitHubIssues(gh.owner, gh.repo, 30, resolved.releasedAt, opts.from).catch(() => []);
409
- if (issues.length > 0) {
410
- onProgress(`Caching ${issues.length} issues`);
411
- const issueDocs = [...issues.map((issue) => ({
412
- path: `issues/issue-${issue.number}.md`,
413
- content: formatIssueAsMarkdown(issue)
414
- })), {
415
- path: "issues/_INDEX.md",
416
- content: generateIssueIndex(issues)
417
- }];
418
- if (repoInfo) writeToRepoCache(repoInfo.owner, repoInfo.repo, issueDocs);
419
- else writeToCache(packageName, version, issueDocs);
420
- for (const issue of issues) docsToIndex.push({
421
- id: `issue-${issue.number}`,
422
- content: sanitizeMarkdown(`#${issue.number}: ${issue.title}\n\n${issue.body || ""}`),
423
- metadata: {
424
- package: packageName,
425
- source: `issues/issue-${issue.number}.md`,
426
- type: "issue",
427
- number: issue.number
428
- }
429
- });
430
- }
431
- }
432
- if (features.discussions && gh && isGhAvailable() && !existsSync(discussionsDir)) {
433
- onProgress("Fetching discussions via GitHub API");
434
- const discussions = await fetchGitHubDiscussions(gh.owner, gh.repo, 20, resolved.releasedAt, opts.from).catch(() => []);
435
- if (discussions.length > 0) {
436
- onProgress(`Caching ${discussions.length} discussions`);
437
- const discussionDocs = [...discussions.map((d) => ({
438
- path: `discussions/discussion-${d.number}.md`,
439
- content: formatDiscussionAsMarkdown(d)
440
- })), {
441
- path: "discussions/_INDEX.md",
442
- content: generateDiscussionIndex(discussions)
443
- }];
444
- if (repoInfo) writeToRepoCache(repoInfo.owner, repoInfo.repo, discussionDocs);
445
- else writeToCache(packageName, version, discussionDocs);
446
- for (const d of discussions) docsToIndex.push({
447
- id: `discussion-${d.number}`,
448
- content: sanitizeMarkdown(`#${d.number}: ${d.title}\n\n${d.body || ""}`),
449
- metadata: {
450
- package: packageName,
451
- source: `discussions/discussion-${d.number}.md`,
452
- type: "discussion",
453
- number: d.number
454
- }
455
- });
456
- }
457
- }
458
- if (features.releases && gh && isGhAvailable() && !existsSync(releasesPath)) {
459
- onProgress("Fetching releases via GitHub API");
460
- const changelogRef = isPrerelease(version) ? getPrereleaseChangelogRef(packageName) : void 0;
461
- const releaseDocs = await fetchReleaseNotes(gh.owner, gh.repo, version, resolved.gitRef, packageName, opts.from, changelogRef).catch(() => []);
462
- let blogDocs = [];
463
- if (getBlogPreset(packageName)) {
464
- onProgress("Fetching blog release notes");
465
- blogDocs = await fetchBlogReleases(packageName, version).catch(() => []);
466
- }
467
- const allDocs = [...releaseDocs, ...blogDocs];
468
- const blogEntries = blogDocs.filter((d) => !d.path.endsWith("_INDEX.md")).map((d) => {
469
- const versionMatch = d.path.match(/blog-(.+)\.md$/);
470
- const fm = parseFrontmatter(d.content);
471
- return {
472
- version: versionMatch?.[1] ?? "",
473
- title: fm.title ?? `Release ${versionMatch?.[1]}`,
474
- date: fm.date ?? ""
475
- };
476
- }).filter((b) => b.version);
477
- const ghReleases = releaseDocs.filter((d) => d.path.startsWith("releases/") && !d.path.endsWith("CHANGELOG.md")).map((d) => {
478
- const fm = parseFrontmatter(d.content);
479
- const tag = fm.tag ?? "";
480
- const name = fm.name ?? tag;
481
- const published = fm.published ?? "";
482
- return {
483
- id: 0,
484
- tag,
485
- name,
486
- prerelease: false,
487
- createdAt: published,
488
- publishedAt: published,
489
- markdown: ""
490
- };
491
- }).filter((r) => r.tag);
492
- const hasChangelog = allDocs.some((d) => d.path === "releases/CHANGELOG.md");
493
- if (ghReleases.length > 0 || blogEntries.length > 0) allDocs.push({
494
- path: "releases/_INDEX.md",
495
- content: generateReleaseIndex({
496
- releases: ghReleases,
497
- packageName,
498
- blogReleases: blogEntries,
499
- hasChangelog
500
- })
501
- });
502
- if (allDocs.length > 0) {
503
- onProgress(`Caching ${allDocs.length} releases`);
504
- if (repoInfo) writeToRepoCache(repoInfo.owner, repoInfo.repo, allDocs);
505
- else writeToCache(packageName, version, allDocs);
506
- for (const doc of allDocs) docsToIndex.push({
507
- id: doc.path,
508
- content: doc.content,
509
- metadata: {
510
- package: packageName,
511
- source: doc.path,
512
- type: "release"
513
- }
514
- });
515
- }
516
- }
517
- return {
518
- docSource,
519
- docsType,
520
- docsToIndex,
521
- hasIssues: features.issues && existsSync(issuesDir),
522
- hasDiscussions: features.discussions && existsSync(discussionsDir),
523
- hasReleases: features.releases && existsSync(releasesPath),
524
- warnings,
525
- repoInfo,
526
- usedCache: useCache
527
- };
528
- }
529
- function parentDocId(id) {
530
- const idx = id.indexOf("#chunk-");
531
- return idx === -1 ? id : id.slice(0, idx);
532
- }
533
- function capDocs(allDocs, max, onProgress) {
534
- if (allDocs.length <= max) return;
535
- const TYPE_PRIORITY = {
536
- doc: 0,
537
- issue: 1,
538
- discussion: 2,
539
- release: 3,
540
- source: 4,
541
- types: 5
542
- };
543
- allDocs.sort((a, b) => {
544
- const ta = TYPE_PRIORITY[a.metadata?.type || "doc"] ?? 3;
545
- const tb = TYPE_PRIORITY[b.metadata?.type || "doc"] ?? 3;
546
- if (ta !== tb) return ta - tb;
547
- return a.id.localeCompare(b.id);
548
- });
549
- onProgress(`Indexing capped at ${max}/${allDocs.length} docs (prioritized by type)`);
550
- allDocs.length = max;
551
- }
552
- async function indexResources(opts) {
553
- const { packageName, version, cwd, onProgress } = opts;
554
- const features = opts.features ?? readConfig().features ?? defaultFeatures;
555
- if (!features.search) return;
556
- const dbPath = getPackageDbPath(packageName, version);
557
- const dbExists = existsSync(dbPath);
558
- const allDocs = [...opts.docsToIndex];
559
- const pkgDir = resolvePkgDir(packageName, cwd, version);
560
- if (features.search && pkgDir) {
561
- onProgress("Scanning exports");
562
- const entryFiles = await resolveEntryFiles(pkgDir);
563
- for (const e of entryFiles) allDocs.push({
564
- id: e.path,
565
- content: e.content,
566
- metadata: {
567
- package: packageName,
568
- source: `pkg/${e.path}`,
569
- type: e.type
570
- }
571
- });
572
- }
573
- if (allDocs.length === 0) return;
574
- capDocs(allDocs, MAX_INDEX_DOCS, onProgress);
575
- if (!dbExists) {
576
- onProgress(`Building search index (${allDocs.length} docs)`);
577
- try {
578
- await createIndex(allDocs, {
579
- dbPath,
580
- onProgress: ({ phase, current, total }) => {
581
- if (phase === "storing") {
582
- const d = allDocs[current - 1];
583
- onProgress(`Storing ${d?.metadata?.type === "source" || d?.metadata?.type === "types" ? "code" : d?.metadata?.type || "doc"} (${current}/${total})`);
584
- } else if (phase === "embedding") onProgress(`Creating embeddings (${current}/${total})`);
585
- }
586
- });
587
- } catch (err) {
588
- if (err instanceof SearchDepsUnavailableError) onProgress("Search indexing skipped (native deps unavailable)");
589
- else throw err;
590
- }
591
- return;
592
- }
593
- let existingIds;
594
- try {
595
- existingIds = await listIndexIds({ dbPath });
596
- } catch (err) {
597
- if (err instanceof SearchDepsUnavailableError) {
598
- onProgress("Search indexing skipped (native deps unavailable)");
599
- return;
600
- }
601
- throw err;
602
- }
603
- const existingParentIds = new Set(existingIds.map(parentDocId));
604
- const incomingIds = new Set(allDocs.map((d) => d.id));
605
- const newDocs = allDocs.filter((d) => !existingParentIds.has(d.id));
606
- const removeIds = existingIds.filter((id) => !incomingIds.has(parentDocId(id)));
607
- if (newDocs.length === 0 && removeIds.length === 0) {
608
- onProgress("Search index up to date");
609
- return;
610
- }
611
- const parts = [];
612
- if (newDocs.length > 0) parts.push(`+${newDocs.length} new`);
613
- if (removeIds.length > 0) parts.push(`-${removeIds.length} stale`);
614
- onProgress(`Updating search index (${parts.join(", ")})`);
615
- try {
616
- await createIndex(newDocs, {
617
- dbPath,
618
- removeIds,
619
- onProgress: ({ phase, current, total }) => {
620
- if (phase === "storing") {
621
- const d = newDocs[current - 1];
622
- onProgress(`Storing ${d?.metadata?.type === "source" || d?.metadata?.type === "types" ? "code" : d?.metadata?.type || "doc"} (${current}/${total})`);
623
- } else if (phase === "embedding") onProgress(`Creating embeddings (${current}/${total})`);
624
- }
625
- });
626
- } catch (err) {
627
- if (err instanceof SearchDepsUnavailableError) onProgress("Search indexing skipped (native deps unavailable)");
628
- else throw err;
629
- }
630
- }
631
- function ejectReferences(skillDir, packageName, cwd, version, docsType, features, repoInfo) {
632
- const f = features ?? readConfig().features ?? defaultFeatures;
633
- const cacheDir = getCacheDir(packageName, version);
634
- const refsDir = join(skillDir, "references");
635
- const repoDir = repoInfo ? getRepoCacheDir(repoInfo.owner, repoInfo.repo) : cacheDir;
636
- if (!hasShippedDocs(packageName, cwd, version) && docsType !== "readme") copyCachedSubdir(cacheDir, refsDir, "docs");
637
- if (f.issues) copyCachedSubdir(repoDir, refsDir, "issues");
638
- if (f.discussions) copyCachedSubdir(repoDir, refsDir, "discussions");
639
- if (f.releases) copyCachedSubdir(repoDir, refsDir, "releases");
640
- }
641
- function copyCachedSubdir(cacheDir, refsDir, subdir) {
642
- const srcDir = join(cacheDir, subdir);
643
- if (!existsSync(srcDir)) return;
644
- const destDir = join(refsDir, subdir);
645
- mkdirSync(destDir, { recursive: true });
646
- function walk(dir, rel) {
647
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
648
- const srcPath = join(dir, entry.name);
649
- const destPath = join(destDir, rel ? `${rel}/${entry.name}` : entry.name);
650
- if (entry.isDirectory()) {
651
- mkdirSync(destPath, { recursive: true });
652
- walk(srcPath, rel ? `${rel}/${entry.name}` : entry.name);
653
- } else copyFileSync(srcPath, destPath);
654
- }
655
- }
656
- walk(srcDir, "");
657
- }
658
- async function ensureGitignore(skillsDir, cwd, isGlobal) {
659
- if (isGlobal) return;
660
- const gitignorePath = join(cwd, ".gitignore");
661
- const pattern = ".skilld";
662
- if (existsSync(gitignorePath)) {
663
- if (readFileSync(gitignorePath, "utf-8").split("\n").some((line) => line.trim() === pattern)) return;
664
- }
665
- if (!isInteractive()) {
666
- const entry = `\n# Skilld references (recreated by \`skilld install\`)\n${pattern}\n`;
667
- if (existsSync(gitignorePath)) appendFileSync(gitignorePath, `${readFileSync(gitignorePath, "utf-8").endsWith("\n") ? "" : "\n"}${entry}`);
668
- else writeFileSync(gitignorePath, entry);
669
- return;
670
- }
671
- const relSkillsDir = relative(cwd, skillsDir) || ".";
672
- p.log.info(`\x1B[1mGit guidance:\x1B[0m\n \x1B[32m✓\x1B[0m Commit: \x1B[36m${relSkillsDir}/*/SKILL.md\x1B[0m\n \x1B[32m✓\x1B[0m Commit: \x1B[36m${relSkillsDir}/skilld-lock.yaml\x1B[0m\n \x1B[31m✗\x1B[0m Ignore: \x1B[36m${pattern}\x1B[0m \x1B[90m(recreated by \`skilld install\`)\x1B[0m`);
673
- const add = await p.confirm({
674
- message: `Add \`${pattern}\` to .gitignore?`,
675
- initialValue: true
676
- });
677
- if (p.isCancel(add) || !add) return;
678
- const entry = `\n# Skilld references (recreated by \`skilld install\`)\n${pattern}\n`;
679
- if (existsSync(gitignorePath)) appendFileSync(gitignorePath, `${readFileSync(gitignorePath, "utf-8").endsWith("\n") ? "" : "\n"}${entry}`);
680
- else writeFileSync(gitignorePath, entry);
681
- p.log.success("Updated .gitignore");
682
- }
683
- const SKILLD_MARKER_START = "<!-- skilld -->";
684
- const SKILLD_MARKER_END = "<!-- /skilld -->";
685
- const DEFAULT_SKILL_HINT = "Before modifying code, evaluate each installed skill against the current task.\nFor each skill, determine YES/NO relevance and invoke all YES skills before proceeding.";
686
- function getSkillInstructions(agent) {
687
- return `${SKILLD_MARKER_START}\n${targets[agent].skillActivationHint || DEFAULT_SKILL_HINT}\n${SKILLD_MARKER_END}`;
688
- }
689
- function getMdcSkillInstructions(agent) {
690
- return `---\ndescription: "Activates installed skilld skills before code changes"\nalwaysApply: true\n---\n\n${targets[agent].skillActivationHint || DEFAULT_SKILL_HINT}`;
691
- }
692
- async function ensureAgentInstructions(agent, cwd, isGlobal) {
693
- if (isGlobal) return;
694
- const agentConfig = targets[agent];
695
- if (!agentConfig.instructionFile) return;
696
- const filePath = join(cwd, agentConfig.instructionFile);
697
- if (agentConfig.instructionFile.endsWith(".mdc")) {
698
- if (existsSync(filePath)) return;
699
- const content = `${getMdcSkillInstructions(agent)}\n`;
700
- if (!isInteractive()) {
701
- mkdirSync(join(filePath, ".."), { recursive: true });
702
- writeFileSync(filePath, content);
703
- return;
704
- }
705
- p.note(`This tells your agent to check installed skills before making
706
- code changes. Without it, skills are available but may not
707
- activate automatically.
708
-
709
- \x1B[90m${getMdcSkillInstructions(agent)}\x1B[0m`, `Create ${agentConfig.instructionFile}`);
710
- const add = await p.confirm({
711
- message: `Create ${agentConfig.instructionFile} with skill activation instructions?`,
712
- initialValue: true
713
- });
714
- if (p.isCancel(add) || !add) return;
715
- mkdirSync(join(filePath, ".."), { recursive: true });
716
- writeFileSync(filePath, content);
717
- p.log.success(`Created ${agentConfig.instructionFile}`);
718
- return;
719
- }
720
- if (existsSync(filePath)) {
721
- if (readFileSync(filePath, "utf-8").includes("<!-- skilld -->")) return;
722
- }
723
- if (!isInteractive()) {
724
- if (existsSync(filePath)) appendFileSync(filePath, `${readFileSync(filePath, "utf-8").endsWith("\n") ? "" : "\n"}\n${getSkillInstructions(agent)}\n`);
725
- else writeFileSync(filePath, `${getSkillInstructions(agent)}\n`);
726
- return;
727
- }
728
- const action = existsSync(filePath) ? "Append to" : "Create";
729
- p.note(`This tells your agent to check installed skills before making
730
- code changes. Without it, skills are available but may not
731
- activate automatically.
732
-
733
- \x1B[90m${getSkillInstructions(agent).replace(/\n/g, "\n")}\x1B[0m`, `${action} ${agentConfig.instructionFile}`);
734
- const add = await p.confirm({
735
- message: `${action} ${agentConfig.instructionFile} with skill activation instructions?`,
736
- initialValue: true
737
- });
738
- if (p.isCancel(add) || !add) return;
739
- if (existsSync(filePath)) appendFileSync(filePath, `${readFileSync(filePath, "utf-8").endsWith("\n") ? "" : "\n"}\n${getSkillInstructions(agent)}\n`);
740
- else writeFileSync(filePath, `${getSkillInstructions(agent)}\n`);
741
- p.log.success(`Updated ${agentConfig.instructionFile}`);
742
- }
743
- const DEFAULT_SECTIONS = ["best-practices", "api-changes"];
744
- async function selectSkillSections(message = "Enhance SKILL.md") {
745
- p.log.info("Budgets adapt to package release density.");
746
- const selected = await p.multiselect({
747
- message,
748
- options: [
749
- {
750
- label: "API changes",
751
- value: "api-changes",
752
- hint: "new/deprecated APIs from version history"
753
- },
754
- {
755
- label: "Best practices",
756
- value: "best-practices",
757
- hint: "gotchas, pitfalls, patterns"
758
- },
759
- {
760
- label: "Custom section",
761
- value: "custom",
762
- hint: "add your own section"
763
- }
764
- ],
765
- initialValues: DEFAULT_SECTIONS,
766
- required: false
767
- });
768
- if (p.isCancel(selected)) return {
769
- sections: [],
770
- cancelled: true
771
- };
772
- const sections = selected;
773
- if (sections.length === 0) return {
774
- sections: [],
775
- cancelled: false
776
- };
777
- if (sections.length > 1) {
778
- const n = sections.length;
779
- const budgetLines = [];
780
- for (const s of sections) switch (s) {
781
- case "api-changes":
782
- budgetLines.push(` API changes ${maxItems(6, 12, n)}–${maxItems(6, Math.round(12 * 1.6), n)} items (adapts to release churn)`);
783
- break;
784
- case "best-practices":
785
- budgetLines.push(` Best practices ${maxItems(4, 10, n)}–${maxItems(4, Math.round(10 * 1.3), n)} items`);
786
- break;
787
- case "custom":
788
- budgetLines.push(` Custom ≤${maxLines(50, 80, n)} lines`);
789
- break;
790
- }
791
- p.log.info(`Budget (${n} sections):\n${budgetLines.join("\n")}`);
792
- }
793
- let customPrompt;
794
- if (sections.includes("custom")) {
795
- const heading = await p.text({
796
- message: "Section heading",
797
- placeholder: "e.g. \"Migration from v2\" or \"SSR Patterns\""
798
- });
799
- if (p.isCancel(heading)) return {
800
- sections: [],
801
- cancelled: true
802
- };
803
- const body = await p.text({
804
- message: "Instructions for this section",
805
- placeholder: "e.g. \"Document breaking changes and migration steps from v2 to v3\""
806
- });
807
- if (p.isCancel(body)) return {
808
- sections: [],
809
- cancelled: true
810
- };
811
- customPrompt = {
812
- heading,
813
- body
814
- };
815
- }
816
- return {
817
- sections,
818
- customPrompt,
819
- cancelled: false
820
- };
821
- }
822
- async function selectLlmConfig(presetModel, message, updateCtx) {
823
- if (presetModel) {
824
- if ((await getAvailableModels()).some((m) => m.id === presetModel)) return {
825
- model: presetModel,
826
- sections: DEFAULT_SECTIONS
827
- };
828
- if (!isInteractive()) return null;
829
- }
830
- if (!isInteractive()) return null;
831
- const config = readConfig();
832
- const available = await getAvailableModels();
833
- if (available.length === 0) {
834
- p.log.warn(NO_MODELS_MESSAGE);
835
- return null;
836
- }
837
- let defaultModel;
838
- if (config.model && available.some((m) => m.id === config.model)) defaultModel = config.model;
839
- else {
840
- if (config.model) p.log.warn(`Configured model \x1B[36m${config.model}\x1B[0m is unavailable — using auto-selected fallback`);
841
- defaultModel = available.find((m) => m.recommended)?.id ?? available[0].id;
842
- }
843
- const defaultModelName = getModelName(defaultModel);
844
- const providerHint = available.find((m) => m.id === defaultModel)?.providerName ?? "";
845
- const sourceHint = config.model === defaultModel ? "configured" : "recommended";
846
- const defaultHint = providerHint ? `${providerHint} · ${sourceHint}` : sourceHint;
847
- let enhanceMessage = message ? `${message}?` : "Enhance SKILL.md?";
848
- let defaultToSkip = false;
849
- if (updateCtx) {
850
- const diff = updateCtx.bumpType ?? (updateCtx.oldVersion && updateCtx.newVersion ? semverDiff(updateCtx.oldVersion, updateCtx.newVersion) : null);
851
- const isSmallBump = diff === "patch" || diff === "prerelease" || diff === "prepatch" || diff === "preminor" || diff === "premajor";
852
- const ageParts = [];
853
- if (diff) ageParts.push(diff);
854
- if (updateCtx.syncedAt) {
855
- const syncedAtMs = new Date(updateCtx.syncedAt).getTime();
856
- if (Number.isFinite(syncedAtMs)) {
857
- const days = Math.floor((Date.now() - syncedAtMs) / 864e5);
858
- ageParts.push(days === 0 ? "today" : days === 1 ? "1d ago" : `${days}d ago`);
859
- }
860
- }
861
- if (updateCtx.wasEnhanced) ageParts.push("LLM-enhanced");
862
- const hint = [updateCtx.oldVersion && updateCtx.newVersion ? `${updateCtx.oldVersion} → ${updateCtx.newVersion}` : null, ...ageParts].filter(Boolean).join(" · ");
863
- if (hint) enhanceMessage = `Enhance SKILL.md? \x1B[90m(${hint})\x1B[0m`;
864
- if (updateCtx.wasEnhanced && isSmallBump) defaultToSkip = true;
865
- }
866
- const choice = await p.select({
867
- message: enhanceMessage,
868
- options: [
869
- {
870
- label: defaultModelName,
871
- value: "default",
872
- hint: defaultHint
873
- },
874
- {
875
- label: "Different model",
876
- value: "pick",
877
- hint: "choose another enhancement model"
878
- },
879
- {
880
- label: "Prompt only",
881
- value: "prompt",
882
- hint: "write prompts for manual use"
883
- },
884
- {
885
- label: "Skip",
886
- value: "skip",
887
- hint: "base skill with docs, issues, and types"
888
- }
889
- ],
890
- ...defaultToSkip ? { initialValue: "skip" } : {}
891
- });
892
- if (p.isCancel(choice)) return null;
893
- if (choice === "skip") return null;
894
- if (choice === "prompt") {
895
- const { sections, customPrompt, cancelled } = await selectSkillSections(message ? `${message} (prompt only)` : "Select sections for prompt generation");
896
- if (cancelled || sections.length === 0) return null;
897
- return {
898
- model: defaultModel,
899
- sections,
900
- customPrompt,
901
- promptOnly: true
902
- };
903
- }
904
- let model;
905
- if (choice === "pick") {
906
- const picked = await pickModel(available);
907
- if (!picked) return null;
908
- updateConfig({ model: picked });
909
- model = picked;
910
- } else model = defaultModel;
911
- if (!model) return null;
912
- const modelName = getModelName(model);
913
- const { sections, customPrompt, cancelled } = await selectSkillSections(message ? `${message} (${modelName})` : `Enhance SKILL.md with ${modelName}`);
914
- if (cancelled || sections.length === 0) return null;
915
- return {
916
- model,
917
- sections,
918
- customPrompt
919
- };
920
- }
921
- async function enhanceSkillWithLLM(opts) {
922
- const { packageName, cachePackageName, version, skillDir, dirName, model, resolved, relatedSkills, hasIssues, hasDiscussions, hasReleases, hasChangelog, docsType, hasShippedDocs: shippedDocs, pkgFiles, force, debug, sections, customPrompt, packages, features, eject, overheadLines } = opts;
923
- const cacheKey = cachePackageName || packageName;
924
- const effectiveFeatures = features;
925
- const llmLog = p.taskLog({
926
- title: `Agent exploring ${packageName}`,
927
- limit: 3
928
- });
929
- const docFiles = listReferenceFiles(skillDir);
930
- const { optimized, wasOptimized, usage, cost, warnings, error, debugLogsDir } = await optimizeDocs({
931
- packageName: cacheKey,
932
- skillDir,
933
- model,
934
- version,
935
- hasGithub: hasIssues || hasDiscussions,
936
- hasReleases,
937
- hasChangelog,
938
- docFiles,
939
- docsType,
940
- hasShippedDocs: shippedDocs,
941
- noCache: force,
942
- debug,
943
- sections,
944
- customPrompt,
945
- features: effectiveFeatures,
946
- pkgFiles,
947
- overheadLines,
948
- onProgress: createToolProgress(llmLog)
949
- });
950
- if (wasOptimized) {
951
- const costParts = [];
952
- if (usage) {
953
- const totalK = Math.round(usage.totalTokens / 1e3);
954
- costParts.push(`${totalK}k tokens`);
955
- }
956
- if (cost) costParts.push(`$${cost.toFixed(2)}`);
957
- const costSuffix = costParts.length > 0 ? ` (${costParts.join(", ")})` : "";
958
- llmLog.success(`Generated best practices${costSuffix}`);
959
- if (debugLogsDir) p.log.info(`Debug logs: ${relative(process.cwd(), debugLogsDir)}`);
960
- if (error) p.log.warn(`\x1B[33mPartial failure: ${error}\x1B[0m`);
961
- if (warnings?.length) for (const w of warnings) p.log.warn(`\x1B[33m${w}\x1B[0m`);
962
- writeGeneratedSkillMd(skillDir, {
963
- name: packageName,
964
- version,
965
- releasedAt: resolved.releasedAt,
966
- distTags: resolved.distTags,
967
- body: optimized,
968
- relatedSkills,
969
- hasIssues,
970
- hasDiscussions,
971
- hasReleases,
972
- hasChangelog,
973
- docsType,
974
- hasShippedDocs: shippedDocs,
975
- pkgFiles,
976
- generatedBy: getModelLabel(model),
977
- dirName,
978
- packages,
979
- repoUrl: resolved.repoUrl,
980
- features,
981
- eject
982
- });
983
- } else if (error && /\b429\b|rate.?limit|exhausted.*capacity|quota.*reset/i.test(error)) llmLog.error(`Rate limited by LLM provider. Try again shortly or use a different model via \`skilld config\``);
984
- else llmLog.error(`Enhancement failed${error ? `: ${error}` : ""}`);
985
- }
986
- function writePromptFiles(opts) {
987
- const { skillDir, sections, customPrompt, features } = opts;
988
- const docFiles = listReferenceFiles(skillDir);
989
- const prompts = buildAllSectionPrompts({
990
- packageName: opts.packageName,
991
- skillDir,
992
- version: opts.version,
993
- hasIssues: opts.hasIssues,
994
- hasDiscussions: opts.hasDiscussions,
995
- hasReleases: opts.hasReleases,
996
- hasChangelog: opts.hasChangelog,
997
- docFiles,
998
- docsType: opts.docsType,
999
- hasShippedDocs: opts.hasShippedDocs,
1000
- pkgFiles: opts.pkgFiles,
1001
- customPrompt,
1002
- features,
1003
- overheadLines: opts.overheadLines,
1004
- sections
1005
- });
1006
- const skilldDir = join(skillDir, ".skilld");
1007
- mkdirSync(skilldDir, { recursive: true });
1008
- for (const [section, prompt] of prompts) writeFileSync(join(skilldDir, `PROMPT_${section}.md`), prompt);
1009
- const written = [...prompts.keys()];
1010
- if (written.length > 0) {
1011
- const relDir = relative(process.cwd(), skillDir);
1012
- const promptFiles = written.map((s) => `PROMPT_${s}.md`).join(", ");
1013
- const outputFileList = written.map((s) => SECTION_OUTPUT_FILES[s]).join(", ");
1014
- p.log.info(`Prompt files written to ${relDir}/.skilld/\n\x1B[2m\x1B[3m Read each prompt file (${promptFiles}) in ${relDir}/.skilld/, read the\n referenced files, then write your output to the matching file (${outputFileList}).\n When done, run: skilld assemble\x1B[0m`);
1015
- }
1016
- return written;
1017
- }
1018
- export { writePromptFiles as S, linkAllReferences as _, classifyCachedDoc as a, selectLlmConfig as b, ejectReferences as c, ensureGitignore as d, fetchAndCacheResources as f, indexResources as g, handleShippedSkills as h, SKILLD_MARKER_START as i, enhanceSkillWithLLM as l, forceClearCache as m, RESOLVE_STEP_LABELS as n, detectChangelog as o, findRelatedSkills as p, SKILLD_MARKER_END as r, detectDocsType as s, DEFAULT_SECTIONS as t, ensureAgentInstructions as u, resolveBaseDir as v, selectSkillSections as x, resolveLocalDep as y };
1019
-
1020
- //# sourceMappingURL=sync-shared2.mjs.map