@hasna/skills 0.10.91 → 0.10.93

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.93";
16373
16373
  var package_default = {
16374
16374
  name: "@hasna/skills",
16375
16375
  version,
@@ -20802,6 +20802,82 @@ function assertPinnedLauncherRuntimeSafe(runtime) {
20802
20802
  }
20803
20803
  }
20804
20804
 
20805
+ // src/lib/linux-access-acl.ts
20806
+ var LINUX_ENODATA = 61;
20807
+ var LINUX_EOPNOTSUPP = 95;
20808
+ var LINUX_ACL_QUERY_ATTEMPTS = 4;
20809
+ var LINUX_ACL_FREE_WHEN_UNSUPPORTED = new Set([
20810
+ 0xef53n,
20811
+ 0x58465342n,
20812
+ 0x9123683en,
20813
+ 0x01021994n
20814
+ ]);
20815
+ function queryAttribute(probe2, errno, fd, attribute) {
20816
+ for (let attempt = 0;attempt < LINUX_ACL_QUERY_ATTEMPTS; attempt++) {
20817
+ errno.set?.(0);
20818
+ const result = probe2.fgetxattr(fd, attribute);
20819
+ const code = errno.get();
20820
+ if (result !== -1 && result !== -1n)
20821
+ return "present";
20822
+ if (code === LINUX_ENODATA)
20823
+ return "absent";
20824
+ if (code === LINUX_EOPNOTSUPP)
20825
+ return "unsupported";
20826
+ }
20827
+ return "unverified";
20828
+ }
20829
+ function linuxAccessAclAbsent(probe2, fd, creationParent) {
20830
+ if (probe2.arch !== "x64" && probe2.arch !== "arm64")
20831
+ return false;
20832
+ const errno = probe2.errno;
20833
+ if (!errno)
20834
+ return false;
20835
+ let unsupported = false;
20836
+ for (const attribute of ["system.posix_acl_access", ...creationParent ? ["system.posix_acl_default"] : []]) {
20837
+ const state = queryAttribute(probe2, errno, fd, attribute);
20838
+ if (state === "present" || state === "unverified")
20839
+ return false;
20840
+ if (state === "unsupported")
20841
+ unsupported = true;
20842
+ }
20843
+ if (!unsupported)
20844
+ return true;
20845
+ const type = probe2.filesystemType(fd);
20846
+ return type !== null && LINUX_ACL_FREE_WHEN_UNSUPPORTED.has(type);
20847
+ }
20848
+ function withGlibcAclProbe(use) {
20849
+ try {
20850
+ const { dlopen, ptr, read: read2, FFIType } = __require("bun:ffi");
20851
+ const library = dlopen("libc.so.6", {
20852
+ fgetxattr: { args: ["i32", "ptr", "ptr", "u64"], returns: FFIType.i64_fast },
20853
+ fstatfs: { args: ["i32", "ptr"], returns: "i32" },
20854
+ __errno_location: { args: [], returns: "ptr" }
20855
+ });
20856
+ try {
20857
+ const location = library.symbols.__errno_location();
20858
+ const names = new Map;
20859
+ return use({
20860
+ arch: process.arch,
20861
+ errno: location ? { get: () => read2.i32(location) } : null,
20862
+ fgetxattr: (fd, attribute) => {
20863
+ let name = names.get(attribute);
20864
+ if (!name)
20865
+ names.set(attribute, name = Buffer.from(attribute + "\x00"));
20866
+ return library.symbols.fgetxattr(fd, ptr(name), null, 0);
20867
+ },
20868
+ filesystemType: (fd) => {
20869
+ const statfs = new BigUint64Array(32);
20870
+ return library.symbols.fstatfs(fd, ptr(statfs)) === 0 ? statfs[0] : null;
20871
+ }
20872
+ });
20873
+ } finally {
20874
+ library.close();
20875
+ }
20876
+ } catch {
20877
+ return false;
20878
+ }
20879
+ }
20880
+
20805
20881
  // src/lib/retained-review-artifacts.ts
20806
20882
  import { createHash as createHash29 } from "crypto";
20807
20883
  import { constants as constants18, closeSync as closeSync17, fstatSync as fstatSync17, fsyncSync as fsyncSync6, lstatSync as lstatSync29, mkdirSync as mkdirSync8, openSync as openSync17, readSync as readSync12, writeSync as writeSync4 } from "fs";
@@ -21796,28 +21872,7 @@ function hookProjectionRefusal(reason2) {
21796
21872
  throw new Error(`CLAUDE_MANAGED_HOOK_${reason2}`);
21797
21873
  }
