skilld 0.1.2 → 0.2.1

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 (40) hide show
  1. package/README.md +70 -120
  2. package/dist/_chunks/config.mjs +8 -2
  3. package/dist/_chunks/config.mjs.map +1 -1
  4. package/dist/_chunks/llm.mjs +710 -204
  5. package/dist/_chunks/llm.mjs.map +1 -1
  6. package/dist/_chunks/pool.mjs +115 -0
  7. package/dist/_chunks/pool.mjs.map +1 -0
  8. package/dist/_chunks/releases.mjs +689 -179
  9. package/dist/_chunks/releases.mjs.map +1 -1
  10. package/dist/_chunks/storage.mjs +311 -19
  11. package/dist/_chunks/storage.mjs.map +1 -1
  12. package/dist/_chunks/sync-parallel.mjs +134 -378
  13. package/dist/_chunks/sync-parallel.mjs.map +1 -1
  14. package/dist/_chunks/types.d.mts +9 -6
  15. package/dist/_chunks/types.d.mts.map +1 -1
  16. package/dist/_chunks/utils.d.mts +137 -68
  17. package/dist/_chunks/utils.d.mts.map +1 -1
  18. package/dist/_chunks/version.d.mts +43 -6
  19. package/dist/_chunks/version.d.mts.map +1 -1
  20. package/dist/agent/index.d.mts +58 -15
  21. package/dist/agent/index.d.mts.map +1 -1
  22. package/dist/agent/index.mjs +4 -2
  23. package/dist/cache/index.d.mts +2 -2
  24. package/dist/cache/index.mjs +2 -2
  25. package/dist/cli.mjs +2175 -1435
  26. package/dist/cli.mjs.map +1 -1
  27. package/dist/index.d.mts +4 -3
  28. package/dist/index.mjs +2 -2
  29. package/dist/retriv/index.d.mts +16 -2
  30. package/dist/retriv/index.d.mts.map +1 -1
  31. package/dist/retriv/index.mjs +44 -15
  32. package/dist/retriv/index.mjs.map +1 -1
  33. package/dist/retriv/worker.d.mts +33 -0
  34. package/dist/retriv/worker.d.mts.map +1 -0
  35. package/dist/retriv/worker.mjs +47 -0
  36. package/dist/retriv/worker.mjs.map +1 -0
  37. package/dist/sources/index.d.mts +2 -2
  38. package/dist/sources/index.mjs +2 -2
  39. package/dist/types.d.mts +5 -3
  40. package/package.json +11 -7
@@ -1,26 +1,16 @@
1
- import { a as getCacheDir, i as getPackageDbPath, s as getVersionKey, t as CACHE_DIR } from "./config.mjs";
2
- import { _ as writeToCache, a as getShippedSkills, c as linkGithub, d as linkReleases, f as linkShippedSkill, g as resolvePkgDir, h as readCachedDocs, i as getPkgKeyFiles, l as linkPkg, m as listReferenceFiles, n as clearCache, o as hasShippedDocs, r as ensureCacheDir, s as isCached, u as linkReferences } from "./storage.mjs";
1
+ import { s as getVersionKey } from "./config.mjs";
2
+ import { _ as listReferenceFiles, b as resolvePkgDir, d as linkPkgNamed, i as getPkgKeyFiles, o as hasShippedDocs, r as ensureCacheDir, s as isCached } from "./storage.mjs";
3
3
  import "../cache/index.mjs";
4
- import { createIndex } from "../retriv/index.mjs";
5
- import { A as resolveEntryFiles, E as parseGitHubUrl, S as fetchReadmeContent, _ as normalizeLlmsLinks, a as fetchPkgDist, c as readLocalDependencies, d as resolvePackageDocs, f as resolvePackageDocsWithAttempts, h as fetchLlmsTxt, p as downloadLlmsDocs, r as fetchNpmPackage, t as fetchReleaseNotes, u as resolveLocalPackageDocs, v as parseMarkdownLinks, y as fetchGitDocs } from "./releases.mjs";
4
+ import { h as searchNpmPackages, l as readLocalDependencies, m as resolvePackageDocsWithAttempts, o as fetchPkgDist } from "./releases.mjs";
6
5
  import "../sources/index.mjs";
7
- import { c as sanitizeName, i as generateSkillMd, p as agents, r as optimizeDocs } from "./llm.mjs";
6
+ import { a as generateSkillMd, i as optimizeDocs, m as computeSkillDirName, n as getModelLabel, x as agents } from "./llm.mjs";
8
7
  import "../agent/index.mjs";
