skilllib 1.12.0 → 1.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -195,7 +195,7 @@ A skill's pin goes in one of two places:
|
|
|
195
195
|
- **`skilllib.json` in the repo (shared).** Commit it with the skill folders, and teammates get the same skills. This is where pins go when the repo already has a `skilllib.json`.
|
|
196
196
|
- **Your personal pins (`~/.skilllib/pins/`).** One file per repo, matched by its `origin` remote, so every clone and worktree of the repo shares it. skilllib keeps the copies and links out of git through the repo's `.git/info/exclude` (only your clone; the repo's `.gitignore` doesn't change). Nothing shows up in `git status`. This is where pins go when the repo has no `skilllib.json`.
|
|
197
197
|
|
|
198
|
-
`skilllib add <name> --personal` or `--shared` picks the place, and moves an existing pin without changing its version. In the app, a library skill's actions offer *Make it personal* or *Pin it for the team*, and its tag says `
|
|
198
|
+
`skilllib add <name> --personal` or `--shared` picks the place, and moves an existing pin without changing its version. In the app, a library skill's actions offer *Make it personal* or *Pin it for the team*, and its tag says `personal v2`. A copy git already has can't become personal. A skill pinned in both places uses the repo's pin; doctor offers to drop yours (a choice: yours would keep the skill if the team unpinned it).
|
|
199
199
|
|
|
200
200
|
**On another machine:** keep `~/.skilllib/pins/` and `~/.skilllib/library/` in git (a dotfiles repo, say). Clone a repo there and run `skilllib sync`: your personal skills come back, out of git again. A repo without a remote keeps its pins by folder, so they stay on that machine until it gets one: adding a remote (or renaming it) keeps them. Only a repo's top folder can have personal pins, not a monorepo package's.
|
|
201
201
|
|
|
@@ -235,7 +235,7 @@ function installDirs(enabled, keep) {
|
|
|
235
235
|
import { execFileSync } from "child_process";
|
|
236
236
|
import { createHash as createHash2 } from "crypto";
|
|
237
237
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
238
|
-
import { basename as basename2, dirname as dirname2, join as join5, resolve as resolve3 } from "path";
|
|
238
|
+
import { basename as basename2, dirname as dirname2, isAbsolute, join as join5, relative as relative2, resolve as resolve3, sep as sep3 } from "path";
|
|
239
239
|
|
|
240
240
|
// src/versions.ts
|
|
241
241
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
@@ -425,7 +425,7 @@ function writeManifest(root, manifest) {
|
|
|
425
425
|
for (const [name, dep] of Object.entries(readPins(file).skills)) if (onDisk[name] && manifest.skills[name] && !manifest.skills[name].personal) mine[name] = dep;
|
|
426
426
|
if (Object.keys(mine).length) writeChanged(file, { ...pinsHeader(repoOf(root)), skills: sorted(mine) });
|
|
427
427
|
else rmSync3(file, { force: true });
|
|
428
|
-
excludePersonal(root, mine);
|
|
428
|
+
excludePersonal(root, Object.fromEntries(Object.entries(mine).filter(([name]) => !shared[name])));
|
|
429
429
|
}
|
|
430
430
|
function writeChanged(path, data) {
|
|
431
431
|
try {
|
|
@@ -530,7 +530,15 @@ function excludePersonal(root, skills = Object.fromEntries(Object.entries(readMa
|
|
|
530
530
|
const end = start === -1 ? -1 : lines.indexOf(EXCLUDE_END, start);
|
|
531
531
|
const rest = start === -1 ? lines : [...lines.slice(0, start), ...lines.slice(end === -1 ? start + 1 : end + 1)];
|
|
532
532
|
while (rest.length && rest[rest.length - 1] === "") rest.pop();
|
|
533
|
-
const
|
|
533
|
+
const top = realNative(root) ?? root;
|
|
534
|
+
const places = (dir) => {
|
|
535
|
+
const real = realNative(join5(root, dir));
|
|
536
|
+
const rel = real ? relative2(top, real).split(sep3).join("/") : "";
|
|
537
|
+
return rel && rel !== ".." && !rel.startsWith("../") && !isAbsolute(rel) && rel !== dir ? [dir, rel] : [dir];
|
|
538
|
+
};
|
|
539
|
+
const paths = Object.entries(skills).flatMap(
|
|
540
|
+
([name, d]) => [d.dir ?? PROJECT_SKILLS_DIR, ...d.links ?? []].flatMap(places).map((dir) => `/${dir}/${name.replace(/[[\]*?\\]/g, "\\$&")}`)
|
|
541
|
+
);
|
|
534
542
|
const block = paths.length ? [EXCLUDE_START, ...[...new Set(paths)].sort(), EXCLUDE_END] : [];
|
|
535
543
|
const next = [...rest, ...rest.length && block.length ? [""] : [], ...block].join("\n") + (rest.length || block.length ? "\n" : "");
|
|
536
544
|
if (next === text) return;
|
|
@@ -688,11 +696,11 @@ import { join as join9 } from "path";
|
|
|
688
696
|
// src/library.ts
|
|
689
697
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
690
698
|
import { cpSync as cpSync2, existsSync as existsSync7, lstatSync as lstatSync2, mkdirSync as mkdirSync5, readdirSync as readdirSync5, readFileSync as readFileSync4, readlinkSync as readlinkSync2, renameSync as renameSync3, rmSync as rmSync4, symlinkSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
691
|
-
import { basename as basename4, dirname as dirname3, isAbsolute, join as join8, posix, relative as
|
|
699
|
+
import { basename as basename4, dirname as dirname3, isAbsolute as isAbsolute2, join as join8, posix, relative as relative3, resolve as resolve6, sep as sep5 } from "path";
|
|
692
700
|
|
|
693
701
|
// src/sources.ts
|
|
694
702
|
import { existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readlinkSync } from "fs";
|
|
695
|
-
import { basename as basename3, join as join7, resolve as resolve5, sep as
|
|
703
|
+
import { basename as basename3, join as join7, resolve as resolve5, sep as sep4 } from "path";
|
|
696
704
|
import { tmpdir } from "os";
|
|
697
705
|
function skillsShLock() {
|
|
698
706
|
return readJson(join7(userHome(), ".agents", ".skill-lock.json"))?.skills ?? {};
|
|
@@ -884,7 +892,7 @@ function recordOrigin(name, origin) {
|
|
|
884
892
|
writeState(originsFile(), { ...origins, [name]: origin });
|
|
885
893
|
}
|
|
886
894
|
function originFor(dir) {
|
|
887
|
-
const under = (path, folder) => path.startsWith((realpathOrNull(folder) ?? folder) +
|
|
895
|
+
const under = (path, folder) => path.startsWith((realpathOrNull(folder) ?? folder) + sep4);
|
|
888
896
|
const real = realpathOrNull(dir) ?? dir;
|
|
889
897
|
const temps = [tmpdir(), "/tmp", "/var/tmp"];
|
|
890
898
|
const homeInTemp = temps.some((t) => under(realpathOrNull(userHome()) ?? userHome(), t));
|
|
@@ -922,7 +930,7 @@ function notWritable(root, dir) {
|
|
|
922
930
|
p = join8(p, part);
|
|
923
931
|
if (!entryExists(p)) return null;
|
|
924
932
|
const real = realpathOrNull(p);
|
|
925
|
-
if (!isDir(p) || real === null || real !== top && !real.startsWith(top +
|
|
933
|
+
if (!isDir(p) || real === null || real !== top && !real.startsWith(top + sep5)) return relative3(root, p).split(sep5).join("/");
|
|
926
934
|
}
|
|
927
935
|
return null;
|
|
928
936
|
}
|
|
@@ -973,7 +981,7 @@ function syncBlocker(root, name, dep = readManifest(root).skills[name]) {
|
|
|
973
981
|
}
|
|
974
982
|
function linkDir(target, link2) {
|
|
975
983
|
if (process.platform === "win32") symlinkSync(target, link2, "junction");
|
|
976
|
-
else symlinkSync(
|
|
984
|
+
else symlinkSync(relative3(dirname3(link2), target), link2);
|
|
977
985
|
}
|
|
978
986
|
function entryExists(path) {
|
|
979
987
|
return isLink(path) || existsSync7(path);
|
|
@@ -1005,7 +1013,7 @@ function lostAt(path, gone) {
|
|
|
1005
1013
|
let last = null;
|
|
1006
1014
|
for (let hop = 0; hop < 40; hop++) {
|
|
1007
1015
|
const here = canon(at);
|
|
1008
|
-
if (moved.some((m) => key(here) === m || key(here).startsWith(m +
|
|
1016
|
+
if (moved.some((m) => key(here) === m || key(here).startsWith(m + sep5))) return last ?? here;
|
|
1009
1017
|
if (!isLink(here)) return existsSync7(join8(here, "SKILL.md")) ? null : here;
|
|
1010
1018
|
last = here;
|
|
1011
1019
|
at = resolve6(dirname3(here), readlinkSync2(here).replace(/^\\\\\?\\(UNC\\)?/, (_m, unc) => unc ? "\\\\" : ""));
|
|
@@ -1019,7 +1027,7 @@ function blindWithoutGlobal(name, copies, roots, nestedOf = (root) => nestedEntr
|
|
|
1019
1027
|
const holds = (root) => ALL_PROJECT_DIRS.some((d) => reposChoice(root, d) && entryExists(join8(root, d, name))) || pinsSkill(root, name);
|
|
1020
1028
|
const keepsTop = (root, id) => harness(id).projectDirs.some((d) => existsSync7(join8(root, d, name, "SKILL.md")) && survives(join8(root, d, name), gone));
|
|
1021
1029
|
const shown = (root, p) => {
|
|
1022
|
-
const under = (dir) => ((d) => p.startsWith(d +
|
|
1030
|
+
const under = (dir) => ((d) => p.startsWith(d + sep5) ? relative3(d, p).split(sep5).join("/") : null)(realNative(dir) ?? dir);
|
|
1023
1031
|
return under(root) ?? ((h) => h === null ? p : `~/${h}`)(under(userHome()));
|
|
1024
1032
|
};
|
|
1025
1033
|
const blindIn = (root) => enabled.flatMap((id) => {
|
|
@@ -1047,16 +1055,16 @@ function blindWithoutGlobal(name, copies, roots, nestedOf = (root) => nestedEntr
|
|
|
1047
1055
|
const had = byWho.get(k);
|
|
1048
1056
|
if (!had || !(had.link || had.folder || had.shared) && (b.link || b.folder || b.shared)) byWho.set(k, b);
|
|
1049
1057
|
}
|
|
1050
|
-
const within = (d) => unique.some((r) => d === r || d.startsWith(r +
|
|
1058
|
+
const within = (d) => unique.some((r) => d === r || d.startsWith(r + sep5));
|
|
1051
1059
|
const home2 = realNative(userHome()) ?? userHome();
|
|
1052
|
-
const belowHome = (d) => (realNative(d) ?? d) !== home2 && !home2.startsWith((realNative(d) ?? d) +
|
|
1060
|
+
const belowHome = (d) => (realNative(d) ?? d) !== home2 && !home2.startsWith((realNative(d) ?? d) + sep5);
|
|
1053
1061
|
const gitTop = (from) => {
|
|
1054
1062
|
for (let d = from; d !== dirname3(d) && belowHome(d); d = dirname3(d)) if (existsSync7(join8(d, ".git"))) return d;
|
|
1055
1063
|
return null;
|
|
1056
1064
|
};
|
|
1057
1065
|
const servedAbove = (b) => {
|
|
1058
1066
|
const top = gitTop(b.root);
|
|
1059
|
-
const inside = (d) => belowHome(d) && (top ? d === top || d.startsWith(top +
|
|
1067
|
+
const inside = (d) => belowHome(d) && (top ? d === top || d.startsWith(top + sep5) : within(d));
|
|
1060
1068
|
for (let d = dirname3(b.root); inside(d) && d !== dirname3(d); d = dirname3(d)) if (keepsTop(d, b.id)) return true;
|
|
1061
1069
|
return false;
|
|
1062
1070
|
};
|
|
@@ -1073,7 +1081,7 @@ function strandedLinks(name, copies, roots, label = basename4) {
|
|
|
1073
1081
|
).map(
|
|
1074
1082
|
(d) => `${d}/${name} in ${label(root)}`
|
|
1075
1083
|
),
|
|
1076
|
-
...nestedEntries(root).filter((n) => n.name === name && isLink(n.path) && !survives(n.path, gone) && once(n.path)).map((n) => `${
|
|
1084
|
+
...nestedEntries(root).filter((n) => n.name === name && isLink(n.path) && !survives(n.path, gone) && once(n.path)).map((n) => `${relative3(root, n.path).split(sep5).join("/")} in ${label(root)}`)
|
|
1077
1085
|
]);
|
|
1078
1086
|
}
|
|
1079
1087
|
function copiesDiffer(paths) {
|
|
@@ -1082,7 +1090,7 @@ function copiesDiffer(paths) {
|
|
|
1082
1090
|
function blindSaid(name, blind, label = basename4) {
|
|
1083
1091
|
const who = blind.map((b) => `${harness(b.id).name} in ${label(b.root)}`).join(", ");
|
|
1084
1092
|
const folders2 = [...new Set(blind.flatMap((b) => b.folder ? [`${b.folder} in ${label(b.root)}`] : []))];
|
|
1085
|
-
const links = [...new Set(blind.flatMap((b) => b.link ? [b.link.startsWith("~") ||
|
|
1093
|
+
const links = [...new Set(blind.flatMap((b) => b.link ? [b.link.startsWith("~") || isAbsolute2(b.link) ? b.link : `${b.link} in ${label(b.root)}`] : []))];
|
|
1086
1094
|
const sharedBy = /* @__PURE__ */ new Map();
|
|
1087
1095
|
for (const b of blind.filter((x) => x.shared)) {
|
|
1088
1096
|
if (!sharedBy.has(b.shared.to)) sharedBy.set(b.shared.to, { to: b.shared.to, where: /* @__PURE__ */ new Set(), dirs: /* @__PURE__ */ new Set() });
|
|
@@ -1178,7 +1186,7 @@ function projectStatus(root) {
|
|
|
1178
1186
|
}
|
|
1179
1187
|
for (const [name, paths] of byName) {
|
|
1180
1188
|
const real = paths.find((p) => !isLink(p)) ?? paths[0];
|
|
1181
|
-
const location =
|
|
1189
|
+
const location = relative3(root, join8(real, "..")).split(sep5).join("/");
|
|
1182
1190
|
const source = lock[name]?.source;
|
|
1183
1191
|
unmanaged.push({
|
|
1184
1192
|
name,
|
|
@@ -1226,7 +1234,7 @@ function nestedEntries(root, { depth = PACKAGE_DEPTH, enabled = enabledHarnesses
|
|
|
1226
1234
|
const top = realpathOrNull(root) ?? root;
|
|
1227
1235
|
const found = packageSkillFolders(root, subs ?? packageDirs(root, depth)).flatMap(
|
|
1228
1236
|
({ sub, skillsDir, location }) => skillDirsIn(join8(sub, skillsDir)).flatMap((path) => {
|
|
1229
|
-
if (isLink(path) && (realpathOrNull(path) ?? "").startsWith(top +
|
|
1237
|
+
if (isLink(path) && (realpathOrNull(path) ?? "").startsWith(top + sep5)) return [];
|
|
1230
1238
|
const name = basename4(path);
|
|
1231
1239
|
const readers = NESTED_READERS.filter(([d]) => realpathOrNull(join8(sub, d, name)) === realpathOrNull(path)).flatMap(([, r]) => r);
|
|
1232
1240
|
return [{ name, path, location, skillsDir, readers: enabled.filter((id) => readers.includes(id)) }];
|
|
@@ -1257,7 +1265,7 @@ function packageSkillFolders(root, subs = packageDirs(root)) {
|
|
|
1257
1265
|
return subs.flatMap(
|
|
1258
1266
|
(sub) => NESTED_READERS.flatMap(([skillsDir]) => {
|
|
1259
1267
|
if (!existsSync7(join8(sub, skillsDir))) return [];
|
|
1260
|
-
const location =
|
|
1268
|
+
const location = relative3(root, join8(sub, skillsDir)).split(sep5).join("/");
|
|
1261
1269
|
return reposChoice(root, location) ? [{ sub, skillsDir, location }] : [];
|
|
1262
1270
|
})
|
|
1263
1271
|
);
|
|
@@ -1636,7 +1644,7 @@ function stash(dir, bucket) {
|
|
|
1636
1644
|
return target;
|
|
1637
1645
|
}
|
|
1638
1646
|
function removeUntracked(root, name, path) {
|
|
1639
|
-
const stuck = notWritable(root,
|
|
1647
|
+
const stuck = notWritable(root, relative3(root, dirname3(path)));
|
|
1640
1648
|
if (stuck) throw new Error(`${stuck} isn't a folder in the repo: skilllib leaves what's in it alone`);
|
|
1641
1649
|
const to = stash(path, "trash");
|
|
1642
1650
|
unlinkEverywhere(root, name);
|
|
@@ -1801,12 +1809,12 @@ function refreshAgentSkill() {
|
|
|
1801
1809
|
// src/commands.ts
|
|
1802
1810
|
import { existsSync as existsSync14, readFileSync as readFileSync7 } from "fs";
|
|
1803
1811
|
import { parseArgs as parseFlags } from "util";
|
|
1804
|
-
import { basename as basename10, join as join15, resolve as resolve8, sep as
|
|
1812
|
+
import { basename as basename10, join as join15, resolve as resolve8, sep as sep9 } from "path";
|
|
1805
1813
|
|
|
1806
1814
|
// src/git.ts
|
|
1807
1815
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1808
1816
|
import { existsSync as existsSync9, realpathSync as realpathSync3 } from "fs";
|
|
1809
|
-
import { isAbsolute as
|
|
1817
|
+
import { isAbsolute as isAbsolute3, join as join10, relative as relative4, sep as sep6 } from "path";
|
|
1810
1818
|
function isShared(state) {
|
|
1811
1819
|
return state === "committed" || state === "changed";
|
|
1812
1820
|
}
|
|
@@ -1835,7 +1843,7 @@ function gitInfo(root, extraDirs = []) {
|
|
|
1835
1843
|
};
|
|
1836
1844
|
const realRoot = real(root);
|
|
1837
1845
|
const places = new Map(
|
|
1838
|
-
[.../* @__PURE__ */ new Set([...ALL_PROJECT_DIRS, MANIFEST_FILE, ...extraDirs])].filter((d) => existsSync9(join10(root, d))).map((d) => [d,
|
|
1846
|
+
[.../* @__PURE__ */ new Set([...ALL_PROJECT_DIRS, MANIFEST_FILE, ...extraDirs])].filter((d) => existsSync9(join10(root, d))).map((d) => [d, relative4(realRoot, real(join10(root, d))).split(sep6).join("/")]).filter(([, place]) => place !== "" && place !== ".." && !place.startsWith("../") && !isAbsolute3(place))
|
|
1839
1847
|
);
|
|
1840
1848
|
const dirs = [...new Set(places.values())];
|
|
1841
1849
|
if (dirs.length === 0) return { of: () => "new" };
|
|
@@ -1863,7 +1871,7 @@ function gitInfo(root, extraDirs = []) {
|
|
|
1863
1871
|
};
|
|
1864
1872
|
}
|
|
1865
1873
|
function relativeTo(root, path) {
|
|
1866
|
-
return
|
|
1874
|
+
return relative4(root, path).split("\\").join("/");
|
|
1867
1875
|
}
|
|
1868
1876
|
|
|
1869
1877
|
// src/here.ts
|
|
@@ -1872,7 +1880,7 @@ import { basename as basename8 } from "path";
|
|
|
1872
1880
|
|
|
1873
1881
|
// src/health.ts
|
|
1874
1882
|
import { existsSync as existsSync12, lstatSync as lstatSync4, readdirSync as readdirSync8 } from "fs";
|
|
1875
|
-
import { basename as basename7, dirname as dirname6, isAbsolute as
|
|
1883
|
+
import { basename as basename7, dirname as dirname6, isAbsolute as isAbsolute4, join as join13, relative as relative5, sep as sep8 } from "path";
|
|
1876
1884
|
|
|
1877
1885
|
// src/plugins.ts
|
|
1878
1886
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
@@ -2175,7 +2183,7 @@ function cursorPluginSkills(plugins = cursorPlugins()) {
|
|
|
2175
2183
|
|
|
2176
2184
|
// src/tidy.ts
|
|
2177
2185
|
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readdirSync as readdirSync7, unlinkSync as unlinkSync3 } from "fs";
|
|
2178
|
-
import { basename as basename6, dirname as dirname5, join as join12, sep as
|
|
2186
|
+
import { basename as basename6, dirname as dirname5, join as join12, sep as sep7 } from "path";
|
|
2179
2187
|
function entriesOf(dirs, name) {
|
|
2180
2188
|
return dirs.flatMap((dir) => {
|
|
2181
2189
|
const path = join12(dir, name);
|
|
@@ -2233,7 +2241,7 @@ function planProjectTidy(root, { keep = {}, enabled = enabledHarnesses() } = {})
|
|
|
2233
2241
|
const realRoot = realpathOrNull(root) ?? root;
|
|
2234
2242
|
for (const name of namesIn(dirs)) {
|
|
2235
2243
|
const entries = entriesOf(dirs, name);
|
|
2236
|
-
const external = entries.find((e) => e.link && e.real && !(e.real +
|
|
2244
|
+
const external = entries.find((e) => e.link && e.real && !(e.real + sep7).startsWith(realRoot + sep7));
|
|
2237
2245
|
if (external) {
|
|
2238
2246
|
const differs = entries.some((e) => e !== external && e.real && existsSync11(join12(e.real, "SKILL.md")) && treeHash(e.real) !== treeHash(external.real));
|
|
2239
2247
|
report.skipped.push({ name, reason: `${rel(external.path)} links outside the repo`, ...differs ? { differs } : {} });
|
|
@@ -2465,7 +2473,7 @@ function brokenLinksIn(root, opened) {
|
|
|
2465
2473
|
const realRoot = realNative(root) ?? root;
|
|
2466
2474
|
const ownerOf = (real) => {
|
|
2467
2475
|
let nearest = null;
|
|
2468
|
-
for (let d = dirname6(dirname6(real)); d.startsWith(realRoot +
|
|
2476
|
+
for (let d = dirname6(dirname6(real)); d.startsWith(realRoot + sep8); d = dirname6(d)) {
|
|
2469
2477
|
if (!existsSync12(join13(d, MANIFEST_FILE))) continue;
|
|
2470
2478
|
if (opened.has(d)) return { opened: true, own: d };
|
|
2471
2479
|
nearest ??= d;
|
|
@@ -2497,13 +2505,13 @@ function brokenLinksIn(root, opened) {
|
|
|
2497
2505
|
return `Removed the broken link ${dir}/${name} in ${where} (restorable with skilllib restore)`;
|
|
2498
2506
|
}
|
|
2499
2507
|
};
|
|
2500
|
-
const placeOf = (p) =>
|
|
2508
|
+
const placeOf = (p) => relative5(realRoot, p).split(sep8).join("/");
|
|
2501
2509
|
git2 ??= gitInfo(
|
|
2502
2510
|
root,
|
|
2503
|
-
folders2.map((f) => placeOf(f.real)).filter((p) => p !== "" && p !== ".." && !p.startsWith("../") && !
|
|
2511
|
+
folders2.map((f) => placeOf(f.real)).filter((p) => p !== "" && p !== ".." && !p.startsWith("../") && !isAbsolute4(p))
|
|
2504
2512
|
);
|
|
2505
2513
|
const tracked = isShared(git2?.of(placeOf(join13(real, name))));
|
|
2506
|
-
const recorded = !!own && mends(own,
|
|
2514
|
+
const recorded = !!own && mends(own, relative5(own, real).split(sep8).join("/"), name);
|
|
2507
2515
|
const pinnedThere = own ? ` ${placeOf(own)} has its own skilllib.json${recorded ? `, which records this link: skilllib sync in ${placeOf(own)} brings back what it points to` : ""}.` : "";
|
|
2508
2516
|
return [
|
|
2509
2517
|
{
|
|
@@ -2911,7 +2919,7 @@ function repoIssues(root, ctx) {
|
|
|
2911
2919
|
ctx.issues.push({
|
|
2912
2920
|
id: `stuck:${root}:${name}`,
|
|
2913
2921
|
severity: "problem",
|
|
2914
|
-
title: `${name} in ${where}: sync can't put it back as skilllib.json pins it`,
|
|
2922
|
+
title: `${name} in ${where}: sync can't put it back as ${pins[name]?.personal ? "your personal pins have" : "skilllib.json pins"} it`,
|
|
2915
2923
|
// What to do depends on what's there: a folder that links out of the repo is a setup of yours, and a pin
|
|
2916
2924
|
// your library has no version of can't come back by sync anyway.
|
|
2917
2925
|
detail: `${blocker.said}, so sync can't put it back there.${blocker.outside ? " skilllib only installs inside the repo: point that folder back into the repo, or remove the skill here (skilllib remove)." : versionForHash(name, pins[name]?.hash ?? "") !== null ? " Move it out of the way, then run skilllib sync." : " Move it out of the way; your library has no copy of the pinned version either."}`,
|
|
@@ -2925,8 +2933,8 @@ function repoIssues(root, ctx) {
|
|
|
2925
2933
|
ctx.issues.push({
|
|
2926
2934
|
id: `pin:${root}:${s.name}`,
|
|
2927
2935
|
severity: "problem",
|
|
2928
|
-
title: `${s.name}: the version ${where}'s skilllib.json
|
|
2929
|
-
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}; ${where}'s skilllib.json then
|
|
2936
|
+
title: `${s.name}: the version ${s.personal ? "your personal pins" : `${where}'s skilllib.json`} pin${s.personal ? "" : "s"} isn't in your library`,
|
|
2937
|
+
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.`}`,
|
|
2930
2938
|
at: rows(root, [s.name]),
|
|
2931
2939
|
// It changes what the shared skilllib.json pins: a choice, never run by `doctor --fix`.
|
|
2932
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}` }] }
|
|
@@ -2967,7 +2975,7 @@ function repoIssues(root, ctx) {
|
|
|
2967
2975
|
ctx.issues.push({
|
|
2968
2976
|
id: `unavailable:${root}:${s.name}`,
|
|
2969
2977
|
severity: "problem",
|
|
2970
|
-
title: `${s.name}: pinned in ${where}'s skilllib.json, but not in your library`,
|
|
2978
|
+
title: `${s.name}: pinned in ${s.personal ? "your personal pins" : `${where}'s skilllib.json`}, but not in your library`,
|
|
2971
2979
|
detail: `Its folder is missing, and your library (${tildify(libraryDir())}) has no copy to restore: import it, or set SKILLLIB_HOME to the skilllib folder whose library has it.`,
|
|
2972
2980
|
at: rows(root, [s.name]),
|
|
2973
2981
|
// Editing skilllib.json is the team's call: a choice, never run by `doctor --fix`.
|
|
@@ -3134,7 +3142,7 @@ function repoIssues(root, ctx) {
|
|
|
3134
3142
|
id: `local:${root}:${s.name}`,
|
|
3135
3143
|
severity: "suggestion",
|
|
3136
3144
|
title: `${s.name} in ${where} isn't in your library`,
|
|
3137
|
-
detail: "Import it into the library to reuse it in other projects and keep it backed up. The repo then tracks it in skilllib.json.",
|
|
3145
|
+
detail: "Import it into the library to reuse it in other projects and keep it backed up. The repo then tracks it (in skilllib.json, or your personal pins when it has none).",
|
|
3138
3146
|
at: rows(root, [s.name]),
|
|
3139
3147
|
choices: [
|
|
3140
3148
|
{
|
|
@@ -3197,7 +3205,7 @@ function repoIssues(root, ctx) {
|
|
|
3197
3205
|
id: `npx:${root}:${s.name}`,
|
|
3198
3206
|
severity: "suggestion",
|
|
3199
3207
|
title: `${s.name} in ${where} came from npx skills, not your library`,
|
|
3200
|
-
detail: `Installed with \`npx skills add\`${s.source ? ` (${s.source})` : ""}. Import it into your library to reuse it in other projects and keep it backed up${teams ? `; ${where}'s copy stays as it is (${committed(s) ? "committed" : `${untrackable(s)}, so skilllib can't track it here`})` : `; ${where} then tracks it in skilllib.json`}.`,
|
|
3208
|
+
detail: `Installed with \`npx skills add\`${s.source ? ` (${s.source})` : ""}. Import it into your library to reuse it in other projects and keep it backed up${teams ? `; ${where}'s copy stays as it is (${committed(s) ? "committed" : `${untrackable(s)}, so skilllib can't track it here`})` : `; ${where} then tracks it (in skilllib.json, or your personal pins when it has none)`}.`,
|
|
3201
3209
|
at: rows(root, [s.name]),
|
|
3202
3210
|
choices: [
|
|
3203
3211
|
{
|
|
@@ -3739,9 +3747,9 @@ function projectOfFactory(projects2) {
|
|
|
3739
3747
|
const sorted2 = [...projects2].sort((a, b) => b.length - a.length);
|
|
3740
3748
|
return (cwd) => {
|
|
3741
3749
|
if (!cwd) return null;
|
|
3742
|
-
const known = sorted2.find((p) => cwd === p || cwd.startsWith(p +
|
|
3750
|
+
const known = sorted2.find((p) => cwd === p || cwd.startsWith(p + sep9));
|
|
3743
3751
|
if (known) return known;
|
|
3744
|
-
const repo = cwd.split(`${
|
|
3752
|
+
const repo = cwd.split(`${sep9}.claude${sep9}worktrees${sep9}`)[0];
|
|
3745
3753
|
return isProjectCandidate(repo) ? repo : null;
|
|
3746
3754
|
};
|
|
3747
3755
|
}
|
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-LORSEDNW.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-3QD6RQGL.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-3QD6RQGL.js")).tui();
|
|
147
147
|
case "status":
|
|
148
148
|
return status(args);
|
|
149
149
|
case "add":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skilllib",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.1",
|
|
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",
|