@hasna/skills 0.10.55 → 0.10.56
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 +4 -0
- package/bin/index.js +94 -1
- package/bin/mcp.js +94 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +94 -1
- package/dist/sdk/index.js +94 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -696,6 +696,10 @@ remain absent only while unchanged native settings and registrations select a
|
|
|
696
696
|
different extant, witnessed user version. Individual missing files, current
|
|
697
697
|
registered roots, aliases and registration changes still refuse; historical
|
|
698
698
|
source hashes and typed settings witnesses remain intact.
|
|
699
|
+
An explicit Claude discovery review must cover every registered active cache
|
|
700
|
+
manifest, skill root and hook source. Installation refuses an incomplete review
|
|
701
|
+
that covers only an older cache, before retirement of that cache can break prompt
|
|
702
|
+
delivery. Hook behavior still requires the existing no-skill-injection review.
|
|
699
703
|
|
|
700
704
|
For a Codex 0.160.0, 0.160.1 or 0.161.0 remote refresh omitted by an existing qualified-name disable,
|
|
701
705
|
a fresh native catalog and discovery review can plan the new exact path disable
|
package/bin/index.js
CHANGED
|
@@ -36888,7 +36888,7 @@ var package_default;
|
|
|
36888
36888
|
var init_package = __esm(() => {
|
|
36889
36889
|
package_default = {
|
|
36890
36890
|
name: "@hasna/skills",
|
|
36891
|
-
version: "0.10.
|
|
36891
|
+
version: "0.10.56",
|
|
36892
36892
|
description: "Skills library for AI coding agents",
|
|
36893
36893
|
type: "module",
|
|
36894
36894
|
bin: {
|
|
@@ -59361,6 +59361,97 @@ function read2(path, changes) {
|
|
|
59361
59361
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
59362
59362
|
return readFileSync21(path, "utf8");
|
|
59363
59363
|
}
|
|
59364
|
+
function assertReviewedClaudeCacheClosures(home, config, review) {
|
|
59365
|
+
const enabledIds = Object.entries(config.enabledPlugins ?? {}).filter(([, enabled]) => enabled === true).map(([id]) => id);
|
|
59366
|
+
if (enabledIds.length === 0)
|
|
59367
|
+
return;
|
|
59368
|
+
const registryPath = join37(home, ".claude/plugins/installed_plugins.json");
|
|
59369
|
+
const cacheRoot = join37(home, ".claude/plugins/cache");
|
|
59370
|
+
const registryText = read2(registryPath);
|
|
59371
|
+
if (registryText === null)
|
|
59372
|
+
return;
|
|
59373
|
+
const registry = parseConfig(registryText, registryPath);
|
|
59374
|
+
if (registry.version !== 2 || !registry.plugins || typeof registry.plugins !== "object" || Array.isArray(registry.plugins))
|
|
59375
|
+
throw new Error("Reviewed Claude installed plugin registrations are invalid");
|
|
59376
|
+
const cachePath = (path) => typeof path === "string" && isAbsolute22(path) && resolve21(path) === path && (path === cacheRoot || path.startsWith(cacheRoot + sep8));
|
|
59377
|
+
const activeRoots = new Set;
|
|
59378
|
+
for (const id of enabledIds) {
|
|
59379
|
+
const rows = registry.plugins[id];
|
|
59380
|
+
if (rows === undefined)
|
|
59381
|
+
continue;
|
|
59382
|
+
if (!Array.isArray(rows))
|
|
59383
|
+
throw new Error("Reviewed Claude enabled plugin registrations must be an array");
|
|
59384
|
+
for (const row of rows) {
|
|
59385
|
+
if (!row || typeof row !== "object" || Array.isArray(row) || typeof row.installPath !== "string") {
|
|
59386
|
+
throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
|
|
59387
|
+
}
|
|
59388
|
+
if (typeof row.installPath === "string" && (row.installPath === cacheRoot || row.installPath.startsWith(cacheRoot + sep8))) {
|
|
59389
|
+
if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename11(row.installPath) !== row.version)) {
|
|
59390
|
+
throw new Error("Reviewed Claude discovery has a malformed active cache registration");
|
|
59391
|
+
}
|
|
59392
|
+
if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute22(row.projectPath) && resolve21(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
|
|
59393
|
+
throw new Error("Reviewed Claude discovery has an unsupported active cache registration scope");
|
|
59394
|
+
activeRoots.add(row.installPath);
|
|
59395
|
+
}
|
|
59396
|
+
}
|
|
59397
|
+
}
|
|
59398
|
+
if (activeRoots.size === 0)
|
|
59399
|
+
return;
|
|
59400
|
+
const registrySource = review.sources.find((source) => source.path === registryPath && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && (source.hashMode === undefined || source.hashMode === "bytes"));
|
|
59401
|
+
if (!registrySource)
|
|
59402
|
+
throw new Error("Reviewed Claude discovery must bind installed registrations for active cache plugins");
|
|
59403
|
+
const reviewedFile = (path, manifest = false) => review.sources.some((source) => source.path === path && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && (source.hashMode === undefined || source.hashMode === "bytes" || manifest && source.hashMode === "claude-plugin-manifest-v1"));
|
|
59404
|
+
const requireSource = (path, description, manifest = false) => {
|
|
59405
|
+
if (!reviewedFile(path, manifest))
|
|
59406
|
+
throw new Error(`Reviewed active Claude cache plugin ${description} is not bound`);
|
|
59407
|
+
};
|
|
59408
|
+
for (const root2 of activeRoots) {
|
|
59409
|
+
safe4(root2);
|
|
59410
|
+
const rootStat = lstatSync25(root2, { throwIfNoEntry: false });
|
|
59411
|
+
if (!rootStat?.isDirectory() || realpathSync11(root2) !== root2)
|
|
59412
|
+
throw new Error("Reviewed Claude active cache plugin root is missing or unsafe");
|
|
59413
|
+
const manifestPath = join37(root2, ".claude-plugin/plugin.json");
|
|
59414
|
+
const manifestStat = lstatSync25(manifestPath, { throwIfNoEntry: false });
|
|
59415
|
+
let manifest;
|
|
59416
|
+
if (manifestStat) {
|
|
59417
|
+
if (!manifestStat.isFile())
|
|
59418
|
+
throw new Error("Reviewed active Claude cache plugin manifest is unsupported");
|
|
59419
|
+
requireSource(manifestPath, "manifest", true);
|
|
59420
|
+
const raw = read2(manifestPath);
|
|
59421
|
+
if (raw === null)
|
|
59422
|
+
throw new Error("Reviewed Claude active cache plugin manifest is missing");
|
|
59423
|
+
manifest = JSON.parse(raw);
|
|
59424
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
|
|
59425
|
+
throw new Error("Reviewed Claude active cache plugin manifest is invalid");
|
|
59426
|
+
} else if (!review.sources.some((source) => source.path === manifestPath && source.hashMode === "path-bytes" && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined)) {
|
|
59427
|
+
throw new Error("Reviewed Claude active cache plugin manifest absence requires a path-bytes witness");
|
|
59428
|
+
}
|
|
59429
|
+
const roots = new Set([join37(root2, "skills")]);
|
|
59430
|
+
if (lstatSync25(join37(root2, "SKILL.md"), { throwIfNoEntry: false })?.isFile())
|
|
59431
|
+
roots.add(root2);
|
|
59432
|
+
const declared = manifest?.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
59433
|
+
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
59434
|
+
throw new Error("Reviewed Claude active cache plugin has unsupported skill paths");
|
|
59435
|
+
for (const value of declared) {
|
|
59436
|
+
const path = resolve21(root2, value);
|
|
59437
|
+
if (path !== root2 && !path.startsWith(root2 + sep8))
|
|
59438
|
+
throw new Error("Reviewed Claude active cache plugin skill path escapes its root");
|
|
59439
|
+
roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname20(path) : path);
|
|
59440
|
+
}
|
|
59441
|
+
for (const skillRoot of roots)
|
|
59442
|
+
if (!review.roots.includes(skillRoot))
|
|
59443
|
+
throw new Error("Reviewed Claude active cache plugin skill root is not covered");
|
|
59444
|
+
const hooksPath = join37(root2, "hooks/hooks.json");
|
|
59445
|
+
const hooksStat = lstatSync25(hooksPath, { throwIfNoEntry: false });
|
|
59446
|
+
if (hooksStat) {
|
|
59447
|
+
if (!hooksStat.isFile())
|
|
59448
|
+
throw new Error("Reviewed Claude plugin hook manifest is unsupported");
|
|
59449
|
+
requireSource(hooksPath, "hook manifest");
|
|
59450
|
+
}
|
|
59451
|
+
if (typeof manifest?.hooks === "string")
|
|
59452
|
+
requireSource(resolve21(root2, manifest.hooks), "manifest-declared hook source");
|
|
59453
|
+
}
|
|
59454
|
+
}
|
|
59364
59455
|
function projectNativeDiscoveryFields(text3, format, fields2, path) {
|
|
59365
59456
|
const object6 = format === "yaml" ? parseHermesConfig(text3) : parseConfig(text3, path, format === "toml");
|
|
59366
59457
|
if (!object6 || typeof object6 !== "object" || Array.isArray(object6))
|
|
@@ -59937,6 +60028,8 @@ function resolveAgentDiscovery(options) {
|
|
|
59937
60028
|
throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
|
|
59938
60029
|
return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
|
|
59939
60030
|
});
|
|
60031
|
+
if (agent === "claude")
|
|
60032
|
+
assertReviewedClaudeCacheClosures(home, config, review);
|
|
59940
60033
|
const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
|
|
59941
60034
|
const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual11(candidate2, source)) === index);
|
|
59942
60035
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.10.
|
|
20022
|
+
version: "0.10.56",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -50788,6 +50788,97 @@ function read(path, changes) {
|
|
|
50788
50788
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
50789
50789
|
return readFileSync15(path, "utf8");
|
|
50790
50790
|
}
|
|
50791
|
+
function assertReviewedClaudeCacheClosures(home, config2, review) {
|
|
50792
|
+
const enabledIds = Object.entries(config2.enabledPlugins ?? {}).filter(([, enabled]) => enabled === true).map(([id]) => id);
|
|
50793
|
+
if (enabledIds.length === 0)
|
|
50794
|
+
return;
|
|
50795
|
+
const registryPath = join26(home, ".claude/plugins/installed_plugins.json");
|
|
50796
|
+
const cacheRoot = join26(home, ".claude/plugins/cache");
|
|
50797
|
+
const registryText = read(registryPath);
|
|
50798
|
+
if (registryText === null)
|
|
50799
|
+
return;
|
|
50800
|
+
const registry2 = parseConfig(registryText, registryPath);
|
|
50801
|
+
if (registry2.version !== 2 || !registry2.plugins || typeof registry2.plugins !== "object" || Array.isArray(registry2.plugins))
|
|
50802
|
+
throw new Error("Reviewed Claude installed plugin registrations are invalid");
|
|
50803
|
+
const cachePath = (path) => typeof path === "string" && isAbsolute15(path) && resolve17(path) === path && (path === cacheRoot || path.startsWith(cacheRoot + sep6));
|
|
50804
|
+
const activeRoots = new Set;
|
|
50805
|
+
for (const id of enabledIds) {
|
|
50806
|
+
const rows = registry2.plugins[id];
|
|
50807
|
+
if (rows === undefined)
|
|
50808
|
+
continue;
|
|
50809
|
+
if (!Array.isArray(rows))
|
|
50810
|
+
throw new Error("Reviewed Claude enabled plugin registrations must be an array");
|
|
50811
|
+
for (const row of rows) {
|
|
50812
|
+
if (!row || typeof row !== "object" || Array.isArray(row) || typeof row.installPath !== "string") {
|
|
50813
|
+
throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
|
|
50814
|
+
}
|
|
50815
|
+
if (typeof row.installPath === "string" && (row.installPath === cacheRoot || row.installPath.startsWith(cacheRoot + sep6))) {
|
|
50816
|
+
if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename9(row.installPath) !== row.version)) {
|
|
50817
|
+
throw new Error("Reviewed Claude discovery has a malformed active cache registration");
|
|
50818
|
+
}
|
|
50819
|
+
if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute15(row.projectPath) && resolve17(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
|
|
50820
|
+
throw new Error("Reviewed Claude discovery has an unsupported active cache registration scope");
|
|
50821
|
+
activeRoots.add(row.installPath);
|
|
50822
|
+
}
|
|
50823
|
+
}
|
|
50824
|
+
}
|
|
50825
|
+
if (activeRoots.size === 0)
|
|
50826
|
+
return;
|
|
50827
|
+
const registrySource = review.sources.find((source) => source.path === registryPath && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && (source.hashMode === undefined || source.hashMode === "bytes"));
|
|
50828
|
+
if (!registrySource)
|
|
50829
|
+
throw new Error("Reviewed Claude discovery must bind installed registrations for active cache plugins");
|
|
50830
|
+
const reviewedFile = (path, manifest = false) => review.sources.some((source) => source.path === path && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && (source.hashMode === undefined || source.hashMode === "bytes" || manifest && source.hashMode === "claude-plugin-manifest-v1"));
|
|
50831
|
+
const requireSource = (path, description, manifest = false) => {
|
|
50832
|
+
if (!reviewedFile(path, manifest))
|
|
50833
|
+
throw new Error(`Reviewed active Claude cache plugin ${description} is not bound`);
|
|
50834
|
+
};
|
|
50835
|
+
for (const root of activeRoots) {
|
|
50836
|
+
safe4(root);
|
|
50837
|
+
const rootStat = lstatSync18(root, { throwIfNoEntry: false });
|
|
50838
|
+
if (!rootStat?.isDirectory() || realpathSync5(root) !== root)
|
|
50839
|
+
throw new Error("Reviewed Claude active cache plugin root is missing or unsafe");
|
|
50840
|
+
const manifestPath = join26(root, ".claude-plugin/plugin.json");
|
|
50841
|
+
const manifestStat = lstatSync18(manifestPath, { throwIfNoEntry: false });
|
|
50842
|
+
let manifest;
|
|
50843
|
+
if (manifestStat) {
|
|
50844
|
+
if (!manifestStat.isFile())
|
|
50845
|
+
throw new Error("Reviewed active Claude cache plugin manifest is unsupported");
|
|
50846
|
+
requireSource(manifestPath, "manifest", true);
|
|
50847
|
+
const raw = read(manifestPath);
|
|
50848
|
+
if (raw === null)
|
|
50849
|
+
throw new Error("Reviewed Claude active cache plugin manifest is missing");
|
|
50850
|
+
manifest = JSON.parse(raw);
|
|
50851
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
|
|
50852
|
+
throw new Error("Reviewed Claude active cache plugin manifest is invalid");
|
|
50853
|
+
} else if (!review.sources.some((source) => source.path === manifestPath && source.hashMode === "path-bytes" && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined)) {
|
|
50854
|
+
throw new Error("Reviewed Claude active cache plugin manifest absence requires a path-bytes witness");
|
|
50855
|
+
}
|
|
50856
|
+
const roots = new Set([join26(root, "skills")]);
|
|
50857
|
+
if (lstatSync18(join26(root, "SKILL.md"), { throwIfNoEntry: false })?.isFile())
|
|
50858
|
+
roots.add(root);
|
|
50859
|
+
const declared = manifest?.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
50860
|
+
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
50861
|
+
throw new Error("Reviewed Claude active cache plugin has unsupported skill paths");
|
|
50862
|
+
for (const value of declared) {
|
|
50863
|
+
const path = resolve17(root, value);
|
|
50864
|
+
if (path !== root && !path.startsWith(root + sep6))
|
|
50865
|
+
throw new Error("Reviewed Claude active cache plugin skill path escapes its root");
|
|
50866
|
+
roots.add(path.endsWith(`${sep6}SKILL.md`) ? dirname16(path) : path);
|
|
50867
|
+
}
|
|
50868
|
+
for (const skillRoot of roots)
|
|
50869
|
+
if (!review.roots.includes(skillRoot))
|
|
50870
|
+
throw new Error("Reviewed Claude active cache plugin skill root is not covered");
|
|
50871
|
+
const hooksPath = join26(root, "hooks/hooks.json");
|
|
50872
|
+
const hooksStat = lstatSync18(hooksPath, { throwIfNoEntry: false });
|
|
50873
|
+
if (hooksStat) {
|
|
50874
|
+
if (!hooksStat.isFile())
|
|
50875
|
+
throw new Error("Reviewed Claude plugin hook manifest is unsupported");
|
|
50876
|
+
requireSource(hooksPath, "hook manifest");
|
|
50877
|
+
}
|
|
50878
|
+
if (typeof manifest?.hooks === "string")
|
|
50879
|
+
requireSource(resolve17(root, manifest.hooks), "manifest-declared hook source");
|
|
50880
|
+
}
|
|
50881
|
+
}
|
|
50791
50882
|
function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
50792
50883
|
const object7 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
|
|
50793
50884
|
if (!object7 || typeof object7 !== "object" || Array.isArray(object7))
|
|
@@ -51254,6 +51345,8 @@ function resolveAgentDiscovery(options) {
|
|
|
51254
51345
|
throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
|
|
51255
51346
|
return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
|
|
51256
51347
|
});
|
|
51348
|
+
if (agent === "claude")
|
|
51349
|
+
assertReviewedClaudeCacheClosures(home, config2, review);
|
|
51257
51350
|
const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
|
|
51258
51351
|
const unique = combined.filter((source, index) => combined.findIndex((candidate) => isDeepStrictEqual4(candidate, source)) === index);
|
|
51259
51352
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24140,7 +24140,7 @@ restoreLaunchCwd();
|
|
|
24140
24140
|
// package.json
|
|
24141
24141
|
var package_default = {
|
|
24142
24142
|
name: "@hasna/skills",
|
|
24143
|
-
version: "0.10.
|
|
24143
|
+
version: "0.10.56",
|
|
24144
24144
|
description: "Skills library for AI coding agents",
|
|
24145
24145
|
type: "module",
|
|
24146
24146
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24143,7 +24143,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24143
24143
|
// package.json
|
|
24144
24144
|
var package_default = {
|
|
24145
24145
|
name: "@hasna/skills",
|
|
24146
|
-
version: "0.10.
|
|
24146
|
+
version: "0.10.56",
|
|
24147
24147
|
description: "Skills library for AI coding agents",
|
|
24148
24148
|
type: "module",
|
|
24149
24149
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -11833,7 +11833,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11833
11833
|
// package.json
|
|
11834
11834
|
var package_default = {
|
|
11835
11835
|
name: "@hasna/skills",
|
|
11836
|
-
version: "0.10.
|
|
11836
|
+
version: "0.10.56",
|
|
11837
11837
|
description: "Skills library for AI coding agents",
|
|
11838
11838
|
type: "module",
|
|
11839
11839
|
bin: {
|
|
@@ -20693,6 +20693,97 @@ function read2(path, changes) {
|
|
|
20693
20693
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
20694
20694
|
return readFileSync20(path, "utf8");
|
|
20695
20695
|
}
|
|
20696
|
+
function assertReviewedClaudeCacheClosures(home, config, review) {
|
|
20697
|
+
const enabledIds = Object.entries(config.enabledPlugins ?? {}).filter(([, enabled]) => enabled === true).map(([id]) => id);
|
|
20698
|
+
if (enabledIds.length === 0)
|
|
20699
|
+
return;
|
|
20700
|
+
const registryPath = join32(home, ".claude/plugins/installed_plugins.json");
|
|
20701
|
+
const cacheRoot = join32(home, ".claude/plugins/cache");
|
|
20702
|
+
const registryText = read2(registryPath);
|
|
20703
|
+
if (registryText === null)
|
|
20704
|
+
return;
|
|
20705
|
+
const registry = parseConfig(registryText, registryPath);
|
|
20706
|
+
if (registry.version !== 2 || !registry.plugins || typeof registry.plugins !== "object" || Array.isArray(registry.plugins))
|
|
20707
|
+
throw new Error("Reviewed Claude installed plugin registrations are invalid");
|
|
20708
|
+
const cachePath = (path) => typeof path === "string" && isAbsolute21(path) && resolve20(path) === path && (path === cacheRoot || path.startsWith(cacheRoot + sep8));
|
|
20709
|
+
const activeRoots = new Set;
|
|
20710
|
+
for (const id of enabledIds) {
|
|
20711
|
+
const rows = registry.plugins[id];
|
|
20712
|
+
if (rows === undefined)
|
|
20713
|
+
continue;
|
|
20714
|
+
if (!Array.isArray(rows))
|
|
20715
|
+
throw new Error("Reviewed Claude enabled plugin registrations must be an array");
|
|
20716
|
+
for (const row of rows) {
|
|
20717
|
+
if (!row || typeof row !== "object" || Array.isArray(row) || typeof row.installPath !== "string") {
|
|
20718
|
+
throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
|
|
20719
|
+
}
|
|
20720
|
+
if (typeof row.installPath === "string" && (row.installPath === cacheRoot || row.installPath.startsWith(cacheRoot + sep8))) {
|
|
20721
|
+
if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename11(row.installPath) !== row.version)) {
|
|
20722
|
+
throw new Error("Reviewed Claude discovery has a malformed active cache registration");
|
|
20723
|
+
}
|
|
20724
|
+
if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute21(row.projectPath) && resolve20(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
|
|
20725
|
+
throw new Error("Reviewed Claude discovery has an unsupported active cache registration scope");
|
|
20726
|
+
activeRoots.add(row.installPath);
|
|
20727
|
+
}
|
|
20728
|
+
}
|
|
20729
|
+
}
|
|
20730
|
+
if (activeRoots.size === 0)
|
|
20731
|
+
return;
|
|
20732
|
+
const registrySource = review.sources.find((source) => source.path === registryPath && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && (source.hashMode === undefined || source.hashMode === "bytes"));
|
|
20733
|
+
if (!registrySource)
|
|
20734
|
+
throw new Error("Reviewed Claude discovery must bind installed registrations for active cache plugins");
|
|
20735
|
+
const reviewedFile = (path, manifest = false) => review.sources.some((source) => source.path === path && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && (source.hashMode === undefined || source.hashMode === "bytes" || manifest && source.hashMode === "claude-plugin-manifest-v1"));
|
|
20736
|
+
const requireSource = (path, description, manifest = false) => {
|
|
20737
|
+
if (!reviewedFile(path, manifest))
|
|
20738
|
+
throw new Error(`Reviewed active Claude cache plugin ${description} is not bound`);
|
|
20739
|
+
};
|
|
20740
|
+
for (const root2 of activeRoots) {
|
|
20741
|
+
safe4(root2);
|
|
20742
|
+
const rootStat = lstatSync25(root2, { throwIfNoEntry: false });
|
|
20743
|
+
if (!rootStat?.isDirectory() || realpathSync11(root2) !== root2)
|
|
20744
|
+
throw new Error("Reviewed Claude active cache plugin root is missing or unsafe");
|
|
20745
|
+
const manifestPath = join32(root2, ".claude-plugin/plugin.json");
|
|
20746
|
+
const manifestStat = lstatSync25(manifestPath, { throwIfNoEntry: false });
|
|
20747
|
+
let manifest;
|
|
20748
|
+
if (manifestStat) {
|
|
20749
|
+
if (!manifestStat.isFile())
|
|
20750
|
+
throw new Error("Reviewed active Claude cache plugin manifest is unsupported");
|
|
20751
|
+
requireSource(manifestPath, "manifest", true);
|
|
20752
|
+
const raw = read2(manifestPath);
|
|
20753
|
+
if (raw === null)
|
|
20754
|
+
throw new Error("Reviewed Claude active cache plugin manifest is missing");
|
|
20755
|
+
manifest = JSON.parse(raw);
|
|
20756
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
|
|
20757
|
+
throw new Error("Reviewed Claude active cache plugin manifest is invalid");
|
|
20758
|
+
} else if (!review.sources.some((source) => source.path === manifestPath && source.hashMode === "path-bytes" && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined)) {
|
|
20759
|
+
throw new Error("Reviewed Claude active cache plugin manifest absence requires a path-bytes witness");
|
|
20760
|
+
}
|
|
20761
|
+
const roots = new Set([join32(root2, "skills")]);
|
|
20762
|
+
if (lstatSync25(join32(root2, "SKILL.md"), { throwIfNoEntry: false })?.isFile())
|
|
20763
|
+
roots.add(root2);
|
|
20764
|
+
const declared = manifest?.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
20765
|
+
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
20766
|
+
throw new Error("Reviewed Claude active cache plugin has unsupported skill paths");
|
|
20767
|
+
for (const value of declared) {
|
|
20768
|
+
const path = resolve20(root2, value);
|
|
20769
|
+
if (path !== root2 && !path.startsWith(root2 + sep8))
|
|
20770
|
+
throw new Error("Reviewed Claude active cache plugin skill path escapes its root");
|
|
20771
|
+
roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname20(path) : path);
|
|
20772
|
+
}
|
|
20773
|
+
for (const skillRoot of roots)
|
|
20774
|
+
if (!review.roots.includes(skillRoot))
|
|
20775
|
+
throw new Error("Reviewed Claude active cache plugin skill root is not covered");
|
|
20776
|
+
const hooksPath = join32(root2, "hooks/hooks.json");
|
|
20777
|
+
const hooksStat = lstatSync25(hooksPath, { throwIfNoEntry: false });
|
|
20778
|
+
if (hooksStat) {
|
|
20779
|
+
if (!hooksStat.isFile())
|
|
20780
|
+
throw new Error("Reviewed Claude plugin hook manifest is unsupported");
|
|
20781
|
+
requireSource(hooksPath, "hook manifest");
|
|
20782
|
+
}
|
|
20783
|
+
if (typeof manifest?.hooks === "string")
|
|
20784
|
+
requireSource(resolve20(root2, manifest.hooks), "manifest-declared hook source");
|
|
20785
|
+
}
|
|
20786
|
+
}
|
|
20696
20787
|
function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
20697
20788
|
const object6 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
|
|
20698
20789
|
if (!object6 || typeof object6 !== "object" || Array.isArray(object6))
|
|
@@ -21281,6 +21372,8 @@ function resolveAgentDiscovery(options) {
|
|
|
21281
21372
|
throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
|
|
21282
21373
|
return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
|
|
21283
21374
|
});
|
|
21375
|
+
if (agent === "claude")
|
|
21376
|
+
assertReviewedClaudeCacheClosures(home, config, review);
|
|
21284
21377
|
const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
|
|
21285
21378
|
const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual7(candidate2, source)) === index);
|
|
21286
21379
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
package/dist/sdk/index.js
CHANGED
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.10.
|
|
32376
|
+
version: "0.10.56",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -43533,6 +43533,97 @@ function read2(path, changes) {
|
|
|
43533
43533
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
43534
43534
|
return readFileSync15(path, "utf8");
|
|
43535
43535
|
}
|
|
43536
|
+
function assertReviewedClaudeCacheClosures(home, config, review) {
|
|
43537
|
+
const enabledIds = Object.entries(config.enabledPlugins ?? {}).filter(([, enabled]) => enabled === true).map(([id2]) => id2);
|
|
43538
|
+
if (enabledIds.length === 0)
|
|
43539
|
+
return;
|
|
43540
|
+
const registryPath = join26(home, ".claude/plugins/installed_plugins.json");
|
|
43541
|
+
const cacheRoot = join26(home, ".claude/plugins/cache");
|
|
43542
|
+
const registryText = read2(registryPath);
|
|
43543
|
+
if (registryText === null)
|
|
43544
|
+
return;
|
|
43545
|
+
const registry = parseConfig(registryText, registryPath);
|
|
43546
|
+
if (registry.version !== 2 || !registry.plugins || typeof registry.plugins !== "object" || Array.isArray(registry.plugins))
|
|
43547
|
+
throw new Error("Reviewed Claude installed plugin registrations are invalid");
|
|
43548
|
+
const cachePath = (path) => typeof path === "string" && isAbsolute20(path) && resolve19(path) === path && (path === cacheRoot || path.startsWith(cacheRoot + sep6));
|
|
43549
|
+
const activeRoots = new Set;
|
|
43550
|
+
for (const id2 of enabledIds) {
|
|
43551
|
+
const rows = registry.plugins[id2];
|
|
43552
|
+
if (rows === undefined)
|
|
43553
|
+
continue;
|
|
43554
|
+
if (!Array.isArray(rows))
|
|
43555
|
+
throw new Error("Reviewed Claude enabled plugin registrations must be an array");
|
|
43556
|
+
for (const row of rows) {
|
|
43557
|
+
if (!row || typeof row !== "object" || Array.isArray(row) || typeof row.installPath !== "string") {
|
|
43558
|
+
throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
|
|
43559
|
+
}
|
|
43560
|
+
if (typeof row.installPath === "string" && (row.installPath === cacheRoot || row.installPath.startsWith(cacheRoot + sep6))) {
|
|
43561
|
+
if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename9(row.installPath) !== row.version)) {
|
|
43562
|
+
throw new Error("Reviewed Claude discovery has a malformed active cache registration");
|
|
43563
|
+
}
|
|
43564
|
+
if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute20(row.projectPath) && resolve19(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
|
|
43565
|
+
throw new Error("Reviewed Claude discovery has an unsupported active cache registration scope");
|
|
43566
|
+
activeRoots.add(row.installPath);
|
|
43567
|
+
}
|
|
43568
|
+
}
|
|
43569
|
+
}
|
|
43570
|
+
if (activeRoots.size === 0)
|
|
43571
|
+
return;
|
|
43572
|
+
const registrySource = review.sources.find((source) => source.path === registryPath && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && (source.hashMode === undefined || source.hashMode === "bytes"));
|
|
43573
|
+
if (!registrySource)
|
|
43574
|
+
throw new Error("Reviewed Claude discovery must bind installed registrations for active cache plugins");
|
|
43575
|
+
const reviewedFile = (path, manifest = false) => review.sources.some((source) => source.path === path && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && (source.hashMode === undefined || source.hashMode === "bytes" || manifest && source.hashMode === "claude-plugin-manifest-v1"));
|
|
43576
|
+
const requireSource = (path, description, manifest = false) => {
|
|
43577
|
+
if (!reviewedFile(path, manifest))
|
|
43578
|
+
throw new Error(`Reviewed active Claude cache plugin ${description} is not bound`);
|
|
43579
|
+
};
|
|
43580
|
+
for (const root2 of activeRoots) {
|
|
43581
|
+
safe4(root2);
|
|
43582
|
+
const rootStat = lstatSync21(root2, { throwIfNoEntry: false });
|
|
43583
|
+
if (!rootStat?.isDirectory() || realpathSync9(root2) !== root2)
|
|
43584
|
+
throw new Error("Reviewed Claude active cache plugin root is missing or unsafe");
|
|
43585
|
+
const manifestPath = join26(root2, ".claude-plugin/plugin.json");
|
|
43586
|
+
const manifestStat = lstatSync21(manifestPath, { throwIfNoEntry: false });
|
|
43587
|
+
let manifest;
|
|
43588
|
+
if (manifestStat) {
|
|
43589
|
+
if (!manifestStat.isFile())
|
|
43590
|
+
throw new Error("Reviewed active Claude cache plugin manifest is unsupported");
|
|
43591
|
+
requireSource(manifestPath, "manifest", true);
|
|
43592
|
+
const raw = read2(manifestPath);
|
|
43593
|
+
if (raw === null)
|
|
43594
|
+
throw new Error("Reviewed Claude active cache plugin manifest is missing");
|
|
43595
|
+
manifest = JSON.parse(raw);
|
|
43596
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
|
|
43597
|
+
throw new Error("Reviewed Claude active cache plugin manifest is invalid");
|
|
43598
|
+
} else if (!review.sources.some((source) => source.path === manifestPath && source.hashMode === "path-bytes" && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined)) {
|
|
43599
|
+
throw new Error("Reviewed Claude active cache plugin manifest absence requires a path-bytes witness");
|
|
43600
|
+
}
|
|
43601
|
+
const roots = new Set([join26(root2, "skills")]);
|
|
43602
|
+
if (lstatSync21(join26(root2, "SKILL.md"), { throwIfNoEntry: false })?.isFile())
|
|
43603
|
+
roots.add(root2);
|
|
43604
|
+
const declared = manifest?.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
|
|
43605
|
+
if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
|
|
43606
|
+
throw new Error("Reviewed Claude active cache plugin has unsupported skill paths");
|
|
43607
|
+
for (const value of declared) {
|
|
43608
|
+
const path = resolve19(root2, value);
|
|
43609
|
+
if (path !== root2 && !path.startsWith(root2 + sep6))
|
|
43610
|
+
throw new Error("Reviewed Claude active cache plugin skill path escapes its root");
|
|
43611
|
+
roots.add(path.endsWith(`${sep6}SKILL.md`) ? dirname17(path) : path);
|
|
43612
|
+
}
|
|
43613
|
+
for (const skillRoot of roots)
|
|
43614
|
+
if (!review.roots.includes(skillRoot))
|
|
43615
|
+
throw new Error("Reviewed Claude active cache plugin skill root is not covered");
|
|
43616
|
+
const hooksPath = join26(root2, "hooks/hooks.json");
|
|
43617
|
+
const hooksStat = lstatSync21(hooksPath, { throwIfNoEntry: false });
|
|
43618
|
+
if (hooksStat) {
|
|
43619
|
+
if (!hooksStat.isFile())
|
|
43620
|
+
throw new Error("Reviewed Claude plugin hook manifest is unsupported");
|
|
43621
|
+
requireSource(hooksPath, "hook manifest");
|
|
43622
|
+
}
|
|
43623
|
+
if (typeof manifest?.hooks === "string")
|
|
43624
|
+
requireSource(resolve19(root2, manifest.hooks), "manifest-declared hook source");
|
|
43625
|
+
}
|
|
43626
|
+
}
|
|
43536
43627
|
function projectNativeDiscoveryFields(text2, format, fields2, path) {
|
|
43537
43628
|
const object6 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
|
|
43538
43629
|
if (!object6 || typeof object6 !== "object" || Array.isArray(object6))
|
|
@@ -44121,6 +44212,8 @@ function resolveAgentDiscovery(options) {
|
|
|
44121
44212
|
throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
|
|
44122
44213
|
return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
|
|
44123
44214
|
});
|
|
44215
|
+
if (agent === "claude")
|
|
44216
|
+
assertReviewedClaudeCacheClosures(home, config, review);
|
|
44124
44217
|
const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
|
|
44125
44218
|
const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual6(candidate2, source)) === index);
|
|
44126
44219
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|