@hasna/skills 0.10.91 → 0.10.92
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/THIRD_PARTY_NOTICES +1 -1
- package/bin/index.js +247 -24
- package/bin/mcp.js +263 -24
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +245 -23
- package/dist/lib/agent-discovery.d.ts +14 -1
- package/dist/runtime-prerequisites.js +1 -1
- package/dist/sdk/index.js +497 -275
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16369,7 +16369,7 @@ import { basename as basename15, dirname as dirname27, isAbsolute as isAbsolute2
|
|
|
16369
16369
|
import { homedir as homedir5 } from "os";
|
|
16370
16370
|
import { isAbsolute as isAbsolute25, join as join39, resolve as resolve25 } from "path";
|
|
16371
16371
|
// package.json
|
|
16372
|
-
var version = "0.10.
|
|
16372
|
+
var version = "0.10.92";
|
|
16373
16373
|
var package_default = {
|
|
16374
16374
|
name: "@hasna/skills",
|
|
16375
16375
|
version,
|
|
@@ -26733,9 +26733,96 @@ function nativeDiscoveryAliasTargets(roots, home) {
|
|
|
26733
26733
|
}
|
|
26734
26734
|
return [...targets];
|
|
26735
26735
|
}
|
|
26736
|
+
function hooksAreManagedBridge(agent, hooks, managed) {
|
|
26737
|
+
const table = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
26738
|
+
const exactKeys3 = (value, keys2) => Object.keys(value).sort().join("\x00") === [...keys2].sort().join("\x00");
|
|
26739
|
+
if (!table(hooks) || typeof managed.command !== "string" || !isAbsolute28(managed.command))
|
|
26740
|
+
return false;
|
|
26741
|
+
const events3 = agent === "gemini" ? ["BeforeAgent", "SessionStart", "BeforeTool"] : ["beforeSubmitPrompt", "sessionStart"];
|
|
26742
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
26743
|
+
if (!events3.includes(event) || !Array.isArray(entries) || entries.length !== 1)
|
|
26744
|
+
return false;
|
|
26745
|
+
let expected;
|
|
26746
|
+
try {
|
|
26747
|
+
expected = renderAgentHookCommand(managed.command, agent, managed.profile, event);
|
|
26748
|
+
} catch {
|
|
26749
|
+
return false;
|
|
26750
|
+
}
|
|
26751
|
+
const entry = entries[0];
|
|
26752
|
+
if (!table(entry))
|
|
26753
|
+
return false;
|
|
26754
|
+
if (agent === "cursor") {
|
|
26755
|
+
if (!exactKeys3(entry, ["command", "timeout"]) || entry.timeout !== 15 || entry.command !== expected)
|
|
26756
|
+
return false;
|
|
26757
|
+
continue;
|
|
26758
|
+
}
|
|
26759
|
+
if (!exactKeys3(entry, event === "BeforeTool" ? ["hooks", "matcher"] : ["hooks"]) || event === "BeforeTool" && entry.matcher !== "activate_skill" || !Array.isArray(entry.hooks) || entry.hooks.length !== 1)
|
|
26760
|
+
return false;
|
|
26761
|
+
const hook = entry.hooks[0];
|
|
26762
|
+
if (!table(hook) || !exactKeys3(hook, ["command", "timeout", "type"]) || hook.type !== "command" || hook.timeout !== 15000 || hook.command !== expected)
|
|
26763
|
+
return false;
|
|
26764
|
+
}
|
|
26765
|
+
return true;
|
|
26766
|
+
}
|
|
26767
|
+
function sumiPluginLoadsOnlyBuiltins(text2) {
|
|
26768
|
+
for (const match of text2.matchAll(/\b(?:import|require)\s*\(\s*(.?)/g)) {
|
|
26769
|
+
const rest = text2.slice(match.index + match[0].length - match[1].length);
|
|
26770
|
+
if (!/^(['"])node:[A-Za-z0-9_/.-]+\1/.test(rest))
|
|
26771
|
+
return false;
|
|
26772
|
+
}
|
|
26773
|
+
for (const match of text2.matchAll(/\b(?:from|import)\s*(['"])([^'"]*)\1/g))
|
|
26774
|
+
if (!match[2].startsWith("node:"))
|
|
26775
|
+
return false;
|
|
26776
|
+
return true;
|
|
26777
|
+
}
|
|
26778
|
+
var CODEX_MARKETPLACE_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
26779
|
+
function codexRootFreeControls(config) {
|
|
26780
|
+
const table = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date));
|
|
26781
|
+
const adapted = [];
|
|
26782
|
+
const servers = config.mcp_servers;
|
|
26783
|
+
if (table(servers) && Object.keys(servers).length <= AGENT_POLICY_LIMITS.discoveryRoots && Object.entries(servers).every(([name, row]) => name !== "codex_apps" && name !== "_codex_apps" && isCodexLocalStdioMcpServer(name, row)))
|
|
26784
|
+
adapted.push("mcp_servers");
|
|
26785
|
+
const agents = config.agents;
|
|
26786
|
+
if (table(agents) && Object.keys(agents).length <= AGENT_POLICY_LIMITS.fields && Object.values(agents).every((value) => typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)))
|
|
26787
|
+
adapted.push("agents");
|
|
26788
|
+
return adapted;
|
|
26789
|
+
}
|
|
26790
|
+
var CODEX_HOOK_EVENTS = new Set(["PreToolUse", "PermissionRequest", "PostToolUse", "PreCompact", "PostCompact", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart", "SubagentStop", "Stop", "Interrupt"]);
|
|
26791
|
+
function codexPluginHookHandlers(value) {
|
|
26792
|
+
const table = (item) => Boolean(item && typeof item === "object" && !Array.isArray(item));
|
|
26793
|
+
const identifier6 = (item) => typeof item === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(item);
|
|
26794
|
+
const text2 = (item) => typeof item === "string" && item.length <= AGENT_POLICY_LIMITS.pathCharacters && !item.includes("\x00");
|
|
26795
|
+
if (!table(value) || !Object.keys(value).every((key2) => key2 === "hooks" || key2 === "description") || value.description !== undefined && !text2(value.description) || !table(value.hooks) || Object.keys(value.hooks).length > 32)
|
|
26796
|
+
return null;
|
|
26797
|
+
const result = { commands: 0, servers: new Set };
|
|
26798
|
+
for (const [event, groups] of Object.entries(value.hooks)) {
|
|
26799
|
+
if (!CODEX_HOOK_EVENTS.has(event) || !Array.isArray(groups) || groups.length > 32)
|
|
26800
|
+
return null;
|
|
26801
|
+
for (const group of groups) {
|
|
26802
|
+
if (!table(group) || !Object.keys(group).every((key2) => key2 === "hooks" || key2 === "matcher") || group.matcher !== undefined && !text2(group.matcher) || !Array.isArray(group.hooks) || group.hooks.length > 32)
|
|
26803
|
+
return null;
|
|
26804
|
+
for (const handler of group.hooks) {
|
|
26805
|
+
if (!table(handler) || handler.timeout !== undefined && !(Number.isSafeInteger(handler.timeout) && handler.timeout >= 0) || handler.statusMessage !== undefined && !text2(handler.statusMessage))
|
|
26806
|
+
return null;
|
|
26807
|
+
if (handler.type === "command") {
|
|
26808
|
+
if (!text2(handler.command) || !handler.command || !Object.keys(handler).every((key2) => ["type", "command", "commandWindows", "command_windows", "timeout", "async", "statusMessage", "additionalContextLimit"].includes(key2)))
|
|
26809
|
+
return null;
|
|
26810
|
+
result.commands++;
|
|
26811
|
+
} else if (handler.type === "mcp_tool") {
|
|
26812
|
+
if (!identifier6(handler.server) || !identifier6(handler.tool) || handler.input !== undefined && !table(handler.input) || !Object.keys(handler).every((key2) => ["type", "server", "tool", "input", "timeout", "statusMessage"].includes(key2)))
|
|
26813
|
+
return null;
|
|
26814
|
+
result.servers.add(handler.server);
|
|
26815
|
+
} else
|
|
26816
|
+
return null;
|
|
26817
|
+
}
|
|
26818
|
+
}
|
|
26819
|
+
}
|
|
26820
|
+
return result;
|
|
26821
|
+
}
|
|
26736
26822
|
function resolveDiscovery(options, exclusion) {
|
|
26737
26823
|
const canonical9 = options.canonical ?? resolve28, home = resolve28(options.home), agent = options.agent;
|
|
26738
26824
|
const sources = [], roots = new Set, builtinNames = [];
|
|
26825
|
+
const sumiLocalPluginTexts = new Map;
|
|
26739
26826
|
const budget3 = discoveryByteBudget();
|
|
26740
26827
|
let remainingEntries = AGENT_POLICY_LIMITS.discoveryDirectoryEntries;
|
|
26741
26828
|
const entries = (path) => {
|
|
@@ -26812,12 +26899,38 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26812
26899
|
const controls = agent === "claude" ? claudeDiscoveryControlKeys(configText ?? "{}") : agent === "codex" ? codexDiscoveryControlKeys(config) : Object.keys(config).filter((key2) => /skill|plugin|hook|marketplace|extension|instruction/i.test(key2));
|
|
26813
26900
|
const fieldKey = (key2) => /^[A-Za-z_$][A-Za-z0-9_$.-]{0,22}$/.test(key2) && !/(?:sk|rk|pk)_(?:live|test)_|whsec_/i.test(key2) ? JSON.stringify(key2) : "[withheld field key]";
|
|
26814
26901
|
const unsupportedFields = (names, label) => unresolved(`unknown discovery controls have no root-exclusion adapter for ${label}${names.slice(0, 8).map(fieldKey).join(", ")}${names.length > 8 ? ", additional keys withheld" : ""} in ${quotedPath(canonical9(configPath))}`);
|
|
26815
|
-
const
|
|
26902
|
+
const adapted = agent === "codex" ? codexRootFreeControls(config) : agent === "opencode" && Array.isArray(config.instructions) && config.instructions.length === 0 ? ["instructions"] : [];
|
|
26903
|
+
const unsupportedControls = controls.filter((key2) => !supported[agent].includes(key2) && !adapted.includes(key2));
|
|
26816
26904
|
if (unsupportedControls.length)
|
|
26817
26905
|
unsupportedFields(unsupportedControls, "");
|
|
26818
26906
|
if (config.hooks !== undefined && !object6(config.hooks))
|
|
26819
26907
|
unresolved("hooks must be an object");
|
|
26820
|
-
const
|
|
26908
|
+
const managedSupervisorOnly = () => {
|
|
26909
|
+
const supervisor = exclusion.managedHook?.supervisor;
|
|
26910
|
+
if (agent !== "hermes" || !supervisor || typeof supervisor.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(supervisor.sha256) || !object6(config.hooks))
|
|
26911
|
+
return false;
|
|
26912
|
+
let expected;
|
|
26913
|
+
try {
|
|
26914
|
+
expected = hermesHookDefinitions(supervisor);
|
|
26915
|
+
} catch {
|
|
26916
|
+
return false;
|
|
26917
|
+
}
|
|
26918
|
+
for (const [event, entries2] of Object.entries(config.hooks)) {
|
|
26919
|
+
const want = expected[event];
|
|
26920
|
+
if (!want || !Array.isArray(entries2) || entries2.length !== 1 || !object6(entries2[0]))
|
|
26921
|
+
return false;
|
|
26922
|
+
const entry = entries2[0];
|
|
26923
|
+
if (Object.keys(entry).sort().join("\x00") !== Object.keys(want).sort().join("\x00") || Object.keys(want).some((key2) => entry[key2] !== want[key2]))
|
|
26924
|
+
return false;
|
|
26925
|
+
}
|
|
26926
|
+
try {
|
|
26927
|
+
return hashRawDiscoveryFile(canonical9(supervisor.path), budget3) === supervisor.sha256;
|
|
26928
|
+
} catch {
|
|
26929
|
+
return false;
|
|
26930
|
+
}
|
|
26931
|
+
};
|
|
26932
|
+
const managedBridgeOnly = (agent === "gemini" || agent === "cursor") && exclusion.managedHook !== undefined && hooksAreManagedBridge(agent, config.hooks, exclusion.managedHook) || managedSupervisorOnly();
|
|
26933
|
+
const hasHooks = controls.includes("hooks") && config.hooks !== undefined && Object.keys(config.hooks).length > 0 && !managedBridgeOnly;
|
|
26821
26934
|
let hasPluginConfigs = false, hasFunctionHookEnvironment = false;
|
|
26822
26935
|
const verifyCurrentOpaqueSettings = (label) => {
|
|
26823
26936
|
const retained = exclusion.retained;
|
|
@@ -26922,7 +27035,9 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26922
27035
|
unresolved("malformed Sumi plugins");
|
|
26923
27036
|
}
|
|
26924
27037
|
if (agent === "hermes" && config.skills !== undefined) {
|
|
26925
|
-
keys2(config.skills, ["external_dirs"], "Hermes skills");
|
|
27038
|
+
keys2(config.skills, ["external_dirs", "creation_nudge_interval"], "Hermes skills");
|
|
27039
|
+
if (config.skills.creation_nudge_interval !== undefined && !(Number.isSafeInteger(config.skills.creation_nudge_interval) && config.skills.creation_nudge_interval >= 0))
|
|
27040
|
+
unresolved("malformed Hermes skills creation_nudge_interval");
|
|
26926
27041
|
if (config.skills.external_dirs !== undefined)
|
|
26927
27042
|
paths(typeof config.skills.external_dirs === "string" ? [config.skills.external_dirs] : config.skills.external_dirs, "Hermes external directories");
|
|
26928
27043
|
}
|
|
@@ -26942,10 +27057,21 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26942
27057
|
unresolved(`malformed Codex ${key2}`);
|
|
26943
27058
|
for (const value of Object.values(config.plugins ?? {}))
|
|
26944
27059
|
keys2(value, ["enabled"], "Codex plugin registration");
|
|
26945
|
-
for (const value of Object.values(config.marketplaces ?? {}))
|
|
26946
|
-
keys2(value, ["source_type", "source"], "Codex marketplace registration");
|
|
27060
|
+
for (const value of Object.values(config.marketplaces ?? {})) {
|
|
27061
|
+
keys2(value, ["source_type", "source", "last_updated"], "Codex marketplace registration");
|
|
27062
|
+
if (value.last_updated !== undefined && (typeof value.last_updated !== "string" || !CODEX_MARKETPLACE_TIMESTAMP.test(value.last_updated)))
|
|
27063
|
+
unresolved("malformed Codex marketplace refresh time");
|
|
27064
|
+
}
|
|
26947
27065
|
if (config.skills !== undefined)
|
|
26948
|
-
keys2(config.skills, ["config", "bundled"], "Codex skills");
|
|
27066
|
+
keys2(config.skills, ["config", "bundled", "native_allowed_paths"], "Codex skills");
|
|
27067
|
+
if (config.skills?.native_allowed_paths !== undefined) {
|
|
27068
|
+
for (const value of paths(config.skills.native_allowed_paths, "Codex native allowed skill paths")) {
|
|
27069
|
+
if (!isAbsolute28(value))
|
|
27070
|
+
unresolved("relative Codex native allowed skill path");
|
|
27071
|
+
const path = canonical9(value);
|
|
27072
|
+
roots.add(path.endsWith(`${sep15}SKILL.md`) ? dirname29(path) : path);
|
|
27073
|
+
}
|
|
27074
|
+
}
|
|
26949
27075
|
if (config.skills?.bundled !== undefined) {
|
|
26950
27076
|
keys2(config.skills.bundled, ["enabled"], "Codex bundled skills");
|
|
26951
27077
|
if (typeof config.skills.bundled.enabled !== "boolean")
|
|
@@ -27003,7 +27129,9 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27003
27129
|
const target = join41(path, name);
|
|
27004
27130
|
if (!lstatSync34(target).isFile())
|
|
27005
27131
|
unresolved("local plugin packages need a dedicated discovery adapter");
|
|
27006
|
-
witness(target);
|
|
27132
|
+
const text2 = witness(target);
|
|
27133
|
+
if (exclusion && text2 !== null)
|
|
27134
|
+
sumiLocalPluginTexts.set(canonical9(target), text2);
|
|
27007
27135
|
}
|
|
27008
27136
|
}
|
|
27009
27137
|
for (const root2 of roots) {
|
|
@@ -27137,16 +27265,88 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27137
27265
|
const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual9(candidate2, source)) === index);
|
|
27138
27266
|
return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
|
|
27139
27267
|
}
|
|
27140
|
-
|
|
27268
|
+
const reviewedManifestAbsence = (root2, manifestPath) => {
|
|
27269
|
+
const retained = exclusion?.retained;
|
|
27270
|
+
if (agent !== "claude" || retained?.method !== "reviewed" || ![join41(root2, "skills"), root2].some((path) => retained.roots.includes(path)))
|
|
27271
|
+
return false;
|
|
27272
|
+
const saved = retained.sources.filter((source) => source.path === canonical9(manifestPath) && source.hashMode === "path-bytes" && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined);
|
|
27273
|
+
return saved.length > 0 && saved.every((source) => projected(source, undefined, budget3) === source.sha256);
|
|
27274
|
+
};
|
|
27275
|
+
const reviewedInstalledCopy = (root2, path, identity5) => {
|
|
27276
|
+
const retained = exclusion?.retained;
|
|
27277
|
+
if (agent !== "codex" || retained?.method !== "reviewed")
|
|
27278
|
+
return false;
|
|
27279
|
+
const inside = relative10(root2, canonical9(path));
|
|
27280
|
+
if (!inside || inside === ".." || inside.startsWith(`..${sep15}`) || isAbsolute28(inside))
|
|
27281
|
+
return false;
|
|
27282
|
+
const parent = canonical9(join41(nativeHome, "plugins/cache", identity5.marketplace, identity5.name));
|
|
27283
|
+
const current = hashRawDiscoveryFile(canonical9(path), budget3);
|
|
27284
|
+
if (current === null)
|
|
27285
|
+
return false;
|
|
27286
|
+
return retained.sources.some((source) => {
|
|
27287
|
+
if (source.sha256 !== current || source.format !== undefined || source.fields !== undefined || ![undefined, "bytes"].includes(source.hashMode) || source.managedPlugins !== undefined || source.reviewArtifact !== undefined || !source.path.startsWith(parent + sep15))
|
|
27288
|
+
return false;
|
|
27289
|
+
const [version2, ...rest] = source.path.slice(parent.length + 1).split(sep15);
|
|
27290
|
+
return Boolean(version2) && version2 !== "." && version2 !== ".." && rest.join(sep15) === inside;
|
|
27291
|
+
});
|
|
27292
|
+
};
|
|
27293
|
+
const verifyCodexPluginHookClosure = (root2, identity5, marketplaceSource, servers, declarations) => {
|
|
27294
|
+
const retained = exclusion.retained;
|
|
27295
|
+
if (retained.codexSemanticCache || retained.codexInstallationInputs || retained.codexRetiredMaterializations)
|
|
27296
|
+
return verifyOpaqueClosure(declarations);
|
|
27297
|
+
const within2 = (base, path) => path === base || path.startsWith(base + sep15);
|
|
27298
|
+
const executables = [];
|
|
27299
|
+
for (const server of servers) {
|
|
27300
|
+
const row = config.mcp_servers?.[server];
|
|
27301
|
+
if (server === "codex_apps" || server === "_codex_apps" || !isCodexLocalStdioMcpServer(server, row))
|
|
27302
|
+
unresolved("plugin hook MCP server is not a configured local stdio server");
|
|
27303
|
+
for (const value of [row.command, ...row.args ?? []])
|
|
27304
|
+
for (const candidate2 of [value, value.slice(value.indexOf("=") + 1)]) {
|
|
27305
|
+
if (!isAbsolute28(candidate2) || /[\0$]/.test(candidate2) || executables.length >= AGENT_POLICY_LIMITS.discoveryRoots)
|
|
27306
|
+
continue;
|
|
27307
|
+
const path = canonical9(candidate2);
|
|
27308
|
+
if (lstatSync34(path, { throwIfNoEntry: false })?.isFile())
|
|
27309
|
+
executables.push(path);
|
|
27310
|
+
}
|
|
27311
|
+
}
|
|
27312
|
+
const cacheRoot = canonical9(join41(nativeHome, "plugins/cache"));
|
|
27313
|
+
const ownCache = canonical9(join41(nativeHome, "plugins/cache", identity5.marketplace, identity5.name));
|
|
27314
|
+
const ownCatalog = canonical9(join41(marketplaceSource, ".agents/plugins/marketplace.json"));
|
|
27315
|
+
const account = canonical9(home);
|
|
27316
|
+
const marketplaceRoots = Object.values(config.marketplaces ?? {}).filter((market) => market?.source_type === "local" && typeof market.source === "string" && isAbsolute28(market.source)).map((market) => canonical9(market.source)).filter((path) => !within2(path, account));
|
|
27317
|
+
const otherPlugin = (path) => !within2(root2, path) && !within2(ownCache, path) && path !== ownCatalog && !executables.includes(path) && (within2(cacheRoot, path) || marketplaceRoots.some((base) => within2(base, path)));
|
|
27318
|
+
const sources2 = retained.sources.filter((source) => source.path !== canonical9(configPath) && !otherPlugin(source.path));
|
|
27319
|
+
if (sources2.some((source) => source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.reviewArtifact !== undefined || ![undefined, "bytes", "path-bytes", "executable-v1"].includes(source.hashMode)))
|
|
27320
|
+
return verifyOpaqueClosure(declarations);
|
|
27321
|
+
const declared = declarations.map((path) => relative10(root2, path));
|
|
27322
|
+
const declaration = (path) => declarations.includes(path) || path.startsWith(ownCache + sep15) && declared.includes(path.slice(ownCache.length + 1).split(sep15).slice(1).join(sep15));
|
|
27323
|
+
if (!sources2.some((source) => !declaration(source.path) && source.sha256 !== null))
|
|
27324
|
+
unresolved("opaque executable dependency closure is unavailable");
|
|
27325
|
+
for (const source of sources2)
|
|
27326
|
+
if (projected(source, undefined, budget3) !== source.sha256) {
|
|
27327
|
+
throw new NativeDiscoveryWitnessChange("input", source.path, `Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
27328
|
+
}
|
|
27329
|
+
verifyDiscoveryDirectories((retained.directories ?? []).filter((directory2) => !otherPlugin(directory2.path)));
|
|
27330
|
+
};
|
|
27331
|
+
const readCodexHookDeclaration = (text2, path) => {
|
|
27332
|
+
if (text2 === null)
|
|
27333
|
+
return null;
|
|
27334
|
+
try {
|
|
27335
|
+
return codexPluginHookHandlers(parseConfig(text2, path));
|
|
27336
|
+
} catch {
|
|
27337
|
+
return null;
|
|
27338
|
+
}
|
|
27339
|
+
};
|
|
27340
|
+
function plugin(root2, codex) {
|
|
27141
27341
|
root2 = canonical9(root2);
|
|
27142
27342
|
safe6(root2);
|
|
27143
27343
|
if (exclusion)
|
|
27144
27344
|
roots.add(root2);
|
|
27145
27345
|
const manifestPath = join41(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
|
|
27146
27346
|
const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
|
|
27147
|
-
if (raw === null)
|
|
27347
|
+
if (raw === null && !reviewedManifestAbsence(root2, manifestPath))
|
|
27148
27348
|
unresolved("plugin manifest missing");
|
|
27149
|
-
const manifest = parseConfig(raw, manifestPath);
|
|
27349
|
+
const manifest = raw === null ? {} : parseConfig(raw, manifestPath);
|
|
27150
27350
|
if (exclusion && Object.keys(manifest).some((key2) => /skill|plugin|hook|marketplace|extension|instruction/i.test(key2) && !["skills", "hooks"].includes(key2)))
|
|
27151
27351
|
unresolved("unknown plugin discovery controls");
|
|
27152
27352
|
const hooks = witness(join41(root2, "hooks/hooks.json"));
|
|
@@ -27154,20 +27354,35 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27154
27354
|
if (!exclusion || exclusion.retained?.method !== "reviewed")
|
|
27155
27355
|
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
27156
27356
|
const required = [manifestPath, ...hooks !== null ? [join41(root2, "hooks/hooks.json")] : []];
|
|
27357
|
+
const scoped2 = agent === "codex" && codex !== undefined ? [] : undefined;
|
|
27157
27358
|
if (manifest.hooks !== undefined) {
|
|
27158
|
-
if (typeof manifest.hooks
|
|
27159
|
-
|
|
27160
|
-
|
|
27161
|
-
|
|
27162
|
-
|
|
27163
|
-
|
|
27164
|
-
|
|
27359
|
+
if (scoped2 && manifest.hooks && typeof manifest.hooks === "object" && !Array.isArray(manifest.hooks)) {
|
|
27360
|
+
const inline = codexPluginHookHandlers(manifest.hooks);
|
|
27361
|
+
if (!inline)
|
|
27362
|
+
unresolved("unsupported inline plugin hooks");
|
|
27363
|
+
scoped2.push(inline);
|
|
27364
|
+
} else {
|
|
27365
|
+
if (typeof manifest.hooks !== "string")
|
|
27366
|
+
unresolved("unsupported plugin hooks target");
|
|
27367
|
+
const hook = resolve28(root2, manifest.hooks);
|
|
27368
|
+
if (!hook.startsWith(root2 + sep15))
|
|
27369
|
+
unresolved("plugin hooks target escapes its root");
|
|
27370
|
+
required.push(hook);
|
|
27371
|
+
if (scoped2)
|
|
27372
|
+
scoped2.push(readCodexHookDeclaration(witness(hook), hook));
|
|
27373
|
+
}
|
|
27374
|
+
}
|
|
27375
|
+
if (scoped2 && hooks !== null)
|
|
27376
|
+
scoped2.push(readCodexHookDeclaration(hooks, join41(root2, "hooks/hooks.json")));
|
|
27165
27377
|
for (const path of required) {
|
|
27166
27378
|
const saved = exclusion.retained.sources.filter((source) => source.path === canonical9(path) && source.sha256 !== null && source.format === undefined && source.fields === undefined);
|
|
27167
|
-
if (
|
|
27379
|
+
if (saved.length ? saved.some((source) => projected(source, undefined, budget3) !== source.sha256) : !(scoped2 && reviewedInstalledCopy(root2, path, codex)))
|
|
27168
27380
|
unresolved("plugin hooks require current independently reviewed sources");
|
|
27169
27381
|
}
|
|
27170
|
-
|
|
27382
|
+
if (scoped2 && scoped2.every((item) => item !== null && item.commands === 0)) {
|
|
27383
|
+
verifyCodexPluginHookClosure(root2, codex, codex.source, new Set(scoped2.flatMap((item) => [...item.servers])), required.map((path) => canonical9(path)));
|
|
27384
|
+
} else
|
|
27385
|
+
verifyOpaqueClosure(required.map((path) => canonical9(path)));
|
|
27171
27386
|
}
|
|
27172
27387
|
const commands = join41(root2, "commands");
|
|
27173
27388
|
safe6(commands);
|
|
@@ -27223,7 +27438,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27223
27438
|
const catalog = parseConfig(catalogText, join41(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
|
|
27224
27439
|
if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
|
|
27225
27440
|
unresolved("plugin catalog source is missing or ambiguous");
|
|
27226
|
-
plugin(resolve28(market.source, matches[0].source.path));
|
|
27441
|
+
plugin(resolve28(market.source, matches[0].source.path), exclusion ? { marketplace, name, source: market.source } : undefined);
|
|
27227
27442
|
}
|
|
27228
27443
|
} else if (agent === "opencode") {
|
|
27229
27444
|
if (witness(join41(home, ".config/opencode/opencode.jsonc")) !== null || witness(join41(home, ".config/opencode/config.json")) !== null)
|
|
@@ -27240,7 +27455,11 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27240
27455
|
if (lstatSync34(localPlugins, { throwIfNoEntry: false }) && entries(localPlugins).some((name) => name !== "skills-cli.js"))
|
|
27241
27456
|
unresolved("additional local plugin hooks require review");
|
|
27242
27457
|
} else if (agent === "sumi") {
|
|
27243
|
-
|
|
27458
|
+
const pinnedLocalPlugin = (source) => {
|
|
27459
|
+
const retained = exclusion?.retained, text2 = sumiLocalPluginTexts.get(source.path);
|
|
27460
|
+
return retained?.method === "reviewed" && text2 !== undefined && sumiPluginLoadsOnlyBuiltins(text2) && retained.sources.some((saved) => saved.path === source.path && saved.sha256 === source.sha256 && saved.sha256 !== null && [undefined, "bytes"].includes(saved.hashMode) && saved.format === undefined && saved.fields === undefined && saved.managedPlugins === undefined && saved.reviewArtifact === undefined);
|
|
27461
|
+
};
|
|
27462
|
+
if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical9(configPath) && source.path !== canonical9(join41(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null && !pinnedLocalPlugin(source)))
|
|
27244
27463
|
unresolved("external or foreign local plugins require review");
|
|
27245
27464
|
} else if (agent === "hermes") {
|
|
27246
27465
|
for (const directory2 of [join41(home, ".hermes/plugins"), join41(home, ".hermes/hermes-agent")]) {
|
|
@@ -27348,7 +27567,10 @@ function configuredRoots(home, explicitHome) {
|
|
|
27348
27567
|
if (recorded && (!executable2 || executable2.target !== recorded.target || onPath && onPath.target !== recorded.target) || !executable2 && (binding.builtinNames?.length || binding.agent === "gemini" && binding.sources.some((source) => basename18(source.path) === "package.json"))) {
|
|
27349
27568
|
throw new Error("Native discovery runtime target is unresolved or changed");
|
|
27350
27569
|
}
|
|
27351
|
-
|
|
27570
|
+
const managedCommand = current.commands?.[agent], managedProfile = current.profiles?.[agent];
|
|
27571
|
+
const supervisor = current.supervisors?.[agent];
|
|
27572
|
+
const managedHook = (binding.agent === "gemini" || binding.agent === "cursor") && typeof managedCommand === "string" && typeof managedProfile === "string" ? { command: managedCommand, profile: managedProfile } : binding.agent === "hermes" && typeof managedCommand === "string" && typeof managedProfile === "string" && supervisor && typeof supervisor === "object" && typeof supervisor.path === "string" && typeof supervisor.runtime === "string" && typeof supervisor.sha256 === "string" ? { command: managedCommand, profile: managedProfile, supervisor: { path: supervisor.path, runtime: supervisor.runtime, sha256: supervisor.sha256 } } : undefined;
|
|
27573
|
+
roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: binding.agent, canonical: physicalPath, retained: binding, executable: executable2, narrowed, ...managedHook ? { managedHook } : {} }));
|
|
27352
27574
|
}
|
|
27353
27575
|
};
|
|
27354
27576
|
if (homePolicy)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type IntegrationAgent } from "./agent-adapters.js";
|
|
2
2
|
import { type DiscoveryDirectory } from "./agent-discovery-directories.js";
|
|
3
3
|
import { type CodexReviewedHookSource } from "./codex-hook-mcp-dependencies.js";
|
|
4
4
|
import { CLAUDE_ORPHAN_MARKER_MODE } from "./claude-plugin-cache-marker.js";
|
|
@@ -214,8 +214,21 @@ export declare function resolveAgentDiscoveryExclusionRoots(options: Omit<Discov
|
|
|
214
214
|
retained?: AgentDiscoveryBinding;
|
|
215
215
|
narrowed?: NarrowedWitnessLedger;
|
|
216
216
|
nativeHome?: string;
|
|
217
|
+
managedHook?: ManagedBridgeHook;
|
|
217
218
|
}): readonly string[];
|
|
218
219
|
/** Metadata-only closure of aliases inside the selected native discovery
|
|
219
220
|
* trees. Never search the account generally or load a payload/plugin. Each
|
|
220
221
|
* resolved alias remains excluded while unrelated portable paths stay usable. */
|
|
221
222
|
export declare function nativeDiscoveryAliasTargets(roots: readonly string[], home: string): string[];
|
|
223
|
+
/** The Skills bridge command and selection profile the policy records for an
|
|
224
|
+
* agent (`bridge.commands` and `bridge.profiles`), from which hook install
|
|
225
|
+
* renders its managed hook commands. */
|
|
226
|
+
export interface ManagedBridgeHook {
|
|
227
|
+
command: string;
|
|
228
|
+
profile: string;
|
|
229
|
+
supervisor?: {
|
|
230
|
+
path: string;
|
|
231
|
+
runtime: string;
|
|
232
|
+
sha256: string;
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -49,7 +49,7 @@ var __require = import.meta.require;
|
|
|
49
49
|
// src/runtime-prerequisites.ts
|
|
50
50
|
import { isAbsolute as isAbsolute7 } from "path";
|
|
51
51
|
// package.json
|
|
52
|
-
var version = "0.10.
|
|
52
|
+
var version = "0.10.92";
|
|
53
53
|
var package_default = {
|
|
54
54
|
name: "@hasna/skills",
|
|
55
55
|
version,
|