21798
21874
  function hasNoLinuxAccessAcl(fd, creationParent) {
21799
- try {
21800
- const { dlopen, read: read2, ptr } = __require("bun:ffi");
21801
- const library = dlopen("libc.so.6", {
21802
- fgetxattr: { args: ["i32", "ptr", "ptr", "u64"], returns: "i64" },
21803
- __errno_location: { args: [], returns: "ptr" }
21804
- });
21805
- try {
21806
- const errno = library.symbols.__errno_location();
21807
- if (!errno)
21808
- return false;
21809
- for (const attribute of ["system.posix_acl_access", ...creationParent ? ["system.posix_acl_default"] : []]) {
21810
- const name = Buffer.from(attribute + "\x00");
21811
- if (Number(library.symbols.fgetxattr(fd, ptr(name), null, 0)) !== -1 || read2.i32(errno) !== 61)
21812
- return false;
21813
- }
21814
- return true;
21815
- } finally {
21816
- library.close();
21817
- }
21818
- } catch {
21819
- return false;
21820
- }
21875
+ return withGlibcAclProbe((probe2) => linuxAccessAclAbsent(probe2, fd, creationParent));
21821
21876
  }
21822
21877
  function assertNativePathAcl(path, observed, options = {}) {
21823
21878
  if (process.platform !== "darwin" && process.platform !== "linux")
@@ -22758,7 +22813,7 @@ function planCodexPluginNameReconcile(request) {
22758
22813
  pathDenies: [...pathDenies].sort(),
22759
22814
  ...discovery ? { discovery } : {}
22760
22815
  };
22761
- const plan = planAgentIntegration({ home, dataDir: dataDir2, projectDir, agents: ["codex"], codexNativeCatalog: catalog, codexReconcile: input });
22816
+ const plan = planAgentIntegration({ home, dataDir: dataDir2, projectDir, agents: ["codex"], codexNativeCatalog: catalog, codexReconcile: input, allowRootAliases: aliases.length > 0 });
22762
22817
  assertCodexReconcileScope(plan, { configPath, policyPath, prior, names, remoteIdentities, discovery: discovery ?? savedDiscovery });
22763
22818
  const configChange = plan.changes.find((change) => resolve24(change.path) === configPath);
22764
22819
  const plannedRules = configChange ? Bun.TOML.parse(configChange.after).skills?.config ?? [] : [];
@@ -25764,6 +25819,19 @@ function discoveryRootUnresolved(agent, command, detail) {
25764
25819
  function isDiscoveryRootUnresolved(error) {
25765
25820
  return error instanceof Error && error.message.startsWith(`${DISCOVERY_ROOT_UNRESOLVED}: `);
25766
25821
  }
25822
+
25823
+ class NativeDiscoveryUnresolved extends Error {
25824
+ agent;
25825
+ detail;
25826
+ source;
25827
+ reviewable = false;
25828
+ constructor(agent, detail, source) {
25829
+ super(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
25830
+ this.agent = agent;
25831
+ this.detail = detail;
25832
+ this.source = source;
25833
+ }
25834
+ }
25767
25835
  var quotedPath = (path) => JSON.stringify(path).replace(/[^\x20-\x7e]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
25768
25836
  var realpathOrNull = (path) => {
25769
25837
  try {
@@ -26733,9 +26801,96 @@ function nativeDiscoveryAliasTargets(roots, home) {
26733
26801
  }
26734
26802
  return [...targets];
26735
26803
  }
26804
+ function hooksAreManagedBridge(agent, hooks, managed) {
26805
+ const table = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
26806
+ const exactKeys3 = (value, keys2) => Object.keys(value).sort().join("\x00") === [...keys2].sort().join("\x00");
26807
+ if (!table(hooks) || typeof managed.command !== "string" || !isAbsolute28(managed.command))
26808
+ return false;
26809
+ const events3 = agent === "gemini" ? ["BeforeAgent", "SessionStart", "BeforeTool"] : ["beforeSubmitPrompt", "sessionStart"];
26810
+ for (const [event, entries] of Object.entries(hooks)) {
26811
+ if (!events3.includes(event) || !Array.isArray(entries) || entries.length !== 1)
26812
+ return false;
26813
+ let expected;
26814
+ try {
26815
+ expected = renderAgentHookCommand(managed.command, agent, managed.profile, event);
26816
+ } catch {
26817
+ return false;
26818
+ }
26819
+ const entry = entries[0];
26820
+ if (!table(entry))
26821
+ return false;
26822
+ if (agent === "cursor") {
26823
+ if (!exactKeys3(entry, ["command", "timeout"]) || entry.timeout !== 15 || entry.command !== expected)
26824
+ return false;
26825
+ continue;
26826
+ }
26827
+ if (!exactKeys3(entry, event === "BeforeTool" ? ["hooks", "matcher"] : ["hooks"]) || event === "BeforeTool" && entry.matcher !== "activate_skill" || !Array.isArray(entry.hooks) || entry.hooks.length !== 1)
26828
+ return false;
26829
+ const hook = entry.hooks[0];
26830
+ if (!table(hook) || !exactKeys3(hook, ["command", "timeout", "type"]) || hook.type !== "command" || hook.timeout !== 15000 || hook.command !== expected)
26831
+ return false;
26832
+ }
26833
+ return true;
26834
+ }
26835
+ function sumiPluginLoadsOnlyBuiltins(text2) {
26836
+ for (const match of text2.matchAll(/\b(?:import|require)\s*\(\s*(.?)/g)) {
26837
+ const rest = text2.slice(match.index + match[0].length - match[1].length);
26838
+ if (!/^(['"])node:[A-Za-z0-9_/.-]+\1/.test(rest))
26839
+ return false;
26840
+ }
26841
+ for (const match of text2.matchAll(/\b(?:from|import)\s*(['"])([^'"]*)\1/g))
26842
+ if (!match[2].startsWith("node:"))
26843
+ return false;
26844
+ return true;
26845
+ }
26846
+ 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})$/;
26847
+ function codexRootFreeControls(config) {
26848
+ const table = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date));
26849
+ const adapted = [];
26850
+ const servers = config.mcp_servers;
26851
+ 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)))
26852
+ adapted.push("mcp_servers");
26853
+ const agents = config.agents;
26854
+ 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)))
26855
+ adapted.push("agents");
26856
+ return adapted;
26857
+ }
26858
+ var CODEX_HOOK_EVENTS = new Set(["PreToolUse", "PermissionRequest", "PostToolUse", "PreCompact", "PostCompact", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart", "SubagentStop", "Stop", "Interrupt"]);
26859
+ function codexPluginHookHandlers(value) {
26860
+ const table = (item) => Boolean(item && typeof item === "object" && !Array.isArray(item));
26861
+ const identifier6 = (item) => typeof item === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(item);
26862
+ const text2 = (item) => typeof item === "string" && item.length <= AGENT_POLICY_LIMITS.pathCharacters && !item.includes("\x00");
26863
+ 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)
26864
+ return null;
26865
+ const result = { commands: 0, servers: new Set };
26866
+ for (const [event, groups] of Object.entries(value.hooks)) {
26867
+ if (!CODEX_HOOK_EVENTS.has(event) || !Array.isArray(groups) || groups.length > 32)
26868
+ return null;
26869
+ for (const group of groups) {
26870
+ 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)
26871
+ return null;
26872
+ for (const handler of group.hooks) {
26873
+ if (!table(handler) || handler.timeout !== undefined && !(Number.isSafeInteger(handler.timeout) && handler.timeout >= 0) || handler.statusMessage !== undefined && !text2(handler.statusMessage))
26874
+ return null;
26875
+ if (handler.type === "command") {
26876
+ if (!text2(handler.command) || !handler.command || !Object.keys(handler).every((key2) => ["type", "command", "commandWindows", "command_windows", "timeout", "async", "statusMessage", "additionalContextLimit"].includes(key2)))
26877
+ return null;
26878
+ result.commands++;
26879
+ } else if (handler.type === "mcp_tool") {
26880
+ 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)))
26881
+ return null;
26882
+ result.servers.add(handler.server);
26883
+ } else
26884
+ return null;
26885
+ }
26886
+ }
26887
+ }
26888
+ return result;
26889
+ }
26736
26890
  function resolveDiscovery(options, exclusion) {
26737
26891
  const canonical9 = options.canonical ?? resolve28, home = resolve28(options.home), agent = options.agent;
26738
26892
  const sources = [], roots = new Set, builtinNames = [];
26893
+ const sumiLocalPluginTexts = new Map;
26739
26894
  const budget3 = discoveryByteBudget();
26740
26895
  let remainingEntries = AGENT_POLICY_LIMITS.discoveryDirectoryEntries;
26741
26896
  const entries = (path) => {
@@ -26772,8 +26927,8 @@ function resolveDiscovery(options, exclusion) {
26772
26927
  const configPath = exclusion?.nativeHome === undefined ? agentDiscoveryConfigPath(home, agent) : join41(nativeHome, agent === "codex" ? "config.toml" : "settings.json");
26773
26928
  const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? [...SUMI_DISCOVERY_PROJECTION_FIELDS] : ["version"]);
26774
26929
  const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
26775
- const unresolved = (detail) => {
26776
- throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
26930
+ const unresolved = (detail, source) => {
26931
+ throw new NativeDiscoveryUnresolved(agent, detail, source);
26777
26932
  };
26778
26933
  let opaqueClosureVerified = false;
26779
26934
  const verifyOpaqueClosure = (declarations) => {
@@ -26787,6 +26942,17 @@ function resolveDiscovery(options, exclusion) {
26787
26942
  verifyDiscovery(retained, undefined, undefined, undefined, exclusion?.narrowed, canonical9(configPath));
26788
26943
  opaqueClosureVerified = true;
26789
26944
  };
26945
+ const reviewRemedy = (source, check) => {
26946
+ try {
26947
+ check();
26948
+ } catch (error) {
26949
+ if (error instanceof NativeDiscoveryUnresolved) {
26950
+ error.reviewable = true;
26951
+ error.source ??= source;
26952
+ }
26953
+ throw error;
26954
+ }
26955
+ };
26790
26956
  if (exclusion) {
26791
26957
  if (configText !== null && agent !== "codex" && agent !== "hermes")
26792
26958
  parseNativeJsonObject(configText);
@@ -26812,12 +26978,38 @@ function resolveDiscovery(options, exclusion) {
26812
26978
  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
26979
  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
26980
  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));
26981
+ const adapted = agent === "codex" ? codexRootFreeControls(config) : agent === "opencode" && Array.isArray(config.instructions) && config.instructions.length === 0 ? ["instructions"] : [];
26982
+ const unsupportedControls = controls.filter((key2) => !supported[agent].includes(key2) && !adapted.includes(key2));
26816
26983
  if (unsupportedControls.length)
26817
26984
  unsupportedFields(unsupportedControls, "");
26818
26985
  if (config.hooks !== undefined && !object6(config.hooks))
26819
26986
  unresolved("hooks must be an object");
26820
- const hasHooks = controls.includes("hooks") && config.hooks !== undefined && Object.keys(config.hooks).length > 0;
26987
+ const managedSupervisorOnly = () => {
26988
+ const supervisor = exclusion.managedHook?.supervisor;
26989
+ if (agent !== "hermes" || !supervisor || typeof supervisor.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(supervisor.sha256) || !object6(config.hooks))
26990
+ return false;
26991
+ let expected;
26992
+ try {
26993
+ expected = hermesHookDefinitions(supervisor);
26994
+ } catch {
26995
+ return false;
26996
+ }
26997
+ for (const [event, entries2] of Object.entries(config.hooks)) {
26998
+ const want = expected[event];
26999
+ if (!want || !Array.isArray(entries2) || entries2.length !== 1 || !object6(entries2[0]))
27000
+ return false;
27001
+ const entry = entries2[0];
27002
+ if (Object.keys(entry).sort().join("\x00") !== Object.keys(want).sort().join("\x00") || Object.keys(want).some((key2) => entry[key2] !== want[key2]))
27003
+ return false;
27004
+ }
27005
+ try {
27006
+ return hashRawDiscoveryFile(canonical9(supervisor.path), budget3) === supervisor.sha256;
27007
+ } catch {
27008
+ return false;
27009
+ }
27010
+ };
27011
+ const managedBridgeOnly = (agent === "gemini" || agent === "cursor") && exclusion.managedHook !== undefined && hooksAreManagedBridge(agent, config.hooks, exclusion.managedHook) || managedSupervisorOnly();
27012
+ const hasHooks = controls.includes("hooks") && config.hooks !== undefined && Object.keys(config.hooks).length > 0 && !managedBridgeOnly;
26821
27013
  let hasPluginConfigs = false, hasFunctionHookEnvironment = false;
26822
27014
  const verifyCurrentOpaqueSettings = (label) => {
26823
27015
  const retained = exclusion.retained;
@@ -26832,7 +27024,7 @@ function resolveDiscovery(options, exclusion) {
26832
27024
  unresolved(`non-enumerable ${label} require a current settings review`);
26833
27025
  };
26834
27026
  if (hasHooks)
26835
- verifyCurrentOpaqueSettings("hooks");
27027
+ reviewRemedy(canonical9(configPath), () => verifyCurrentOpaqueSettings("hooks"));
26836
27028
  if (agent === "claude") {
26837
27029
  if (config.env !== undefined) {
26838
27030
  if (!object6(config.env) || Object.keys(config.env).length > 512)
@@ -26845,7 +27037,7 @@ function resolveDiscovery(options, exclusion) {
26845
27037
  if (hasFunctionHookEnvironment) {
26846
27038
  if (!["0", "1"].includes(config.env.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS))
26847
27039
  unresolved("malformed Claude env.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS flag");
26848
- verifyCurrentOpaqueSettings("function-hook environment controls");
27040
+ reviewRemedy(canonical9(configPath), () => verifyCurrentOpaqueSettings("function-hook environment controls"));
26849
27041
  }
26850
27042
  }
26851
27043
  if (config.pluginConfigs !== undefined) {
@@ -26872,7 +27064,7 @@ function resolveDiscovery(options, exclusion) {
26872
27064
  }
26873
27065
  hasPluginConfigs = Object.keys(config.pluginConfigs).length > 0;
26874
27066
  if (hasPluginConfigs)
26875
- verifyCurrentOpaqueSettings("plugin configurations");
27067
+ reviewRemedy(canonical9(configPath), () => verifyCurrentOpaqueSettings("plugin configurations"));
26876
27068
  }
26877
27069
  if (config.enabledPlugins !== undefined && !object6(config.enabledPlugins))
26878
27070
  unresolved("enabledPlugins must be an object");
@@ -26922,7 +27114,9 @@ function resolveDiscovery(options, exclusion) {
26922
27114
  unresolved("malformed Sumi plugins");
26923
27115
  }
26924
27116
  if (agent === "hermes" && config.skills !== undefined) {
26925
- keys2(config.skills, ["external_dirs"], "Hermes skills");
27117
+ keys2(config.skills, ["external_dirs", "creation_nudge_interval"], "Hermes skills");
27118
+ if (config.skills.creation_nudge_interval !== undefined && !(Number.isSafeInteger(config.skills.creation_nudge_interval) && config.skills.creation_nudge_interval >= 0))
27119
+ unresolved("malformed Hermes skills creation_nudge_interval");
26926
27120
  if (config.skills.external_dirs !== undefined)
26927
27121
  paths(typeof config.skills.external_dirs === "string" ? [config.skills.external_dirs] : config.skills.external_dirs, "Hermes external directories");
26928
27122
  }
@@ -26942,10 +27136,21 @@ function resolveDiscovery(options, exclusion) {
26942
27136
  unresolved(`malformed Codex ${key2}`);
26943
27137
  for (const value of Object.values(config.plugins ?? {}))
26944
27138
  keys2(value, ["enabled"], "Codex plugin registration");
26945
- for (const value of Object.values(config.marketplaces ?? {}))
26946
- keys2(value, ["source_type", "source"], "Codex marketplace registration");
27139
+ for (const value of Object.values(config.marketplaces ?? {})) {
27140
+ keys2(value, ["source_type", "source", "last_updated"], "Codex marketplace registration");
27141
+ if (value.last_updated !== undefined && (typeof value.last_updated !== "string" || !CODEX_MARKETPLACE_TIMESTAMP.test(value.last_updated)))
27142
+ unresolved("malformed Codex marketplace refresh time");
27143
+ }
26947
27144
  if (config.skills !== undefined)
26948
- keys2(config.skills, ["config", "bundled"], "Codex skills");
27145
+ keys2(config.skills, ["config", "bundled", "native_allowed_paths"], "Codex skills");
27146
+ if (config.skills?.native_allowed_paths !== undefined) {
27147
+ for (const value of paths(config.skills.native_allowed_paths, "Codex native allowed skill paths")) {
27148
+ if (!isAbsolute28(value))
27149
+ unresolved("relative Codex native allowed skill path");
27150
+ const path = canonical9(value);
27151
+ roots.add(path.endsWith(`${sep15}SKILL.md`) ? dirname29(path) : path);
27152
+ }
27153
+ }
26949
27154
  if (config.skills?.bundled !== undefined) {
26950
27155
  keys2(config.skills.bundled, ["enabled"], "Codex bundled skills");
26951
27156
  if (typeof config.skills.bundled.enabled !== "boolean")
@@ -26971,7 +27176,7 @@ function resolveDiscovery(options, exclusion) {
26971
27176
  if (Object.keys(config.enabledPlugins ?? config.plugins ?? config.extensions ?? {}).length > 512)
26972
27177
  unresolved("unbounded native registrations");
26973
27178
  if (hasHooks || hasPluginConfigs || hasFunctionHookEnvironment)
26974
- verifyOpaqueClosure([]);
27179
+ reviewRemedy(canonical9(configPath), () => verifyOpaqueClosure([]));
26975
27180
  }
26976
27181
  if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
26977
27182
  throw new Error("Invalid --discovery-inputs version or agents");
@@ -27003,7 +27208,9 @@ function resolveDiscovery(options, exclusion) {
27003
27208
  const target = join41(path, name);
27004
27209
  if (!lstatSync34(target).isFile())
27005
27210
  unresolved("local plugin packages need a dedicated discovery adapter");
27006
- witness(target);
27211
+ const text2 = witness(target);
27212
+ if (exclusion && text2 !== null)
27213
+ sumiLocalPluginTexts.set(canonical9(target), text2);
27007
27214
  }
27008
27215
  }
27009
27216
  for (const root2 of roots) {
@@ -27137,38 +27344,126 @@ function resolveDiscovery(options, exclusion) {
27137
27344
  const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual9(candidate2, source)) === index);
27138
27345
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
27139
27346
  }
27140
- function plugin(root2) {
27347
+ const reviewedManifestAbsence = (root2, manifestPath) => {
27348
+ const retained = exclusion?.retained;
27349
+ if (agent !== "claude" || retained?.method !== "reviewed" || ![join41(root2, "skills"), root2].some((path) => retained.roots.includes(path)))
27350
+ return false;
27351
+ 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);
27352
+ return saved.length > 0 && saved.every((source) => projected(source, undefined, budget3) === source.sha256);
27353
+ };
27354
+ const reviewedInstalledCopy = (root2, path, identity5) => {
27355
+ const retained = exclusion?.retained;
27356
+ if (agent !== "codex" || retained?.method !== "reviewed")
27357
+ return false;
27358
+ const inside = relative10(root2, canonical9(path));
27359
+ if (!inside || inside === ".." || inside.startsWith(`..${sep15}`) || isAbsolute28(inside))
27360
+ return false;
27361
+ const parent = canonical9(join41(nativeHome, "plugins/cache", identity5.marketplace, identity5.name));
27362
+ const current = hashRawDiscoveryFile(canonical9(path), budget3);
27363
+ if (current === null)
27364
+ return false;
27365
+ return retained.sources.some((source) => {
27366
+ 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))
27367
+ return false;
27368
+ const [version2, ...rest] = source.path.slice(parent.length + 1).split(sep15);
27369
+ return Boolean(version2) && version2 !== "." && version2 !== ".." && rest.join(sep15) === inside;
27370
+ });
27371
+ };
27372
+ const verifyCodexPluginHookClosure = (root2, identity5, marketplaceSource, servers, declarations) => {
27373
+ const retained = exclusion.retained;
27374
+ if (retained.codexSemanticCache || retained.codexInstallationInputs || retained.codexRetiredMaterializations)
27375
+ return verifyOpaqueClosure(declarations);
27376
+ const within2 = (base, path) => path === base || path.startsWith(base + sep15);
27377
+ const executables = [];
27378
+ for (const server of servers) {
27379
+ const row = config.mcp_servers?.[server];
27380
+ if (server === "codex_apps" || server === "_codex_apps" || !isCodexLocalStdioMcpServer(server, row))
27381
+ unresolved("plugin hook MCP server is not a configured local stdio server");
27382
+ for (const value of [row.command, ...row.args ?? []])
27383
+ for (const candidate2 of [value, value.slice(value.indexOf("=") + 1)]) {
27384
+ if (!isAbsolute28(candidate2) || /[\0$]/.test(candidate2) || executables.length >= AGENT_POLICY_LIMITS.discoveryRoots)
27385
+ continue;
27386
+ const path = canonical9(candidate2);
27387
+ if (lstatSync34(path, { throwIfNoEntry: false })?.isFile())
27388
+ executables.push(path);
27389
+ }
27390
+ }
27391
+ const cacheRoot = canonical9(join41(nativeHome, "plugins/cache"));
27392
+ const ownCache = canonical9(join41(nativeHome, "plugins/cache", identity5.marketplace, identity5.name));
27393
+ const ownCatalog = canonical9(join41(marketplaceSource, ".agents/plugins/marketplace.json"));
27394
+ const account = canonical9(home);
27395
+ 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));
27396
+ const otherPlugin = (path) => !within2(root2, path) && !within2(ownCache, path) && path !== ownCatalog && !executables.includes(path) && (within2(cacheRoot, path) || marketplaceRoots.some((base) => within2(base, path)));
27397
+ const sources2 = retained.sources.filter((source) => source.path !== canonical9(configPath) && !otherPlugin(source.path));
27398
+ 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)))
27399
+ return verifyOpaqueClosure(declarations);
27400
+ const declared = declarations.map((path) => relative10(root2, path));
27401
+ const declaration = (path) => declarations.includes(path) || path.startsWith(ownCache + sep15) && declared.includes(path.slice(ownCache.length + 1).split(sep15).slice(1).join(sep15));
27402
+ if (!sources2.some((source) => !declaration(source.path) && source.sha256 !== null))
27403
+ unresolved("opaque executable dependency closure is unavailable");
27404
+ for (const source of sources2)
27405
+ if (projected(source, undefined, budget3) !== source.sha256) {
27406
+ throw new NativeDiscoveryWitnessChange("input", source.path, `Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
27407
+ }
27408
+ verifyDiscoveryDirectories((retained.directories ?? []).filter((directory2) => !otherPlugin(directory2.path)));
27409
+ };
27410
+ const readCodexHookDeclaration = (text2, path) => {
27411
+ if (text2 === null)
27412
+ return null;
27413
+ try {
27414
+ return codexPluginHookHandlers(parseConfig(text2, path));
27415
+ } catch {
27416
+ return null;
27417
+ }
27418
+ };
27419
+ function plugin(root2, codex) {
27141
27420
  root2 = canonical9(root2);
27142
27421
  safe6(root2);
27143
27422
  if (exclusion)
27144
27423
  roots.add(root2);
27145
27424
  const manifestPath = join41(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
27146
27425
  const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
27147
- if (raw === null)
27426
+ if (raw === null && !reviewedManifestAbsence(root2, manifestPath))
27148
27427
  unresolved("plugin manifest missing");
27149
- const manifest = parseConfig(raw, manifestPath);
27428
+ const manifest = raw === null ? {} : parseConfig(raw, manifestPath);
27150
27429
  if (exclusion && Object.keys(manifest).some((key2) => /skill|plugin|hook|marketplace|extension|instruction/i.test(key2) && !["skills", "hooks"].includes(key2)))
27151
27430
  unresolved("unknown plugin discovery controls");
27152
27431
  const hooks = witness(join41(root2, "hooks/hooks.json"));
27153
- if (manifest.hooks !== undefined || hooks !== null) {
27154
- if (!exclusion || exclusion.retained?.method !== "reviewed")
27155
- unresolved("plugin hooks require a separate no-skill-injection review");
27156
- const required = [manifestPath, ...hooks !== null ? [join41(root2, "hooks/hooks.json")] : []];
27157
- 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
- }
27165
- for (const path of required) {
27166
- 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))
27168
- unresolved("plugin hooks require current independently reviewed sources");
27169
- }
27170
- verifyOpaqueClosure(required.map((path) => canonical9(path)));
27171
- }
27432
+ if (manifest.hooks !== undefined || hooks !== null)
27433
+ reviewRemedy(canonical9(hooks !== null ? join41(root2, "hooks/hooks.json") : manifestPath), () => {
27434
+ if (!exclusion || exclusion.retained?.method !== "reviewed")
27435
+ unresolved("plugin hooks require a separate no-skill-injection review");
27436
+ const required = [manifestPath, ...hooks !== null ? [join41(root2, "hooks/hooks.json")] : []];
27437
+ const scoped2 = agent === "codex" && codex !== undefined ? [] : undefined;
27438
+ if (manifest.hooks !== undefined) {
27439
+ if (scoped2 && manifest.hooks && typeof manifest.hooks === "object" && !Array.isArray(manifest.hooks)) {
27440
+ const inline = codexPluginHookHandlers(manifest.hooks);
27441
+ if (!inline)
27442
+ unresolved("unsupported inline plugin hooks");
27443
+ scoped2.push(inline);
27444
+ } else {
27445
+ if (typeof manifest.hooks !== "string")
27446
+ unresolved("unsupported plugin hooks target");
27447
+ const hook = resolve28(root2, manifest.hooks);
27448
+ if (!hook.startsWith(root2 + sep15))
27449
+ unresolved("plugin hooks target escapes its root");
27450
+ required.push(hook);
27451
+ if (scoped2)
27452
+ scoped2.push(readCodexHookDeclaration(witness(hook), hook));
27453
+ }
27454
+ }
27455
+ if (scoped2 && hooks !== null)
27456
+ scoped2.push(readCodexHookDeclaration(hooks, join41(root2, "hooks/hooks.json")));
27457
+ for (const path of required) {
27458
+ const saved = exclusion.retained.sources.filter((source) => source.path === canonical9(path) && source.sha256 !== null && source.format === undefined && source.fields === undefined);
27459
+ if (saved.length ? saved.some((source) => projected(source, undefined, budget3) !== source.sha256) : !(scoped2 && reviewedInstalledCopy(root2, path, codex)))
27460
+ unresolved("plugin hooks require current independently reviewed sources", canonical9(path));
27461
+ }
27462
+ if (scoped2 && scoped2.every((item) => item !== null && item.commands === 0)) {
27463
+ verifyCodexPluginHookClosure(root2, codex, codex.source, new Set(scoped2.flatMap((item) => [...item.servers])), required.map((path) => canonical9(path)));
27464
+ } else
27465
+ verifyOpaqueClosure(required.map((path) => canonical9(path)));
27466
+ });
27172
27467
  const commands = join41(root2, "commands");
27173
27468
  safe6(commands);
27174
27469
  if (agent === "claude" && (manifest.commands !== undefined || lstatSync34(commands, { throwIfNoEntry: false })?.isDirectory() && entries(commands).length))
@@ -27223,7 +27518,7 @@ function resolveDiscovery(options, exclusion) {
27223
27518
  const catalog = parseConfig(catalogText, join41(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
27224
27519
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
27225
27520
  unresolved("plugin catalog source is missing or ambiguous");
27226
- plugin(resolve28(market.source, matches[0].source.path));
27521
+ plugin(resolve28(market.source, matches[0].source.path), exclusion ? { marketplace, name, source: market.source } : undefined);
27227
27522
  }
27228
27523
  } else if (agent === "opencode") {
27229
27524
  if (witness(join41(home, ".config/opencode/opencode.jsonc")) !== null || witness(join41(home, ".config/opencode/config.json")) !== null)
@@ -27240,7 +27535,11 @@ function resolveDiscovery(options, exclusion) {
27240
27535
  if (lstatSync34(localPlugins, { throwIfNoEntry: false }) && entries(localPlugins).some((name) => name !== "skills-cli.js"))
27241
27536
  unresolved("additional local plugin hooks require review");
27242
27537
  } 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))
27538
+ const pinnedLocalPlugin = (source) => {
27539
+ const retained = exclusion?.retained, text2 = sumiLocalPluginTexts.get(source.path);
27540
+ 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);
27541
+ };
27542
+ 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
27543
  unresolved("external or foreign local plugins require review");
27245
27544
  } else if (agent === "hermes") {
27246
27545
  for (const directory2 of [join41(home, ".hermes/plugins"), join41(home, ".hermes/hermes-agent")]) {
@@ -27303,6 +27602,11 @@ function physicalPath(path) {
27303
27602
  }
27304
27603
  return resolve29(realpathSync14(cursor), ...suffix);
27305
27604
  }
27605
+ function portableAuthoringDiscoveryRefusal(error, destination) {
27606
+ const head = `Native discovery is unresolved (${error.agent}: ${error.detail}).${error.source ? ` Checked source: ${error.source}.` : ""}`;
27607
+ const review = `skills hook install --agent ${error.agent} --discovery-inputs <reviewed-file> --apply`;
27608
+ return error.reviewable ? `${head} Portable Skills authoring stays refused: an unreviewed ${error.agent} hook or plugin option could load skills written to ${destination}. This command takes no --discovery-inputs option; review the current ${error.agent} configuration with ${review}, then retry.` : `${head} Portable Skills authoring stays refused: Skills cannot list every skill root this ${error.agent} configuration registers, so it cannot rule out ${destination}. This command takes no --discovery-inputs option; correct that ${error.agent} configuration entry, then retry. Where a reviewed discovery binding covers it, record one with ${review}.`;
27609
+ }
27306
27610
  function contains2(root2, path) {
27307
27611
  const boundary = root2.endsWith(sep16) ? root2 : root2 + sep16;
27308
27612
  return path === root2 || path.startsWith(boundary);
@@ -27348,7 +27652,10 @@ function configuredRoots(home, explicitHome) {
27348
27652
  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
27653
  throw new Error("Native discovery runtime target is unresolved or changed");
27350
27654
  }
27351
- roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: binding.agent, canonical: physicalPath, retained: binding, executable: executable2, narrowed }));
27655
+ const managedCommand = current.commands?.[agent], managedProfile = current.profiles?.[agent];
27656
+ const supervisor = current.supervisors?.[agent];
27657
+ 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;
27658
+ roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: binding.agent, canonical: physicalPath, retained: binding, executable: executable2, narrowed, ...managedHook ? { managedHook } : {} }));
27352
27659
  }
27353
27660
  };
27354
27661
  if (homePolicy)
@@ -27400,11 +27707,18 @@ function assertPortableAuthoringPath(path, options = {}) {
27400
27707
  if (native) {
27401
27708
  throw new Error(`Refusing portable Skills authoring below native discovery root '${native}': ${lexical}. Set HASNA_SKILLS_DIR to a private authoring corpus outside native roots.`);
27402
27709
  }
27403
- const roots = configuredRoots(home, options.homeDir !== undefined);
27404
- for (const value of selected) {
27405
- if (physicalPath(value.path) === physicalPath(join42(home, `.${value.agent}`)))
27406
- continue;
27407
- roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: value.agent, nativeHome: value.path, canonical: physicalPath, executable: null }));
27710
+ const roots = [];
27711
+ try {
27712
+ roots.push(...configuredRoots(home, options.homeDir !== undefined));
27713
+ for (const value of selected) {
27714
+ if (physicalPath(value.path) === physicalPath(join42(home, `.${value.agent}`)))
27715
+ continue;
27716
+ roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: value.agent, nativeHome: value.path, canonical: physicalPath, executable: null }));
27717
+ }
27718
+ } catch (error) {
27719
+ if (error instanceof NativeDiscoveryUnresolved)
27720
+ throw new Error(portableAuthoringDiscoveryRefusal(error, lexical));
27721
+ throw error;
27408
27722
  }
27409
27723
  for (const root2 of roots) {
27410
27724
  if (candidates.some((candidate2) => [resolve29(root2), physicalPath(root2)].some((nativeRoot) => contains2(nativeRoot, candidate2) || contains2(candidate2, nativeRoot)))) {