9
- import { a as defaultFeatures, i as writeLock, n as selectModel, o as readConfig, r as selectSkillSections, s as registerProject, t as ensureGitignore } from "../cli.mjs";
10
- import { join } from "node:path";
11
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
12
- import * as p from "@clack/prompts";
8
+ import { _ as defaultFeatures, a as fetchAndCacheResources, c as handleShippedSkills, d as resolveBaseDir, f as resolveLocalDep, g as formatDuration, h as writeLock, i as detectChangelog, l as indexResources, m as readLock, n as selectLlmConfig, o as findRelatedSkills, p as parsePackages, r as RESOLVE_STEP_LABELS, s as forceClearCache, t as ensureGitignore, u as linkAllReferences, v as readConfig, y as registerProject } from "../cli.mjs";
9
+ import { join } from "pathe";
10
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
13
11
  import pLimit from "p-limit";
12
+ import * as p from "@clack/prompts";
14
13
  import logUpdate from "log-update";
15
- const RESOLVE_STEP_LABELS = {
16
- "npm": "npm registry",
17
- "github-docs": "GitHub docs",
18
- "github-meta": "GitHub meta",
19
- "github-search": "GitHub search",
20
- "readme": "README",
21
- "llms.txt": "llms.txt",
22
- "local": "node_modules"
23
- };
24
14
  const STATUS_ICONS = {
25
15
  pending: "○",
26
16
  resolving: "◐",
@@ -62,8 +52,9 @@ async function syncPackagesParallel(config) {
62
52
  const dim = "\x1B[90m";
63
53
  const name = s.name.padEnd(maxNameLen);
64
54
  const version = s.version ? `${dim}${s.version}${reset} ` : "";
55
+ const elapsed = (s.status === "done" || s.status === "error") && s.startedAt && s.completedAt ? ` ${dim}(${formatDuration(s.completedAt - s.startedAt)})${reset}` : "";
65
56
  const preview = s.streamPreview ? ` ${dim}${s.streamPreview}${reset}` : "";
66
- return ` ${color}${icon}${reset} ${name} ${version}${s.message}${preview}`;
57
+ return ` ${color}${icon}${reset} ${name} ${version}${s.message}${elapsed}${preview}`;
67
58
  });
68
59
  const doneCount = [...states.values()].filter((s) => s.status === "done").length;
69
60
  const errorCount = [...states.values()].filter((s) => s.status === "error").length;
@@ -71,6 +62,8 @@ async function syncPackagesParallel(config) {
71
62
  }
