skilllib 1.12.1 → 1.12.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.
|
@@ -478,7 +478,7 @@ function repoOf(root) {
|
|
|
478
478
|
const seen = changedAt(config);
|
|
479
479
|
let repo = null;
|
|
480
480
|
if (out2[0] === "") {
|
|
481
|
-
const urls = (gitIn(root, ["config", "--get-regexp", "^remote\\..*\\.url$"]) ?? "").split("\n").filter(Boolean).map((l) => l.
|
|
481
|
+
const urls = (gitIn(root, ["config", "--get-regexp", "^remote\\..*\\.url$"]) ?? "").split("\n").filter(Boolean).map((l) => [l.slice(0, l.indexOf(" ")), l.slice(l.indexOf(" ") + 1)]);
|
|
482
482
|
const url = (urls.find(([k]) => k === "remote.origin.url") ?? urls[0])?.[1];
|
|
483
483
|
const remote = url ? normalizeRemote(url) : void 0;
|
|
484
484
|
repo = { file: pinsFileFor(root, remote, folder), folder, common, ...remote ? { remote } : {}, root };
|
|
@@ -1395,8 +1395,8 @@ function addSkill(root, name, { force = false, version, allowTracked = false, pe
|
|
|
1395
1395
|
if (moving && !keep) return { name, action: "skipped", reason: `your library has no copy of the version it pins, so its pin can't move as it is: skilllib update ${name} first` };
|
|
1396
1396
|
const target = keep ?? (version === void 0 ? latestVersion(name) : getVersion(name, version));
|
|
1397
1397
|
if (!target) return { name, action: "skipped", reason: version === void 0 ? "not in the library" : `version ${version} doesn't exist` };
|
|
1398
|
-
const plan = addPlan(root, name, target.hash, force);
|
|
1399
|
-
if ("reason" in plan) return { name, action: "skipped", reason: plan.reason };
|
|
1398
|
+
const plan = addPlan(root, name, target.hash, moving ? false : force);
|
|
1399
|
+
if ("reason" in plan) return { name, action: "skipped", reason: moving ? `${withoutForceHint(plan.reason)}, so its pin can't move: save the edits to your library first` : plan.reason };
|
|
1400
1400
|
const { manifest, recorded, primary, wantedLinks, to, local } = plan;
|
|
1401
1401
|
const mine = pinsWhere(root, recorded, personal);
|
|
1402
1402
|
if (typeof mine === "string") return { name, action: "skipped", reason: mine };
|
|
@@ -2400,6 +2400,8 @@ function intoLibrary(path, force = false) {
|
|
|
2400
2400
|
if (r.status === "exists") throw new Error(`${r.name}: your library has a different ${r.name}; update or delete it first`);
|
|
2401
2401
|
return r.name;
|
|
2402
2402
|
}
|
|
2403
|
+
var pinsOf = (where, personal) => personal ? "your personal pins" : `${where}'s skilllib.json`;
|
|
2404
|
+
var newPinsOf = (root, where) => pinsOf(where, !existsSync12(join13(root, MANIFEST_FILE)) && personalPinsFile(root) !== null);
|
|
2403
2405
|
function track(root, name) {
|
|
2404
2406
|
const c = addSkill(root, name);
|
|
2405
2407
|
if (c.action === "skipped") throw new Error(`${name}: ${c.reason}`);
|
|
@@ -2937,7 +2939,7 @@ function repoIssues(root, ctx) {
|
|
|
2937
2939
|
detail: `${s.state === "folder missing" ? "The folder is missing, and no" : "The copy is another version, and no"} version in Your skills has the content it pins (pinned from another library?). ${untrackable(s) ? `${untrackable(s)}, so skilllib can't install it here.` : `Install your newest v${s.latest}; ${s.personal ? "your personal pins" : `${where}'s skilllib.json`} then pin${s.personal ? "" : "s"} it.`}`,
|
|
2938
2940
|
at: rows(root, [s.name]),
|
|
2939
2941
|
// It changes what the shared skilllib.json pins: a choice, never run by `doctor --fix`.
|
|
2940
|
-
...untrackable(s) ? {} : { choices: [{ label: `Install your newest (v${s.latest})`, hint: `${where}'s skilllib.json pins it`, run: () => `${s.name}: installed v${track(root, s.name).to}` }] }
|
|
2942
|
+
...untrackable(s) ? {} : { choices: [{ label: `Install your newest (v${s.latest})`, hint: s.personal ? "your personal pins pin it" : `${where}'s skilllib.json pins it`, run: () => `${s.name}: installed v${track(root, s.name).to}` }] }
|
|
2941
2943
|
});
|
|
2942
2944
|
if (restorable.length > 0) {
|
|
2943
2945
|
ctx.issues.push({
|
|
@@ -2981,12 +2983,12 @@ function repoIssues(root, ctx) {
|
|
|
2981
2983
|
// Editing skilllib.json is the team's call: a choice, never run by `doctor --fix`.
|
|
2982
2984
|
choices: [
|
|
2983
2985
|
{
|
|
2984
|
-
label: "Remove it from skilllib.json",
|
|
2986
|
+
label: s.personal ? "Remove it from your personal pins" : "Remove it from skilllib.json",
|
|
2985
2987
|
hint: `${where} stops pinning it`,
|
|
2986
2988
|
run: () => {
|
|
2987
2989
|
const r = removeSkill(root, s.name);
|
|
2988
2990
|
if (r.action !== "removed") throw new Error(`${s.name}: ${r.reason}`);
|
|
2989
|
-
return `${s.name}: removed from ${where
|
|
2991
|
+
return `${s.name}: removed from ${pinsOf(where, s.personal)}`;
|
|
2990
2992
|
}
|
|
2991
2993
|
}
|
|
2992
2994
|
]
|
|
@@ -3003,7 +3005,7 @@ function repoIssues(root, ctx) {
|
|
|
3003
3005
|
choices: [
|
|
3004
3006
|
{
|
|
3005
3007
|
label: "Update to the newest versions",
|
|
3006
|
-
hint: isShared(gitOf(root)?.of(MANIFEST_FILE)) ? "git will see the new copies and skilllib.json" : "replaces each copy with your library's newest",
|
|
3008
|
+
hint: behind.some((s) => !s.personal) && isShared(gitOf(root)?.of(MANIFEST_FILE)) ? "git will see the new copies and skilllib.json" : "replaces each copy with your library's newest",
|
|
3007
3009
|
run: () => summarize(where, "updated", updateProject(root, behind.map((s) => s.name)))
|
|
3008
3010
|
}
|
|
3009
3011
|
]
|
|
@@ -3086,7 +3088,7 @@ function repoIssues(root, ctx) {
|
|
|
3086
3088
|
if (partial.length) {
|
|
3087
3089
|
const missing2 = [...new Set(partial.flatMap((s) => s.visibility.filter((v) => v.paths === 0).map((v) => v.id)))];
|
|
3088
3090
|
const tracked = [...new Set(blindHere.flatMap(({ would }) => would.blocked))];
|
|
3089
|
-
const manifestShared = partial.some((s) => s.managed) && isShared(gitOf(root)?.of(MANIFEST_FILE));
|
|
3091
|
+
const manifestShared = partial.some((s) => s.managed && !s.personal) && isShared(gitOf(root)?.of(MANIFEST_FILE));
|
|
3090
3092
|
const run = (allowTracked) => () => {
|
|
3091
3093
|
if (allowTracked) allowTrackedLinks(root);
|
|
3092
3094
|
const r = linkAll(root, { allowTracked, names: partial.map((s) => s.name) });
|
|
@@ -3162,9 +3164,9 @@ function repoIssues(root, ctx) {
|
|
|
3162
3164
|
id: `adopt:${root}:${s.name}`,
|
|
3163
3165
|
severity: "suggestion",
|
|
3164
3166
|
title: `${s.name} in ${where} isn't tracked`,
|
|
3165
|
-
detail:
|
|
3167
|
+
detail: `It's identical to the library copy; track it (in ${newPinsOf(root, where)}) so future library changes reach it.`,
|
|
3166
3168
|
at: rows(root, [s.name]),
|
|
3167
|
-
choices: [{ label: "Track it", hint: `adds it to ${where}
|
|
3169
|
+
choices: [{ label: "Track it", hint: `adds it to ${newPinsOf(root, where)}`, run: () => `${track(root, s.name).name} tracked in ${where}` }]
|
|
3168
3170
|
});
|
|
3169
3171
|
} else if (s.state === "untracked, differs from library" || s.state === "repo skill, differs from library") {
|
|
3170
3172
|
const teams = s.state.startsWith("repo skill") || committed(s) || !!untrackable(s);
|
|
@@ -3226,7 +3228,7 @@ function repoIssues(root, ctx) {
|
|
|
3226
3228
|
id: `not-in-lib:${root}:${s.name}`,
|
|
3227
3229
|
severity: "suggestion",
|
|
3228
3230
|
title: `${s.name} in ${where} is tracked, but no longer in your library`,
|
|
3229
|
-
detail: `${where}'s skilllib.json pins it, but your library has no copy: if the folder goes, sync can't restore it, and your other repos can't add it. Copy ${where}'s copy back into the library.`,
|
|
3231
|
+
detail: `${s.personal ? "Your personal pins have" : `${where}'s skilllib.json pins`} it, but your library has no copy: if the folder goes, sync can't restore it, and your other repos can't add it. Copy ${where}'s copy back into the library.`,
|
|
3230
3232
|
at: rows(root, [s.name]),
|
|
3231
3233
|
choices: [
|
|
3232
3234
|
{
|
|
@@ -3958,7 +3960,7 @@ function add(args) {
|
|
|
3958
3960
|
printChanges(root, changes, args);
|
|
3959
3961
|
const pins = readManifest(root).skills;
|
|
3960
3962
|
const done = changes.filter((c) => c.action !== "skipped" && pins[c.name]);
|
|
3961
|
-
if (!args.json && done.some((c) => pins[c.name].personal))
|
|
3963
|
+
if (!args.json && args.personal === void 0 && done.some((c) => c.action === "installed" && !c.moved && pins[c.name].personal))
|
|
3962
3964
|
info(dim(`Pinned in your personal pins (${tildify(personalPinsDir())}) and kept out of git. --shared pins in skilllib.json for the team.`));
|
|
3963
3965
|
rememberProjects([root]);
|
|
3964
3966
|
}
|
|
@@ -4390,7 +4392,7 @@ function moveGlobal(args, names, yours) {
|
|
|
4390
4392
|
for (const [root, c] of m.changes) warnHeldLinks(root, c, `in ${basename10(root)}`);
|
|
4391
4393
|
if (m.left) warn(`${m.name}: ${m.left.join(", ")} now point${m.left.length === 1 ? "s" : ""} at nothing (the repo's own copy serves that package): remove ${m.left.length === 1 ? "it" : "them"}`);
|
|
4392
4394
|
}
|
|
4393
|
-
const toCommit = [...new Set(moves.filter((m) => m.moved).flatMap((m) => m.changes.
|
|
4395
|
+
const toCommit = [...new Set(moves.filter((m) => m.moved).flatMap((m) => m.changes.filter(([r]) => pinsIn(r)[m.name] && !pinsIn(r)[m.name].personal).map(([r]) => r)))];
|
|
4394
4396
|
if (toCommit.length) info(dim(`Commit skilllib.json and the skill folders in ${toCommit.map((r) => basename10(r)).join(", ")}.`));
|
|
4395
4397
|
}
|
|
4396
4398
|
function tidy(args) {
|
|
@@ -4497,11 +4499,14 @@ export {
|
|
|
4497
4499
|
versionHistory,
|
|
4498
4500
|
forgetLatest,
|
|
4499
4501
|
latestVersion,
|
|
4502
|
+
versionForHash,
|
|
4500
4503
|
projectHere,
|
|
4501
4504
|
readManifest,
|
|
4505
|
+
personalPinsFile,
|
|
4502
4506
|
knownProjects,
|
|
4503
4507
|
rememberProjects,
|
|
4504
4508
|
readableRoots,
|
|
4509
|
+
pinsIn,
|
|
4505
4510
|
setJsonMode,
|
|
4506
4511
|
isJsonMode,
|
|
4507
4512
|
json,
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
unhide,
|
|
32
32
|
update,
|
|
33
33
|
usage
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-YQFLFWAY.js";
|
|
35
35
|
|
|
36
36
|
// src/index.ts
|
|
37
37
|
import { readFileSync } from "fs";
|
|
@@ -140,10 +140,10 @@ async function main() {
|
|
|
140
140
|
if (!["--version", "-v", "version", "help", "--help", "-h"].includes(command ?? "")) refreshAgentSkill();
|
|
141
141
|
switch (command) {
|
|
142
142
|
case void 0:
|
|
143
|
-
return process.stdin.isTTY && process.stdout.isTTY ? (await import("./tui-
|
|
143
|
+
return process.stdin.isTTY && process.stdout.isTTY ? (await import("./tui-P3HKRNHI.js")).tui() : overview(args);
|
|
144
144
|
case "ui":
|
|
145
145
|
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("`skilllib ui` needs an interactive terminal. Agents and scripts: use `skilllib status --json`.");
|
|
146
|
-
return (await import("./tui-
|
|
146
|
+
return (await import("./tui-P3HKRNHI.js")).tui();
|
|
147
147
|
case "status":
|
|
148
148
|
return status(args);
|
|
149
149
|
case "add":
|
|
@@ -43,6 +43,8 @@ import {
|
|
|
43
43
|
moveGlobalSkills,
|
|
44
44
|
movedTogether,
|
|
45
45
|
notWritable,
|
|
46
|
+
personalPinsFile,
|
|
47
|
+
pinsIn,
|
|
46
48
|
pluginBackups,
|
|
47
49
|
projectHere,
|
|
48
50
|
projectOfFactory,
|
|
@@ -81,10 +83,11 @@ import {
|
|
|
81
83
|
updatePlugin,
|
|
82
84
|
updateProject,
|
|
83
85
|
usageIssues,
|
|
86
|
+
versionForHash,
|
|
84
87
|
versionHistory,
|
|
85
88
|
visibleProjects,
|
|
86
89
|
withoutForceHint
|
|
87
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-YQFLFWAY.js";
|
|
88
91
|
|
|
89
92
|
// src/tui/index.tsx
|
|
90
93
|
import { render } from "ink";
|
|
@@ -445,7 +448,7 @@ function globalFixes(m) {
|
|
|
445
448
|
];
|
|
446
449
|
}
|
|
447
450
|
function pinFix(w, repo, s) {
|
|
448
|
-
if (s.source !== "lib" || !w.library.some((l) => l.name === s.name)) return null;
|
|
451
|
+
if (s.source !== "lib" || !s.pinMoves || !w.library.some((l) => l.name === s.name)) return null;
|
|
449
452
|
if (s.personal)
|
|
450
453
|
return { label: "Pin it for the team", preview: `Move ${s.name}'s pin into ${repo}'s skilllib.json. Git will see skilllib.json and ${s.dir}/${s.name}: commit them to share it.`, run: (w2) => w2.ops.pin(repo, s.name, false) };
|
|
451
454
|
if (s.inGit || isShared(s.git)) return null;
|
|
@@ -2095,7 +2098,7 @@ function uniqueNames(roots) {
|
|
|
2095
2098
|
})
|
|
2096
2099
|
);
|
|
2097
2100
|
}
|
|
2098
|
-
function localSkill(root, s, git) {
|
|
2101
|
+
function localSkill(root, s, git, pins, canBePersonal) {
|
|
2099
2102
|
const managed = {
|
|
2100
2103
|
"edited locally": { edited: true },
|
|
2101
2104
|
"edited locally, update available": { edited: true },
|
|
@@ -2114,7 +2117,9 @@ function localSkill(root, s, git) {
|
|
|
2114
2117
|
const state = git ? git.of(relativeTo(root, s.path)) : null;
|
|
2115
2118
|
const others = otherCopies(root, s.name, s.location);
|
|
2116
2119
|
const own = other[s.state]?.source === "untracked" && isShared(state) ? { source: "repo" } : {};
|
|
2117
|
-
const
|
|
2120
|
+
const dep = pins[s.name];
|
|
2121
|
+
const inGit = s.managed && (isShared(state) || (dep?.links ?? []).some((d) => isShared(git?.of(`${d}/${s.name}`))));
|
|
2122
|
+
const pinMoves = s.managed && !!dep && versionForHash(s.name, dep.hash) !== null && !s.state.startsWith("edited") && (s.personal || canBePersonal);
|
|
2118
2123
|
return {
|
|
2119
2124
|
name: s.name,
|
|
2120
2125
|
source: "lib",
|
|
@@ -2122,7 +2127,7 @@ function localSkill(root, s, git) {
|
|
|
2122
2127
|
path: s.path,
|
|
2123
2128
|
// Agents that actually load it here: visibility lists every agent you use, with `paths: 0` for the blind ones.
|
|
2124
2129
|
agents: s.visibility.filter((v) => v.paths > 0).map((v) => v.id),
|
|
2125
|
-
...s.managed ? { version: s.version ?? void 0, ...s.personal ? { personal: true } : {}, ...inGit ? { inGit: true } : {}, ...managed[s.state] } : { ...other[s.state], ...own },
|
|
2130
|
+
...s.managed ? { version: s.version ?? void 0, ...s.personal ? { personal: true } : {}, ...inGit ? { inGit: true } : {}, ...pinMoves ? { pinMoves: true } : {}, ...managed[s.state] } : { ...other[s.state], ...own },
|
|
2126
2131
|
...s.source ? { origin: `npx skills: ${s.source}` } : {},
|
|
2127
2132
|
...others.length ? { others } : {},
|
|
2128
2133
|
git: state
|
|
@@ -2219,11 +2224,11 @@ function loadWorld() {
|
|
|
2219
2224
|
name: names.get(root),
|
|
2220
2225
|
path: root,
|
|
2221
2226
|
manifest: hasManifest ? git ? git.of("skilllib.json") : "no git" : "none",
|
|
2222
|
-
skills: status.map((s) => {
|
|
2223
|
-
const local = localSkill(root, s, git);
|
|
2227
|
+
skills: ((pins, canBePersonal) => status.map((s) => {
|
|
2228
|
+
const local = localSkill(root, s, git, pins, canBePersonal);
|
|
2224
2229
|
const own = s.state.includes("missing") ? "" : readSkillInfo(s.path).description;
|
|
2225
2230
|
return own && own !== descriptions[s.name] ? { ...local, description: own } : local;
|
|
2226
|
-
}),
|
|
2231
|
+
}))(pinsIn(root), personalPinsFile(root) !== null),
|
|
2227
2232
|
...loadedHere(root)
|
|
2228
2233
|
};
|
|
2229
2234
|
});
|
|
@@ -2376,8 +2381,14 @@ function realOps(roots, backups, machine, libraryNames) {
|
|
|
2376
2381
|
},
|
|
2377
2382
|
pin: (repo, name, personal) => {
|
|
2378
2383
|
const c = addSkill(rootOf(repo), name, { personal });
|
|
2379
|
-
if (c.action === "skipped") return failed(
|
|
2380
|
-
|
|
2384
|
+
if (c.action === "skipped") return failed(`${name}: ${withoutForceHint(c.reason ?? "skipped")}`);
|
|
2385
|
+
const moved = personal ? `${name} is now personal in ${repo}: only on your machines, kept out of git` : `${name} is now pinned in ${repo}'s skilllib.json for the team`;
|
|
2386
|
+
if (c.uncommitted?.length) {
|
|
2387
|
+
const copy = `${copyDir(repo, name)}/${name}`;
|
|
2388
|
+
return failed(`${moved}, but not linked in ${c.uncommitted.join(", ")}: git tracks ${c.uncommitted.length === 1 ? "it" : "them"} and ${copy} isn't committed, so agents reading ${c.uncommitted.length === 1 ? "it" : "them"} can't use ${name}. Commit ${copy}, then run skilllib link --allow-tracked`);
|
|
2389
|
+
}
|
|
2390
|
+
if (c.blocked?.length || c.inTheWay?.length) return withLinks(c, repo, name);
|
|
2391
|
+
return moved;
|
|
2381
2392
|
},
|
|
2382
2393
|
copyToLibrary: (repo, name) => ((s) => s ? toLibrary(s.path) : failed(`${name} isn't in ${repo} any more`))(find(repo, name)),
|
|
2383
2394
|
deleteGlobal: (m) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skilllib",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.2",
|
|
4
4
|
"description": "Manage your agent skills like dependencies. One library for all your skills; each repo gets only the ones it needs, and every agent can see them. Works with Claude Code, Cursor, Codex, Zed and Grok.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skills",
|
package/skills/skilllib/SKILL.md
CHANGED
|
@@ -25,7 +25,7 @@ Also: `usage --json` (Claude Code usage only, so "unused" is a hint; each skill
|
|
|
25
25
|
|
|
26
26
|
## Changing (ask first unless the user asked for this exact change)
|
|
27
27
|
|
|
28
|
-
`add <name>...` (pins in `skilllib.json` when the repo has one, else in personal pins; `--personal` / `--shared` choose, and move an existing pin without changing its version) · `remove <name>...` (skills skilllib manages, `source: library`) · `update [name...]` · `sync` (installs what `skilllib.json` pins; takes no names). Pass several names in one call. Their result is `[{name, action, from, to, dirs, reason, blocked, uncommitted, inTheWay, backedUp}]
|
|
28
|
+
`add <name>...` (pins in `skilllib.json` when the repo has one, else in personal pins; `--personal` / `--shared` choose, and move an existing pin without changing its version) · `remove <name>...` (skills skilllib manages, `source: library`) · `update [name...]` · `sync` (installs what's pinned, in `skilllib.json` and your personal pins; takes no names). Pass several names in one call. Their result is `[{name, action, from, to, dirs, personal, moved, reason, blocked, uncommitted, inTheWay, backedUp}]` (`personal: true` when the pin is in your personal pins; `moved` when `--personal`/`--shared` moved it), which is the whole result: there's no need to check files afterwards. `import <dir>...` (into the library) prints `[{path, name, status}]` and exits 1 if a folder was skipped (`exists`, `no SKILL.md`). `doctor --fix` prints doctor's issues, each fix with `fixed: {ok, message}`.
|
|
29
29
|
|
|
30
30
|
- `skipped` + `reason`: nothing changed for that skill, and the command exits 1; tell the user. `--force` overwrites local edits (`backedUp` says where they went; `skilllib restore <name>` brings them back), but only use it with their OK.
|
|
31
31
|
- `removed` + `reason`: removed from skilllib.json, but something was left as it is (its copy behind a folder that links out of the repo, a file of the user's, or a link in such a folder). Tell the user what the reason says is still there.
|