@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/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.91";
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 unsupportedControls = controls.filter((key2) => !supported[agent].includes(key2));
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 hasHooks = controls.includes("hooks") && config.hooks !== undefined && Object.keys(config.hooks).length > 0;
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
- function plugin(root2) {
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 !== "string")
27159
- unresolved("unsupported plugin hooks target");
27160
- const hook = resolve28(root2, manifest.hooks);
27161
- if (!hook.startsWith(root2 + sep15))
27162
- unresolved("plugin hooks target escapes its root");
27163
- required.push(hook);
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 (!saved.length || saved.some((source) => projected(source, undefined, budget3) !== source.sha256))
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
- verifyOpaqueClosure(required.map((path) => canonical9(path)));
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
- 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))
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
- roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: binding.agent, canonical: physicalPath, retained: binding, executable: executable2, narrowed }));
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 { IntegrationAgent } from "./agent-adapters.js";
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.91";
52
+ var version = "0.10.92";
53
53
  var package_default = {
54
54
  name: "@hasna/skills",
55
55
  version,