skilllib 1.12.1 → 1.13.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 +12 -7
- package/dist/{chunk-LORSEDNW.js → chunk-C6TSR7NT.js} +531 -88
- package/dist/index.js +18 -6
- package/dist/json.d.cts +665 -2
- package/dist/json.d.ts +665 -2
- package/dist/{tui-3QD6RQGL.js → tui-3MBVYSVM.js} +44 -17
- package/package.json +6 -2
- package/skills/skilllib/SKILL.md +4 -4
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
FEATURED_HARNESSES,
|
|
2
3
|
HARNESSES,
|
|
3
4
|
PROJECT_SKILLS_DIR,
|
|
4
5
|
addRefusal,
|
|
@@ -43,6 +44,8 @@ import {
|
|
|
43
44
|
moveGlobalSkills,
|
|
44
45
|
movedTogether,
|
|
45
46
|
notWritable,
|
|
47
|
+
personalPinsFile,
|
|
48
|
+
pinsIn,
|
|
46
49
|
pluginBackups,
|
|
47
50
|
projectHere,
|
|
48
51
|
projectOfFactory,
|
|
@@ -81,10 +84,11 @@ import {
|
|
|
81
84
|
updatePlugin,
|
|
82
85
|
updateProject,
|
|
83
86
|
usageIssues,
|
|
87
|
+
versionForHash,
|
|
84
88
|
versionHistory,
|
|
85
89
|
visibleProjects,
|
|
86
90
|
withoutForceHint
|
|
87
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-C6TSR7NT.js";
|
|
88
92
|
|
|
89
93
|
// src/tui/index.tsx
|
|
90
94
|
import { render } from "ink";
|
|
@@ -445,7 +449,7 @@ function globalFixes(m) {
|
|
|
445
449
|
];
|
|
446
450
|
}
|
|
447
451
|
function pinFix(w, repo, s) {
|
|
448
|
-
if (s.source !== "lib" || !w.library.some((l) => l.name === s.name)) return null;
|
|
452
|
+
if (s.source !== "lib" || !s.pinMoves || !w.library.some((l) => l.name === s.name)) return null;
|
|
449
453
|
if (s.personal)
|
|
450
454
|
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
455
|
if (s.inGit || isShared(s.git)) return null;
|
|
@@ -1506,8 +1510,11 @@ function App({ initial, reload, loadUsage: loadUsage2 }) {
|
|
|
1506
1510
|
}
|
|
1507
1511
|
useEffect(() => {
|
|
1508
1512
|
const folder = () => !initial.roots.length && askForFolder("Where are your projects? skilllib finds the git repos in there");
|
|
1509
|
-
if (!initial.agentsChosen)
|
|
1510
|
-
|
|
1513
|
+
if (!initial.agentsChosen) {
|
|
1514
|
+
const installed = HARNESSES.filter((h) => h.installed()).map((h) => h.id);
|
|
1515
|
+
const ids = [...FEATURED_HARNESSES, ...installed.filter((id) => !FEATURED_HARNESSES.includes(id))];
|
|
1516
|
+
setModal({ kind: "agents", selected: initial.agents, ids, cursor: 0, then: folder });
|
|
1517
|
+
} else folder();
|
|
1511
1518
|
}, []);
|
|
1512
1519
|
function openInGlobal(name) {
|
|
1513
1520
|
goPlace("global");
|
|
@@ -1527,14 +1534,24 @@ function App({ initial, reload, loadUsage: loadUsage2 }) {
|
|
|
1527
1534
|
cells: [{ text, width: 34, color: c }, { text: sub, grow: true, color: color.faint }],
|
|
1528
1535
|
onEnter
|
|
1529
1536
|
});
|
|
1537
|
+
const shown = [
|
|
1538
|
+
...FEATURED_HARNESSES.map((id) => harness(id)),
|
|
1539
|
+
...world.agents.filter((id) => !FEATURED_HARNESSES.includes(id)).map((id) => harness(id))
|
|
1540
|
+
];
|
|
1530
1541
|
return [
|
|
1531
1542
|
header("#agents", "Agents"),
|
|
1532
|
-
...
|
|
1543
|
+
...shown.map(
|
|
1533
1544
|
(h) => row(`agent:${h.id}`, `${world.agents.includes(h.id) ? "[x]" : "[ ]"} ${h.icon} ${h.name}`, h.projectDirs.join(", "), () => {
|
|
1534
1545
|
const wasOn = world.agents.includes(h.id);
|
|
1535
1546
|
apply((w) => (w.ops.setAgents(wasOn ? w.agents.filter((a) => a !== h.id) : HARNESSES.map((x) => x.id).filter((id) => id === h.id || w.agents.includes(id))), `${h.name} ${wasOn ? "off" : "on"}`));
|
|
1536
1547
|
})
|
|
1537
1548
|
),
|
|
1549
|
+
row(
|
|
1550
|
+
"agents:more",
|
|
1551
|
+
"More agents\u2026",
|
|
1552
|
+
"Copilot, Gemini, OpenCode, Windsurf, and the rest",
|
|
1553
|
+
() => setModal({ kind: "agents", selected: world.agents, ids: HARNESSES.map((h) => h.id), cursor: 0 })
|
|
1554
|
+
),
|
|
1538
1555
|
// The skill that lets your agents use skilllib: installing adds a global skill, so it asks first.
|
|
1539
1556
|
row(
|
|
1540
1557
|
"agent-skill",
|
|
@@ -1664,11 +1681,13 @@ function App({ initial, reload, loadUsage: loadUsage2 }) {
|
|
|
1664
1681
|
if (m.kind === "add") return addKeys(m, input, key);
|
|
1665
1682
|
}
|
|
1666
1683
|
function agentsKeys(m, input, key) {
|
|
1684
|
+
const list2 = m.ids.map((id) => harness(id));
|
|
1667
1685
|
if (key.escape && !m.then) return setModal(null);
|
|
1668
1686
|
if (key.upArrow) return setModal({ ...m, cursor: Math.max(0, m.cursor - 1) });
|
|
1669
|
-
if (key.downArrow) return setModal({ ...m, cursor: Math.min(
|
|
1687
|
+
if (key.downArrow) return setModal({ ...m, cursor: Math.min(list2.length - 1, m.cursor + 1) });
|
|
1670
1688
|
if (input === " ") {
|
|
1671
|
-
const id =
|
|
1689
|
+
const id = list2[m.cursor]?.id;
|
|
1690
|
+
if (!id) return;
|
|
1672
1691
|
return setModal({ ...m, selected: m.selected.includes(id) ? m.selected.filter((x) => x !== id) : [...m.selected, id] });
|
|
1673
1692
|
}
|
|
1674
1693
|
if (key.return) {
|
|
@@ -1902,11 +1921,11 @@ function App({ initial, reload, loadUsage: loadUsage2 }) {
|
|
|
1902
1921
|
empty: "",
|
|
1903
1922
|
header: /* @__PURE__ */ jsx2(Text2, { color: color.muted, children: "skilllib makes your skills visible to each one. Detected ones are ticked." }),
|
|
1904
1923
|
headerHeight: 1,
|
|
1905
|
-
rows:
|
|
1924
|
+
rows: modal.ids.map((id) => harness(id)).map((h) => ({
|
|
1906
1925
|
key: h.id,
|
|
1907
1926
|
cells: [
|
|
1908
1927
|
{ text: modal.selected.includes(h.id) ? "[x] " : "[ ] ", width: 4, color: modal.selected.includes(h.id) ? color.green : color.faint },
|
|
1909
|
-
{ text: `${h.icon} ${h.name}`, width:
|
|
1928
|
+
{ text: `${h.icon} ${h.name}`, width: 22, color: h.color },
|
|
1910
1929
|
{ text: `reads ${h.projectDirs.join(", ")}`, grow: true, color: color.faint }
|
|
1911
1930
|
]
|
|
1912
1931
|
}))
|
|
@@ -2095,7 +2114,7 @@ function uniqueNames(roots) {
|
|
|
2095
2114
|
})
|
|
2096
2115
|
);
|
|
2097
2116
|
}
|
|
2098
|
-
function localSkill(root, s, git) {
|
|
2117
|
+
function localSkill(root, s, git, pins, canBePersonal) {
|
|
2099
2118
|
const managed = {
|
|
2100
2119
|
"edited locally": { edited: true },
|
|
2101
2120
|
"edited locally, update available": { edited: true },
|
|
@@ -2114,7 +2133,9 @@ function localSkill(root, s, git) {
|
|
|
2114
2133
|
const state = git ? git.of(relativeTo(root, s.path)) : null;
|
|
2115
2134
|
const others = otherCopies(root, s.name, s.location);
|
|
2116
2135
|
const own = other[s.state]?.source === "untracked" && isShared(state) ? { source: "repo" } : {};
|
|
2117
|
-
const
|
|
2136
|
+
const dep = pins[s.name];
|
|
2137
|
+
const inGit = s.managed && (isShared(state) || (dep?.links ?? []).some((d) => isShared(git?.of(`${d}/${s.name}`))));
|
|
2138
|
+
const pinMoves = s.managed && !!dep && versionForHash(s.name, dep.hash) !== null && !s.state.startsWith("edited") && (s.personal || canBePersonal);
|
|
2118
2139
|
return {
|
|
2119
2140
|
name: s.name,
|
|
2120
2141
|
source: "lib",
|
|
@@ -2122,7 +2143,7 @@ function localSkill(root, s, git) {
|
|
|
2122
2143
|
path: s.path,
|
|
2123
2144
|
// Agents that actually load it here: visibility lists every agent you use, with `paths: 0` for the blind ones.
|
|
2124
2145
|
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 },
|
|
2146
|
+
...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
2147
|
...s.source ? { origin: `npx skills: ${s.source}` } : {},
|
|
2127
2148
|
...others.length ? { others } : {},
|
|
2128
2149
|
git: state
|
|
@@ -2219,11 +2240,11 @@ function loadWorld() {
|
|
|
2219
2240
|
name: names.get(root),
|
|
2220
2241
|
path: root,
|
|
2221
2242
|
manifest: hasManifest ? git ? git.of("skilllib.json") : "no git" : "none",
|
|
2222
|
-
skills: status.map((s) => {
|
|
2223
|
-
const local = localSkill(root, s, git);
|
|
2243
|
+
skills: ((pins, canBePersonal) => status.map((s) => {
|
|
2244
|
+
const local = localSkill(root, s, git, pins, canBePersonal);
|
|
2224
2245
|
const own = s.state.includes("missing") ? "" : readSkillInfo(s.path).description;
|
|
2225
2246
|
return own && own !== descriptions[s.name] ? { ...local, description: own } : local;
|
|
2226
|
-
}),
|
|
2247
|
+
}))(pinsIn(root), personalPinsFile(root) !== null),
|
|
2227
2248
|
...loadedHere(root)
|
|
2228
2249
|
};
|
|
2229
2250
|
});
|
|
@@ -2376,8 +2397,14 @@ function realOps(roots, backups, machine, libraryNames) {
|
|
|
2376
2397
|
},
|
|
2377
2398
|
pin: (repo, name, personal) => {
|
|
2378
2399
|
const c = addSkill(rootOf(repo), name, { personal });
|
|
2379
|
-
if (c.action === "skipped") return failed(
|
|
2380
|
-
|
|
2400
|
+
if (c.action === "skipped") return failed(`${name}: ${withoutForceHint(c.reason ?? "skipped")}`);
|
|
2401
|
+
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`;
|
|
2402
|
+
if (c.uncommitted?.length) {
|
|
2403
|
+
const copy = `${copyDir(repo, name)}/${name}`;
|
|
2404
|
+
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`);
|
|
2405
|
+
}
|
|
2406
|
+
if (c.blocked?.length || c.inTheWay?.length) return withLinks(c, repo, name);
|
|
2407
|
+
return moved;
|
|
2381
2408
|
},
|
|
2382
2409
|
copyToLibrary: (repo, name) => ((s) => s ? toLibrary(s.path) : failed(`${name} isn't in ${repo} any more`))(find(repo, name)),
|
|
2383
2410
|
deleteGlobal: (m) => {
|
package/package.json
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skilllib",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Manage your agent skills like dependencies. One library
|
|
3
|
+
"version": "1.13.0",
|
|
4
|
+
"description": "Manage your agent skills like dependencies. One library, install from skills.sh, and every agent you use can see them.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skills",
|
|
7
7
|
"skills",
|
|
8
|
+
"skills.sh",
|
|
8
9
|
"skill-md",
|
|
9
10
|
"claude-code",
|
|
10
11
|
"claude",
|
|
11
12
|
"cursor",
|
|
12
13
|
"codex",
|
|
14
|
+
"gemini",
|
|
15
|
+
"github-copilot",
|
|
16
|
+
"opencode",
|
|
13
17
|
"zed",
|
|
14
18
|
"grok",
|
|
15
19
|
"ai-agents",
|
package/skills/skilllib/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: skilllib
|
|
3
|
-
description: Agent skills (SKILL.md) loaded in this repo and the user's skill library, via the skilllib CLI. Use for which skills you can use here and where each comes from, which library skills to add, what's out of date, broken or duplicated,
|
|
3
|
+
description: Agent skills (SKILL.md) loaded in this repo and the user's skill library, via the skilllib CLI. Use for which skills you can use here and where each comes from, which library skills to add, what's out of date, broken or duplicated, adding, removing or updating skills, and installing skills from skills.sh or GitHub.
|
|
4
4
|
allowed-tools: Bash(skilllib:*)
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ allowed-tools: Bash(skilllib:*)
|
|
|
8
8
|
|
|
9
9
|
The user manages agent skills with the `skilllib` CLI. Their **library** (`~/.skilllib/library`, "my skills", "the registry") holds versioned skills that load nowhere until added to a repo. A repo pins its skills in `skilllib.json` (shared, committed), or in the user's personal pins (`~/.skilllib/pins`, kept out of git: `personal: true` in the output).
|
|
10
10
|
|
|
11
|
-
Always pass `--json` (one line of JSON on stdout; messages go to stderr). A failure exits 1 with `{"error": "..."}` as that line. `link`, `tidy`, `global` (its listing, `keep` and `unkeep`), `restore`, `new`, `folders`, `harnesses`, `scan` and `unhide` print messages only (read the exit code). Unknown flags are errors. Never run bare `skilllib` or `skilllib ui`: that's an interactive app.
|
|
11
|
+
Always pass `--json` (one line of JSON on stdout; messages go to stderr). A failure exits 1 with `{"error": "..."}` as that line. `link`, `tidy`, `global` (its listing, `keep` and `unkeep`), `restore`, `new`, `folders`, `harnesses`, `scan`, `search` (without `--json`) and `unhide` print messages only (read the exit code). Unknown flags are errors. Never run bare `skilllib` or `skilllib ui`: that's an interactive app.
|
|
12
12
|
|
|
13
13
|
## Reading
|
|
14
14
|
|
|
@@ -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.
|
|
@@ -42,7 +42,7 @@ Also: `usage --json` (Claude Code usage only, so "unused" is a hint; each skill
|
|
|
42
42
|
|
|
43
43
|
**Fixes and decisions.** `apply <id> [choice]` runs one problem's `fix`, or the choice you name: its label (safest), or its number from 1. With several choices it needs one. Prints `{id, applied, ok, message}`. Ids come from `issues` in `status --json` (Now, below) or from `doctor --json` (every repo; pass `apply` the same `--days` you gave `doctor`). A few issues span repos and offer different choices in each; then `apply` takes only the label.
|
|
44
44
|
|
|
45
|
-
**Importing.** `import <dir>...` copies skill folders (each with a `SKILL.md`) into the library;
|
|
45
|
+
**Importing and installing.** `import <dir>...` copies skill folders (each with a `SKILL.md`) into the library; it doesn't download anything. `install <source>...` does: `owner/repo`, `owner/repo@skill`, a `https://skills.sh/...` or GitHub URL, or a local folder. It puts each skill in the library. In a repo it also adds them here; `--global` loads them in every repo instead (and marks them kept). A repo with several skills is listed, not installed, until you pass `@name` or `--all`. `search <query>` lists matches from skills.sh. `install` and `search` accept `--json`.
|
|
46
46
|
|
|
47
47
|
## Now
|
|
48
48
|
|