skilld 0.15.0 → 0.15.2
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 +35 -21
- package/dist/cli.mjs +43 -18
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,33 +59,21 @@ If you need to re-configure skilld, just run `npx -y skilld config` to update yo
|
|
|
59
59
|
- **LLM is optional** - Skills work without any LLM, but enhancing with one makes them significantly better.
|
|
60
60
|
- **Multi-agent** - Run `skilld install --agent gemini-cli` to sync skills to another agent. The doc cache is shared.
|
|
61
61
|
|
|
62
|
-
##
|
|
62
|
+
## Installation
|
|
63
63
|
|
|
64
|
-
###
|
|
64
|
+
### Global
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
Install globally to use `skilld` across all projects without `npx`:
|
|
67
67
|
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
```bash
|
|
69
|
+
npm install -g skilld
|
|
70
|
+
# or
|
|
71
|
+
pnpm add -g skilld
|
|
71
72
|
```
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Context7 is an MCP that fetches raw doc chunks at query time. You get different results each prompt, no curation, and it requires their server. Skilld is local-first: it generates a SKILL.md that lives in your project, tied to your actual package versions. No MCP dependency, no per-prompt latency, and it goes further with LLM-enhanced sections, prompt injection sanitization, and semantic search.
|
|
74
|
+
Then run `skilld` in any project directory.
|
|
76
75
|
|
|
77
|
-
###
|
|
78
|
-
|
|
79
|
-
Skilld pulls issues from GitHub which could be abused for potential prompt injection.
|
|
80
|
-
|
|
81
|
-
Skilld treats all data as untrusted, running in permissioned environments and using best practices to avoid injections.
|
|
82
|
-
However, always be cautious when using skills from untrusted sources.
|
|
83
|
-
|
|
84
|
-
### Do skills update when my deps update?
|
|
85
|
-
|
|
86
|
-
Yes. Run `skilld update` to regenerate outdated skills, or add `skilld update -b` to your prepare script and they regenerate in the background whenever you install packages.
|
|
87
|
-
|
|
88
|
-
## Installation
|
|
76
|
+
### Per-Project
|
|
89
77
|
|
|
90
78
|
If you'd like to install skilld and track the lock file references, add it as a dev dependency:
|
|
91
79
|
|
|
@@ -109,6 +97,32 @@ Add to `package.json` to keep skills fresh on install:
|
|
|
109
97
|
}
|
|
110
98
|
```
|
|
111
99
|
|
|
100
|
+
## FAQ
|
|
101
|
+
|
|
102
|
+
### Why don't the skills run?
|
|
103
|
+
|
|
104
|
+
Try this in your project/user prompt:
|
|
105
|
+
|
|
106
|
+
```md
|
|
107
|
+
Before modifying code, evaluate each installed skill against the current task.
|
|
108
|
+
For each skill, determine YES/NO relevance and invoke all YES skills before proceeding.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### How is this different from Context7?
|
|
112
|
+
|
|
113
|
+
Context7 is an MCP that fetches raw doc chunks at query time. You get different results each prompt, no curation, and it requires their server. Skilld is local-first: it generates a SKILL.md that lives in your project, tied to your actual package versions. No MCP dependency, no per-prompt latency, and it goes further with LLM-enhanced sections, prompt injection sanitization, and semantic search.
|
|
114
|
+
|
|
115
|
+
### Will I be prompt injected?
|
|
116
|
+
|
|
117
|
+
Skilld pulls issues from GitHub which could be abused for potential prompt injection.
|
|
118
|
+
|
|
119
|
+
Skilld treats all data as untrusted, running in permissioned environments and using best practices to avoid injections.
|
|
120
|
+
However, always be cautious when using skills from untrusted sources.
|
|
121
|
+
|
|
122
|
+
### Do skills update when my deps update?
|
|
123
|
+
|
|
124
|
+
Yes. Run `skilld update` to regenerate outdated skills, or add `skilld update -b` to your prepare script and they regenerate in the background whenever you install packages.
|
|
125
|
+
|
|
112
126
|
## CLI Usage
|
|
113
127
|
|
|
114
128
|
```bash
|
package/dist/cli.mjs
CHANGED
|
@@ -1304,7 +1304,8 @@ async function fetchAndCacheResources(opts) {
|
|
|
1304
1304
|
hasDiscussions: features.discussions && existsSync(discussionsDir),
|
|
1305
1305
|
hasReleases: features.releases && existsSync(releasesPath),
|
|
1306
1306
|
warnings,
|
|
1307
|
-
repoInfo
|
|
1307
|
+
repoInfo,
|
|
1308
|
+
usedCache: useCache
|
|
1308
1309
|
};
|
|
1309
1310
|
}
|
|
1310
1311
|
async function indexResources(opts) {
|
|
@@ -1527,7 +1528,7 @@ async function selectSkillSections(message = "Generate SKILL.md with LLM") {
|
|
|
1527
1528
|
cancelled: false
|
|
1528
1529
|
};
|
|
1529
1530
|
}
|
|
1530
|
-
async function selectLlmConfig(presetModel, message) {
|
|
1531
|
+
async function selectLlmConfig(presetModel, message, usedCache) {
|
|
1531
1532
|
if (presetModel) return {
|
|
1532
1533
|
model: presetModel,
|
|
1533
1534
|
sections: DEFAULT_SECTIONS
|
|
@@ -1535,6 +1536,10 @@ async function selectLlmConfig(presetModel, message) {
|
|
|
1535
1536
|
if (!isInteractive()) return null;
|
|
1536
1537
|
const defaultModel = await selectModel(true);
|
|
1537
1538
|
if (!defaultModel) return null;
|
|
1539
|
+
if (usedCache) return {
|
|
1540
|
+
model: defaultModel,
|
|
1541
|
+
sections: DEFAULT_SECTIONS
|
|
1542
|
+
};
|
|
1538
1543
|
const defaultModelName = getModelName(defaultModel);
|
|
1539
1544
|
const choice = await p.select({
|
|
1540
1545
|
message: "Generate enhanced SKILL.md?",
|
|
@@ -1943,8 +1948,9 @@ async function syncPackagesParallel(config) {
|
|
|
1943
1948
|
for (const [, data] of skillData) for (const w of data.warnings) p.log.warn(`\x1B[33m${w}\x1B[0m`);
|
|
1944
1949
|
if (errors.length > 0) for (const { pkg, reason } of errors) p.log.error(` ${pkg}: ${reason}`);
|
|
1945
1950
|
const globalConfig = readConfig();
|
|
1951
|
+
const allCached = successfulPkgs.every((pkg) => skillData.get(pkg)?.usedCache);
|
|
1946
1952
|
if (successfulPkgs.length > 0 && !globalConfig.skipLlm && !(config.yes && !config.model)) {
|
|
1947
|
-
const llmConfig = await selectLlmConfig(config.model);
|
|
1953
|
+
const llmConfig = await selectLlmConfig(config.model, void 0, allCached);
|
|
1948
1954
|
if (llmConfig) {
|
|
1949
1955
|
p.log.step(getModelLabel(llmConfig.model));
|
|
1950
1956
|
for (const pkg of successfulPkgs) states.set(pkg, {
|
|
@@ -2089,7 +2095,8 @@ async function syncBaseSkill(packageSpec, config, cwd, update) {
|
|
|
2089
2095
|
relatedSkills,
|
|
2090
2096
|
packages: allPackages.length > 1 ? allPackages : void 0,
|
|
2091
2097
|
warnings: resources.warnings,
|
|
2092
|
-
features
|
|
2098
|
+
features,
|
|
2099
|
+
usedCache: resources.usedCache
|
|
2093
2100
|
};
|
|
2094
2101
|
}
|
|
2095
2102
|
async function enhanceWithLLM(packageName, data, config, cwd, update, sections, customPrompt) {
|
|
@@ -2470,7 +2477,7 @@ async function syncSinglePackage(packageSpec, config) {
|
|
|
2470
2477
|
if (resources.hasIssues) resParts.push("issues");
|
|
2471
2478
|
if (resources.hasDiscussions) resParts.push("discussions");
|
|
2472
2479
|
if (resources.hasReleases) resParts.push("releases");
|
|
2473
|
-
resSpin.stop(`Fetched ${resParts.length > 0 ? resParts.join(", ") : "resources"}`);
|
|
2480
|
+
resSpin.stop(resources.usedCache ? `Loaded ${resParts.length > 0 ? resParts.join(", ") : "resources"} (cached)` : `Fetched ${resParts.length > 0 ? resParts.join(", ") : "resources"}`);
|
|
2474
2481
|
for (const w of resources.warnings) p.log.warn(`\x1B[33m${w}\x1B[0m`);
|
|
2475
2482
|
linkAllReferences(skillDir, packageName, cwd, version, resources.docsType, void 0, features, resources.repoInfo);
|
|
2476
2483
|
if (features.search) {
|
|
@@ -2526,7 +2533,7 @@ async function syncSinglePackage(packageSpec, config) {
|
|
|
2526
2533
|
writeFileSync(join(skillDir, "SKILL.md"), baseSkillMd);
|
|
2527
2534
|
p.log.success(config.mode === "update" ? `Updated skill: ${relative(cwd, skillDir)}` : `Created base skill: ${relative(cwd, skillDir)}`);
|
|
2528
2535
|
if (!readConfig().skipLlm && (!config.yes || config.model)) {
|
|
2529
|
-
const llmConfig = await selectLlmConfig(config.model);
|
|
2536
|
+
const llmConfig = await selectLlmConfig(config.model, void 0, resources.usedCache);
|
|
2530
2537
|
if (llmConfig) {
|
|
2531
2538
|
p.log.step(getModelLabel(llmConfig.model));
|
|
2532
2539
|
await enhanceSkillWithLLM({
|
|
@@ -3421,35 +3428,46 @@ const removeCommandDef = defineCommand({
|
|
|
3421
3428
|
});
|
|
3422
3429
|
var search_exports = /* @__PURE__ */ __exportAll({
|
|
3423
3430
|
findPackageDbs: () => findPackageDbs,
|
|
3431
|
+
listLockPackages: () => listLockPackages,
|
|
3424
3432
|
parseFilterPrefix: () => parseFilterPrefix,
|
|
3425
3433
|
searchCommand: () => searchCommand,
|
|
3426
3434
|
searchCommandDef: () => searchCommandDef
|
|
3427
3435
|
});
|
|
3428
3436
|
function findPackageDbs(packageFilter) {
|
|
3429
|
-
const
|
|
3437
|
+
const lock = readProjectLock(process.cwd());
|
|
3438
|
+
if (!lock) return [];
|
|
3439
|
+
return filterLockDbs(lock, packageFilter);
|
|
3440
|
+
}
|
|
3441
|
+
function readProjectLock(cwd) {
|
|
3430
3442
|
const shared = getSharedSkillsDir(cwd);
|
|
3431
3443
|
if (shared) {
|
|
3432
3444
|
const lock = readLock(shared);
|
|
3433
|
-
if (lock) return
|
|
3445
|
+
if (lock) return lock;
|
|
3434
3446
|
}
|
|
3435
3447
|
const agent = detectTargetAgent();
|
|
3436
|
-
if (!agent) return
|
|
3437
|
-
|
|
3448
|
+
if (!agent) return null;
|
|
3449
|
+
return readLock(`${cwd}/${targets[agent].skillsDir}`);
|
|
3450
|
+
}
|
|
3451
|
+
function listLockPackages(cwd = process.cwd()) {
|
|
3452
|
+
const lock = readProjectLock(cwd);
|
|
3438
3453
|
if (!lock) return [];
|
|
3439
|
-
return
|
|
3454
|
+
return [...new Set(Object.values(lock.skills).map((s) => s.packageName).filter(Boolean))];
|
|
3440
3455
|
}
|
|
3441
3456
|
function filterLockDbs(lock, packageFilter) {
|
|
3442
3457
|
if (!lock) return [];
|
|
3443
|
-
const
|
|
3458
|
+
const tokenize = (s) => s.toLowerCase().replace(/@/g, "").split(/[-_/]+/).filter(Boolean);
|
|
3444
3459
|
return Object.values(lock.skills).filter((info) => {
|
|
3445
3460
|
if (!info.packageName || !info.version) return false;
|
|
3446
3461
|
if (!packageFilter) return true;
|
|
3447
|
-
const
|
|
3448
|
-
|
|
3462
|
+
const filterTokens = tokenize(packageFilter);
|
|
3463
|
+
const nameTokens = tokenize(info.packageName);
|
|
3464
|
+
return filterTokens.every((ft) => nameTokens.some((nt) => nt.includes(ft) || ft.includes(nt)));
|
|
3449
3465
|
}).map((info) => {
|
|
3450
3466
|
const exact = getPackageDbPath(info.packageName, info.version);
|
|
3451
3467
|
if (existsSync(exact)) return exact;
|
|
3452
|
-
|
|
3468
|
+
const fallback = findAnyPackageDb(info.packageName);
|
|
3469
|
+
if (fallback) p.log.warn(`Using cached search index for ${info.packageName} (v${info.version} not indexed). Run \`skilld update ${info.packageName}\` to re-index.`);
|
|
3470
|
+
return fallback;
|
|
3453
3471
|
}).filter((db) => !!db);
|
|
3454
3472
|
}
|
|
3455
3473
|
function findAnyPackageDb(name) {
|
|
@@ -3493,8 +3511,11 @@ function parseFilterPrefix(rawQuery) {
|
|
|
3493
3511
|
async function searchCommand(rawQuery, packageFilter) {
|
|
3494
3512
|
const dbs = findPackageDbs(packageFilter);
|
|
3495
3513
|
if (dbs.length === 0) {
|
|
3496
|
-
if (packageFilter)
|
|
3497
|
-
|
|
3514
|
+
if (packageFilter) {
|
|
3515
|
+
const available = listLockPackages();
|
|
3516
|
+
if (available.length > 0) p.log.warn(`No docs indexed for "${packageFilter}". Available: ${available.join(", ")}`);
|
|
3517
|
+
else p.log.warn(`No docs indexed for "${packageFilter}". Run \`skilld add ${packageFilter}\` first.`);
|
|
3518
|
+
} else p.log.warn("No docs indexed yet. Run `skilld add <package>` first.");
|
|
3498
3519
|
return;
|
|
3499
3520
|
}
|
|
3500
3521
|
const { query, filter } = parseFilterPrefix(rawQuery);
|
|
@@ -3570,7 +3591,11 @@ const SPINNER_FRAMES = [
|
|
|
3570
3591
|
async function interactiveSearch(packageFilter) {
|
|
3571
3592
|
const dbs = findPackageDbs(packageFilter);
|
|
3572
3593
|
if (dbs.length === 0) {
|
|
3573
|
-
|
|
3594
|
+
let msg;
|
|
3595
|
+
if (packageFilter) {
|
|
3596
|
+
const available = listLockPackages();
|
|
3597
|
+
msg = available.length > 0 ? `No docs indexed for "${packageFilter}". Available: ${available.join(", ")}` : `No docs indexed for "${packageFilter}". Run \`skilld add ${packageFilter}\` first.`;
|
|
3598
|
+
} else msg = "No docs indexed yet. Run `skilld add <package>` first.";
|
|
3574
3599
|
process.stderr.write(`\x1B[33m${msg}\x1B[0m\n`);
|
|
3575
3600
|
return;
|
|
3576
3601
|
}
|