@hasna/skills 0.10.55 → 0.10.57
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 +787 -107
- 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.d.ts +2 -0
- package/dist/index.js +179 -1
- package/dist/lib/agent-integration.d.ts +2 -0
- package/dist/lib/claude-prospective-review.d.ts +140 -0
- package/dist/sdk/index.js +94 -1
- package/docs/plugin-admission.md +59 -0
- package/package.json +1 -1
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.57",
|
|
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.57",
|
|
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.57",
|
|
24147
24147
|
description: "Skills library for AI coding agents",
|
|
24148
24148
|
type: "module",
|
|
24149
24149
|
bin: {
|
package/dist/index.d.ts
CHANGED
|
@@ -65,6 +65,8 @@ export type { ClaudeManagedHookProjection, ClaudeManagedHookProjectionOptions }
|
|
|
65
65
|
export { captureDiscoveryDirectories, captureDiscoveryByteSources, captureDiscoveryPathSources, DISCOVERY_ROOT_UNRESOLVED, isDiscoveryRootUnresolved, type DiscoveryDirectory, type DiscoveryExecutable, type DiscoverySource } from "./lib/agent-discovery.js";
|
|
66
66
|
export { captureClaudeMarketplaceRegistry, captureClaudeMarketplaceRegistryV2 } from "./lib/claude-marketplace-registry.js";
|
|
67
67
|
export { captureClaudeMarketplaceEntry, hashClaudeMarketplaceEntry, CLAUDE_MARKETPLACE_ENTRY_HASH_MODE, type ClaudeMarketplaceEntrySelector, type ClaudeMarketplaceEntryWitness } from "./lib/claude-marketplace-entry-witness.js";
|
|
68
|
+
export { captureClaudeProspectiveCandidateClosure } from "./lib/claude-prospective-review.js";
|
|
69
|
+
export { hashClaudePluginManifest } from "./lib/claude-plugin-manifest-witness.js";
|
|
68
70
|
export { captureClaudeSettings, captureClaudeSettingsV2, captureClaudeSettingsV3, captureClaudeSettingsV4, upgradeClaudeSettingsWitness, upgradeClaudeSettingsWitnessV4 } from "./lib/claude-settings-witness.js";
|
|
69
71
|
export { buildPluginProjection, pluginTreeDigest, type PluginProjectionManifest, type PluginPayloadMapping, type PluginFileWitness } from "./lib/plugin-projection.js";
|
|
70
72
|
export { planPluginAdmission, admitPlugin, resolveAdmittedPlugin, pluginResolverCommand, type PluginAdmissionTarget, type PluginAdmissionBinding, type PluginAdmissionPrincipal, type PluginAdmissionPlan, type PluginAdmissionIdentity, type PluginBundleIdentity, type PluginRoutingIdentity, type PluginAdmissionReceipt } from "./lib/plugin-admission.js";
|
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.57",
|
|
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 } : {} };
|
|
@@ -48250,6 +48343,89 @@ async function reconcileSkillSession(input2, options = {}) {
|
|
|
48250
48343
|
const result = replaceSkillSession(skillSessionSnapshotBinding(snapshot2), replacement, { ...plan, planDigest }, options, () => assertPlanWindow(issuedAt, expiresAt, (options.now ?? Date.now)()));
|
|
48251
48344
|
return { applied: true, plan, planDigest, ...result };
|
|
48252
48345
|
}
|
|
48346
|
+
// src/lib/claude-prospective-review.ts
|
|
48347
|
+
import { createHash as createHash35 } from "crypto";
|
|
48348
|
+
import { lstatSync as lstatSync28, readFileSync as readFileSync30, readdirSync as readdirSync17, realpathSync as realpathSync15 } from "fs";
|
|
48349
|
+
import { basename as basename14, dirname as dirname30, isAbsolute as isAbsolute26, join as join44, relative as relative12, resolve as resolve27, sep as sep15 } from "path";
|
|
48350
|
+
var MAX_CANDIDATE_FILES = 16384;
|
|
48351
|
+
var MAX_CANDIDATE_BYTES = 256 * 1024 * 1024;
|
|
48352
|
+
var sha6 = (data) => createHash35("sha256").update(data).digest("hex");
|
|
48353
|
+
function refuse4(reason2) {
|
|
48354
|
+
throw new Error(`CLAUDE_CANDIDATE_REVIEW_REFUSED: ${reason2}`);
|
|
48355
|
+
}
|
|
48356
|
+
var GENERATED_MARKETPLACE_TIME = Object.freeze({ $generated: "claude-utc-timestamp", field: "known-marketplaces.lastUpdated" });
|
|
48357
|
+
var GENERATED_FRESH_PLUGIN_INSTALLED_AT = Object.freeze({ $generated: "claude-utc-timestamp", field: "installed_plugins.fresh-row.installedAt" });
|
|
48358
|
+
var GENERATED_PLUGIN_UPDATE_TIME = Object.freeze({ $generated: "claude-utc-timestamp", field: "installed_plugins.selected-row.lastUpdated" });
|
|
48359
|
+
function abs(path, name) {
|
|
48360
|
+
if (typeof path !== "string" || !path || path.length > 4096 || /[\x00-\x1f\x7f]/.test(path) || !isAbsolute26(path) || resolve27(path) !== path)
|
|
48361
|
+
refuse4(`${name} must be a normalized absolute path`);
|
|
48362
|
+
}
|
|
48363
|
+
function safeFile(path, name) {
|
|
48364
|
+
abs(path, name);
|
|
48365
|
+
for (let at = dirname30(path);; at = dirname30(at)) {
|
|
48366
|
+
const stat3 = lstatSync28(at, { throwIfNoEntry: false });
|
|
48367
|
+
if (!stat3?.isDirectory() || stat3.isSymbolicLink())
|
|
48368
|
+
refuse4(`${name} traverses a symlink or non-directory`);
|
|
48369
|
+
if (at === dirname30(at))
|
|
48370
|
+
break;
|
|
48371
|
+
}
|
|
48372
|
+
const stat2 = lstatSync28(path, { throwIfNoEntry: false });
|
|
48373
|
+
if (!stat2?.isFile() || stat2.isSymbolicLink() || stat2.size > MAX_CANDIDATE_BYTES)
|
|
48374
|
+
refuse4(`${name} is not a bounded regular file`);
|
|
48375
|
+
return readFileSync30(path);
|
|
48376
|
+
}
|
|
48377
|
+
function canonicalDir(path, name) {
|
|
48378
|
+
abs(path, name);
|
|
48379
|
+
if (realpathSync15(path) !== path || !lstatSync28(path).isDirectory())
|
|
48380
|
+
refuse4(`${name} is not a canonical directory`);
|
|
48381
|
+
for (let at = path;; at = dirname30(at)) {
|
|
48382
|
+
const stat2 = lstatSync28(at);
|
|
48383
|
+
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
48384
|
+
refuse4(`${name} traverses a symlink`);
|
|
48385
|
+
if (at === dirname30(at))
|
|
48386
|
+
break;
|
|
48387
|
+
}
|
|
48388
|
+
}
|
|
48389
|
+
function captureClaudeProspectiveCandidateClosure(root2) {
|
|
48390
|
+
canonicalDir(root2, "candidate root");
|
|
48391
|
+
const entries = [];
|
|
48392
|
+
let bytes = 0;
|
|
48393
|
+
const walk2 = (directory2) => {
|
|
48394
|
+
const beforeDirectory = lstatSync28(directory2);
|
|
48395
|
+
const names = readdirSync17(directory2).sort();
|
|
48396
|
+
if (new Set(names).size !== names.length)
|
|
48397
|
+
refuse4("candidate contains ambiguous directory entries");
|
|
48398
|
+
for (const name of names) {
|
|
48399
|
+
if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\\") || /[\x00-\x1f\x7f]/.test(name))
|
|
48400
|
+
refuse4("candidate contains an unsupported path member");
|
|
48401
|
+
const path = join44(directory2, name), rel = relative12(root2, path).split(sep15).join("/");
|
|
48402
|
+
if (entries.length >= MAX_CANDIDATE_FILES)
|
|
48403
|
+
refuse4("candidate closure exceeds its member limit");
|
|
48404
|
+
const stat2 = lstatSync28(path);
|
|
48405
|
+
if (stat2.isSymbolicLink())
|
|
48406
|
+
refuse4("candidate closure contains a symlink");
|
|
48407
|
+
if (stat2.isDirectory()) {
|
|
48408
|
+
entries.push([rel, "directory", null]);
|
|
48409
|
+
walk2(path);
|
|
48410
|
+
} else if (stat2.isFile()) {
|
|
48411
|
+
bytes += stat2.size;
|
|
48412
|
+
if (bytes > MAX_CANDIDATE_BYTES)
|
|
48413
|
+
refuse4("candidate closure exceeds its byte limit");
|
|
48414
|
+
const content = safeFile(path, "candidate member"), after = lstatSync28(path);
|
|
48415
|
+
if (!after.isFile() || after.dev !== stat2.dev || after.ino !== stat2.ino || after.size !== stat2.size || after.mtimeMs !== stat2.mtimeMs || after.ctimeMs !== stat2.ctimeMs)
|
|
48416
|
+
refuse4("candidate member changed while being read");
|
|
48417
|
+
entries.push([rel, "file", sha6(content)]);
|
|
48418
|
+
} else
|
|
48419
|
+
refuse4("candidate closure contains a special file");
|
|
48420
|
+
}
|
|
48421
|
+
const afterDirectory = lstatSync28(directory2);
|
|
48422
|
+
if (!afterDirectory.isDirectory() || afterDirectory.dev !== beforeDirectory.dev || afterDirectory.ino !== beforeDirectory.ino || afterDirectory.mtimeMs !== beforeDirectory.mtimeMs || afterDirectory.ctimeMs !== beforeDirectory.ctimeMs)
|
|
48423
|
+
refuse4("candidate directory changed while being read");
|
|
48424
|
+
};
|
|
48425
|
+
walk2(root2);
|
|
48426
|
+
entries.sort((a, b) => a[0].localeCompare(b[0]));
|
|
48427
|
+
return sha6(JSON.stringify(entries));
|
|
48428
|
+
}
|
|
48253
48429
|
export {
|
|
48254
48430
|
writeStationSnapshot,
|
|
48255
48431
|
writeStationHydration,
|
|
@@ -48390,6 +48566,7 @@ export {
|
|
|
48390
48566
|
inspectPrivatePublication,
|
|
48391
48567
|
importSkillsLocalSnapshot,
|
|
48392
48568
|
homePathFor,
|
|
48569
|
+
hashClaudePluginManifest,
|
|
48393
48570
|
hashClaudeMarketplaceEntry,
|
|
48394
48571
|
getToolPrimitive,
|
|
48395
48572
|
getStorageStatus,
|
|
@@ -48473,6 +48650,7 @@ export {
|
|
|
48473
48650
|
captureClaudeSettingsV3,
|
|
48474
48651
|
captureClaudeSettingsV2,
|
|
48475
48652
|
captureClaudeSettings,
|
|
48653
|
+
captureClaudeProspectiveCandidateClosure,
|
|
48476
48654
|
captureClaudeMarketplaceRegistryV2,
|
|
48477
48655
|
captureClaudeMarketplaceRegistry,
|
|
48478
48656
|
captureClaudeMarketplaceEntry,
|
|
@@ -99,6 +99,8 @@ export interface AgentIntegrationPlan {
|
|
|
99
99
|
}
|
|
100
100
|
/** Normalize only the verified OS prefix, never hide user-controlled links. */
|
|
101
101
|
export declare function canonicalSystemPath(path: string): string;
|
|
102
|
+
export declare function canonicalAgentPath(path: string, aliases: AgentRootAlias[]): string;
|
|
103
|
+
export declare function recheckRootAliases(aliases: AgentRootAlias[]): void;
|
|
102
104
|
/** Parse the reviewed exact-target binding used by native migration. Paths are
|
|
103
105
|
* deliberately project-relative so a manifest cannot silently select another
|
|
104
106
|
* checkout when a project root changes. */
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { type ReviewedDiscoveryInputs } from "./agent-discovery.js";
|
|
2
|
+
export declare const CLAUDE_PROSPECTIVE_REVIEW_SCHEMA: "skills.claude-plugin-prospective-review/v1";
|
|
3
|
+
export interface ClaudeProspectiveReviewRequest {
|
|
4
|
+
schema: typeof CLAUDE_PROSPECTIVE_REVIEW_SCHEMA;
|
|
5
|
+
scope: {
|
|
6
|
+
kind: "user" | "project";
|
|
7
|
+
projectPath: string;
|
|
8
|
+
};
|
|
9
|
+
native: {
|
|
10
|
+
home: string;
|
|
11
|
+
executable: {
|
|
12
|
+
path: string;
|
|
13
|
+
target: string;
|
|
14
|
+
version: string;
|
|
15
|
+
sha256: string;
|
|
16
|
+
};
|
|
17
|
+
configRoot: string;
|
|
18
|
+
pluginRoot: string;
|
|
19
|
+
};
|
|
20
|
+
skills: {
|
|
21
|
+
dataDir: string;
|
|
22
|
+
policySha256: string;
|
|
23
|
+
discoverySha256: string;
|
|
24
|
+
discoveryInputs: ReviewedDiscoveryInputs;
|
|
25
|
+
};
|
|
26
|
+
candidate: {
|
|
27
|
+
marketplace: string;
|
|
28
|
+
root: string;
|
|
29
|
+
catalogPath: string;
|
|
30
|
+
catalogSha256: string;
|
|
31
|
+
closureSha256: string;
|
|
32
|
+
plugins: Array<{
|
|
33
|
+
plugin: string;
|
|
34
|
+
manifestPath: string;
|
|
35
|
+
manifestSha256: string;
|
|
36
|
+
optionsPatch: Record<string, unknown>;
|
|
37
|
+
}>;
|
|
38
|
+
releaseManifest?: {
|
|
39
|
+
path: string;
|
|
40
|
+
sha256: string;
|
|
41
|
+
};
|
|
42
|
+
functionHooksEnv?: "1";
|
|
43
|
+
};
|
|
44
|
+
preimages: {
|
|
45
|
+
settings: {
|
|
46
|
+
path: string;
|
|
47
|
+
sha256: string | null;
|
|
48
|
+
};
|
|
49
|
+
userSettings: {
|
|
50
|
+
path: string;
|
|
51
|
+
sha256: string | null;
|
|
52
|
+
};
|
|
53
|
+
projectSettings: {
|
|
54
|
+
path: string;
|
|
55
|
+
sha256: string | null;
|
|
56
|
+
};
|
|
57
|
+
projectSettingsLocal: {
|
|
58
|
+
path: string;
|
|
59
|
+
sha256: string | null;
|
|
60
|
+
};
|
|
61
|
+
marketplaces: {
|
|
62
|
+
path: string;
|
|
63
|
+
sha256: string | null;
|
|
64
|
+
};
|
|
65
|
+
installedPlugins: {
|
|
66
|
+
path: string;
|
|
67
|
+
sha256: string | null;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/** Complete semantic documents expected after native adoption. The one
|
|
71
|
+
* timestamps generated by Claude for the owned marketplace and registration
|
|
72
|
+
* rows use exact typed field markers documented below; every other field is
|
|
73
|
+
* concrete. */
|
|
74
|
+
delta: {
|
|
75
|
+
settingsAfter: Record<string, unknown>;
|
|
76
|
+
knownMarketplacesAfter: Record<string, unknown>;
|
|
77
|
+
installedPluginsAfter: Record<string, unknown>;
|
|
78
|
+
};
|
|
79
|
+
review: {
|
|
80
|
+
pluginHooks: "reviewed-no-skill-injection";
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export interface ClaudeProspectiveReviewReceipt {
|
|
84
|
+
schema: typeof CLAUDE_PROSPECTIVE_REVIEW_SCHEMA;
|
|
85
|
+
accepted: true;
|
|
86
|
+
applied: false;
|
|
87
|
+
scope: ClaudeProspectiveReviewRequest["scope"];
|
|
88
|
+
pluginIds: string[];
|
|
89
|
+
native: {
|
|
90
|
+
executablePath: string;
|
|
91
|
+
executableTarget: string;
|
|
92
|
+
executableVersion: string;
|
|
93
|
+
executableSha256: string;
|
|
94
|
+
configRoot: string;
|
|
95
|
+
pluginRoot: string;
|
|
96
|
+
};
|
|
97
|
+
candidate: {
|
|
98
|
+
marketplace: string;
|
|
99
|
+
plugins: Array<{
|
|
100
|
+
plugin: string;
|
|
101
|
+
pluginId: string;
|
|
102
|
+
manifestSha256: string;
|
|
103
|
+
optionsPatchSha256: string;
|
|
104
|
+
}>;
|
|
105
|
+
closureSha256: string;
|
|
106
|
+
catalogSha256: string;
|
|
107
|
+
releaseManifest?: {
|
|
108
|
+
path: string;
|
|
109
|
+
sha256: string;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
targetSettingsPath: string;
|
|
113
|
+
skillsDiscoverySha256: string;
|
|
114
|
+
requestFileSha256: string;
|
|
115
|
+
semanticDocumentsSha256: string;
|
|
116
|
+
operationSha256: string;
|
|
117
|
+
expectedPostStateSha256: string;
|
|
118
|
+
preimages: Array<{
|
|
119
|
+
name: string;
|
|
120
|
+
path: string;
|
|
121
|
+
sha256: string | null;
|
|
122
|
+
}>;
|
|
123
|
+
ownedDelta: {
|
|
124
|
+
settingsEnabledPlugins: string[];
|
|
125
|
+
selectedPluginOptions: string[];
|
|
126
|
+
functionHooksEnv: string | null;
|
|
127
|
+
knownMarketplace: string;
|
|
128
|
+
installedPlugins: string[];
|
|
129
|
+
unrelatedGraph: "preserved-by-complete-semantic-documents";
|
|
130
|
+
};
|
|
131
|
+
identityPrecondition: "Harnesses must resolve the installed Claude home/config/plugin roots and executable path/version/hash plus the installed Skills data directory through their package-owned consumers, then compare every value before trusting this receipt; this validator checks supplied paths and current bytes only and does not establish actual consumer identity";
|
|
132
|
+
reviewPrecondition: "The caller's no-skill-injection assessment is bound to the raw request hash; structural checks are not semantic hook review, and Harnesses must retain its existing review record and verify the exact candidate binding";
|
|
133
|
+
limitation: "validation-only; native installer atomicity and crash recovery are not established";
|
|
134
|
+
}
|
|
135
|
+
/** Fingerprint every file byte and directory name under the frozen candidate.
|
|
136
|
+
* Symlinks and special files refuse; the digest binds the complete closure. */
|
|
137
|
+
export declare function captureClaudeProspectiveCandidateClosure(root: string): string;
|
|
138
|
+
/** Validate a frozen candidate review against the current filesystem. This is
|
|
139
|
+
* intentionally a planner only: its receipt is not native-install authority. */
|
|
140
|
+
export declare function reviewClaudeProspectiveCandidate(request: ClaudeProspectiveReviewRequest, requestFileSha256: string): ClaudeProspectiveReviewReceipt;
|
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.57",
|
|
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 } : {} };
|