skilld 0.0.1 → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +119 -88
- package/dist/_chunks/config.mjs +20 -0
- package/dist/_chunks/config.mjs.map +1 -0
- package/dist/_chunks/llm.mjs +877 -0
- package/dist/_chunks/llm.mjs.map +1 -0
- package/dist/_chunks/releases.mjs +986 -0
- package/dist/_chunks/releases.mjs.map +1 -0
- package/dist/_chunks/storage.mjs +198 -0
- package/dist/_chunks/storage.mjs.map +1 -0
- package/dist/_chunks/sync-parallel.mjs +540 -0
- package/dist/_chunks/sync-parallel.mjs.map +1 -0
- package/dist/_chunks/types.d.mts +87 -0
- package/dist/_chunks/types.d.mts.map +1 -0
- package/dist/_chunks/utils.d.mts +352 -0
- package/dist/_chunks/utils.d.mts.map +1 -0
- package/dist/_chunks/version.d.mts +147 -0
- package/dist/_chunks/version.d.mts.map +1 -0
- package/dist/agent/index.d.mts +205 -0
- package/dist/agent/index.d.mts.map +1 -0
- package/dist/agent/index.mjs +2 -0
- package/dist/cache/index.d.mts +2 -0
- package/dist/cache/index.mjs +3 -0
- package/dist/cli.mjs +2650 -449
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +5 -14
- package/dist/index.mjs +7 -181
- package/dist/retriv/index.d.mts +12 -0
- package/dist/retriv/index.d.mts.map +1 -0
- package/dist/retriv/index.mjs +76 -0
- package/dist/retriv/index.mjs.map +1 -0
- package/dist/sources/index.d.mts +2 -0
- package/dist/sources/index.mjs +3 -0
- package/dist/types.d.mts +4 -37
- package/package.json +39 -13
- package/dist/agents.d.mts +0 -56
- package/dist/agents.d.mts.map +0 -1
- package/dist/agents.mjs +0 -148
- package/dist/agents.mjs.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/npm.d.mts +0 -48
- package/dist/npm.d.mts.map +0 -1
- package/dist/npm.mjs +0 -90
- package/dist/npm.mjs.map +0 -1
- package/dist/split-text.d.mts +0 -24
- package/dist/split-text.d.mts.map +0 -1
- package/dist/split-text.mjs +0 -87
- package/dist/split-text.mjs.map +0 -1
- package/dist/types.d.mts.map +0 -1
|
@@ -0,0 +1,540 @@
|
|
|
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";
|
|
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";
|
|
6
|
+
import "../sources/index.mjs";
|
|
7
|
+
import { c as sanitizeName, i as generateSkillMd, p as agents, r as optimizeDocs } from "./llm.mjs";
|
|
8
|
+
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";
|
|
13
|
+
import pLimit from "p-limit";
|
|
14
|
+
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
|
+
const STATUS_ICONS = {
|
|
25
|
+
pending: "○",
|
|
26
|
+
resolving: "◐",
|
|
27
|
+
downloading: "◒",
|
|
28
|
+
embedding: "◓",
|
|
29
|
+
exploring: "◔",
|
|
30
|
+
thinking: "◔",
|
|
31
|
+
generating: "◑",
|
|
32
|
+
done: "✓",
|
|
33
|
+
error: "✗"
|
|
34
|
+
};
|
|
35
|
+
const STATUS_COLORS = {
|
|
36
|
+
pending: "\x1B[90m",
|
|
37
|
+
resolving: "\x1B[36m",
|
|
38
|
+
downloading: "\x1B[36m",
|
|
39
|
+
embedding: "\x1B[36m",
|
|
40
|
+
exploring: "\x1B[34m",
|
|
41
|
+
thinking: "\x1B[35m",
|
|
42
|
+
generating: "\x1B[33m",
|
|
43
|
+
done: "\x1B[32m",
|
|
44
|
+
error: "\x1B[31m"
|
|
45
|
+
};
|
|
46
|
+
async function syncPackagesParallel(config) {
|
|
47
|
+
const { packages, concurrency = 5 } = config;
|
|
48
|
+
const agent = agents[config.agent];
|
|
49
|
+
const states = /* @__PURE__ */ new Map();
|
|
50
|
+
const cwd = process.cwd();
|
|
51
|
+
for (const pkg of packages) states.set(pkg, {
|
|
52
|
+
name: pkg,
|
|
53
|
+
status: "pending",
|
|
54
|
+
message: "Waiting..."
|
|
55
|
+
});
|
|
56
|
+
function render() {
|
|
57
|
+
const maxNameLen = Math.max(...packages.map((p) => p.length), 20);
|
|
58
|
+
const lines = [...states.values()].map((s) => {
|
|
59
|
+
const icon = STATUS_ICONS[s.status];
|
|
60
|
+
const color = STATUS_COLORS[s.status];
|
|
61
|
+
const reset = "\x1B[0m";
|
|
62
|
+
const dim = "\x1B[90m";
|
|
63
|
+
const name = s.name.padEnd(maxNameLen);
|
|
64
|
+
const version = s.version ? `${dim}${s.version}${reset} ` : "";
|
|
65
|
+
const preview = s.streamPreview ? ` ${dim}${s.streamPreview}${reset}` : "";
|
|
66
|
+
return ` ${color}${icon}${reset} ${name} ${version}${s.message}${preview}`;
|
|
67
|
+
});
|
|
68
|
+
const doneCount = [...states.values()].filter((s) => s.status === "done").length;
|
|
69
|
+
const errorCount = [...states.values()].filter((s) => s.status === "error").length;
|
|
70
|
+
logUpdate(`\x1B[1mSyncing ${packages.length} packages\x1B[0m (${doneCount} done${errorCount > 0 ? `, ${errorCount} failed` : ""})\n` + lines.join("\n"));
|
|
71
|
+
}
|
|
72
|
+
function update(pkg, status, message, version) {
|
|
73
|
+
const state = states.get(pkg);
|
|
74
|
+
state.status = status;
|
|
75
|
+
state.message = message;
|
|
76
|
+
state.streamPreview = void 0;
|
|
77
|
+
if (version) state.version = version;
|
|
78
|
+
render();
|
|
79
|
+
}
|
|
80
|
+
ensureCacheDir();
|
|
81
|
+
render();
|
|
82
|
+
const limit = pLimit(concurrency);
|
|
83
|
+
const baseResults = await Promise.allSettled(packages.map((pkg) => limit(() => syncBaseSkill(pkg, config, cwd, update))));
|
|
84
|
+
logUpdate.done();
|
|
85
|
+
const successfulPkgs = [];
|
|
86
|
+
const errors = [];
|
|
87
|
+
for (let i = 0; i < baseResults.length; i++) {
|
|
88
|
+
const r = baseResults[i];
|
|
89
|
+
if (r.status === "fulfilled" && r.value !== "shipped") successfulPkgs.push(packages[i]);
|
|
90
|
+
else if (r.status === "rejected") {
|
|
91
|
+
const err = r.reason;
|
|
92
|
+
const reason = err instanceof Error ? `${err.message}\n${err.stack}` : String(err);
|
|
93
|
+
errors.push({
|
|
94
|
+
pkg: packages[i],
|
|
95
|
+
reason
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
p.log.success(`Created ${successfulPkgs.length} base skills`);
|
|
100
|
+
if (errors.length > 0) for (const { pkg, reason } of errors) p.log.error(` ${pkg}: ${reason}`);
|
|
101
|
+
const globalConfig = readConfig();
|
|
102
|
+
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
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
await ensureGitignore(agent.skillsDir, cwd, config.global);
|
|
128
|
+
p.outro(`Synced ${successfulPkgs.length}/${packages.length} packages`);
|
|
129
|
+
}
|
|
130
|
+
async function syncBaseSkill(packageName, config, cwd, update) {
|
|
131
|
+
const localVersion = (await readLocalDependencies(cwd).catch(() => [])).find((d) => d.name === packageName)?.version;
|
|
132
|
+
const { package: resolvedPkg, attempts } = await resolvePackageDocsWithAttempts(packageName, {
|
|
133
|
+
version: localVersion,
|
|
134
|
+
cwd,
|
|
135
|
+
onProgress: (step) => update(packageName, "resolving", RESOLVE_STEP_LABELS[step])
|
|
136
|
+
});
|
|
137
|
+
let resolved = resolvedPkg;
|
|
138
|
+
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
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!resolved) {
|
|
154
|
+
const npmAttempt = attempts.find((a) => a.source === "npm");
|
|
155
|
+
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";
|
|
158
|
+
update(packageName, "error", reason);
|
|
159
|
+
throw new Error(`Could not find docs for: ${packageName}`);
|
|
160
|
+
}
|
|
161
|
+
const version = localVersion || resolved.version || "latest";
|
|
162
|
+
const versionKey = getVersionKey(version);
|
|
163
|
+
if (!existsSync(join(cwd, "node_modules", packageName))) {
|
|
164
|
+
update(packageName, "downloading", "Downloading dist...", versionKey);
|
|
165
|
+
await fetchPkgDist(packageName, version);
|
|
166
|
+
}
|
|
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);
|
|
184
|
+
return "shipped";
|
|
185
|
+
}
|
|
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
|
+
}
|
|
194
|
+
const useCache = isCached(packageName, version);
|
|
195
|
+
if (useCache) update(packageName, "downloading", "Using cache", versionKey);
|
|
196
|
+
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));
|
|
200
|
+
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
|
+
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
|
+
}
|
|
345
|
+
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
|
+
}
|
|
363
|
+
});
|
|
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;
|
|
376
|
+
const relatedSkills = await findRelatedSkills(packageName, baseDir);
|
|
377
|
+
const shippedDocs = hasShippedDocs(packageName, cwd, version);
|
|
378
|
+
const pkgFiles = getPkgKeyFiles(packageName, cwd, version);
|
|
379
|
+
const skillMd = generateSkillMd({
|
|
380
|
+
name: packageName,
|
|
381
|
+
version,
|
|
382
|
+
releasedAt: resolved.releasedAt,
|
|
383
|
+
description: resolved.description,
|
|
384
|
+
dependencies: resolved.dependencies,
|
|
385
|
+
distTags: resolved.distTags,
|
|
386
|
+
relatedSkills,
|
|
387
|
+
hasGithub: false,
|
|
388
|
+
hasReleases,
|
|
389
|
+
hasChangelog,
|
|
390
|
+
docsType,
|
|
391
|
+
hasShippedDocs: shippedDocs,
|
|
392
|
+
pkgFiles
|
|
393
|
+
});
|
|
394
|
+
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
|
+
if (!config.global) registerProject(cwd);
|
|
403
|
+
update(packageName, "done", "Skill ready", versionKey);
|
|
404
|
+
return "synced";
|
|
405
|
+
}
|
|
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;
|
|
471
|
+
const docFiles = listReferenceFiles(skillDir);
|
|
472
|
+
update(packageName, "generating", config.model, versionKey);
|
|
473
|
+
const { optimized, wasOptimized, error } = await optimizeDocs({
|
|
474
|
+
packageName,
|
|
475
|
+
skillDir,
|
|
476
|
+
model: config.model,
|
|
477
|
+
version,
|
|
478
|
+
hasGithub,
|
|
479
|
+
hasReleases,
|
|
480
|
+
hasChangelog,
|
|
481
|
+
docFiles,
|
|
482
|
+
noCache: config.force,
|
|
483
|
+
sections,
|
|
484
|
+
customPrompt,
|
|
485
|
+
onProgress: (progress) => {
|
|
486
|
+
update(packageName, progress.type === "reasoning" ? "exploring" : "generating", progress.chunk.startsWith("[") ? progress.chunk : config.model, versionKey);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
if (error) {
|
|
490
|
+
update(packageName, "error", error, versionKey);
|
|
491
|
+
throw new Error(error);
|
|
492
|
+
}
|
|
493
|
+
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
|
+
const skillMd = generateSkillMd({
|
|
499
|
+
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
|
|
512
|
+
});
|
|
513
|
+
writeFileSync(join(skillDir, "SKILL.md"), skillMd);
|
|
514
|
+
}
|
|
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;
|
|
537
|
+
}
|
|
538
|
+
export { syncPackagesParallel };
|
|
539
|
+
|
|
540
|
+
//# sourceMappingURL=sync-parallel.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-parallel.mjs","names":[],"sources":["../../src/commands/sync-parallel.ts"],"sourcesContent":["import type { AgentType, OptimizeModel, SkillSection } from '../agent'\nimport type { ResolveStep } from '../sources'\nimport { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport * as p from '@clack/prompts'\nimport logUpdate from 'log-update'\nimport pLimit from 'p-limit'\nimport {\n agents,\n\n generateSkillMd,\n optimizeDocs,\n\n sanitizeName,\n\n} from '../agent'\nimport {\n CACHE_DIR,\n clearCache,\n ensureCacheDir,\n getCacheDir,\n getPackageDbPath,\n getPkgKeyFiles,\n getShippedSkills,\n getVersionKey,\n hasShippedDocs,\n isCached,\n linkGithub,\n linkPkg,\n linkReferences,\n linkReleases,\n linkShippedSkill,\n listReferenceFiles,\n readCachedDocs,\n resolvePkgDir,\n writeToCache,\n} from '../cache'\nimport { defaultFeatures, readConfig, registerProject } from '../core/config'\nimport { writeLock } from '../core/lockfile'\nimport { createIndex } from '../retriv'\nimport {\n downloadLlmsDocs,\n fetchGitDocs,\n fetchLlmsTxt,\n fetchNpmPackage,\n fetchPkgDist,\n fetchReadmeContent,\n fetchReleaseNotes,\n normalizeLlmsLinks,\n parseGitHubUrl,\n parseMarkdownLinks,\n readLocalDependencies,\n resolveEntryFiles,\n resolveLocalPackageDocs,\n resolvePackageDocs,\n resolvePackageDocsWithAttempts,\n} from '../sources'\n\nimport { ensureGitignore, selectModel, selectSkillSections } from './sync'\n\ntype PackageStatus = 'pending' | 'resolving' | 'downloading' | 'embedding' | 'exploring' | 'thinking' | 'generating' | 'done' | 'error'\n\nconst RESOLVE_STEP_LABELS: Record<ResolveStep, string> = {\n 'npm': 'npm registry',\n 'github-docs': 'GitHub docs',\n 'github-meta': 'GitHub meta',\n 'github-search': 'GitHub search',\n 'readme': 'README',\n 'llms.txt': 'llms.txt',\n 'local': 'node_modules',\n}\n\ninterface PackageState {\n name: string\n status: PackageStatus\n message: string\n version?: string\n streamPreview?: string\n}\n\nconst STATUS_ICONS: Record<PackageStatus, string> = {\n pending: '○',\n resolving: '◐',\n downloading: '◒',\n embedding: '◓',\n exploring: '◔',\n thinking: '◔',\n generating: '◑',\n done: '✓',\n error: '✗',\n}\n\nconst STATUS_COLORS: Record<PackageStatus, string> = {\n pending: '\\x1B[90m',\n resolving: '\\x1B[36m',\n downloading: '\\x1B[36m',\n embedding: '\\x1B[36m',\n exploring: '\\x1B[34m', // Blue for exploring\n thinking: '\\x1B[35m', // Magenta for thinking\n generating: '\\x1B[33m',\n done: '\\x1B[32m',\n error: '\\x1B[31m',\n}\n\nexport interface ParallelSyncConfig {\n packages: string[]\n global: boolean\n agent: AgentType\n model?: OptimizeModel\n yes?: boolean\n force?: boolean\n concurrency?: number\n}\n\nexport async function syncPackagesParallel(config: ParallelSyncConfig): Promise<void> {\n const { packages, concurrency = 5 } = config\n const agent = agents[config.agent]\n const states = new Map<string, PackageState>()\n const cwd = process.cwd()\n\n // Initialize all packages as pending\n for (const pkg of packages) {\n states.set(pkg, { name: pkg, status: 'pending', message: 'Waiting...' })\n }\n\n // Render function\n function render() {\n const maxNameLen = Math.max(...packages.map(p => p.length), 20)\n const lines = [...states.values()].map((s) => {\n const icon = STATUS_ICONS[s.status]\n const color = STATUS_COLORS[s.status]\n const reset = '\\x1B[0m'\n const dim = '\\x1B[90m'\n const name = s.name.padEnd(maxNameLen)\n const version = s.version ? `${dim}${s.version}${reset} ` : ''\n const preview = s.streamPreview ? ` ${dim}${s.streamPreview}${reset}` : ''\n return ` ${color}${icon}${reset} ${name} ${version}${s.message}${preview}`\n })\n\n const doneCount = [...states.values()].filter(s => s.status === 'done').length\n const errorCount = [...states.values()].filter(s => s.status === 'error').length\n const header = `\\x1B[1mSyncing ${packages.length} packages\\x1B[0m (${doneCount} done${errorCount > 0 ? `, ${errorCount} failed` : ''})\\n`\n\n logUpdate(header + lines.join('\\n'))\n }\n\n function update(pkg: string, status: PackageStatus, message: string, version?: string) {\n const state = states.get(pkg)!\n state.status = status\n state.message = message\n state.streamPreview = undefined // Clear preview on status change\n if (version)\n state.version = version\n render()\n }\n\n ensureCacheDir()\n render()\n\n const limit = pLimit(concurrency)\n\n // Phase 1: Generate base skills (no LLM)\n const baseResults = await Promise.allSettled(\n packages.map(pkg =>\n limit(() => syncBaseSkill(pkg, config, cwd, update)),\n ),\n )\n\n logUpdate.done()\n\n // Collect successful packages for LLM phase (exclude shipped — they need no LLM)\n const successfulPkgs: string[] = []\n const errors: Array<{ pkg: string, reason: string }> = []\n for (let i = 0; i < baseResults.length; i++) {\n const r = baseResults[i]!\n if (r.status === 'fulfilled' && r.value !== 'shipped') {\n successfulPkgs.push(packages[i]!)\n }\n else if (r.status === 'rejected') {\n const err = r.reason\n const reason = err instanceof Error ? `${err.message}\\n${err.stack}` : String(err)\n errors.push({ pkg: packages[i]!, reason })\n }\n }\n\n p.log.success(`Created ${successfulPkgs.length} base skills`)\n\n if (errors.length > 0) {\n for (const { pkg, reason } of errors) {\n p.log.error(` ${pkg}: ${reason}`)\n }\n }\n\n // Phase 2: Ask about LLM enhancement (skip if -y without model, or skipLlm config)\n const globalConfig = readConfig()\n if (successfulPkgs.length > 0 && !globalConfig.skipLlm && !(config.yes && !config.model)) {\n const { sections, customPrompt, cancelled } = config.model\n ? { sections: ['best-practices', 'api'] as SkillSection[], customPrompt: undefined, cancelled: false }\n : await selectSkillSections()\n\n if (!cancelled && sections.length > 0) {\n const model = config.model ?? await selectModel(false)\n\n if (model) {\n // Reset states for LLM phase\n for (const pkg of successfulPkgs) {\n states.set(pkg, { name: pkg, status: 'pending', message: 'Waiting...' })\n }\n render()\n\n const llmResults = await Promise.allSettled(\n successfulPkgs.map(pkg =>\n limit(() => enhanceWithLLM(pkg, { ...config, model }, cwd, update, sections, customPrompt)),\n ),\n )\n\n logUpdate.done()\n\n const llmSucceeded = llmResults.filter(r => r.status === 'fulfilled').length\n p.log.success(`Enhanced ${llmSucceeded}/${successfulPkgs.length} skills with LLM`)\n }\n }\n }\n\n await ensureGitignore(agent.skillsDir, cwd, config.global)\n\n p.outro(`Synced ${successfulPkgs.length}/${packages.length} packages`)\n}\n\ntype UpdateFn = (pkg: string, status: PackageStatus, message: string, version?: string) => void\n\n/** Phase 1: Generate base skill (no LLM). Returns 'shipped' if shipped skill was linked. */\nasync function syncBaseSkill(\n packageName: string,\n config: ParallelSyncConfig,\n cwd: string,\n update: UpdateFn,\n): Promise<'shipped' | 'synced'> {\n const localDeps = await readLocalDependencies(cwd).catch(() => [])\n const localVersion = localDeps.find(d => d.name === packageName)?.version\n\n const { package: resolvedPkg, attempts } = await resolvePackageDocsWithAttempts(packageName, {\n version: localVersion,\n cwd,\n onProgress: step => update(packageName, 'resolving', RESOLVE_STEP_LABELS[step]),\n })\n let resolved = resolvedPkg\n\n if (!resolved) {\n const pkgPath = join(cwd, 'package.json')\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n const deps = { ...pkg.dependencies, ...pkg.devDependencies }\n const depVersion = deps[packageName]\n\n if (depVersion?.startsWith('link:')) {\n update(packageName, 'resolving', 'Local package...')\n const { resolve } = await import('node:path')\n const localPath = resolve(cwd, depVersion.slice(5))\n resolved = await resolveLocalPackageDocs(localPath)\n }\n }\n }\n\n if (!resolved) {\n const npmAttempt = attempts.find(a => a.source === 'npm')\n let reason: string\n if (npmAttempt?.status === 'not-found') {\n reason = npmAttempt.message || 'Not on npm'\n }\n else {\n const failed = attempts.filter(a => a.status !== 'success')\n const messages = failed.map(a => a.message || a.source).join('; ')\n reason = messages || 'No docs found'\n }\n update(packageName, 'error', reason)\n throw new Error(`Could not find docs for: ${packageName}`)\n }\n\n const version = localVersion || resolved.version || 'latest'\n const versionKey = getVersionKey(version)\n\n // Download npm dist if not in node_modules\n if (!existsSync(join(cwd, 'node_modules', packageName))) {\n update(packageName, 'downloading', 'Downloading dist...', versionKey)\n await fetchPkgDist(packageName, version)\n }\n\n // Shipped skills: symlink directly, skip all doc fetching/caching/LLM\n const shippedSkills = getShippedSkills(packageName, cwd, version)\n if (shippedSkills.length > 0) {\n const agent = agents[config.agent]\n const baseDir = config.global\n ? join(CACHE_DIR, 'skills')\n : join(cwd, agent.skillsDir)\n mkdirSync(baseDir, { recursive: true })\n\n for (const shipped of shippedSkills) {\n linkShippedSkill(baseDir, shipped.skillName, shipped.skillDir)\n writeLock(baseDir, shipped.skillName, {\n packageName,\n version,\n source: 'shipped',\n syncedAt: new Date().toISOString().split('T')[0],\n generator: 'skilld',\n })\n }\n if (!config.global)\n registerProject(cwd)\n update(packageName, 'done', 'Shipped', versionKey)\n return 'shipped'\n }\n\n // Force: nuke cached references + search index so all existsSync guards re-fetch\n if (config.force) {\n clearCache(packageName, version)\n const forcedDbPath = getPackageDbPath(packageName, version)\n if (existsSync(forcedDbPath))\n rmSync(forcedDbPath, { recursive: true, force: true })\n }\n\n const useCache = isCached(packageName, version)\n if (useCache) {\n update(packageName, 'downloading', 'Using cache', versionKey)\n }\n else {\n update(packageName, 'downloading', config.force ? 'Re-fetching docs...' : 'Fetching docs...', versionKey)\n }\n\n const agent = agents[config.agent]\n const baseDir = config.global\n ? join(CACHE_DIR, 'skills')\n : join(cwd, agent.skillsDir)\n\n const skillDir = join(baseDir, sanitizeName(packageName))\n mkdirSync(skillDir, { recursive: true })\n\n let docSource: string = resolved.readmeUrl || 'readme'\n let docsType: 'llms.txt' | 'readme' | 'docs' = 'readme'\n const docsToIndex: Array<{ id: string, content: string, metadata: Record<string, any> }> = []\n\n if (!useCache) {\n const cachedDocs: Array<{ path: string, content: string }> = []\n\n // Try versioned git docs first\n if (resolved.gitDocsUrl && resolved.repoUrl) {\n const gh = parseGitHubUrl(resolved.repoUrl)\n if (gh) {\n update(packageName, 'downloading', 'Git docs...', versionKey)\n const gitDocs = await fetchGitDocs(gh.owner, gh.repo, version, packageName)\n if (gitDocs && gitDocs.files.length > 0) {\n update(packageName, 'downloading', `0/${gitDocs.files.length} docs @ ${gitDocs.ref}`, versionKey)\n\n const BATCH_SIZE = 20\n const results: Array<{ file: string, content: string } | null> = []\n let downloaded = 0\n\n for (let i = 0; i < gitDocs.files.length; i += BATCH_SIZE) {\n const batch = gitDocs.files.slice(i, i + BATCH_SIZE)\n const batchResults = await Promise.all(\n batch.map(async (file) => {\n const url = `${gitDocs.baseUrl}/${file}`\n const res = await fetch(url, { headers: { 'User-Agent': 'skilld/1.0' } }).catch(() => null)\n if (!res?.ok)\n return null\n const content = await res.text()\n downloaded++\n update(packageName, 'downloading', `${downloaded}/${gitDocs.files.length} docs @ ${gitDocs.ref}`, versionKey)\n return { file, content }\n }),\n )\n results.push(...batchResults)\n }\n\n for (const r of results) {\n if (r) {\n const cachePath = gitDocs.docsPrefix ? r.file.replace(gitDocs.docsPrefix, '') : r.file\n cachedDocs.push({ path: cachePath, content: r.content })\n docsToIndex.push({\n id: cachePath,\n content: r.content,\n metadata: { package: packageName, source: cachePath, type: 'doc' },\n })\n }\n }\n\n const downloadedCount = results.filter(Boolean).length\n if (downloadedCount > 0) {\n docSource = `${resolved.repoUrl}/tree/${gitDocs.ref}/docs`\n docsType = 'docs'\n }\n }\n }\n }\n\n if (resolved.llmsUrl && cachedDocs.length === 0) {\n update(packageName, 'downloading', 'Fetching llms.txt...', versionKey)\n const llmsContent = await fetchLlmsTxt(resolved.llmsUrl)\n if (llmsContent) {\n docSource = resolved.llmsUrl!\n docsType = 'llms.txt'\n cachedDocs.push({ path: 'llms.txt', content: normalizeLlmsLinks(llmsContent.raw) })\n\n if (llmsContent.links.length > 0) {\n update(packageName, 'downloading', `0/${llmsContent.links.length} linked docs...`, versionKey)\n const baseUrl = resolved.docsUrl || new URL(resolved.llmsUrl).origin\n const docs = await downloadLlmsDocs(llmsContent, baseUrl)\n\n for (const doc of docs) {\n const localPath = doc.url.startsWith('/') ? doc.url.slice(1) : doc.url\n const cachePath = join('docs', ...localPath.split('/'))\n cachedDocs.push({ path: cachePath, content: doc.content })\n\n docsToIndex.push({\n id: doc.url,\n content: doc.content,\n metadata: { package: packageName, source: cachePath, type: 'doc' },\n })\n }\n }\n }\n }\n\n // Fallback to README\n if (resolved.readmeUrl && cachedDocs.length === 0) {\n update(packageName, 'downloading', 'Fetching README...', versionKey)\n const content = await fetchReadmeContent(resolved.readmeUrl)\n if (content) {\n cachedDocs.push({ path: 'docs/README.md', content })\n docsToIndex.push({\n id: 'README.md',\n content,\n metadata: { package: packageName, source: 'docs/README.md', type: 'doc' },\n })\n }\n }\n\n // Write to global cache\n if (cachedDocs.length > 0) {\n writeToCache(packageName, version, cachedDocs)\n }\n }\n else {\n // Detect docs type from cache\n const cacheDir = getCacheDir(packageName, version)\n if (existsSync(join(cacheDir, 'docs', 'index.md')) || existsSync(join(cacheDir, 'docs', 'guide'))) {\n docsType = 'docs'\n }\n else if (existsSync(join(cacheDir, 'llms.txt'))) {\n docsType = 'llms.txt'\n }\n\n // Load cached docs for indexing if db doesn't exist yet\n const dbPath = getPackageDbPath(packageName, version)\n if (!existsSync(dbPath)) {\n const cached = readCachedDocs(packageName, version)\n for (const doc of cached) {\n docsToIndex.push({\n id: doc.path,\n content: doc.content,\n metadata: { package: packageName, source: doc.path, type: 'doc' },\n })\n }\n }\n }\n\n const features = readConfig().features ?? defaultFeatures\n\n // Fetch release notes\n const releasesPath = join(getCacheDir(packageName, version), 'releases')\n if (features.releases && resolved.repoUrl && !existsSync(releasesPath)) {\n const gh = parseGitHubUrl(resolved.repoUrl)\n if (gh) {\n update(packageName, 'downloading', 'Fetching releases...', versionKey)\n const releaseDocs = await fetchReleaseNotes(gh.owner, gh.repo, version, resolved.gitRef, packageName)\n if (releaseDocs.length > 0) {\n writeToCache(packageName, version, releaseDocs)\n for (const doc of releaseDocs) {\n docsToIndex.push({\n id: doc.path,\n content: doc.content,\n metadata: { package: packageName, source: doc.path, type: 'release' },\n })\n }\n }\n }\n }\n\n // Create symlinks\n update(packageName, 'downloading', 'Linking references...', versionKey)\n try {\n linkPkg(skillDir, packageName, cwd, version)\n if (!hasShippedDocs(packageName, cwd, version) && docsType !== 'readme') {\n linkReferences(skillDir, packageName, version)\n }\n linkGithub(skillDir, packageName, version)\n linkReleases(skillDir, packageName, version)\n }\n catch {}\n\n // Collect entry files for indexing\n update(packageName, 'embedding', 'Scanning exports...', versionKey)\n const pkgDir = resolvePkgDir(packageName, cwd, version)\n const entryFiles = features.search && pkgDir ? await resolveEntryFiles(pkgDir) : []\n if (entryFiles.length > 0) {\n for (const e of entryFiles) {\n docsToIndex.push({\n id: e.path,\n content: e.content,\n metadata: { package: packageName, source: `pkg/${e.path}`, type: e.type },\n })\n }\n }\n\n // Single batch index — one model init, one write\n const dbPath = getPackageDbPath(packageName, version)\n if (docsToIndex.length > 0) {\n update(packageName, 'embedding', `Indexing ${docsToIndex.length} documents...`, versionKey)\n await createIndex(docsToIndex, {\n dbPath,\n onProgress: (current, total, doc) => {\n const type = doc?.type === 'source' || doc?.type === 'types' ? 'code' : 'doc'\n const file = doc?.id ? doc.id.split('/').pop() : ''\n const pct = Math.round((current / total) * 100)\n update(packageName, 'embedding', `Indexing ${type} ${file}... ${current}/${total} ${pct}%`, versionKey)\n },\n })\n }\n\n const hasReleases = existsSync(releasesPath)\n const hasChangelog = pkgDir ? (['CHANGELOG.md', 'changelog.md'].find(f => existsSync(join(pkgDir, f))) || false) : false\n\n const relatedSkills = await findRelatedSkills(packageName, baseDir)\n const shippedDocs = hasShippedDocs(packageName, cwd, version)\n const pkgFiles = getPkgKeyFiles(packageName, cwd, version)\n\n // Write base SKILL.md\n const skillMd = generateSkillMd({\n name: packageName,\n version,\n releasedAt: resolved.releasedAt,\n description: resolved.description,\n dependencies: resolved.dependencies,\n distTags: resolved.distTags,\n relatedSkills,\n hasGithub: false,\n hasReleases,\n hasChangelog,\n docsType,\n hasShippedDocs: shippedDocs,\n pkgFiles,\n })\n writeFileSync(join(skillDir, 'SKILL.md'), skillMd)\n\n writeLock(baseDir, sanitizeName(packageName), {\n packageName,\n version,\n source: docSource,\n syncedAt: new Date().toISOString().split('T')[0],\n generator: 'skilld',\n })\n\n if (!config.global) {\n registerProject(cwd)\n }\n\n update(packageName, 'done', 'Skill ready', versionKey)\n return 'synced'\n}\n\n/** Phase 2: Enhance skill with LLM */\nasync function enhanceWithLLM(\n packageName: string,\n config: ParallelSyncConfig & { model: OptimizeModel },\n cwd: string,\n update: UpdateFn,\n sections?: SkillSection[],\n customPrompt?: string,\n): Promise<void> {\n const localDeps = await readLocalDependencies(cwd).catch(() => [])\n const localVersion = localDeps.find(d => d.name === packageName)?.version\n const resolved = await resolvePackageDocs(packageName, { version: localVersion })\n if (!resolved)\n throw new Error('Package not found')\n\n const version = localVersion || resolved.version || 'latest'\n const versionKey = getVersionKey(version)\n\n const agent = agents[config.agent]\n const baseDir = config.global\n ? join(CACHE_DIR, 'skills')\n : join(cwd, agent.skillsDir)\n\n const skillDir = join(baseDir, sanitizeName(packageName))\n const cacheDir = getCacheDir(packageName, version)\n\n // Load docs content\n let docsContent: string | null = null\n let llmsRaw: string | null = null\n let docsType: 'llms.txt' | 'readme' | 'docs' = 'readme'\n\n // Detect docs type\n if (existsSync(join(cacheDir, 'docs', 'index.md')) || existsSync(join(cacheDir, 'docs', 'guide'))) {\n docsType = 'docs'\n }\n else if (existsSync(join(cacheDir, 'llms.txt'))) {\n docsType = 'llms.txt'\n }\n\n // Priority 1: Git docs\n const guideDir = join(cacheDir, 'docs', 'guide')\n const docsDir = join(cacheDir, 'docs')\n if (existsSync(guideDir) || existsSync(join(docsDir, 'index.md'))) {\n const sections: string[] = []\n const indexPath = join(docsDir, 'index.md')\n if (existsSync(indexPath)) {\n sections.push(readFileSync(indexPath, 'utf-8'))\n }\n if (existsSync(guideDir)) {\n const priorityFiles = ['index.md', 'features.md', 'migration.md', 'why.md']\n const guideFiles = readdirSync(guideDir, { withFileTypes: true })\n .filter(f => f.isFile() && f.name.endsWith('.md'))\n .map(f => f.name)\n .sort((a, b) => {\n const aIdx = priorityFiles.indexOf(a)\n const bIdx = priorityFiles.indexOf(b)\n if (aIdx >= 0 && bIdx >= 0)\n return aIdx - bIdx\n if (aIdx >= 0)\n return -1\n if (bIdx >= 0)\n return 1\n return a.localeCompare(b)\n })\n for (const file of guideFiles.slice(0, 10)) {\n const content = readFileSync(join(guideDir, file), 'utf-8')\n sections.push(`# guide/${file}\\n\\n${content}`)\n }\n }\n if (sections.length > 0)\n docsContent = sections.join('\\n\\n---\\n\\n')\n }\n\n // Priority 2: llms.txt\n if (!docsContent) {\n if (existsSync(join(cacheDir, 'llms.txt'))) {\n llmsRaw = readFileSync(join(cacheDir, 'llms.txt'), 'utf-8')\n }\n if (llmsRaw) {\n const bestPracticesPaths = parseMarkdownLinks(llmsRaw)\n .map(l => l.url)\n .filter(lp => lp.includes('/style-guide/') || lp.includes('/best-practices/') || lp.includes('/typescript/'))\n const sections: string[] = []\n for (const mdPath of bestPracticesPaths) {\n const localPath = mdPath.startsWith('/') ? mdPath.slice(1) : mdPath\n const filePath = join(cacheDir, 'docs', localPath)\n if (existsSync(filePath)) {\n const content = readFileSync(filePath, 'utf-8')\n sections.push(`# ${mdPath}\\n\\n${content}`)\n }\n }\n docsContent = sections.length > 0 ? sections.join('\\n\\n---\\n\\n') : llmsRaw\n }\n }\n\n // Priority 3: README\n if (!docsContent) {\n const readmePath = join(cacheDir, 'docs', 'README.md')\n if (existsSync(readmePath)) {\n docsContent = readFileSync(readmePath, 'utf-8')\n }\n }\n\n const githubPath = join(cacheDir, 'github')\n const hasGithub = existsSync(githubPath)\n const hasReleases = existsSync(join(cacheDir, 'releases'))\n const hasChangelog = ['CHANGELOG.md', 'changelog.md'].find(f => existsSync(join(cwd, 'node_modules', packageName, f))) || false\n\n const docFiles = listReferenceFiles(skillDir)\n update(packageName, 'generating', config.model, versionKey)\n const { optimized, wasOptimized, error } = await optimizeDocs({\n packageName,\n skillDir,\n model: config.model,\n version,\n hasGithub,\n hasReleases,\n hasChangelog,\n docFiles,\n noCache: config.force,\n sections,\n customPrompt,\n onProgress: (progress) => {\n const isReasoning = progress.type === 'reasoning'\n const status = isReasoning ? 'exploring' : 'generating'\n const label = progress.chunk.startsWith('[') ? progress.chunk : config.model\n update(packageName, status, label, versionKey)\n },\n })\n\n if (error) {\n update(packageName, 'error', error, versionKey)\n throw new Error(error)\n }\n\n if (wasOptimized) {\n const relatedSkills = await findRelatedSkills(packageName, baseDir)\n const shippedDocs = hasShippedDocs(packageName, cwd, version)\n const pkgFiles = getPkgKeyFiles(packageName, cwd, version)\n const body = cleanSkillMd(optimized)\n const skillMd = generateSkillMd({\n name: packageName,\n version,\n releasedAt: resolved.releasedAt,\n dependencies: resolved.dependencies,\n distTags: resolved.distTags,\n body,\n relatedSkills,\n hasGithub,\n hasReleases,\n hasChangelog,\n docsType,\n hasShippedDocs: shippedDocs,\n pkgFiles,\n })\n writeFileSync(join(skillDir, 'SKILL.md'), skillMd)\n }\n\n update(packageName, 'done', 'Enhanced', versionKey)\n}\n\nasync function findRelatedSkills(packageName: string, skillsDir: string): Promise<string[]> {\n const related: string[] = []\n\n const npmInfo = await fetchNpmPackage(packageName)\n if (!npmInfo?.dependencies)\n return related\n\n const deps = Object.keys(npmInfo.dependencies)\n\n if (!existsSync(skillsDir))\n return related\n\n const installedSkills = readdirSync(skillsDir)\n\n for (const skill of installedSkills) {\n if (deps.some(d => sanitizeName(d) === skill)) {\n related.push(skill)\n }\n }\n\n return related.slice(0, 5)\n}\n\nfunction cleanSkillMd(content: string): string {\n let cleaned = content\n .replace(/^```markdown\\n?/m, '')\n .replace(/\\n?```$/m, '')\n .trim()\n\n // Strip any accidental frontmatter or leading horizontal rules\n // Match 3+ dashes (handles ---, ------, etc)\n const fmMatch = cleaned.match(/^-{3,}\\n/)\n if (fmMatch) {\n const afterOpen = fmMatch[0].length\n const closeMatch = cleaned.slice(afterOpen).match(/\\n-{3,}/)\n if (closeMatch) {\n // Has closing dashes (frontmatter), strip entire block\n cleaned = cleaned.slice(afterOpen + closeMatch.index! + closeMatch[0].length).trim()\n }\n else {\n // Just leading dashes, strip them\n cleaned = cleaned.slice(afterOpen).trim()\n }\n }\n\n return cleaned\n}\n"],"mappings":"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"}
|