skilld 0.15.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/_chunks/agent.mjs +6 -9
- package/dist/_chunks/agent.mjs.map +1 -1
- package/dist/_chunks/cache2.mjs +68 -3
- package/dist/_chunks/cache2.mjs.map +1 -0
- package/dist/_chunks/formatting.mjs +549 -1
- package/dist/_chunks/formatting.mjs.map +1 -1
- package/dist/_chunks/install.mjs +536 -12
- package/dist/_chunks/install.mjs.map +1 -0
- package/dist/_chunks/list.mjs +60 -3
- package/dist/_chunks/list.mjs.map +1 -0
- package/dist/_chunks/pool.mjs +167 -113
- package/dist/_chunks/pool.mjs.map +1 -1
- package/dist/_chunks/pool2.mjs +115 -0
- package/dist/_chunks/pool2.mjs.map +1 -0
- package/dist/_chunks/prompts.mjs +2 -2
- package/dist/_chunks/prompts.mjs.map +1 -1
- package/dist/_chunks/search-interactive.mjs +236 -5
- package/dist/_chunks/search-interactive.mjs.map +1 -0
- package/dist/_chunks/search.mjs +12 -171
- package/dist/_chunks/sync.mjs +9 -98
- package/dist/_chunks/sync.mjs.map +1 -1
- package/dist/_chunks/sync2.mjs +1 -2
- package/dist/_chunks/uninstall.mjs +200 -8
- package/dist/_chunks/uninstall.mjs.map +1 -0
- package/dist/cli.mjs +99 -836
- package/dist/cli.mjs.map +1 -1
- package/dist/retriv/index.mjs +1 -1
- package/dist/retriv/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/_chunks/config2.mjs +0 -12
- package/dist/_chunks/remove.mjs +0 -12
- package/dist/_chunks/search-interactive2.mjs +0 -236
- package/dist/_chunks/search-interactive2.mjs.map +0 -1
- package/dist/_chunks/search.mjs.map +0 -1
- package/dist/_chunks/search2.mjs +0 -13
- package/dist/_chunks/skills.mjs +0 -552
- package/dist/_chunks/skills.mjs.map +0 -1
- package/dist/_chunks/status.mjs +0 -13
package/dist/cli.mjs
CHANGED
|
@@ -1,87 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { i as getPackageDbPath, o as getCacheDir, t as CACHE_DIR } from "./_chunks/config.mjs";
|
|
3
|
+
import "./_chunks/sanitize.mjs";
|
|
4
|
+
import "./_chunks/cache.mjs";
|
|
5
5
|
import "./_chunks/yaml.mjs";
|
|
6
6
|
import "./_chunks/markdown.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { i as
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { n as shutdownWorker } from "./_chunks/pool.mjs";
|
|
16
|
-
import { a as runWizard, c as SKILLD_MARKER_START, l as classifyCachedDoc, m as selectLlmConfig, p as indexResources, s as SKILLD_MARKER_END } from "./_chunks/sync.mjs";
|
|
17
|
-
import "./_chunks/search.mjs";
|
|
18
|
-
import "./_chunks/search-interactive2.mjs";
|
|
19
|
-
import { homedir } from "node:os";
|
|
20
|
-
import { dirname, join, resolve } from "pathe";
|
|
21
|
-
import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { a as semverGt, n as getSharedSkillsDir, r as mapInsert } from "./_chunks/shared.mjs";
|
|
8
|
+
import { r as fetchNpmRegistryMeta, t as fetchLatestVersion } from "./_chunks/sources.mjs";
|
|
9
|
+
import { _ as targets, g as getAgentVersion, o as unlinkSkillFromAgents } from "./_chunks/prompts.mjs";
|
|
10
|
+
import { r as getAvailableModels, t as detectImportedPackages } from "./_chunks/agent.mjs";
|
|
11
|
+
import { A as version, C as introLine, D as requireInteractive, E as relativeTime, F as readConfig, N as hasCompletedWizard, O as resolveAgent, P as hasConfig, R as updateConfig, S as getRepoHint, T as promptForAgent, _ as removeLockEntry, b as formatStatus, c as timeAgo, d as getSkillsDir, f as isOutdated, h as parsePackages, i as formatSource, j as defaultFeatures, k as sharedArgs, l as timedSpinner, p as iterateSkills, u as getProjectState, w as isInteractive, x as getInstalledGenerators } from "./_chunks/formatting.mjs";
|
|
12
|
+
import { join, resolve } from "pathe";
|
|
13
|
+
import { existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
14
|
+
import { execSync } from "node:child_process";
|
|
22
15
|
import pLimit from "p-limit";
|
|
23
16
|
import * as p from "@clack/prompts";
|
|
24
17
|
import { defineCommand, runMain } from "citty";
|
|
25
|
-
/**
|
|
26
|
-
* Cache management commands
|
|
27
|
-
*/
|
|
28
|
-
const LLM_CACHE_DIR = join(CACHE_DIR, "llm-cache");
|
|
29
|
-
const LLM_CACHE_MAX_AGE = 10080 * 60 * 1e3;
|
|
30
|
-
async function cacheCleanCommand() {
|
|
31
|
-
let expiredLlm = 0;
|
|
32
|
-
let freedBytes = 0;
|
|
33
|
-
if (existsSync(LLM_CACHE_DIR)) {
|
|
34
|
-
const now = Date.now();
|
|
35
|
-
for (const entry of readdirSync(LLM_CACHE_DIR)) {
|
|
36
|
-
const path = join(LLM_CACHE_DIR, entry);
|
|
37
|
-
try {
|
|
38
|
-
const { timestamp } = JSON.parse(readFileSync(path, "utf-8"));
|
|
39
|
-
if (now - timestamp > LLM_CACHE_MAX_AGE) {
|
|
40
|
-
const size = statSync(path).size;
|
|
41
|
-
rmSync(path);
|
|
42
|
-
expiredLlm++;
|
|
43
|
-
freedBytes += size;
|
|
44
|
-
}
|
|
45
|
-
} catch {
|
|
46
|
-
const size = statSync(path).size;
|
|
47
|
-
rmSync(path);
|
|
48
|
-
expiredLlm++;
|
|
49
|
-
freedBytes += size;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
const embeddingDbPath = join(CACHE_DIR, "embeddings.db");
|
|
54
|
-
let embeddingCleared = false;
|
|
55
|
-
if (existsSync(embeddingDbPath)) {
|
|
56
|
-
const size = statSync(embeddingDbPath).size;
|
|
57
|
-
clearEmbeddingCache();
|
|
58
|
-
freedBytes += size;
|
|
59
|
-
embeddingCleared = true;
|
|
60
|
-
}
|
|
61
|
-
const freedKB = Math.round(freedBytes / 1024);
|
|
62
|
-
if (expiredLlm > 0 || embeddingCleared) {
|
|
63
|
-
const parts = [];
|
|
64
|
-
if (expiredLlm > 0) parts.push(`${expiredLlm} expired LLM cache entries`);
|
|
65
|
-
if (embeddingCleared) parts.push("embedding cache");
|
|
66
|
-
p.log.success(`Removed ${parts.join(" + ")} (${freedKB}KB freed)`);
|
|
67
|
-
} else p.log.info("Cache is clean — no expired entries");
|
|
68
|
-
}
|
|
69
|
-
const cacheCommandDef = defineCommand({
|
|
70
|
-
meta: {
|
|
71
|
-
name: "cache",
|
|
72
|
-
description: "Cache management",
|
|
73
|
-
hidden: true
|
|
74
|
-
},
|
|
75
|
-
args: { clean: {
|
|
76
|
-
type: "boolean",
|
|
77
|
-
description: "Remove expired LLM cache entries",
|
|
78
|
-
default: true
|
|
79
|
-
} },
|
|
80
|
-
async run() {
|
|
81
|
-
p.intro(`\x1B[1m\x1B[35mskilld\x1B[0m cache clean`);
|
|
82
|
-
await cacheCleanCommand();
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
18
|
async function configCommand() {
|
|
86
19
|
const config = readConfig();
|
|
87
20
|
const features = config.features ?? defaultFeatures;
|
|
@@ -215,580 +148,6 @@ const configCommandDef = defineCommand({
|
|
|
215
148
|
return configCommand();
|
|
216
149
|
}
|
|
217
150
|
});
|
|
218
|
-
async function installCommand(opts) {
|
|
219
|
-
const cwd = process.cwd();
|
|
220
|
-
const agent = targets[opts.agent];
|
|
221
|
-
const shared = !opts.global && getSharedSkillsDir(cwd);
|
|
222
|
-
const skillsDir = opts.global ? join(homedir(), ".skilld", "skills") : shared || join(cwd, agent.skillsDir);
|
|
223
|
-
const allSkillsDirs = shared ? [shared] : Object.values(targets).map((t) => opts.global ? t.globalSkillsDir : join(cwd, t.skillsDir));
|
|
224
|
-
const allLocks = allSkillsDirs.map((dir) => readLock(dir)).filter((l) => !!l && Object.keys(l.skills).length > 0);
|
|
225
|
-
if (allLocks.length === 0) {
|
|
226
|
-
p.log.warn("No skilld-lock.yaml found. Run `skilld` to sync skills first.");
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const lock = mergeLocks(allLocks);
|
|
230
|
-
const skills = Object.entries(lock.skills);
|
|
231
|
-
const toRestore = [];
|
|
232
|
-
const features = readConfig().features ?? defaultFeatures;
|
|
233
|
-
for (const [name, info] of skills) {
|
|
234
|
-
if (!info.version) continue;
|
|
235
|
-
if (info.source === "shipped") {
|
|
236
|
-
if (!existsSync(join(skillsDir, name))) toRestore.push({
|
|
237
|
-
name,
|
|
238
|
-
info
|
|
239
|
-
});
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
const skillDir = join(skillsDir, name);
|
|
243
|
-
const referencesPath = join(skillDir, ".skilld");
|
|
244
|
-
const skillMdPath = join(skillDir, "SKILL.md");
|
|
245
|
-
if (!existsSync(skillDir) || !existsSync(skillMdPath) || !existsSync(referencesPath) || hasStaleReferences(referencesPath, info.packageName || name, info.version, features)) toRestore.push({
|
|
246
|
-
name,
|
|
247
|
-
info
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
if (toRestore.length === 0) {
|
|
251
|
-
p.log.success("All up to date");
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
p.log.info(`Restoring ${toRestore.length} references`);
|
|
255
|
-
ensureCacheDir();
|
|
256
|
-
const allSkillNames = skills.map(([, info]) => info.packageName || "").filter(Boolean);
|
|
257
|
-
const regenerated = [];
|
|
258
|
-
for (const { name, info } of toRestore) {
|
|
259
|
-
const version = info.version;
|
|
260
|
-
const pkgName = info.packageName || unsanitizeName(name, info.source);
|
|
261
|
-
if (info.source === "shipped") {
|
|
262
|
-
const match = getShippedSkills(pkgName, cwd, version).find((s) => s.skillName === name);
|
|
263
|
-
if (match) {
|
|
264
|
-
linkShippedSkill(skillsDir, name, match.skillDir);
|
|
265
|
-
p.log.success(`Linked ${name}`);
|
|
266
|
-
} else p.log.warn(`${name}: package ${pkgName} no longer ships this skill`);
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
if (info.source === "github" || info.source === "gitlab" || info.source === "local") {
|
|
270
|
-
const match = (await fetchGitSkills({
|
|
271
|
-
type: info.source,
|
|
272
|
-
...info.repo?.includes("/") ? {
|
|
273
|
-
owner: info.repo.split("/")[0],
|
|
274
|
-
repo: info.repo.split("/")[1]
|
|
275
|
-
} : {},
|
|
276
|
-
skillPath: info.path,
|
|
277
|
-
ref: info.ref,
|
|
278
|
-
...info.source === "local" ? { localPath: info.repo } : {}
|
|
279
|
-
})).skills.find((s) => s.name === name);
|
|
280
|
-
if (match) {
|
|
281
|
-
const skillDir = join(skillsDir, name);
|
|
282
|
-
mkdirSync(skillDir, { recursive: true });
|
|
283
|
-
writeFileSync(join(skillDir, "SKILL.md"), sanitizeMarkdown(match.content));
|
|
284
|
-
for (const f of match.files) {
|
|
285
|
-
const filePath = join(skillDir, f.path);
|
|
286
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
287
|
-
writeFileSync(filePath, f.content);
|
|
288
|
-
}
|
|
289
|
-
p.log.success(`Restored ${name} from ${info.repo}`);
|
|
290
|
-
} else p.log.warn(`${name}: skill not found in ${info.repo}`);
|
|
291
|
-
continue;
|
|
292
|
-
}
|
|
293
|
-
const skillDir = join(skillsDir, name);
|
|
294
|
-
const referencesPath = join(skillDir, ".skilld");
|
|
295
|
-
const globalCachePath = getCacheDir(pkgName, version);
|
|
296
|
-
const spin = timedSpinner();
|
|
297
|
-
if (isCached(pkgName, version)) {
|
|
298
|
-
spin.start(`Linking ${name}`);
|
|
299
|
-
mkdirSync(skillDir, { recursive: true });
|
|
300
|
-
mkdirSync(referencesPath, { recursive: true });
|
|
301
|
-
linkPkgSymlink(referencesPath, pkgName, cwd, version);
|
|
302
|
-
for (const pkg of parsePackages(info.packages)) linkPkgNamed(skillDir, pkg.name, cwd, pkg.version);
|
|
303
|
-
if (!pkgHasShippedDocs(pkgName, cwd, version) && !isReadmeOnly(globalCachePath)) {
|
|
304
|
-
const docsLink = join(referencesPath, "docs");
|
|
305
|
-
const cachedDocs = join(globalCachePath, "docs");
|
|
306
|
-
if (existsSync(docsLink)) unlinkSync(docsLink);
|
|
307
|
-
if (existsSync(cachedDocs)) symlinkSync(cachedDocs, docsLink, "junction");
|
|
308
|
-
}
|
|
309
|
-
const repoGh = info.repo ? parseGitHubUrl(`https://github.com/${info.repo}`) : null;
|
|
310
|
-
const repoCachePath = repoGh ? getRepoCacheDir(repoGh.owner, repoGh.repo) : null;
|
|
311
|
-
if (features.issues) {
|
|
312
|
-
const issuesLink = join(referencesPath, "issues");
|
|
313
|
-
const repoIssues = repoCachePath ? join(repoCachePath, "issues") : null;
|
|
314
|
-
const cachedIssues = repoIssues && existsSync(repoIssues) ? repoIssues : join(globalCachePath, "issues");
|
|
315
|
-
if (existsSync(issuesLink)) unlinkSync(issuesLink);
|
|
316
|
-
if (existsSync(cachedIssues)) symlinkSync(cachedIssues, issuesLink, "junction");
|
|
317
|
-
}
|
|
318
|
-
if (features.discussions) {
|
|
319
|
-
const discussionsLink = join(referencesPath, "discussions");
|
|
320
|
-
const repoDiscussions = repoCachePath ? join(repoCachePath, "discussions") : null;
|
|
321
|
-
const cachedDiscussions = repoDiscussions && existsSync(repoDiscussions) ? repoDiscussions : join(globalCachePath, "discussions");
|
|
322
|
-
if (existsSync(discussionsLink)) unlinkSync(discussionsLink);
|
|
323
|
-
if (existsSync(cachedDiscussions)) symlinkSync(cachedDiscussions, discussionsLink, "junction");
|
|
324
|
-
}
|
|
325
|
-
if (features.releases) {
|
|
326
|
-
const releasesLink = join(referencesPath, "releases");
|
|
327
|
-
const repoReleases = repoCachePath ? join(repoCachePath, "releases") : null;
|
|
328
|
-
const cachedReleases = repoReleases && existsSync(repoReleases) ? repoReleases : join(globalCachePath, "releases");
|
|
329
|
-
if (existsSync(releasesLink)) unlinkSync(releasesLink);
|
|
330
|
-
if (existsSync(cachedReleases)) symlinkSync(cachedReleases, releasesLink, "junction");
|
|
331
|
-
}
|
|
332
|
-
const sectionsLink = join(referencesPath, "sections");
|
|
333
|
-
const cachedSections = join(globalCachePath, "sections");
|
|
334
|
-
if (existsSync(sectionsLink)) unlinkSync(sectionsLink);
|
|
335
|
-
if (existsSync(cachedSections)) symlinkSync(cachedSections, sectionsLink, "junction");
|
|
336
|
-
if (features.search && !existsSync(getPackageDbPath(pkgName, version))) {
|
|
337
|
-
spin.message(`Indexing ${name}`);
|
|
338
|
-
await indexResources({
|
|
339
|
-
packageName: pkgName,
|
|
340
|
-
version,
|
|
341
|
-
cwd,
|
|
342
|
-
docsToIndex: readCachedDocs(pkgName, version).map((d) => ({
|
|
343
|
-
id: d.path,
|
|
344
|
-
content: d.content,
|
|
345
|
-
metadata: {
|
|
346
|
-
package: pkgName,
|
|
347
|
-
source: d.path,
|
|
348
|
-
type: classifyCachedDoc(d.path).type
|
|
349
|
-
}
|
|
350
|
-
})),
|
|
351
|
-
features,
|
|
352
|
-
onProgress: (msg) => spin.message(msg)
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
if (!copyFromExistingAgent(skillDir, name, allSkillsDirs)) {
|
|
356
|
-
if (regenerateBaseSkillMd(skillDir, pkgName, version, cwd, allSkillNames, info.source, info.packages)) regenerated.push({
|
|
357
|
-
name,
|
|
358
|
-
pkgName,
|
|
359
|
-
version,
|
|
360
|
-
skillDir,
|
|
361
|
-
packages: info.packages
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
spin.stop(`Linked ${name}`);
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
spin.start(`Downloading ${name}@${version}`);
|
|
368
|
-
const resolved = await resolvePackageDocs(pkgName, { version });
|
|
369
|
-
if (!resolved) {
|
|
370
|
-
spin.stop(`Could not resolve: ${name}`);
|
|
371
|
-
continue;
|
|
372
|
-
}
|
|
373
|
-
const cachedDocs = [];
|
|
374
|
-
const docsToIndex = [];
|
|
375
|
-
const isFrameworkDoc = (path) => filterFrameworkDocs([path], pkgName).length > 0;
|
|
376
|
-
if (resolved.gitDocsUrl && resolved.repoUrl) {
|
|
377
|
-
const gh = parseGitHubUrl(resolved.repoUrl);
|
|
378
|
-
if (gh) {
|
|
379
|
-
const gitDocs = await fetchGitDocs(gh.owner, gh.repo, version, pkgName);
|
|
380
|
-
if (gitDocs?.files.length) {
|
|
381
|
-
const BATCH_SIZE = 20;
|
|
382
|
-
for (let i = 0; i < gitDocs.files.length; i += BATCH_SIZE) {
|
|
383
|
-
const batch = gitDocs.files.slice(i, i + BATCH_SIZE);
|
|
384
|
-
const results = await Promise.all(batch.map(async (file) => {
|
|
385
|
-
const content = await $fetch(`${gitDocs.baseUrl}/${file}`, { responseType: "text" }).catch(() => null);
|
|
386
|
-
if (!content) return null;
|
|
387
|
-
return {
|
|
388
|
-
file,
|
|
389
|
-
content
|
|
390
|
-
};
|
|
391
|
-
}));
|
|
392
|
-
for (const r of results) if (r) {
|
|
393
|
-
const cachePath = gitDocs.docsPrefix ? r.file.replace(gitDocs.docsPrefix, "") : r.file;
|
|
394
|
-
cachedDocs.push({
|
|
395
|
-
path: cachePath,
|
|
396
|
-
content: r.content
|
|
397
|
-
});
|
|
398
|
-
docsToIndex.push({
|
|
399
|
-
id: cachePath,
|
|
400
|
-
content: r.content,
|
|
401
|
-
metadata: {
|
|
402
|
-
package: pkgName,
|
|
403
|
-
source: cachePath,
|
|
404
|
-
type: "doc"
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
if (isShallowGitDocs(cachedDocs.length) && resolved.llmsUrl) {
|
|
410
|
-
cachedDocs.length = 0;
|
|
411
|
-
docsToIndex.length = 0;
|
|
412
|
-
} else if (cachedDocs.length > 0 && resolved.llmsUrl) {
|
|
413
|
-
const llmsContent = await fetchLlmsTxt(resolved.llmsUrl);
|
|
414
|
-
if (llmsContent) {
|
|
415
|
-
const baseUrl = resolved.docsUrl || new URL(resolved.llmsUrl).origin;
|
|
416
|
-
cachedDocs.push({
|
|
417
|
-
path: "llms.txt",
|
|
418
|
-
content: normalizeLlmsLinks(llmsContent.raw)
|
|
419
|
-
});
|
|
420
|
-
if (llmsContent.links.length > 0) {
|
|
421
|
-
const docs = await downloadLlmsDocs(llmsContent, baseUrl);
|
|
422
|
-
for (const doc of docs) {
|
|
423
|
-
if (!isFrameworkDoc(doc.url)) continue;
|
|
424
|
-
const localPath = doc.url.startsWith("/") ? doc.url.slice(1) : doc.url;
|
|
425
|
-
cachedDocs.push({
|
|
426
|
-
path: join("llms-docs", ...localPath.split("/")),
|
|
427
|
-
content: doc.content
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
if (resolved.llmsUrl && cachedDocs.length === 0) {
|
|
437
|
-
const llmsContent = await fetchLlmsTxt(resolved.llmsUrl);
|
|
438
|
-
if (llmsContent) {
|
|
439
|
-
cachedDocs.push({
|
|
440
|
-
path: "llms.txt",
|
|
441
|
-
content: normalizeLlmsLinks(llmsContent.raw)
|
|
442
|
-
});
|
|
443
|
-
if (llmsContent.links.length > 0) {
|
|
444
|
-
const docs = await downloadLlmsDocs(llmsContent, resolved.docsUrl || new URL(resolved.llmsUrl).origin);
|
|
445
|
-
for (const doc of docs) {
|
|
446
|
-
if (!isFrameworkDoc(doc.url)) continue;
|
|
447
|
-
const cachePath = join("docs", ...(doc.url.startsWith("/") ? doc.url.slice(1) : doc.url).split("/"));
|
|
448
|
-
cachedDocs.push({
|
|
449
|
-
path: cachePath,
|
|
450
|
-
content: doc.content
|
|
451
|
-
});
|
|
452
|
-
docsToIndex.push({
|
|
453
|
-
id: doc.url,
|
|
454
|
-
content: doc.content,
|
|
455
|
-
metadata: {
|
|
456
|
-
package: pkgName,
|
|
457
|
-
source: cachePath,
|
|
458
|
-
type: "doc"
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
if (resolved.readmeUrl && cachedDocs.length === 0) {
|
|
466
|
-
const content = await fetchReadmeContent(resolved.readmeUrl);
|
|
467
|
-
if (content) {
|
|
468
|
-
cachedDocs.push({
|
|
469
|
-
path: "docs/README.md",
|
|
470
|
-
content
|
|
471
|
-
});
|
|
472
|
-
docsToIndex.push({
|
|
473
|
-
id: "README.md",
|
|
474
|
-
content,
|
|
475
|
-
metadata: {
|
|
476
|
-
package: pkgName,
|
|
477
|
-
source: "docs/README.md",
|
|
478
|
-
type: "doc"
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
if (cachedDocs.length > 0) {
|
|
484
|
-
writeToCache(pkgName, version, cachedDocs);
|
|
485
|
-
mkdirSync(referencesPath, { recursive: true });
|
|
486
|
-
linkPkgSymlink(referencesPath, pkgName, cwd, version);
|
|
487
|
-
for (const pkg of parsePackages(info.packages)) linkPkgNamed(skillDir, pkg.name, cwd, pkg.version);
|
|
488
|
-
if (!isReadmeOnly(globalCachePath)) {
|
|
489
|
-
const docsLink = join(referencesPath, "docs");
|
|
490
|
-
const cachedDocsDir = join(globalCachePath, "docs");
|
|
491
|
-
if (existsSync(docsLink)) unlinkSync(docsLink);
|
|
492
|
-
if (existsSync(cachedDocsDir)) symlinkSync(cachedDocsDir, docsLink, "junction");
|
|
493
|
-
}
|
|
494
|
-
if (features.search) {
|
|
495
|
-
if (docsToIndex.length > 0) await createIndex(docsToIndex, { dbPath: getPackageDbPath(pkgName, version) });
|
|
496
|
-
const pkgDir = resolvePkgDir(pkgName, cwd, version);
|
|
497
|
-
const entryFiles = pkgDir ? await resolveEntryFiles(pkgDir) : [];
|
|
498
|
-
if (entryFiles.length > 0) await createIndex(entryFiles.map((e) => ({
|
|
499
|
-
id: e.path,
|
|
500
|
-
content: e.content,
|
|
501
|
-
metadata: {
|
|
502
|
-
package: pkgName,
|
|
503
|
-
source: `pkg/${e.path}`,
|
|
504
|
-
type: e.type
|
|
505
|
-
}
|
|
506
|
-
})), { dbPath: getPackageDbPath(pkgName, version) });
|
|
507
|
-
}
|
|
508
|
-
if (!copyFromExistingAgent(skillDir, name, allSkillsDirs)) {
|
|
509
|
-
if (regenerateBaseSkillMd(skillDir, pkgName, version, cwd, allSkillNames, info.source, info.packages)) regenerated.push({
|
|
510
|
-
name,
|
|
511
|
-
pkgName,
|
|
512
|
-
version,
|
|
513
|
-
skillDir,
|
|
514
|
-
packages: info.packages
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
|
-
spin.stop(`Downloaded and linked ${name}`);
|
|
518
|
-
} else spin.stop(`No docs found for ${name}`);
|
|
519
|
-
}
|
|
520
|
-
if (regenerated.length > 0 && !readConfig().skipLlm) {
|
|
521
|
-
const llmConfig = await selectLlmConfig(void 0, `Enhance SKILL.md for ${regenerated.map((r) => r.name).join(", ")}`);
|
|
522
|
-
if (llmConfig) {
|
|
523
|
-
p.log.step(getModelLabel(llmConfig.model));
|
|
524
|
-
for (const { pkgName, version, skillDir, packages: pkgPackages } of regenerated) await enhanceRegenerated(pkgName, version, skillDir, llmConfig.model, llmConfig.sections, llmConfig.customPrompt, pkgPackages);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
for (const [name, info] of Object.entries(lock.skills)) writeLock(skillsDir, name, info);
|
|
528
|
-
if (shared) for (const [name] of skills) linkSkillToAgents(name, shared, cwd);
|
|
529
|
-
else syncLockfilesToDirs(lock, allSkillsDirs.filter((d) => d !== skillsDir));
|
|
530
|
-
await shutdownWorker();
|
|
531
|
-
p.outro("Install complete");
|
|
532
|
-
}
|
|
533
|
-
/** Copy SKILL.md from another agent's skill dir if one exists */
|
|
534
|
-
function copyFromExistingAgent(skillDir, name, allSkillsDirs) {
|
|
535
|
-
const targetMd = join(skillDir, "SKILL.md");
|
|
536
|
-
if (existsSync(targetMd)) return false;
|
|
537
|
-
for (const dir of allSkillsDirs) {
|
|
538
|
-
if (dir === skillDir) continue;
|
|
539
|
-
const candidateMd = join(dir, name, "SKILL.md");
|
|
540
|
-
if (existsSync(candidateMd) && !lstatSync(candidateMd).isSymbolicLink()) {
|
|
541
|
-
mkdirSync(skillDir, { recursive: true });
|
|
542
|
-
copyFileSync(candidateMd, targetMd);
|
|
543
|
-
return true;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
return false;
|
|
547
|
-
}
|
|
548
|
-
/** Try to recover original package name from sanitized name + source */
|
|
549
|
-
function unsanitizeName(sanitized, source) {
|
|
550
|
-
if (source?.includes("ungh://")) {
|
|
551
|
-
const match = source.match(/ungh:\/\/([^/]+)\/(.+)/);
|
|
552
|
-
if (match) return `@${match[1]}/${match[2]}`;
|
|
553
|
-
}
|
|
554
|
-
if (sanitized.startsWith("antfu-")) return `@antfu/${sanitized.slice(6)}`;
|
|
555
|
-
if (sanitized.startsWith("clack-")) return `@clack/${sanitized.slice(6)}`;
|
|
556
|
-
if (sanitized.startsWith("nuxt-")) return `@nuxt/${sanitized.slice(5)}`;
|
|
557
|
-
if (sanitized.startsWith("vue-")) return `@vue/${sanitized.slice(4)}`;
|
|
558
|
-
if (sanitized.startsWith("vueuse-")) return `@vueuse/${sanitized.slice(7)}`;
|
|
559
|
-
return sanitized;
|
|
560
|
-
}
|
|
561
|
-
/** Create pkg symlink inside references dir (links to entire package or cached dist) */
|
|
562
|
-
function linkPkgSymlink(referencesDir, name, cwd, version) {
|
|
563
|
-
const pkgPath = resolvePkgDir(name, cwd, version);
|
|
564
|
-
if (!pkgPath) return;
|
|
565
|
-
const pkgLink = join(referencesDir, "pkg");
|
|
566
|
-
if (existsSync(pkgLink)) unlinkSync(pkgLink);
|
|
567
|
-
symlinkSync(pkgPath, pkgLink, "junction");
|
|
568
|
-
}
|
|
569
|
-
/** Check if cache only has docs/README.md (pkg/ already has this) */
|
|
570
|
-
function isReadmeOnly(cacheDir) {
|
|
571
|
-
const docsDir = join(cacheDir, "docs");
|
|
572
|
-
if (!existsSync(docsDir)) return false;
|
|
573
|
-
const files = readdirSync(docsDir);
|
|
574
|
-
return files.length === 1 && files[0] === "README.md";
|
|
575
|
-
}
|
|
576
|
-
/** Check if package ships its own docs folder */
|
|
577
|
-
function pkgHasShippedDocs(name, cwd, version) {
|
|
578
|
-
const pkgPath = resolvePkgDir(name, cwd, version);
|
|
579
|
-
if (!pkgPath) return false;
|
|
580
|
-
for (const candidate of [
|
|
581
|
-
"docs",
|
|
582
|
-
"documentation",
|
|
583
|
-
"doc"
|
|
584
|
-
]) if (existsSync(join(pkgPath, candidate))) return true;
|
|
585
|
-
return false;
|
|
586
|
-
}
|
|
587
|
-
/** Run LLM enhancement on a regenerated SKILL.md */
|
|
588
|
-
async function enhanceRegenerated(pkgName, version, skillDir, model, sections, customPrompt, packages) {
|
|
589
|
-
const llmLog = p.taskLog({ title: `Agent exploring ${pkgName}` });
|
|
590
|
-
const docFiles = listReferenceFiles(skillDir);
|
|
591
|
-
const globalCachePath = getCacheDir(pkgName, version);
|
|
592
|
-
const hasIssues = existsSync(join(globalCachePath, "issues"));
|
|
593
|
-
const hasDiscussions = existsSync(join(globalCachePath, "discussions"));
|
|
594
|
-
const hasGithub = hasIssues || hasDiscussions;
|
|
595
|
-
const hasReleases = existsSync(join(globalCachePath, "releases"));
|
|
596
|
-
const features = readConfig().features ?? defaultFeatures;
|
|
597
|
-
const { optimized, wasOptimized } = await optimizeDocs({
|
|
598
|
-
packageName: pkgName,
|
|
599
|
-
skillDir,
|
|
600
|
-
model,
|
|
601
|
-
version,
|
|
602
|
-
hasGithub,
|
|
603
|
-
hasReleases,
|
|
604
|
-
docFiles,
|
|
605
|
-
sections,
|
|
606
|
-
customPrompt,
|
|
607
|
-
features,
|
|
608
|
-
pkgFiles: getPkgKeyFiles(pkgName, process.cwd(), version),
|
|
609
|
-
onProgress: createToolProgress(llmLog)
|
|
610
|
-
});
|
|
611
|
-
if (wasOptimized) {
|
|
612
|
-
llmLog.success("Generated best practices");
|
|
613
|
-
const cwd = process.cwd();
|
|
614
|
-
const pkgPath = resolvePkgDir(pkgName, cwd, version);
|
|
615
|
-
let description;
|
|
616
|
-
let dependencies;
|
|
617
|
-
if (pkgPath) {
|
|
618
|
-
const pkgJsonPath = join(pkgPath, "package.json");
|
|
619
|
-
if (existsSync(pkgJsonPath)) {
|
|
620
|
-
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
621
|
-
description = pkg.description;
|
|
622
|
-
dependencies = pkg.dependencies;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
let docsType = "docs";
|
|
626
|
-
if (existsSync(join(globalCachePath, "docs", "llms.txt"))) docsType = "llms.txt";
|
|
627
|
-
else if (isReadmeOnly(globalCachePath)) docsType = "readme";
|
|
628
|
-
const dirName = skillDir.split("/").pop();
|
|
629
|
-
const allPackages = parsePackages(packages).map((p) => ({ name: p.name }));
|
|
630
|
-
const skillMd = generateSkillMd({
|
|
631
|
-
name: pkgName,
|
|
632
|
-
version,
|
|
633
|
-
description,
|
|
634
|
-
dependencies,
|
|
635
|
-
body: optimized,
|
|
636
|
-
relatedSkills: [],
|
|
637
|
-
hasIssues,
|
|
638
|
-
hasDiscussions,
|
|
639
|
-
hasReleases,
|
|
640
|
-
docsType,
|
|
641
|
-
hasShippedDocs: hasShippedDocs(pkgName, cwd, version),
|
|
642
|
-
pkgFiles: getPkgKeyFiles(pkgName, cwd, version),
|
|
643
|
-
dirName,
|
|
644
|
-
packages: allPackages.length > 1 ? allPackages : void 0,
|
|
645
|
-
features
|
|
646
|
-
});
|
|
647
|
-
writeFileSync(join(skillDir, "SKILL.md"), skillMd);
|
|
648
|
-
} else llmLog.error("LLM optimization skipped");
|
|
649
|
-
}
|
|
650
|
-
const installCommandDef = defineCommand({
|
|
651
|
-
meta: {
|
|
652
|
-
name: "install",
|
|
653
|
-
description: "Restore references from lockfile"
|
|
654
|
-
},
|
|
655
|
-
args: {
|
|
656
|
-
global: sharedArgs.global,
|
|
657
|
-
agent: sharedArgs.agent
|
|
658
|
-
},
|
|
659
|
-
async run({ args }) {
|
|
660
|
-
let agent = resolveAgent(args.agent);
|
|
661
|
-
if (!agent) {
|
|
662
|
-
agent = await promptForAgent();
|
|
663
|
-
if (!agent) return;
|
|
664
|
-
}
|
|
665
|
-
p.intro(`\x1B[1m\x1B[35mskilld\x1B[0m install`);
|
|
666
|
-
return installCommand({
|
|
667
|
-
global: args.global,
|
|
668
|
-
agent
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
});
|
|
672
|
-
/** Regenerate base SKILL.md from local metadata if missing */
|
|
673
|
-
function regenerateBaseSkillMd(skillDir, pkgName, version, cwd, allSkillNames, source, packages) {
|
|
674
|
-
const skillMdPath = join(skillDir, "SKILL.md");
|
|
675
|
-
if (existsSync(skillMdPath)) return false;
|
|
676
|
-
const pkgPath = resolvePkgDir(pkgName, cwd, version);
|
|
677
|
-
let description;
|
|
678
|
-
let dependencies;
|
|
679
|
-
if (pkgPath) {
|
|
680
|
-
const pkgJsonPath = join(pkgPath, "package.json");
|
|
681
|
-
if (existsSync(pkgJsonPath)) {
|
|
682
|
-
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
683
|
-
description = pkg.description;
|
|
684
|
-
dependencies = pkg.dependencies;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
const globalCachePath = getCacheDir(pkgName, version);
|
|
688
|
-
let docsType = "docs";
|
|
689
|
-
if (source?.includes("llms.txt") || existsSync(join(globalCachePath, "docs", "llms.txt"))) docsType = "llms.txt";
|
|
690
|
-
else if (isReadmeOnly(globalCachePath)) docsType = "readme";
|
|
691
|
-
const feat = readConfig().features ?? defaultFeatures;
|
|
692
|
-
const hasIssues = feat.issues && existsSync(join(globalCachePath, "issues"));
|
|
693
|
-
const hasDiscussions = feat.discussions && existsSync(join(globalCachePath, "discussions"));
|
|
694
|
-
const hasReleases = feat.releases && existsSync(join(globalCachePath, "releases"));
|
|
695
|
-
const relatedSkills = allSkillNames.filter((n) => n !== pkgName);
|
|
696
|
-
const dirName = skillDir.split("/").pop();
|
|
697
|
-
const allPackages = parsePackages(packages).map((p) => ({ name: p.name }));
|
|
698
|
-
const content = generateSkillMd({
|
|
699
|
-
name: pkgName,
|
|
700
|
-
version,
|
|
701
|
-
description,
|
|
702
|
-
dependencies,
|
|
703
|
-
relatedSkills,
|
|
704
|
-
hasIssues,
|
|
705
|
-
hasDiscussions,
|
|
706
|
-
hasReleases,
|
|
707
|
-
docsType,
|
|
708
|
-
hasShippedDocs: hasShippedDocs(pkgName, cwd, version),
|
|
709
|
-
pkgFiles: getPkgKeyFiles(pkgName, cwd, version),
|
|
710
|
-
dirName,
|
|
711
|
-
packages: allPackages.length > 1 ? allPackages : void 0,
|
|
712
|
-
features: readConfig().features ?? defaultFeatures
|
|
713
|
-
});
|
|
714
|
-
mkdirSync(skillDir, { recursive: true });
|
|
715
|
-
writeFileSync(skillMdPath, content);
|
|
716
|
-
return true;
|
|
717
|
-
}
|
|
718
|
-
/** Check if .skilld/ has broken symlinks or is missing expected references from global cache */
|
|
719
|
-
function hasStaleReferences(referencesPath, pkgName, version, features) {
|
|
720
|
-
for (const entry of readdirSync(referencesPath)) {
|
|
721
|
-
const entryPath = join(referencesPath, entry);
|
|
722
|
-
if (lstatSync(entryPath).isSymbolicLink() && !existsSync(entryPath)) return true;
|
|
723
|
-
}
|
|
724
|
-
if (!existsSync(join(referencesPath, "pkg"))) return true;
|
|
725
|
-
const globalCachePath = getCacheDir(pkgName, version);
|
|
726
|
-
const expected = [
|
|
727
|
-
["docs", existsSync(join(globalCachePath, "docs"))],
|
|
728
|
-
["issues", features.issues && existsSync(join(globalCachePath, "issues"))],
|
|
729
|
-
["discussions", features.discussions && existsSync(join(globalCachePath, "discussions"))],
|
|
730
|
-
["releases", features.releases && existsSync(join(globalCachePath, "releases"))],
|
|
731
|
-
["sections", existsSync(join(globalCachePath, "sections"))]
|
|
732
|
-
];
|
|
733
|
-
for (const [name, shouldExist] of expected) if (shouldExist && !existsSync(join(referencesPath, name))) return true;
|
|
734
|
-
return false;
|
|
735
|
-
}
|
|
736
|
-
function listCommand(opts = {}) {
|
|
737
|
-
const skills = [...iterateSkills({ scope: opts.global ? "global" : "all" })];
|
|
738
|
-
const seen = /* @__PURE__ */ new Set();
|
|
739
|
-
const entries = [];
|
|
740
|
-
for (const skill of skills) {
|
|
741
|
-
const key = skill.info?.packageName || skill.name;
|
|
742
|
-
if (seen.has(key)) continue;
|
|
743
|
-
seen.add(key);
|
|
744
|
-
entries.push({
|
|
745
|
-
name: skill.name,
|
|
746
|
-
version: skill.info?.version || "",
|
|
747
|
-
source: formatSource(skill.info?.source),
|
|
748
|
-
synced: timeAgo(skill.info?.syncedAt)
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
if (opts.json) {
|
|
752
|
-
process.stdout.write(`${JSON.stringify(entries)}\n`);
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
if (entries.length === 0) {
|
|
756
|
-
process.stdout.write("No skills installed\n");
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
const nameW = Math.max(...entries.map((e) => e.name.length));
|
|
760
|
-
const verW = Math.max(...entries.map((e) => e.version.length));
|
|
761
|
-
const srcW = Math.max(...entries.map((e) => e.source.length));
|
|
762
|
-
for (const e of entries) {
|
|
763
|
-
const line = [
|
|
764
|
-
e.name.padEnd(nameW),
|
|
765
|
-
e.version.padEnd(verW),
|
|
766
|
-
e.source.padEnd(srcW),
|
|
767
|
-
e.synced
|
|
768
|
-
].join(" ");
|
|
769
|
-
process.stdout.write(`${line}\n`);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
const listCommandDef = defineCommand({
|
|
773
|
-
meta: {
|
|
774
|
-
name: "list",
|
|
775
|
-
description: "List installed skills"
|
|
776
|
-
},
|
|
777
|
-
args: {
|
|
778
|
-
global: sharedArgs.global,
|
|
779
|
-
json: {
|
|
780
|
-
type: "boolean",
|
|
781
|
-
description: "Output as JSON",
|
|
782
|
-
default: false
|
|
783
|
-
}
|
|
784
|
-
},
|
|
785
|
-
run({ args }) {
|
|
786
|
-
return listCommand({
|
|
787
|
-
global: args.global,
|
|
788
|
-
json: args.json
|
|
789
|
-
});
|
|
790
|
-
}
|
|
791
|
-
});
|
|
792
151
|
async function removeCommand(state, opts) {
|
|
793
152
|
const scope = opts.global ? "global" : "local";
|
|
794
153
|
const allSkills = [...iterateSkills({ scope })];
|
|
@@ -1050,194 +409,98 @@ const infoCommandDef = defineCommand({
|
|
|
1050
409
|
return statusCommand({ global: args.global });
|
|
1051
410
|
}
|
|
1052
411
|
});
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
if (!existsSync(filePath)) return false;
|
|
1061
|
-
const content = readFileSync(filePath, "utf-8");
|
|
1062
|
-
const startIdx = content.indexOf(SKILLD_MARKER_START);
|
|
1063
|
-
if (startIdx === -1) return false;
|
|
1064
|
-
const endIdx = content.indexOf(SKILLD_MARKER_END, startIdx);
|
|
1065
|
-
if (endIdx === -1) return false;
|
|
1066
|
-
const before = content.slice(0, startIdx).replace(/\n+$/, "");
|
|
1067
|
-
const after = content.slice(endIdx + SKILLD_MARKER_END.length).replace(/^\n+/, "");
|
|
1068
|
-
const updated = before + (before && after ? "\n" : "") + after;
|
|
1069
|
-
if (updated.trim() === "") rmSync(filePath);
|
|
1070
|
-
else writeFileSync(filePath, updated.endsWith("\n") ? updated : `${updated}\n`);
|
|
1071
|
-
return true;
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
* Uninstall skilld skills by scope:
|
|
1075
|
-
* - project: Remove project skills (cwd)
|
|
1076
|
-
* - all: All registered projects + global skills + cache
|
|
1077
|
-
*/
|
|
1078
|
-
async function uninstallCommand(opts) {
|
|
1079
|
-
let scope = opts.scope;
|
|
1080
|
-
const registeredProjects = getRegisteredProjects();
|
|
1081
|
-
if (!scope) if (!isInteractive()) scope = "project";
|
|
1082
|
-
else {
|
|
1083
|
-
const allHint = registeredProjects.length > 0 ? `${registeredProjects.length} projects + global + cache` : "global skills + cache";
|
|
1084
|
-
const selected = await p.select({
|
|
1085
|
-
message: "What do you want to uninstall?",
|
|
1086
|
-
options: [{
|
|
1087
|
-
label: "This project",
|
|
1088
|
-
value: "project",
|
|
1089
|
-
hint: "current project only"
|
|
1090
|
-
}, {
|
|
1091
|
-
label: "Everything",
|
|
1092
|
-
value: "all",
|
|
1093
|
-
hint: allHint
|
|
1094
|
-
}]
|
|
1095
|
-
});
|
|
1096
|
-
if (p.isCancel(selected)) {
|
|
1097
|
-
p.cancel("Cancelled");
|
|
1098
|
-
return;
|
|
1099
|
-
}
|
|
1100
|
-
scope = selected;
|
|
412
|
+
function hasGhCli() {
|
|
413
|
+
if (process.env.SKILLD_NO_GH) return false;
|
|
414
|
+
try {
|
|
415
|
+
execSync("gh --version", { stdio: "ignore" });
|
|
416
|
+
return true;
|
|
417
|
+
} catch {
|
|
418
|
+
return false;
|
|
1101
419
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
420
|
+
}
|
|
421
|
+
async function runWizard() {
|
|
422
|
+
if (!isInteractive()) return;
|
|
423
|
+
p.note("Skilld gives your AI agent skill knowledge on your NPM\ndependencies gathered from versioned docs, source code\nand GitHub issues.", "Welcome to skilld");
|
|
424
|
+
const ghInstalled = hasGhCli();
|
|
425
|
+
if (ghInstalled) p.log.success("GitHub CLI detected — will use it to pull issues and discussions.");
|
|
426
|
+
else p.log.warn("GitHub CLI not found. Install it to enable issues/discussions:\n \x1B[36mhttps://cli.github.com\x1B[0m");
|
|
427
|
+
const selected = await p.multiselect({
|
|
428
|
+
message: "Which features would you like to enable?",
|
|
429
|
+
options: [
|
|
430
|
+
{
|
|
431
|
+
label: "Semantic + token search",
|
|
432
|
+
value: "search",
|
|
433
|
+
hint: "local query engine to cut token costs and speed up grep"
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
label: "Release notes",
|
|
437
|
+
value: "releases",
|
|
438
|
+
hint: "track changelogs for installed packages"
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
label: "GitHub issues",
|
|
442
|
+
value: "issues",
|
|
443
|
+
hint: "surface common problems and solutions",
|
|
444
|
+
disabled: !ghInstalled
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
label: "GitHub discussions",
|
|
448
|
+
value: "discussions",
|
|
449
|
+
hint: "include Q&A and community knowledge",
|
|
450
|
+
disabled: !ghInstalled
|
|
1126
451
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
452
|
+
],
|
|
453
|
+
initialValues: [...Object.entries(defaultFeatures).filter(([, v]) => v).map(([k]) => k), ...ghInstalled ? ["issues", "discussions"] : []],
|
|
454
|
+
required: false
|
|
455
|
+
});
|
|
456
|
+
if (p.isCancel(selected)) {
|
|
457
|
+
p.cancel("Setup cancelled");
|
|
458
|
+
process.exit(0);
|
|
459
|
+
}
|
|
460
|
+
const features = {
|
|
461
|
+
search: selected.includes("search"),
|
|
462
|
+
issues: selected.includes("issues"),
|
|
463
|
+
discussions: selected.includes("discussions"),
|
|
464
|
+
releases: selected.includes("releases")
|
|
1136
465
|
};
|
|
1137
|
-
const
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
466
|
+
const allModels = process.env.SKILLD_NO_AGENTS ? [] : await getAvailableModels();
|
|
467
|
+
let modelId;
|
|
468
|
+
if (allModels.length > 0) {
|
|
469
|
+
p.note("Skills work without an LLM, but one can rewrite your\nSKILL.md files with best practices and better structure.\n\x1B[90mThis is separate from the agent where skills are installed —\nthe target agent is auto-detected from your project files.\x1B[0m", "Optional: LLM optimization");
|
|
470
|
+
const modelChoice = await p.select({
|
|
471
|
+
message: "Model for generating SKILL.md",
|
|
472
|
+
options: [{
|
|
473
|
+
label: "Skip",
|
|
474
|
+
value: "",
|
|
475
|
+
hint: "use raw docs, no LLM needed"
|
|
476
|
+
}, ...allModels.map((m) => ({
|
|
477
|
+
label: m.recommended ? `${m.name} (Recommended)` : m.name,
|
|
478
|
+
value: m.id,
|
|
479
|
+
hint: `${m.agentName} · ${m.hint}`
|
|
480
|
+
}))]
|
|
1146
481
|
});
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
if (scope === "all") {
|
|
1158
|
-
const projectPaths = registeredProjects.length > 0 ? registeredProjects : [process.cwd()];
|
|
1159
|
-
if (registeredProjects.length > 0) {
|
|
1160
|
-
p.log.info("Projects to uninstall from:");
|
|
1161
|
-
for (const proj of projectPaths) p.log.message(` ${proj}`);
|
|
1162
|
-
}
|
|
1163
|
-
for (const projectPath of projectPaths) {
|
|
1164
|
-
if (!existsSync(projectPath)) continue;
|
|
1165
|
-
const shortPath = projectPath.replace(process.env.HOME || "", "~");
|
|
1166
|
-
const sharedDir = join(projectPath, SHARED_SKILLS_DIR);
|
|
1167
|
-
if (existsSync(sharedDir)) processSkillsDir(sharedDir, `${shortPath} (.skills)`);
|
|
1168
|
-
for (const [name, agent] of Object.entries(targets)) {
|
|
1169
|
-
if (agentFilter && !agentFilter.includes(name)) continue;
|
|
1170
|
-
processSkillsDir(join(projectPath, agent.skillsDir), shortPath);
|
|
1171
|
-
}
|
|
1172
|
-
projectsToUnregister.push(projectPath);
|
|
1173
|
-
}
|
|
1174
|
-
for (const [name, agent] of Object.entries(targets)) {
|
|
1175
|
-
if (agentFilter && !agentFilter.includes(name)) continue;
|
|
1176
|
-
if (!agent.globalSkillsDir) continue;
|
|
1177
|
-
processSkillsDir(agent.globalSkillsDir, "user");
|
|
1178
|
-
}
|
|
1179
|
-
if (existsSync(CACHE_DIR)) addToRemove("~/.skilld cache", CACHE_DIR);
|
|
1180
|
-
}
|
|
1181
|
-
if (untrackedByDir.size > 0) {
|
|
1182
|
-
const groupedUntracked = /* @__PURE__ */ new Map();
|
|
1183
|
-
for (const [_dir, { label, skills }] of untrackedByDir) {
|
|
1184
|
-
const set = mapInsert(groupedUntracked, label, () => /* @__PURE__ */ new Set());
|
|
1185
|
-
for (const s of skills) set.add(s);
|
|
1186
|
-
}
|
|
1187
|
-
const totalUntracked = [...groupedUntracked.values()].reduce((sum, s) => sum + s.size, 0);
|
|
1188
|
-
p.log.warn(`${totalUntracked} untracked skill(s) will remain (not managed by skilld):`);
|
|
1189
|
-
for (const [label, skills] of groupedUntracked) p.log.message(` ${label}: ${[...skills].join(", ")}`);
|
|
1190
|
-
}
|
|
1191
|
-
if (toRemove.length === 0) {
|
|
1192
|
-
p.log.info("Nothing to uninstall");
|
|
1193
|
-
return;
|
|
1194
|
-
}
|
|
1195
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1196
|
-
for (const item of toRemove) {
|
|
1197
|
-
const [prefix, name] = item.label.includes(": ") ? item.label.split(": ", 2) : ["other", item.label];
|
|
1198
|
-
mapInsert(groups, prefix, () => []).push({
|
|
1199
|
-
name,
|
|
1200
|
-
version: item.version
|
|
482
|
+
if (p.isCancel(modelChoice)) {
|
|
483
|
+
p.cancel("Setup cancelled");
|
|
484
|
+
process.exit(0);
|
|
485
|
+
}
|
|
486
|
+
modelId = modelChoice || void 0;
|
|
487
|
+
} else {
|
|
488
|
+
p.log.warn("No supported LLM CLIs detected (claude, gemini, codex).\n Skills will still work, but won't be LLM-optimized.");
|
|
489
|
+
const proceed = await p.confirm({
|
|
490
|
+
message: "Continue without LLM optimization?",
|
|
491
|
+
initialValue: true
|
|
1201
492
|
});
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
for (const [prefix, items] of groups) p.log.message(` ${prefix}: ${formatGroup(items)}`);
|
|
1206
|
-
if (!opts.yes && isInteractive()) {
|
|
1207
|
-
const confirmed = await p.confirm({ message: "Proceed with uninstall?" });
|
|
1208
|
-
if (p.isCancel(confirmed) || !confirmed) {
|
|
1209
|
-
p.cancel("Cancelled");
|
|
1210
|
-
return;
|
|
493
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
494
|
+
p.cancel("Setup cancelled");
|
|
495
|
+
process.exit(0);
|
|
1211
496
|
}
|
|
1212
497
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
498
|
+
updateConfig({
|
|
499
|
+
features,
|
|
500
|
+
...modelId ? { model: modelId } : { skipLlm: true }
|
|
1216
501
|
});
|
|
1217
|
-
|
|
1218
|
-
const agentTypes = agentFilter || Object.keys(targets);
|
|
1219
|
-
for (const proj of projectsToUnregister) for (const agent of agentTypes) if (removeAgentInstructions(agent, proj)) {
|
|
1220
|
-
const file = targets[agent].instructionFile;
|
|
1221
|
-
p.log.success(`Cleaned ${file}`);
|
|
1222
|
-
}
|
|
1223
|
-
if (scope !== "all") for (const proj of projectsToUnregister) unregisterProject(proj);
|
|
1224
|
-
p.outro("skilld uninstalled");
|
|
502
|
+
p.outro("Thanks, you're all set! Change config anytime with `skilld config`.");
|
|
1225
503
|
}
|
|
1226
|
-
const uninstallCommandDef = defineCommand({
|
|
1227
|
-
meta: {
|
|
1228
|
-
name: "uninstall",
|
|
1229
|
-
description: "Remove skilld data"
|
|
1230
|
-
},
|
|
1231
|
-
args: { ...sharedArgs },
|
|
1232
|
-
async run({ args }) {
|
|
1233
|
-
p.intro(`\x1B[1m\x1B[35mskilld\x1B[0m uninstall`);
|
|
1234
|
-
return uninstallCommand({
|
|
1235
|
-
scope: args.global ? "all" : void 0,
|
|
1236
|
-
agent: args.agent,
|
|
1237
|
-
yes: args.yes
|
|
1238
|
-
});
|
|
1239
|
-
}
|
|
1240
|
-
});
|
|
1241
504
|
const _emit = process.emit;
|
|
1242
505
|
process.emit = (event, ...args) => event === "warning" && args[0]?.name === "ExperimentalWarning" && args[0]?.message?.includes("SQLite") ? false : _emit.apply(process, [event, ...args]);
|
|
1243
506
|
const NOISE_CHARS = "⣿⡿⣷⣾⣽⣻⢿⡷⣯⣟⡾⣵⣳⢾⡽⣞⡷⣝⢯";
|
|
@@ -1352,13 +615,13 @@ runMain(defineCommand({
|
|
|
1352
615
|
add: () => import("./_chunks/sync2.mjs").then((m) => m.addCommandDef),
|
|
1353
616
|
eject: () => import("./_chunks/sync2.mjs").then((m) => m.ejectCommandDef),
|
|
1354
617
|
update: () => import("./_chunks/sync2.mjs").then((m) => m.updateCommandDef),
|
|
1355
|
-
info: () =>
|
|
618
|
+
info: () => infoCommandDef,
|
|
1356
619
|
list: () => import("./_chunks/list.mjs").then((m) => m.listCommandDef),
|
|
1357
|
-
config: () =>
|
|
1358
|
-
remove: () =>
|
|
620
|
+
config: () => configCommandDef,
|
|
621
|
+
remove: () => removeCommandDef,
|
|
1359
622
|
install: () => import("./_chunks/install.mjs").then((m) => m.installCommandDef),
|
|
1360
623
|
uninstall: () => import("./_chunks/uninstall.mjs").then((m) => m.uninstallCommandDef),
|
|
1361
|
-
search: () => import("./_chunks/
|
|
624
|
+
search: () => import("./_chunks/search.mjs").then((m) => m.searchCommandDef),
|
|
1362
625
|
cache: () => import("./_chunks/cache2.mjs").then((m) => m.cacheCommandDef),
|
|
1363
626
|
validate: () => import("./_chunks/validate.mjs").then((m) => m.validateCommandDef)
|
|
1364
627
|
},
|
|
@@ -1718,6 +981,6 @@ runMain(defineCommand({
|
|
|
1718
981
|
}
|
|
1719
982
|
}
|
|
1720
983
|
}));
|
|
1721
|
-
export {
|
|
984
|
+
export { runWizard as t };
|
|
1722
985
|
|
|
1723
986
|
//# sourceMappingURL=cli.mjs.map
|