@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/bin/mcp.js CHANGED
@@ -38,7 +38,7 @@ var package_default;
38
38
  var init_package = __esm(() => {
39
39
  package_default = {
40
40
  name: "@hasna/skills",
41
- version: "0.10.91",
41
+ version: "0.10.92",
42
42
  description: "Skills library for AI coding agents",
43
43
  type: "module",
44
44
  bin: {
@@ -22526,6 +22526,11 @@ var init_agent_opencode = __esm(() => {
22526
22526
  });
22527
22527
 
22528
22528
  // src/lib/agent-adapters.ts
22529
+ function renderAgentHookCommand(command, agent, profileId, event) {
22530
+ if (typeof profileId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) || profileId.includes(".."))
22531
+ throw new Error("Invalid selection profile id");
22532
+ return `'${command.replace(/'/g, `'\\''`)}' hook user-prompt --agent ${agent} --selection-profile ${profileId} --event ${event}`;
22533
+ }
22529
22534
  var INTEGRATION_AGENTS;
22530
22535
  var init_agent_adapters = __esm(() => {
22531
22536
  init_agent_opencode();
@@ -30556,6 +30561,17 @@ function parseHermesConfig(text2) {
30556
30561
  throw new Error(`Expected Hermes ${name} object`);
30557
30562
  return value;
30558
30563
  }
30564
+ function quoted(value) {
30565
+ if (!value || /[\0\r\n]/.test(value))
30566
+ throw new Error("Invalid Hermes Skills command");
30567
+ return `'${value.replace(/'/g, `'\\''`)}'`;
30568
+ }
30569
+ function hermesHookDefinitions(supervisor) {
30570
+ if (!isAbsolute7(supervisor.runtime) || !isAbsolute7(supervisor.path))
30571
+ throw new Error("Expected absolute Hermes supervisor paths");
30572
+ const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
30573
+ return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
30574
+ }
30559
30575
  function assertHermesEnvironment(home) {
30560
30576
  if (process.env.TERMINAL_CWD && process.env.TERMINAL_CWD !== process.cwd())
30561
30577
  throw new NativeBridgeIntegrityRefusal("NATIVE_SKILL_DRIFT: Hermes TERMINAL_CWD must match the checked process working directory; review the effective project before using this adapter");
@@ -38666,9 +38682,94 @@ function nativeDiscoveryAliasTargets(roots, home) {
38666
38682
  }
38667
38683
  return [...targets];
38668
38684
  }
38685
+ function hooksAreManagedBridge(agent, hooks, managed) {
38686
+ const table = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
38687
+ const exactKeys3 = (value, keys2) => Object.keys(value).sort().join("\x00") === [...keys2].sort().join("\x00");
38688
+ if (!table(hooks) || typeof managed.command !== "string" || !isAbsolute22(managed.command))
38689
+ return false;
38690
+ const events3 = agent === "gemini" ? ["BeforeAgent", "SessionStart", "BeforeTool"] : ["beforeSubmitPrompt", "sessionStart"];
38691
+ for (const [event, entries] of Object.entries(hooks)) {
38692
+ if (!events3.includes(event) || !Array.isArray(entries) || entries.length !== 1)
38693
+ return false;
38694
+ let expected;
38695
+ try {
38696
+ expected = renderAgentHookCommand(managed.command, agent, managed.profile, event);
38697
+ } catch {
38698
+ return false;
38699
+ }
38700
+ const entry = entries[0];
38701
+ if (!table(entry))
38702
+ return false;
38703
+ if (agent === "cursor") {
38704
+ if (!exactKeys3(entry, ["command", "timeout"]) || entry.timeout !== 15 || entry.command !== expected)
38705
+ return false;
38706
+ continue;
38707
+ }
38708
+ if (!exactKeys3(entry, event === "BeforeTool" ? ["hooks", "matcher"] : ["hooks"]) || event === "BeforeTool" && entry.matcher !== "activate_skill" || !Array.isArray(entry.hooks) || entry.hooks.length !== 1)
38709
+ return false;
38710
+ const hook = entry.hooks[0];
38711
+ if (!table(hook) || !exactKeys3(hook, ["command", "timeout", "type"]) || hook.type !== "command" || hook.timeout !== 15000 || hook.command !== expected)
38712
+ return false;
38713
+ }
38714
+ return true;
38715
+ }
38716
+ function sumiPluginLoadsOnlyBuiltins(text2) {
38717
+ for (const match of text2.matchAll(/\b(?:import|require)\s*\(\s*(.?)/g)) {
38718
+ const rest = text2.slice(match.index + match[0].length - match[1].length);
38719
+ if (!/^(['"])node:[A-Za-z0-9_/.-]+\1/.test(rest))
38720
+ return false;
38721
+ }
38722
+ for (const match of text2.matchAll(/\b(?:from|import)\s*(['"])([^'"]*)\1/g))
38723
+ if (!match[2].startsWith("node:"))
38724
+ return false;
38725
+ return true;
38726
+ }
38727
+ function codexRootFreeControls(config2) {
38728
+ const table = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date));
38729
+ const adapted = [];
38730
+ const servers = config2.mcp_servers;
38731
+ 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)))
38732
+ adapted.push("mcp_servers");
38733
+ const agents = config2.agents;
38734
+ 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)))
38735
+ adapted.push("agents");
38736
+ return adapted;
38737
+ }
38738
+ function codexPluginHookHandlers(value) {
38739
+ const table = (item) => Boolean(item && typeof item === "object" && !Array.isArray(item));
38740
+ const identifier5 = (item) => typeof item === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(item);
38741
+ const text2 = (item) => typeof item === "string" && item.length <= AGENT_POLICY_LIMITS.pathCharacters && !item.includes("\x00");
38742
+ 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)
38743
+ return null;
38744
+ const result = { commands: 0, servers: new Set };
38745
+ for (const [event, groups] of Object.entries(value.hooks)) {
38746
+ if (!CODEX_HOOK_EVENTS.has(event) || !Array.isArray(groups) || groups.length > 32)
38747
+ return null;
38748
+ for (const group of groups) {
38749
+ 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)
38750
+ return null;
38751
+ for (const handler of group.hooks) {
38752
+ if (!table(handler) || handler.timeout !== undefined && !(Number.isSafeInteger(handler.timeout) && handler.timeout >= 0) || handler.statusMessage !== undefined && !text2(handler.statusMessage))
38753
+ return null;
38754
+ if (handler.type === "command") {
38755
+ if (!text2(handler.command) || !handler.command || !Object.keys(handler).every((key2) => ["type", "command", "commandWindows", "command_windows", "timeout", "async", "statusMessage", "additionalContextLimit"].includes(key2)))
38756
+ return null;
38757
+ result.commands++;
38758
+ } else if (handler.type === "mcp_tool") {
38759
+ if (!identifier5(handler.server) || !identifier5(handler.tool) || handler.input !== undefined && !table(handler.input) || !Object.keys(handler).every((key2) => ["type", "server", "tool", "input", "timeout", "statusMessage"].includes(key2)))
38760
+ return null;
38761
+ result.servers.add(handler.server);
38762
+ } else
38763
+ return null;
38764
+ }
38765
+ }
38766
+ }
38767
+ return result;
38768
+ }
38669
38769
  function resolveDiscovery(options, exclusion) {
38670
38770
  const canonical9 = options.canonical ?? resolve23, home = resolve23(options.home), agent = options.agent;
38671
38771
  const sources = [], roots = new Set, builtinNames = [];
38772
+ const sumiLocalPluginTexts = new Map;
38672
38773
  const budget3 = discoveryByteBudget();
38673
38774
  let remainingEntries = AGENT_POLICY_LIMITS.discoveryDirectoryEntries;
38674
38775
  const entries = (path) => {
@@ -38745,12 +38846,38 @@ function resolveDiscovery(options, exclusion) {
38745
38846
  const controls = agent === "claude" ? claudeDiscoveryControlKeys(configText ?? "{}") : agent === "codex" ? codexDiscoveryControlKeys(config2) : Object.keys(config2).filter((key2) => /skill|plugin|hook|marketplace|extension|instruction/i.test(key2));
38746
38847
  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]";
38747
38848
  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))}`);
38748
- const unsupportedControls = controls.filter((key2) => !supported[agent].includes(key2));
38849
+ const adapted = agent === "codex" ? codexRootFreeControls(config2) : agent === "opencode" && Array.isArray(config2.instructions) && config2.instructions.length === 0 ? ["instructions"] : [];
38850
+ const unsupportedControls = controls.filter((key2) => !supported[agent].includes(key2) && !adapted.includes(key2));
38749
38851
  if (unsupportedControls.length)
38750
38852
  unsupportedFields(unsupportedControls, "");
38751
38853
  if (config2.hooks !== undefined && !object6(config2.hooks))
38752
38854
  unresolved("hooks must be an object");
38753
- const hasHooks = controls.includes("hooks") && config2.hooks !== undefined && Object.keys(config2.hooks).length > 0;
38855
+ const managedSupervisorOnly = () => {
38856
+ const supervisor = exclusion.managedHook?.supervisor;
38857
+ if (agent !== "hermes" || !supervisor || typeof supervisor.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(supervisor.sha256) || !object6(config2.hooks))
38858
+ return false;
38859
+ let expected;
38860
+ try {
38861
+ expected = hermesHookDefinitions(supervisor);
38862
+ } catch {
38863
+ return false;
38864
+ }
38865
+ for (const [event, entries2] of Object.entries(config2.hooks)) {
38866
+ const want = expected[event];
38867
+ if (!want || !Array.isArray(entries2) || entries2.length !== 1 || !object6(entries2[0]))
38868
+ return false;
38869
+ const entry = entries2[0];
38870
+ if (Object.keys(entry).sort().join("\x00") !== Object.keys(want).sort().join("\x00") || Object.keys(want).some((key2) => entry[key2] !== want[key2]))
38871
+ return false;
38872
+ }
38873
+ try {
38874
+ return hashRawDiscoveryFile(canonical9(supervisor.path), budget3) === supervisor.sha256;
38875
+ } catch {
38876
+ return false;
38877
+ }
38878
+ };
38879
+ const managedBridgeOnly = (agent === "gemini" || agent === "cursor") && exclusion.managedHook !== undefined && hooksAreManagedBridge(agent, config2.hooks, exclusion.managedHook) || managedSupervisorOnly();
38880
+ const hasHooks = controls.includes("hooks") && config2.hooks !== undefined && Object.keys(config2.hooks).length > 0 && !managedBridgeOnly;
38754
38881
  let hasPluginConfigs = false, hasFunctionHookEnvironment = false;
38755
38882
  const verifyCurrentOpaqueSettings = (label) => {
38756
38883
  const retained = exclusion.retained;
@@ -38855,7 +38982,9 @@ function resolveDiscovery(options, exclusion) {
38855
38982
  unresolved("malformed Sumi plugins");
38856
38983
  }
38857
38984
  if (agent === "hermes" && config2.skills !== undefined) {
38858
- keys2(config2.skills, ["external_dirs"], "Hermes skills");
38985
+ keys2(config2.skills, ["external_dirs", "creation_nudge_interval"], "Hermes skills");
38986
+ if (config2.skills.creation_nudge_interval !== undefined && !(Number.isSafeInteger(config2.skills.creation_nudge_interval) && config2.skills.creation_nudge_interval >= 0))
38987
+ unresolved("malformed Hermes skills creation_nudge_interval");
38859
38988
  if (config2.skills.external_dirs !== undefined)
38860
38989
  paths(typeof config2.skills.external_dirs === "string" ? [config2.skills.external_dirs] : config2.skills.external_dirs, "Hermes external directories");
38861
38990
  }
@@ -38875,10 +39004,21 @@ function resolveDiscovery(options, exclusion) {
38875
39004
  unresolved(`malformed Codex ${key2}`);
38876
39005
  for (const value of Object.values(config2.plugins ?? {}))
38877
39006
  keys2(value, ["enabled"], "Codex plugin registration");
38878
- for (const value of Object.values(config2.marketplaces ?? {}))
38879
- keys2(value, ["source_type", "source"], "Codex marketplace registration");
39007
+ for (const value of Object.values(config2.marketplaces ?? {})) {
39008
+ keys2(value, ["source_type", "source", "last_updated"], "Codex marketplace registration");
39009
+ if (value.last_updated !== undefined && (typeof value.last_updated !== "string" || !CODEX_MARKETPLACE_TIMESTAMP.test(value.last_updated)))
39010
+ unresolved("malformed Codex marketplace refresh time");
39011
+ }
38880
39012
  if (config2.skills !== undefined)
38881
- keys2(config2.skills, ["config", "bundled"], "Codex skills");
39013
+ keys2(config2.skills, ["config", "bundled", "native_allowed_paths"], "Codex skills");
39014
+ if (config2.skills?.native_allowed_paths !== undefined) {
39015
+ for (const value of paths(config2.skills.native_allowed_paths, "Codex native allowed skill paths")) {
39016
+ if (!isAbsolute22(value))
39017
+ unresolved("relative Codex native allowed skill path");
39018
+ const path = canonical9(value);
39019
+ roots.add(path.endsWith(`${sep11}SKILL.md`) ? dirname24(path) : path);
39020
+ }
39021
+ }
38882
39022
  if (config2.skills?.bundled !== undefined) {
38883
39023
  keys2(config2.skills.bundled, ["enabled"], "Codex bundled skills");
38884
39024
  if (typeof config2.skills.bundled.enabled !== "boolean")
@@ -38936,7 +39076,9 @@ function resolveDiscovery(options, exclusion) {
38936
39076
  const target = join33(path, name);
38937
39077
  if (!lstatSync25(target).isFile())
38938
39078
  unresolved("local plugin packages need a dedicated discovery adapter");
38939
- witness(target);
39079
+ const text2 = witness(target);
39080
+ if (exclusion && text2 !== null)
39081
+ sumiLocalPluginTexts.set(canonical9(target), text2);
38940
39082
  }
38941
39083
  }
38942
39084
  for (const root of roots) {
@@ -39070,16 +39212,88 @@ function resolveDiscovery(options, exclusion) {
39070
39212
  const unique = combined.filter((source, index) => combined.findIndex((candidate) => isDeepStrictEqual6(candidate, source)) === index);
39071
39213
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
39072
39214
  }
39073
- function plugin(root) {
39215
+ const reviewedManifestAbsence = (root, manifestPath) => {
39216
+ const retained = exclusion?.retained;
39217
+ if (agent !== "claude" || retained?.method !== "reviewed" || ![join33(root, "skills"), root].some((path) => retained.roots.includes(path)))
39218
+ return false;
39219
+ 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);
39220
+ return saved.length > 0 && saved.every((source) => projected(source, undefined, budget3) === source.sha256);
39221
+ };
39222
+ const reviewedInstalledCopy = (root, path, identity4) => {
39223
+ const retained = exclusion?.retained;
39224
+ if (agent !== "codex" || retained?.method !== "reviewed")
39225
+ return false;
39226
+ const inside = relative7(root, canonical9(path));
39227
+ if (!inside || inside === ".." || inside.startsWith(`..${sep11}`) || isAbsolute22(inside))
39228
+ return false;
39229
+ const parent = canonical9(join33(nativeHome, "plugins/cache", identity4.marketplace, identity4.name));
39230
+ const current = hashRawDiscoveryFile(canonical9(path), budget3);
39231
+ if (current === null)
39232
+ return false;
39233
+ return retained.sources.some((source) => {
39234
+ 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 + sep11))
39235
+ return false;
39236
+ const [version2, ...rest] = source.path.slice(parent.length + 1).split(sep11);
39237
+ return Boolean(version2) && version2 !== "." && version2 !== ".." && rest.join(sep11) === inside;
39238
+ });
39239
+ };
39240
+ const verifyCodexPluginHookClosure = (root, identity4, marketplaceSource, servers, declarations) => {
39241
+ const retained = exclusion.retained;
39242
+ if (retained.codexSemanticCache || retained.codexInstallationInputs || retained.codexRetiredMaterializations)
39243
+ return verifyOpaqueClosure(declarations);
39244
+ const within = (base, path) => path === base || path.startsWith(base + sep11);
39245
+ const executables = [];
39246
+ for (const server of servers) {
39247
+ const row = config2.mcp_servers?.[server];
39248
+ if (server === "codex_apps" || server === "_codex_apps" || !isCodexLocalStdioMcpServer(server, row))
39249
+ unresolved("plugin hook MCP server is not a configured local stdio server");
39250
+ for (const value of [row.command, ...row.args ?? []])
39251
+ for (const candidate of [value, value.slice(value.indexOf("=") + 1)]) {
39252
+ if (!isAbsolute22(candidate) || /[\0$]/.test(candidate) || executables.length >= AGENT_POLICY_LIMITS.discoveryRoots)
39253
+ continue;
39254
+ const path = canonical9(candidate);
39255
+ if (lstatSync25(path, { throwIfNoEntry: false })?.isFile())
39256
+ executables.push(path);
39257
+ }
39258
+ }
39259
+ const cacheRoot = canonical9(join33(nativeHome, "plugins/cache"));
39260
+ const ownCache = canonical9(join33(nativeHome, "plugins/cache", identity4.marketplace, identity4.name));
39261
+ const ownCatalog = canonical9(join33(marketplaceSource, ".agents/plugins/marketplace.json"));
39262
+ const account = canonical9(home);
39263
+ const marketplaceRoots = Object.values(config2.marketplaces ?? {}).filter((market) => market?.source_type === "local" && typeof market.source === "string" && isAbsolute22(market.source)).map((market) => canonical9(market.source)).filter((path) => !within(path, account));
39264
+ const otherPlugin = (path) => !within(root, path) && !within(ownCache, path) && path !== ownCatalog && !executables.includes(path) && (within(cacheRoot, path) || marketplaceRoots.some((base) => within(base, path)));
39265
+ const sources2 = retained.sources.filter((source) => source.path !== canonical9(configPath) && !otherPlugin(source.path));
39266
+ 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)))
39267
+ return verifyOpaqueClosure(declarations);
39268
+ const declared = declarations.map((path) => relative7(root, path));
39269
+ const declaration = (path) => declarations.includes(path) || path.startsWith(ownCache + sep11) && declared.includes(path.slice(ownCache.length + 1).split(sep11).slice(1).join(sep11));
39270
+ if (!sources2.some((source) => !declaration(source.path) && source.sha256 !== null))
39271
+ unresolved("opaque executable dependency closure is unavailable");
39272
+ for (const source of sources2)
39273
+ if (projected(source, undefined, budget3) !== source.sha256) {
39274
+ throw new NativeDiscoveryWitnessChange("input", source.path, `Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
39275
+ }
39276
+ verifyDiscoveryDirectories((retained.directories ?? []).filter((directory) => !otherPlugin(directory.path)));
39277
+ };
39278
+ const readCodexHookDeclaration = (text2, path) => {
39279
+ if (text2 === null)
39280
+ return null;
39281
+ try {
39282
+ return codexPluginHookHandlers(parseConfig(text2, path));
39283
+ } catch {
39284
+ return null;
39285
+ }
39286
+ };
39287
+ function plugin(root, codex) {
39074
39288
  root = canonical9(root);
39075
39289
  safe6(root);
39076
39290
  if (exclusion)
39077
39291
  roots.add(root);
39078
39292
  const manifestPath = join33(root, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
39079
39293
  const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
39080
- if (raw === null)
39294
+ if (raw === null && !reviewedManifestAbsence(root, manifestPath))
39081
39295
  unresolved("plugin manifest missing");
39082
- const manifest = parseConfig(raw, manifestPath);
39296
+ const manifest = raw === null ? {} : parseConfig(raw, manifestPath);
39083
39297
  if (exclusion && Object.keys(manifest).some((key2) => /skill|plugin|hook|marketplace|extension|instruction/i.test(key2) && !["skills", "hooks"].includes(key2)))
39084
39298
  unresolved("unknown plugin discovery controls");
39085
39299
  const hooks = witness(join33(root, "hooks/hooks.json"));
@@ -39087,20 +39301,35 @@ function resolveDiscovery(options, exclusion) {
39087
39301
  if (!exclusion || exclusion.retained?.method !== "reviewed")
39088
39302
  unresolved("plugin hooks require a separate no-skill-injection review");
39089
39303
  const required2 = [manifestPath, ...hooks !== null ? [join33(root, "hooks/hooks.json")] : []];
39304
+ const scoped = agent === "codex" && codex !== undefined ? [] : undefined;
39090
39305
  if (manifest.hooks !== undefined) {
39091
- if (typeof manifest.hooks !== "string")
39092
- unresolved("unsupported plugin hooks target");
39093
- const hook = resolve23(root, manifest.hooks);
39094
- if (!hook.startsWith(root + sep11))
39095
- unresolved("plugin hooks target escapes its root");
39096
- required2.push(hook);
39097
- }
39306
+ if (scoped && manifest.hooks && typeof manifest.hooks === "object" && !Array.isArray(manifest.hooks)) {
39307
+ const inline = codexPluginHookHandlers(manifest.hooks);
39308
+ if (!inline)
39309
+ unresolved("unsupported inline plugin hooks");
39310
+ scoped.push(inline);
39311
+ } else {
39312
+ if (typeof manifest.hooks !== "string")
39313
+ unresolved("unsupported plugin hooks target");
39314
+ const hook = resolve23(root, manifest.hooks);
39315
+ if (!hook.startsWith(root + sep11))
39316
+ unresolved("plugin hooks target escapes its root");
39317
+ required2.push(hook);
39318
+ if (scoped)
39319
+ scoped.push(readCodexHookDeclaration(witness(hook), hook));
39320
+ }
39321
+ }
39322
+ if (scoped && hooks !== null)
39323
+ scoped.push(readCodexHookDeclaration(hooks, join33(root, "hooks/hooks.json")));
39098
39324
  for (const path of required2) {
39099
39325
  const saved = exclusion.retained.sources.filter((source) => source.path === canonical9(path) && source.sha256 !== null && source.format === undefined && source.fields === undefined);
39100
- if (!saved.length || saved.some((source) => projected(source, undefined, budget3) !== source.sha256))
39326
+ if (saved.length ? saved.some((source) => projected(source, undefined, budget3) !== source.sha256) : !(scoped && reviewedInstalledCopy(root, path, codex)))
39101
39327
  unresolved("plugin hooks require current independently reviewed sources");
39102
39328
  }
39103
- verifyOpaqueClosure(required2.map((path) => canonical9(path)));
39329
+ if (scoped && scoped.every((item) => item !== null && item.commands === 0)) {
39330
+ verifyCodexPluginHookClosure(root, codex, codex.source, new Set(scoped.flatMap((item) => [...item.servers])), required2.map((path) => canonical9(path)));
39331
+ } else
39332
+ verifyOpaqueClosure(required2.map((path) => canonical9(path)));
39104
39333
  }
39105
39334
  const commands = join33(root, "commands");
39106
39335
  safe6(commands);
@@ -39156,7 +39385,7 @@ function resolveDiscovery(options, exclusion) {
39156
39385
  const catalog = parseConfig(catalogText, join33(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
39157
39386
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
39158
39387
  unresolved("plugin catalog source is missing or ambiguous");
39159
- plugin(resolve23(market.source, matches[0].source.path));
39388
+ plugin(resolve23(market.source, matches[0].source.path), exclusion ? { marketplace, name, source: market.source } : undefined);
39160
39389
  }
39161
39390
  } else if (agent === "opencode") {
39162
39391
  if (witness(join33(home, ".config/opencode/opencode.jsonc")) !== null || witness(join33(home, ".config/opencode/config.json")) !== null)
@@ -39173,7 +39402,11 @@ function resolveDiscovery(options, exclusion) {
39173
39402
  if (lstatSync25(localPlugins, { throwIfNoEntry: false }) && entries(localPlugins).some((name) => name !== "skills-cli.js"))
39174
39403
  unresolved("additional local plugin hooks require review");
39175
39404
  } else if (agent === "sumi") {
39176
- if ((config2.plugin?.length ?? 0) > 0 || (config2.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical9(configPath) && source.path !== canonical9(join33(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
39405
+ const pinnedLocalPlugin = (source) => {
39406
+ const retained = exclusion?.retained, text2 = sumiLocalPluginTexts.get(source.path);
39407
+ 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);
39408
+ };
39409
+ if ((config2.plugin?.length ?? 0) > 0 || (config2.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical9(configPath) && source.path !== canonical9(join33(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null && !pinnedLocalPlugin(source)))
39177
39410
  unresolved("external or foreign local plugins require review");
39178
39411
  } else if (agent === "hermes") {
39179
39412
  for (const directory of [join33(home, ".hermes/plugins"), join33(home, ".hermes/hermes-agent")]) {
@@ -39213,11 +39446,12 @@ var DISCOVERY_RUNTIME_COMMANDS, quotedPath = (path) => JSON.stringify(path).repl
39213
39446
  } catch {
39214
39447
  return null;
39215
39448
  }
39216
- }, digest2 = (text2) => createHash26("sha256").update(text2).digest("hex"), CLAUDE_CONTROL_WITNESS_MODES, CLAUDE_CONTROL_FILES, ADOPTABLE_SETTINGS_MATCHES, CLAUDE_PROJECT_DISCOVERY_KEYS;
39449
+ }, digest2 = (text2) => createHash26("sha256").update(text2).digest("hex"), CLAUDE_CONTROL_WITNESS_MODES, CLAUDE_CONTROL_FILES, ADOPTABLE_SETTINGS_MATCHES, CLAUDE_PROJECT_DISCOVERY_KEYS, CODEX_MARKETPLACE_TIMESTAMP, CODEX_HOOK_EVENTS;
39217
39450
  var init_agent_discovery = __esm(() => {
39218
39451
  init_sumi_settings_witness();
39219
39452
  init_agent_policy_limits();
39220
39453
  init_agent_hermes();
39454
+ init_agent_adapters();
39221
39455
  init_agent_discovery_directories();
39222
39456
  init_agent_discovery_bytes();
39223
39457
  init_plugin_projection_store();
@@ -39247,6 +39481,8 @@ var init_agent_discovery = __esm(() => {
39247
39481
  CLAUDE_CONTROL_FILES = new Set(["settings.json", "settings.local.json", "installed_plugins.json", "known_marketplaces.json"]);
39248
39482
  ADOPTABLE_SETTINGS_MATCHES = Object.freeze(["exact", "screen-reader-receipt"]);
39249
39483
  CLAUDE_PROJECT_DISCOVERY_KEYS = Object.freeze(["enabledPlugins", "extraKnownMarketplaces", "additionalMarketplaces", "skillOverrides"]);
39484
+ CODEX_MARKETPLACE_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
39485
+ CODEX_HOOK_EVENTS = new Set(["PreToolUse", "PermissionRequest", "PostToolUse", "PreCompact", "PostCompact", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart", "SubagentStop", "Stop", "Interrupt"]);
39250
39486
  });
39251
39487
 
39252
39488
  // src/lib/authoring-path.ts
@@ -39324,7 +39560,10 @@ function configuredRoots(home, explicitHome) {
39324
39560
  if (recorded && (!executable || executable.target !== recorded.target || onPath && onPath.target !== recorded.target) || !executable && (binding.builtinNames?.length || binding.agent === "gemini" && binding.sources.some((source) => basename15(source.path) === "package.json"))) {
39325
39561
  throw new Error("Native discovery runtime target is unresolved or changed");
39326
39562
  }
39327
- roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: binding.agent, canonical: physicalPath, retained: binding, executable, narrowed }));
39563
+ const managedCommand = current.commands?.[agent], managedProfile = current.profiles?.[agent];
39564
+ const supervisor = current.supervisors?.[agent];
39565
+ 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;
39566
+ roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: binding.agent, canonical: physicalPath, retained: binding, executable, narrowed, ...managedHook ? { managedHook } : {} }));
39328
39567
  }
39329
39568
  };
39330
39569
  if (homePolicy)
package/bin/migrate.js CHANGED
@@ -25,7 +25,7 @@ import { join as join5 } from "path";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@hasna/skills",
28
- version: "0.10.91",
28
+ version: "0.10.92",
29
29
  description: "Skills library for AI coding agents",
30
30
  type: "module",
31
31
  bin: {
package/bin/server.js CHANGED
@@ -83767,7 +83767,7 @@ restoreLaunchCwd();
83767
83767
  // package.json
83768
83768
  var package_default = {
83769
83769
  name: "@hasna/skills",
83770
- version: "0.10.91",
83770
+ version: "0.10.92",
83771
83771
  description: "Skills library for AI coding agents",
83772
83772
  type: "module",
83773
83773
  bin: {
package/bin/worker.js CHANGED
@@ -76186,7 +76186,7 @@ import { randomUUID as randomUUID6 } from "crypto";
76186
76186
  // package.json
76187
76187
  var package_default = {
76188
76188
  name: "@hasna/skills",
76189
- version: "0.10.91",
76189
+ version: "0.10.92",
76190
76190
  description: "Skills library for AI coding agents",
76191
76191
  type: "module",
76192
76192
  bin: {