72
63
  function update(pkg, status, message, version) {
73
64
  const state = states.get(pkg);
65
+ if (!state.startedAt && status !== "pending") state.startedAt = performance.now();
66
+ if ((status === "done" || status === "error") && !state.completedAt) state.completedAt = performance.now();
74
67
  state.status = status;
75
68
  state.message = message;
76
69
  state.streamPreview = void 0;
@@ -80,13 +73,18 @@ async function syncPackagesParallel(config) {
80
73
  ensureCacheDir();
81
74
  render();
82
75
  const limit = pLimit(concurrency);
76
+ const skillData = /* @__PURE__ */ new Map();
83
77
  const baseResults = await Promise.allSettled(packages.map((pkg) => limit(() => syncBaseSkill(pkg, config, cwd, update))));
84
78
  logUpdate.done();
85
79
  const successfulPkgs = [];
80
+ const shippedPkgs = [];
86
81
  const errors = [];
87
82
  for (let i = 0; i < baseResults.length; i++) {
88
83
  const r = baseResults[i];
89
- if (r.status === "fulfilled" && r.value !== "shipped") successfulPkgs.push(packages[i]);
84
+ if (r.status === "fulfilled" && r.value !== "shipped") {
85
+ successfulPkgs.push(packages[i]);
86
+ skillData.set(packages[i], r.value);
87
+ } else if (r.status === "fulfilled" && r.value === "shipped") shippedPkgs.push(packages[i]);
90
88
  else if (r.status === "rejected") {
91
89
  const err = r.reason;
92
90
  const reason = err instanceof Error ? `${err.message}\n${err.stack}` : String(err);
@@ -96,35 +94,32 @@ async function syncPackagesParallel(config) {
96
94
  });
97
95
  }
98
96
  }
99
- p.log.success(`Created ${successfulPkgs.length} base skills`);
97
+ const skillMsg = `Created ${successfulPkgs.length} base skills${shippedPkgs.length > 1 ? ` (Skipping ${shippedPkgs.length})` : ""}`;
98
+ p.log.success(skillMsg);
100
99
  if (errors.length > 0) for (const { pkg, reason } of errors) p.log.error(` ${pkg}: ${reason}`);
101
100
  const globalConfig = readConfig();
102
101
  if (successfulPkgs.length > 0 && !globalConfig.skipLlm && !(config.yes && !config.model)) {
103
- const { sections, customPrompt, cancelled } = config.model ? {
104
- sections: ["best-practices", "api"],
105
- customPrompt: void 0,
106
- cancelled: false
107
- } : await selectSkillSections();
108
- if (!cancelled && sections.length > 0) {
109
- const model = config.model ?? await selectModel(false);
110
- if (model) {
111
- for (const pkg of successfulPkgs) states.set(pkg, {
112
- name: pkg,
113
- status: "pending",
114
- message: "Waiting..."
115
- });
116
- render();
117
- const llmResults = await Promise.allSettled(successfulPkgs.map((pkg) => limit(() => enhanceWithLLM(pkg, {
118
- ...config,
119
- model
120
- }, cwd, update, sections, customPrompt))));
121
- logUpdate.done();
122
- const llmSucceeded = llmResults.filter((r) => r.status === "fulfilled").length;
123
- p.log.success(`Enhanced ${llmSucceeded}/${successfulPkgs.length} skills with LLM`);
124
- }
102
+ const llmConfig = await selectLlmConfig(config.model);
103
+ if (llmConfig) {
104
+ p.log.step(getModelLabel(llmConfig.model));
105
+ for (const pkg of successfulPkgs) states.set(pkg, {
106
+ name: pkg,
107
+ status: "pending",
108
+ message: "Waiting..."
109
+ });
110
+ render();
111
+ const llmResults = await Promise.allSettled(successfulPkgs.map((pkg) => limit(() => enhanceWithLLM(pkg, skillData.get(pkg), {
112
+ ...config,
113
+ model: llmConfig.model
114
+ }, cwd, update, llmConfig.sections, llmConfig.customPrompt))));
115
+ logUpdate.done();
116
+ const llmSucceeded = llmResults.filter((r) => r.status === "fulfilled").length;
117
+ p.log.success(`Enhanced ${llmSucceeded}/${successfulPkgs.length} skills with LLM`);
125
118
  }
126
119
  }
127
120
  await ensureGitignore(agent.skillsDir, cwd, config.global);
121
+ const { shutdownWorker } = await import("./pool.mjs");
122
+ await shutdownWorker();
128
123
  p.outro(`Synced ${successfulPkgs.length}/${packages.length} packages`);
129
124
  }
130
125
  async function syncBaseSkill(packageName, config, cwd, update) {
@@ -136,25 +131,17 @@ async function syncBaseSkill(packageName, config, cwd, update) {
136
131
  });
137
132
  let resolved = resolvedPkg;
138
133
  if (!resolved) {
139
- const pkgPath = join(cwd, "package.json");
140
- if (existsSync(pkgPath)) {
141
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
142
- const depVersion = {
143
- ...pkg.dependencies,
144
- ...pkg.devDependencies
145
- }[packageName];
146
- if (depVersion?.startsWith("link:")) {
147
- update(packageName, "resolving", "Local package...");
148
- const { resolve } = await import("node:path");
149
- resolved = await resolveLocalPackageDocs(resolve(cwd, depVersion.slice(5)));
150
- }
151
- }
134
+ update(packageName, "resolving", "Local package...");
135
+ resolved = await resolveLocalDep(packageName, cwd);
152
136
  }
153
137
  if (!resolved) {
154
138
  const npmAttempt = attempts.find((a) => a.source === "npm");
155
139
  let reason;
156
- if (npmAttempt?.status === "not-found") reason = npmAttempt.message || "Not on npm";
157
- else reason = attempts.filter((a) => a.status !== "success").map((a) => a.message || a.source).join("; ") || "No docs found";
140
+ if (npmAttempt?.status === "not-found") {
141
+ const suggestions = await searchNpmPackages(packageName, 3);
142
+ const hint = suggestions.length > 0 ? ` (try: ${suggestions.map((s) => s.name).join(", ")})` : "";
143
+ reason = (npmAttempt.message || "Not on npm") + hint;
144
+ } else reason = attempts.filter((a) => a.status !== "success").map((a) => a.message || a.source).join("; ") || "No docs found";
158
145
  update(packageName, "error", reason);
159
146
  throw new Error(`Could not find docs for: ${packageName}`);
160
147
  }
@@ -164,218 +151,54 @@ async function syncBaseSkill(packageName, config, cwd, update) {
164
151
  update(packageName, "downloading", "Downloading dist...", versionKey);
165
152
  await fetchPkgDist(packageName, version);
166
153
  }
167
- const shippedSkills = getShippedSkills(packageName, cwd, version);
168
- if (shippedSkills.length > 0) {
169
- const agent = agents[config.agent];
170
- const baseDir = config.global ? join(CACHE_DIR, "skills") : join(cwd, agent.skillsDir);
171
- mkdirSync(baseDir, { recursive: true });
172
- for (const shipped of shippedSkills) {
173
- linkShippedSkill(baseDir, shipped.skillName, shipped.skillDir);
174
- writeLock(baseDir, shipped.skillName, {
175
- packageName,
176
- version,
177
- source: "shipped",
178
- syncedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
179
- generator: "skilld"
180
- });
181
- }
182
- if (!config.global) registerProject(cwd);
183
- update(packageName, "done", "Shipped", versionKey);
154
+ if (handleShippedSkills(packageName, version, cwd, config.agent, config.global)) {
155
+ update(packageName, "done", "Published SKILL.md", versionKey);
184
156
  return "shipped";
185
157
  }
186
- if (config.force) {
187
- clearCache(packageName, version);
188
- const forcedDbPath = getPackageDbPath(packageName, version);
189
- if (existsSync(forcedDbPath)) rmSync(forcedDbPath, {
190
- recursive: true,
191
- force: true
192
- });
193
- }
158
+ if (config.force) forceClearCache(packageName, version);
194
159
  const useCache = isCached(packageName, version);
195
160
  if (useCache) update(packageName, "downloading", "Using cache", versionKey);
196
161
  else update(packageName, "downloading", config.force ? "Re-fetching docs..." : "Fetching docs...", versionKey);
197
- const agent = agents[config.agent];
198
- const baseDir = config.global ? join(CACHE_DIR, "skills") : join(cwd, agent.skillsDir);
199
- const skillDir = join(baseDir, sanitizeName(packageName));
162
+ const baseDir = resolveBaseDir(cwd, config.agent, config.global);
163
+ const skillDirName = computeSkillDirName(packageName, resolved.repoUrl);
164
+ const skillDir = join(baseDir, skillDirName);
200
165
  mkdirSync(skillDir, { recursive: true });
201
- let docSource = resolved.readmeUrl || "readme";
202
- let docsType = "readme";
203
- const docsToIndex = [];
204
- if (!useCache) {
205
- const cachedDocs = [];
206
- if (resolved.gitDocsUrl && resolved.repoUrl) {
207
- const gh = parseGitHubUrl(resolved.repoUrl);
208
- if (gh) {
209
- update(packageName, "downloading", "Git docs...", versionKey);
210
- const gitDocs = await fetchGitDocs(gh.owner, gh.repo, version, packageName);
211
- if (gitDocs && gitDocs.files.length > 0) {
212
- update(packageName, "downloading", `0/${gitDocs.files.length} docs @ ${gitDocs.ref}`, versionKey);
213
- const BATCH_SIZE = 20;
214
- const results = [];
215
- let downloaded = 0;
216
- for (let i = 0; i < gitDocs.files.length; i += BATCH_SIZE) {
217
- const batch = gitDocs.files.slice(i, i + BATCH_SIZE);
218
- const batchResults = await Promise.all(batch.map(async (file) => {
219
- const url = `${gitDocs.baseUrl}/${file}`;
220
- const res = await fetch(url, { headers: { "User-Agent": "skilld/1.0" } }).catch(() => null);
221
- if (!res?.ok) return null;
222
- const content = await res.text();
223
- downloaded++;
224
- update(packageName, "downloading", `${downloaded}/${gitDocs.files.length} docs @ ${gitDocs.ref}`, versionKey);
225
- return {
226
- file,
227
- content
228
- };
229
- }));
230
- results.push(...batchResults);
231
- }
232
- for (const r of results) if (r) {
233
- const cachePath = gitDocs.docsPrefix ? r.file.replace(gitDocs.docsPrefix, "") : r.file;
234
- cachedDocs.push({
235
- path: cachePath,
236
- content: r.content
237
- });
238
- docsToIndex.push({
239
- id: cachePath,
240
- content: r.content,
241
- metadata: {
242
- package: packageName,
243
- source: cachePath,
244
- type: "doc"
245
- }
246
- });
247
- }
248
- if (results.filter(Boolean).length > 0) {
249
- docSource = `${resolved.repoUrl}/tree/${gitDocs.ref}/docs`;
250
- docsType = "docs";
251
- }
252
- }
253
- }
254
- }
255
- if (resolved.llmsUrl && cachedDocs.length === 0) {
256
- update(packageName, "downloading", "Fetching llms.txt...", versionKey);
257
- const llmsContent = await fetchLlmsTxt(resolved.llmsUrl);
258
- if (llmsContent) {
259
- docSource = resolved.llmsUrl;
260
- docsType = "llms.txt";
261
- cachedDocs.push({
262
- path: "llms.txt",
263
- content: normalizeLlmsLinks(llmsContent.raw)
264
- });
265
- if (llmsContent.links.length > 0) {
266
- update(packageName, "downloading", `0/${llmsContent.links.length} linked docs...`, versionKey);
267
- const docs = await downloadLlmsDocs(llmsContent, resolved.docsUrl || new URL(resolved.llmsUrl).origin);
268
- for (const doc of docs) {
269
- const cachePath = join("docs", ...(doc.url.startsWith("/") ? doc.url.slice(1) : doc.url).split("/"));
270
- cachedDocs.push({
271
- path: cachePath,
272
- content: doc.content
273
- });
274
- docsToIndex.push({
275
- id: doc.url,
276
- content: doc.content,
277
- metadata: {
278
- package: packageName,
279
- source: cachePath,
280
- type: "doc"
281
- }
282
- });
283
- }
284
- }
285
- }
286
- }
287
- if (resolved.readmeUrl && cachedDocs.length === 0) {
288
- update(packageName, "downloading", "Fetching README...", versionKey);
289
- const content = await fetchReadmeContent(resolved.readmeUrl);
290
- if (content) {
291
- cachedDocs.push({
292
- path: "docs/README.md",
293
- content
294
- });
295
- docsToIndex.push({
296
- id: "README.md",
297
- content,
298
- metadata: {
299
- package: packageName,
300
- source: "docs/README.md",
301
- type: "doc"
302
- }
303
- });
304
- }
305
- }
306
- if (cachedDocs.length > 0) writeToCache(packageName, version, cachedDocs);
307
- } else {
308
- const cacheDir = getCacheDir(packageName, version);
309
- if (existsSync(join(cacheDir, "docs", "index.md")) || existsSync(join(cacheDir, "docs", "guide"))) docsType = "docs";
310
- else if (existsSync(join(cacheDir, "llms.txt"))) docsType = "llms.txt";
311
- if (!existsSync(getPackageDbPath(packageName, version))) {
312
- const cached = readCachedDocs(packageName, version);
313
- for (const doc of cached) docsToIndex.push({
314
- id: doc.path,
315
- content: doc.content,
316
- metadata: {
317
- package: packageName,
318
- source: doc.path,
319
- type: "doc"
320
- }
321
- });
322
- }
323
- }
324
166
  const features = readConfig().features ?? defaultFeatures;
325
- const releasesPath = join(getCacheDir(packageName, version), "releases");
326
- if (features.releases && resolved.repoUrl && !existsSync(releasesPath)) {
327
- const gh = parseGitHubUrl(resolved.repoUrl);
328
- if (gh) {
329
- update(packageName, "downloading", "Fetching releases...", versionKey);
330
- const releaseDocs = await fetchReleaseNotes(gh.owner, gh.repo, version, resolved.gitRef, packageName);
331
- if (releaseDocs.length > 0) {
332
- writeToCache(packageName, version, releaseDocs);
333
- for (const doc of releaseDocs) docsToIndex.push({
334
- id: doc.path,
335
- content: doc.content,
336
- metadata: {
337
- package: packageName,
338
- source: doc.path,
339
- type: "release"
340
- }
341
- });
342
- }
343
- }
344
- }
167
+ const resources = await fetchAndCacheResources({
168
+ packageName,
169
+ resolved,
170
+ version,
171
+ useCache,
172
+ features,
173
+ onProgress: (msg) => update(packageName, "downloading", msg, versionKey)
174
+ });
345
175
  update(packageName, "downloading", "Linking references...", versionKey);
346
- try {
347
- linkPkg(skillDir, packageName, cwd, version);
348
- if (!hasShippedDocs(packageName, cwd, version) && docsType !== "readme") linkReferences(skillDir, packageName, version);
349
- linkGithub(skillDir, packageName, version);
350
- linkReleases(skillDir, packageName, version);
351
- } catch {}
352
- update(packageName, "embedding", "Scanning exports...", versionKey);
353
- const pkgDir = resolvePkgDir(packageName, cwd, version);
354
- const entryFiles = features.search && pkgDir ? await resolveEntryFiles(pkgDir) : [];
355
- if (entryFiles.length > 0) for (const e of entryFiles) docsToIndex.push({
356
- id: e.path,
357
- content: e.content,
358
- metadata: {
359
- package: packageName,
360
- source: `pkg/${e.path}`,
361
- type: e.type
362
- }
176
+ linkAllReferences(skillDir, packageName, cwd, version, resources.docsType);
177
+ update(packageName, "embedding", "Indexing docs", versionKey);
178
+ await indexResources({
179
+ packageName,
180
+ version,
181
+ cwd,
182
+ docsToIndex: resources.docsToIndex,
183
+ features,
184
+ onProgress: (msg) => update(packageName, "embedding", msg, versionKey)
363
185
  });
364
- const dbPath = getPackageDbPath(packageName, version);
365
- if (docsToIndex.length > 0) {
366
- update(packageName, "embedding", `Indexing ${docsToIndex.length} documents...`, versionKey);
367
- await createIndex(docsToIndex, {
368
- dbPath,
369
- onProgress: (current, total, doc) => {
370
- update(packageName, "embedding", `Indexing ${doc?.type === "source" || doc?.type === "types" ? "code" : "doc"} ${doc?.id ? doc.id.split("/").pop() : ""}... ${current}/${total} ${Math.round(current / total * 100)}%`, versionKey);
371
- }
372
- });
373
- }
374
- const hasReleases = existsSync(releasesPath);
375
- const hasChangelog = pkgDir ? ["CHANGELOG.md", "changelog.md"].find((f) => existsSync(join(pkgDir, f))) || false : false;
186
+ const hasChangelog = detectChangelog(resolvePkgDir(packageName, cwd, version));
376
187
  const relatedSkills = await findRelatedSkills(packageName, baseDir);
377
188
  const shippedDocs = hasShippedDocs(packageName, cwd, version);
378
189
  const pkgFiles = getPkgKeyFiles(packageName, cwd, version);
190
+ const repoSlug = resolved.repoUrl?.match(/github\.com\/([^/]+\/[^/]+?)(?:\.git)?(?:[/#]|$)/)?.[1];
191
+ linkPkgNamed(skillDir, packageName, cwd, version);
192
+ writeLock(baseDir, skillDirName, {
193
+ packageName,
194
+ version,
195
+ repo: repoSlug,
196
+ source: resources.docSource,
197
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
198
+ generator: "skilld"
199
+ });
200
+ const updatedLock = readLock(baseDir)?.skills[skillDirName];
201
+ const allPackages = parsePackages(updatedLock?.packages).map((p) => ({ name: p.name }));
379
202
  const skillMd = generateSkillMd({
380
203
  name: packageName,
381
204
  version,
@@ -384,106 +207,60 @@ async function syncBaseSkill(packageName, config, cwd, update) {
384
207
  dependencies: resolved.dependencies,
385
208
  distTags: resolved.distTags,
386
209
  relatedSkills,
387
- hasGithub: false,
388
- hasReleases,
210
+ hasIssues: resources.hasIssues,
211
+ hasDiscussions: resources.hasDiscussions,
212
+ hasReleases: resources.hasReleases,
389
213
  hasChangelog,
390
- docsType,
214
+ docsType: resources.docsType,
391
215
  hasShippedDocs: shippedDocs,
392
- pkgFiles
216
+ pkgFiles,
217
+ dirName: skillDirName,
218
+ packages: allPackages.length > 1 ? allPackages : void 0,
219
+ repoUrl: resolved.repoUrl
393
220
  });
394
221
  writeFileSync(join(skillDir, "SKILL.md"), skillMd);
395
- writeLock(baseDir, sanitizeName(packageName), {
396
- packageName,
397
- version,
398
- source: docSource,
399
- syncedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
400
- generator: "skilld"
401
- });
402
222
  if (!config.global) registerProject(cwd);
403
- update(packageName, "done", "Skill ready", versionKey);
404
- return "synced";
223
+ update(packageName, "done", "Base skill created", versionKey);
224
+ return {
225
+ resolved,
226
+ version,
227
+ skillDirName,
228
+ docsType: resources.docsType,
229
+ hasIssues: resources.hasIssues,
230
+ hasDiscussions: resources.hasDiscussions,
231
+ hasReleases: resources.hasReleases,
232
+ hasChangelog,
233
+ shippedDocs,
234
+ pkgFiles,
235
+ relatedSkills,
236
+ packages: allPackages.length > 1 ? allPackages : void 0
237
+ };
405
238
  }
406
- async function enhanceWithLLM(packageName, config, cwd, update, sections, customPrompt) {
407
- const localVersion = (await readLocalDependencies(cwd).catch(() => [])).find((d) => d.name === packageName)?.version;
408
- const resolved = await resolvePackageDocs(packageName, { version: localVersion });
409
- if (!resolved) throw new Error("Package not found");
410
- const version = localVersion || resolved.version || "latest";
411
- const versionKey = getVersionKey(version);
412
- const agent = agents[config.agent];
413
- const baseDir = config.global ? join(CACHE_DIR, "skills") : join(cwd, agent.skillsDir);
414
- const skillDir = join(baseDir, sanitizeName(packageName));
415
- const cacheDir = getCacheDir(packageName, version);
416
- let docsContent = null;
417
- let llmsRaw = null;
418
- let docsType = "readme";
419
- if (existsSync(join(cacheDir, "docs", "index.md")) || existsSync(join(cacheDir, "docs", "guide"))) docsType = "docs";
420
- else if (existsSync(join(cacheDir, "llms.txt"))) docsType = "llms.txt";
421
- const guideDir = join(cacheDir, "docs", "guide");
422
- const docsDir = join(cacheDir, "docs");
423
- if (existsSync(guideDir) || existsSync(join(docsDir, "index.md"))) {
424
- const sections = [];
425
- const indexPath = join(docsDir, "index.md");
426
- if (existsSync(indexPath)) sections.push(readFileSync(indexPath, "utf-8"));
427
- if (existsSync(guideDir)) {
428
- const priorityFiles = [
429
- "index.md",
430
- "features.md",
431
- "migration.md",
432
- "why.md"
433
- ];
434
- const guideFiles = readdirSync(guideDir, { withFileTypes: true }).filter((f) => f.isFile() && f.name.endsWith(".md")).map((f) => f.name).sort((a, b) => {
435
- const aIdx = priorityFiles.indexOf(a);
436
- const bIdx = priorityFiles.indexOf(b);
437
- if (aIdx >= 0 && bIdx >= 0) return aIdx - bIdx;
438
- if (aIdx >= 0) return -1;
439
- if (bIdx >= 0) return 1;
440
- return a.localeCompare(b);
441
- });
442
- for (const file of guideFiles.slice(0, 10)) {
443
- const content = readFileSync(join(guideDir, file), "utf-8");
444
- sections.push(`# guide/${file}\n\n${content}`);
445
- }
446
- }
447
- if (sections.length > 0) docsContent = sections.join("\n\n---\n\n");
448
- }
449
- if (!docsContent) {
450
- if (existsSync(join(cacheDir, "llms.txt"))) llmsRaw = readFileSync(join(cacheDir, "llms.txt"), "utf-8");
451
- if (llmsRaw) {
452
- const bestPracticesPaths = parseMarkdownLinks(llmsRaw).map((l) => l.url).filter((lp) => lp.includes("/style-guide/") || lp.includes("/best-practices/") || lp.includes("/typescript/"));
453
- const sections = [];
454
- for (const mdPath of bestPracticesPaths) {
455
- const filePath = join(cacheDir, "docs", mdPath.startsWith("/") ? mdPath.slice(1) : mdPath);
456
- if (existsSync(filePath)) {
457
- const content = readFileSync(filePath, "utf-8");
458
- sections.push(`# ${mdPath}\n\n${content}`);
459
- }
460
- }
461
- docsContent = sections.length > 0 ? sections.join("\n\n---\n\n") : llmsRaw;
462
- }
463
- }
464
- if (!docsContent) {
465
- const readmePath = join(cacheDir, "docs", "README.md");
466
- if (existsSync(readmePath)) docsContent = readFileSync(readmePath, "utf-8");
467
- }
468
- const hasGithub = existsSync(join(cacheDir, "github"));
469
- const hasReleases = existsSync(join(cacheDir, "releases"));
470
- const hasChangelog = ["CHANGELOG.md", "changelog.md"].find((f) => existsSync(join(cwd, "node_modules", packageName, f))) || false;
239
+ async function enhanceWithLLM(packageName, data, config, cwd, update, sections, customPrompt) {
240
+ const versionKey = getVersionKey(data.version);
241
+ const skillDir = join(resolveBaseDir(cwd, config.agent, config.global), data.skillDirName);
242
+ const hasGithub = data.hasIssues || data.hasDiscussions;
471
243
  const docFiles = listReferenceFiles(skillDir);
472
244
  update(packageName, "generating", config.model, versionKey);
473
245
  const { optimized, wasOptimized, error } = await optimizeDocs({
474
246
  packageName,
475
247
  skillDir,
476
248
  model: config.model,
477
- version,
249
+ version: data.version,
478
250
  hasGithub,
479
- hasReleases,
480
- hasChangelog,
251
+ hasReleases: data.hasReleases,
252
+ hasChangelog: data.hasChangelog,
481
253
  docFiles,
254
+ docsType: data.docsType,
255
+ hasShippedDocs: data.shippedDocs,
482
256
  noCache: config.force,
257
+ debug: config.debug,
483
258
  sections,
484
259
  customPrompt,
485
260
  onProgress: (progress) => {
486
- update(packageName, progress.type === "reasoning" ? "exploring" : "generating", progress.chunk.startsWith("[") ? progress.chunk : config.model, versionKey);
261
+ const status = progress.type === "reasoning" ? "exploring" : "generating";
262
+ const sectionPrefix = progress.section ? `[${progress.section}] ` : "";
263
+ update(packageName, status, progress.chunk.startsWith("[") ? `${sectionPrefix}${progress.chunk}` : `${sectionPrefix}${config.model}`, versionKey);
487
264
  }
488
265
  });
489
266
  if (error) {
@@ -491,49 +268,28 @@ async function enhanceWithLLM(packageName, config, cwd, update, sections, custom
491
268
  throw new Error(error);
492
269
  }
493
270
  if (wasOptimized) {
494
- const relatedSkills = await findRelatedSkills(packageName, baseDir);
495
- const shippedDocs = hasShippedDocs(packageName, cwd, version);
496
- const pkgFiles = getPkgKeyFiles(packageName, cwd, version);
497
- const body = cleanSkillMd(optimized);
498
271
  const skillMd = generateSkillMd({
499
272
  name: packageName,
500
- version,
501
- releasedAt: resolved.releasedAt,
502
- dependencies: resolved.dependencies,
503
- distTags: resolved.distTags,
504
- body,
505
- relatedSkills,
506
- hasGithub,
507
- hasReleases,
508
- hasChangelog,
509
- docsType,
510
- hasShippedDocs: shippedDocs,
511
- pkgFiles
273
+ version: data.version,
274
+ releasedAt: data.resolved.releasedAt,
275
+ dependencies: data.resolved.dependencies,
276
+ distTags: data.resolved.distTags,
277
+ body: optimized,
278
+ relatedSkills: data.relatedSkills,
279
+ hasIssues: data.hasIssues,
280
+ hasDiscussions: data.hasDiscussions,
281
+ hasReleases: data.hasReleases,
282
+ hasChangelog: data.hasChangelog,
283
+ docsType: data.docsType,
284
+ hasShippedDocs: data.shippedDocs,
285
+ pkgFiles: data.pkgFiles,
286
+ dirName: data.skillDirName,
287
+ packages: data.packages,
288
+ repoUrl: data.resolved.repoUrl
512
289
  });
513
290
  writeFileSync(join(skillDir, "SKILL.md"), skillMd);
514
291
  }
515
- update(packageName, "done", "Enhanced", versionKey);
516
- }
517
- async function findRelatedSkills(packageName, skillsDir) {
518
- const related = [];
519
- const npmInfo = await fetchNpmPackage(packageName);
520
- if (!npmInfo?.dependencies) return related;
521
- const deps = Object.keys(npmInfo.dependencies);
522
- if (!existsSync(skillsDir)) return related;
523
- const installedSkills = readdirSync(skillsDir);
524
- for (const skill of installedSkills) if (deps.some((d) => sanitizeName(d) === skill)) related.push(skill);
525
- return related.slice(0, 5);
526
- }
527
- function cleanSkillMd(content) {
528
- let cleaned = content.replace(/^```markdown\n?/m, "").replace(/\n?```$/m, "").trim();
529
- const fmMatch = cleaned.match(/^-{3,}\n/);
530
- if (fmMatch) {
531
- const afterOpen = fmMatch[0].length;
532
- const closeMatch = cleaned.slice(afterOpen).match(/\n-{3,}/);
533
- if (closeMatch) cleaned = cleaned.slice(afterOpen + closeMatch.index + closeMatch[0].length).trim();
534
- else cleaned = cleaned.slice(afterOpen).trim();
535
- }
536
- return cleaned;
292
+ update(packageName, "done", "Skill optimized", versionKey);
537
293
  }
538
294
  export { syncPackagesParallel };
539
295