@hasna/skills 0.10.92 → 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/THIRD_PARTY_NOTICES +1 -1
- package/bin/index.js +829 -726
- package/bin/mcp.js +95 -47
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/portable-skills.d.ts +1 -0
- package/dist/index.js +161 -69
- package/dist/lib/agent-discovery.d.ts +13 -0
- package/dist/lib/authoring-path.d.ts +5 -0
- package/dist/lib/linux-access-acl.d.ts +55 -0
- package/dist/runtime-prerequisites.js +1 -1
- package/dist/sdk/index.js +161 -69
- package/package.json +1 -1
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.
|
|
41
|
+
version: "0.10.93",
|
|
42
42
|
description: "Skills library for AI coding agents",
|
|
43
43
|
type: "module",
|
|
44
44
|
bin: {
|
|
@@ -36403,6 +36403,17 @@ var init_runtime_launcher = __esm(() => {
|
|
|
36403
36403
|
PINNED_LAUNCHER_MAX_BYTES = 16 * 1024;
|
|
36404
36404
|
});
|
|
36405
36405
|
|
|
36406
|
+
// src/lib/linux-access-acl.ts
|
|
36407
|
+
var LINUX_ACL_FREE_WHEN_UNSUPPORTED;
|
|
36408
|
+
var init_linux_access_acl = __esm(() => {
|
|
36409
|
+
LINUX_ACL_FREE_WHEN_UNSUPPORTED = new Set([
|
|
36410
|
+
0xef53n,
|
|
36411
|
+
0x58465342n,
|
|
36412
|
+
0x9123683en,
|
|
36413
|
+
0x01021994n
|
|
36414
|
+
]);
|
|
36415
|
+
});
|
|
36416
|
+
|
|
36406
36417
|
// src/lib/retained-review-artifacts.ts
|
|
36407
36418
|
import { createHash as createHash23 } from "crypto";
|
|
36408
36419
|
import { constants as constants15, closeSync as closeSync17, fstatSync as fstatSync17, fsyncSync as fsyncSync4, lstatSync as lstatSync22, mkdirSync as mkdirSync6, openSync as openSync17, readSync as readSync11, writeSync as writeSync3 } from "fs";
|
|
@@ -36524,6 +36535,7 @@ var init_agent_integration = __esm(() => {
|
|
|
36524
36535
|
init_claude_settings_witness();
|
|
36525
36536
|
init_codex_hook_trust_files();
|
|
36526
36537
|
init_runtime_launcher();
|
|
36538
|
+
init_linux_access_acl();
|
|
36527
36539
|
init_retained_review_artifacts();
|
|
36528
36540
|
});
|
|
36529
36541
|
|
|
@@ -38806,8 +38818,8 @@ function resolveDiscovery(options, exclusion) {
|
|
|
38806
38818
|
const configPath = exclusion?.nativeHome === undefined ? agentDiscoveryConfigPath(home, agent) : join33(nativeHome, agent === "codex" ? "config.toml" : "settings.json");
|
|
38807
38819
|
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"]);
|
|
38808
38820
|
const config2 = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
38809
|
-
const unresolved = (detail) => {
|
|
38810
|
-
throw new
|
|
38821
|
+
const unresolved = (detail, source) => {
|
|
38822
|
+
throw new NativeDiscoveryUnresolved(agent, detail, source);
|
|
38811
38823
|
};
|
|
38812
38824
|
let opaqueClosureVerified = false;
|
|
38813
38825
|
const verifyOpaqueClosure = (declarations) => {
|
|
@@ -38821,6 +38833,17 @@ function resolveDiscovery(options, exclusion) {
|
|
|
38821
38833
|
verifyDiscovery(retained, undefined, undefined, undefined, exclusion?.narrowed, canonical9(configPath));
|
|
38822
38834
|
opaqueClosureVerified = true;
|
|
38823
38835
|
};
|
|
38836
|
+
const reviewRemedy = (source, check2) => {
|
|
38837
|
+
try {
|
|
38838
|
+
check2();
|
|
38839
|
+
} catch (error61) {
|
|
38840
|
+
if (error61 instanceof NativeDiscoveryUnresolved) {
|
|
38841
|
+
error61.reviewable = true;
|
|
38842
|
+
error61.source ??= source;
|
|
38843
|
+
}
|
|
38844
|
+
throw error61;
|
|
38845
|
+
}
|
|
38846
|
+
};
|
|
38824
38847
|
if (exclusion) {
|
|
38825
38848
|
if (configText !== null && agent !== "codex" && agent !== "hermes")
|
|
38826
38849
|
parseNativeJsonObject(configText);
|
|
@@ -38892,7 +38915,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
38892
38915
|
unresolved(`non-enumerable ${label} require a current settings review`);
|
|
38893
38916
|
};
|
|
38894
38917
|
if (hasHooks)
|
|
38895
|
-
verifyCurrentOpaqueSettings("hooks");
|
|
38918
|
+
reviewRemedy(canonical9(configPath), () => verifyCurrentOpaqueSettings("hooks"));
|
|
38896
38919
|
if (agent === "claude") {
|
|
38897
38920
|
if (config2.env !== undefined) {
|
|
38898
38921
|
if (!object6(config2.env) || Object.keys(config2.env).length > 512)
|
|
@@ -38905,7 +38928,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
38905
38928
|
if (hasFunctionHookEnvironment) {
|
|
38906
38929
|
if (!["0", "1"].includes(config2.env.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS))
|
|
38907
38930
|
unresolved("malformed Claude env.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS flag");
|
|
38908
|
-
verifyCurrentOpaqueSettings("function-hook environment controls");
|
|
38931
|
+
reviewRemedy(canonical9(configPath), () => verifyCurrentOpaqueSettings("function-hook environment controls"));
|
|
38909
38932
|
}
|
|
38910
38933
|
}
|
|
38911
38934
|
if (config2.pluginConfigs !== undefined) {
|
|
@@ -38932,7 +38955,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
38932
38955
|
}
|
|
38933
38956
|
hasPluginConfigs = Object.keys(config2.pluginConfigs).length > 0;
|
|
38934
38957
|
if (hasPluginConfigs)
|
|
38935
|
-
verifyCurrentOpaqueSettings("plugin configurations");
|
|
38958
|
+
reviewRemedy(canonical9(configPath), () => verifyCurrentOpaqueSettings("plugin configurations"));
|
|
38936
38959
|
}
|
|
38937
38960
|
if (config2.enabledPlugins !== undefined && !object6(config2.enabledPlugins))
|
|
38938
38961
|
unresolved("enabledPlugins must be an object");
|
|
@@ -39044,7 +39067,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
39044
39067
|
if (Object.keys(config2.enabledPlugins ?? config2.plugins ?? config2.extensions ?? {}).length > 512)
|
|
39045
39068
|
unresolved("unbounded native registrations");
|
|
39046
39069
|
if (hasHooks || hasPluginConfigs || hasFunctionHookEnvironment)
|
|
39047
|
-
verifyOpaqueClosure([]);
|
|
39070
|
+
reviewRemedy(canonical9(configPath), () => verifyOpaqueClosure([]));
|
|
39048
39071
|
}
|
|
39049
39072
|
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))
|
|
39050
39073
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
@@ -39297,40 +39320,41 @@ function resolveDiscovery(options, exclusion) {
|
|
|
39297
39320
|
if (exclusion && Object.keys(manifest).some((key2) => /skill|plugin|hook|marketplace|extension|instruction/i.test(key2) && !["skills", "hooks"].includes(key2)))
|
|
39298
39321
|
unresolved("unknown plugin discovery controls");
|
|
39299
39322
|
const hooks = witness(join33(root, "hooks/hooks.json"));
|
|
39300
|
-
if (manifest.hooks !== undefined || hooks !== null)
|
|
39301
|
-
|
|
39302
|
-
|
|
39303
|
-
|
|
39304
|
-
|
|
39305
|
-
|
|
39306
|
-
if (
|
|
39307
|
-
|
|
39308
|
-
|
|
39309
|
-
|
|
39310
|
-
|
|
39311
|
-
|
|
39312
|
-
|
|
39313
|
-
|
|
39314
|
-
|
|
39315
|
-
|
|
39316
|
-
|
|
39317
|
-
|
|
39318
|
-
|
|
39319
|
-
scoped
|
|
39320
|
-
|
|
39321
|
-
|
|
39322
|
-
|
|
39323
|
-
scoped
|
|
39324
|
-
|
|
39325
|
-
|
|
39326
|
-
|
|
39327
|
-
|
|
39328
|
-
|
|
39329
|
-
|
|
39330
|
-
|
|
39331
|
-
|
|
39332
|
-
|
|
39333
|
-
|
|
39323
|
+
if (manifest.hooks !== undefined || hooks !== null)
|
|
39324
|
+
reviewRemedy(canonical9(hooks !== null ? join33(root, "hooks/hooks.json") : manifestPath), () => {
|
|
39325
|
+
if (!exclusion || exclusion.retained?.method !== "reviewed")
|
|
39326
|
+
unresolved("plugin hooks require a separate no-skill-injection review");
|
|
39327
|
+
const required2 = [manifestPath, ...hooks !== null ? [join33(root, "hooks/hooks.json")] : []];
|
|
39328
|
+
const scoped = agent === "codex" && codex !== undefined ? [] : undefined;
|
|
39329
|
+
if (manifest.hooks !== undefined) {
|
|
39330
|
+
if (scoped && manifest.hooks && typeof manifest.hooks === "object" && !Array.isArray(manifest.hooks)) {
|
|
39331
|
+
const inline = codexPluginHookHandlers(manifest.hooks);
|
|
39332
|
+
if (!inline)
|
|
39333
|
+
unresolved("unsupported inline plugin hooks");
|
|
39334
|
+
scoped.push(inline);
|
|
39335
|
+
} else {
|
|
39336
|
+
if (typeof manifest.hooks !== "string")
|
|
39337
|
+
unresolved("unsupported plugin hooks target");
|
|
39338
|
+
const hook = resolve23(root, manifest.hooks);
|
|
39339
|
+
if (!hook.startsWith(root + sep11))
|
|
39340
|
+
unresolved("plugin hooks target escapes its root");
|
|
39341
|
+
required2.push(hook);
|
|
39342
|
+
if (scoped)
|
|
39343
|
+
scoped.push(readCodexHookDeclaration(witness(hook), hook));
|
|
39344
|
+
}
|
|
39345
|
+
}
|
|
39346
|
+
if (scoped && hooks !== null)
|
|
39347
|
+
scoped.push(readCodexHookDeclaration(hooks, join33(root, "hooks/hooks.json")));
|
|
39348
|
+
for (const path of required2) {
|
|
39349
|
+
const saved = exclusion.retained.sources.filter((source) => source.path === canonical9(path) && source.sha256 !== null && source.format === undefined && source.fields === undefined);
|
|
39350
|
+
if (saved.length ? saved.some((source) => projected(source, undefined, budget3) !== source.sha256) : !(scoped && reviewedInstalledCopy(root, path, codex)))
|
|
39351
|
+
unresolved("plugin hooks require current independently reviewed sources", canonical9(path));
|
|
39352
|
+
}
|
|
39353
|
+
if (scoped && scoped.every((item) => item !== null && item.commands === 0)) {
|
|
39354
|
+
verifyCodexPluginHookClosure(root, codex, codex.source, new Set(scoped.flatMap((item) => [...item.servers])), required2.map((path) => canonical9(path)));
|
|
39355
|
+
} else
|
|
39356
|
+
verifyOpaqueClosure(required2.map((path) => canonical9(path)));
|
|
39357
|
+
});
|
|
39334
39358
|
const commands = join33(root, "commands");
|
|
39335
39359
|
safe6(commands);
|
|
39336
39360
|
if (agent === "claude" && (manifest.commands !== undefined || lstatSync25(commands, { throwIfNoEntry: false })?.isDirectory() && entries(commands).length))
|
|
@@ -39440,7 +39464,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
39440
39464
|
unresolved("native discovery roots exceed their bound");
|
|
39441
39465
|
return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join33(home, ".hermes/plugins"), join33(home, ".hermes/hermes-agent")].map((path) => canonical9(path))) } : {} };
|
|
39442
39466
|
}
|
|
39443
|
-
var DISCOVERY_RUNTIME_COMMANDS, quotedPath = (path) => JSON.stringify(path).replace(/[^\x20-\x7e]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`), realpathOrNull = (path) => {
|
|
39467
|
+
var DISCOVERY_RUNTIME_COMMANDS, NativeDiscoveryUnresolved, quotedPath = (path) => JSON.stringify(path).replace(/[^\x20-\x7e]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`), realpathOrNull = (path) => {
|
|
39444
39468
|
try {
|
|
39445
39469
|
return realpathSync8(path);
|
|
39446
39470
|
} catch {
|
|
@@ -39477,6 +39501,18 @@ var init_agent_discovery = __esm(() => {
|
|
|
39477
39501
|
init_codex_semantic_cache_witness();
|
|
39478
39502
|
init_agent_discovery_directories();
|
|
39479
39503
|
DISCOVERY_RUNTIME_COMMANDS = Object.freeze({ gemini: "gemini" });
|
|
39504
|
+
NativeDiscoveryUnresolved = class NativeDiscoveryUnresolved extends Error {
|
|
39505
|
+
agent;
|
|
39506
|
+
detail;
|
|
39507
|
+
source;
|
|
39508
|
+
reviewable = false;
|
|
39509
|
+
constructor(agent, detail, source) {
|
|
39510
|
+
super(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
|
|
39511
|
+
this.agent = agent;
|
|
39512
|
+
this.detail = detail;
|
|
39513
|
+
this.source = source;
|
|
39514
|
+
}
|
|
39515
|
+
};
|
|
39480
39516
|
CLAUDE_CONTROL_WITNESS_MODES = new Set(["claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-settings-v5", "claude-marketplace-registry", "claude-marketplace-registry-v2", "claude-marketplace-entry-v1", "claude-plugin-registry"]);
|
|
39481
39517
|
CLAUDE_CONTROL_FILES = new Set(["settings.json", "settings.local.json", "installed_plugins.json", "known_marketplaces.json"]);
|
|
39482
39518
|
ADOPTABLE_SETTINGS_MATCHES = Object.freeze(["exact", "screen-reader-receipt"]);
|
|
@@ -39515,6 +39551,11 @@ function physicalPath(path) {
|
|
|
39515
39551
|
}
|
|
39516
39552
|
return resolve24(realpathSync9(cursor), ...suffix);
|
|
39517
39553
|
}
|
|
39554
|
+
function portableAuthoringDiscoveryRefusal(error61, destination) {
|
|
39555
|
+
const head = `Native discovery is unresolved (${error61.agent}: ${error61.detail}).${error61.source ? ` Checked source: ${error61.source}.` : ""}`;
|
|
39556
|
+
const review = `skills hook install --agent ${error61.agent} --discovery-inputs <reviewed-file> --apply`;
|
|
39557
|
+
return error61.reviewable ? `${head} Portable Skills authoring stays refused: an unreviewed ${error61.agent} hook or plugin option could load skills written to ${destination}. This command takes no --discovery-inputs option; review the current ${error61.agent} configuration with ${review}, then retry.` : `${head} Portable Skills authoring stays refused: Skills cannot list every skill root this ${error61.agent} configuration registers, so it cannot rule out ${destination}. This command takes no --discovery-inputs option; correct that ${error61.agent} configuration entry, then retry. Where a reviewed discovery binding covers it, record one with ${review}.`;
|
|
39558
|
+
}
|
|
39518
39559
|
function contains(root, path) {
|
|
39519
39560
|
const boundary = root.endsWith(sep12) ? root : root + sep12;
|
|
39520
39561
|
return path === root || path.startsWith(boundary);
|
|
@@ -39615,11 +39656,18 @@ function assertPortableAuthoringPath(path, options = {}) {
|
|
|
39615
39656
|
if (native) {
|
|
39616
39657
|
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.`);
|
|
39617
39658
|
}
|
|
39618
|
-
const roots =
|
|
39619
|
-
|
|
39620
|
-
|
|
39621
|
-
|
|
39622
|
-
|
|
39659
|
+
const roots = [];
|
|
39660
|
+
try {
|
|
39661
|
+
roots.push(...configuredRoots(home, options.homeDir !== undefined));
|
|
39662
|
+
for (const value of selected) {
|
|
39663
|
+
if (physicalPath(value.path) === physicalPath(join34(home, `.${value.agent}`)))
|
|
39664
|
+
continue;
|
|
39665
|
+
roots.push(...resolveAgentDiscoveryExclusionRoots({ home, agent: value.agent, nativeHome: value.path, canonical: physicalPath, executable: null }));
|
|
39666
|
+
}
|
|
39667
|
+
} catch (error61) {
|
|
39668
|
+
if (error61 instanceof NativeDiscoveryUnresolved)
|
|
39669
|
+
throw new Error(portableAuthoringDiscoveryRefusal(error61, lexical));
|
|
39670
|
+
throw error61;
|
|
39623
39671
|
}
|
|
39624
39672
|
for (const root of roots) {
|
|
39625
39673
|
if (candidates.some((candidate) => [resolve24(root), physicalPath(root)].some((nativeRoot) => contains(nativeRoot, candidate) || contains(candidate, nativeRoot)))) {
|
package/bin/migrate.js
CHANGED
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.
|
|
83770
|
+
version: "0.10.93",
|
|
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.
|
|
76189
|
+
version: "0.10.93",
|
|
76190
76190
|
description: "Skills library for AI coding agents",
|
|
76191
76191
|
type: "module",
|
|
76192
76192
|
bin: {
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
+
export declare const AUTHORING_NATIVE_HOOKS_HELP = "\nNative hooks:\n Authoring refuses while another agent's native hooks, plugin hooks or plugin\n options are not covered by a current discovery review, because they could\n load skills from the authoring corpus. The refusal names the agent and the\n file it checked; review that agent's configuration with\n skills hook install --agent <agent> --discovery-inputs <reviewed-file> --apply\n and retry. Authoring commands themselves take no --discovery-inputs option.\n";
|
|
2
3
|
export declare function registerPortableSkillCommands(parent: Command): void;
|
package/dist/index.js
CHANGED
|
@@ -16369,7 +16369,7 @@ import { basename as basename15, dirname as dirname27, isAbsolute as isAbsolute2
|
|
|
16369
16369
|
import { homedir as homedir5 } from "os";
|
|
16370
16370
|
import { isAbsolute as isAbsolute25, join as join39, resolve as resolve25 } from "path";
|
|
16371
16371
|
// package.json
|
|
16372
|
-
var version = "0.10.
|
|
16372
|
+
var version = "0.10.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
|
-
|
|
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 {
|
|
@@ -26859,8 +26927,8 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26859
26927
|
const configPath = exclusion?.nativeHome === undefined ? agentDiscoveryConfigPath(home, agent) : join41(nativeHome, agent === "codex" ? "config.toml" : "settings.json");
|
|
26860
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"]);
|
|
26861
26929
|
const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
|
|
26862
|
-
const unresolved = (detail) => {
|
|
26863
|
-
throw new
|
|
26930
|
+
const unresolved = (detail, source) => {
|
|
26931
|
+
throw new NativeDiscoveryUnresolved(agent, detail, source);
|
|
26864
26932
|
};
|
|
26865
26933
|
let opaqueClosureVerified = false;
|
|
26866
26934
|
const verifyOpaqueClosure = (declarations) => {
|
|
@@ -26874,6 +26942,17 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26874
26942
|
verifyDiscovery(retained, undefined, undefined, undefined, exclusion?.narrowed, canonical9(configPath));
|
|
26875
26943
|
opaqueClosureVerified = true;
|
|
26876
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
|
+
};
|
|
26877
26956
|
if (exclusion) {
|
|
26878
26957
|
if (configText !== null && agent !== "codex" && agent !== "hermes")
|
|
26879
26958
|
parseNativeJsonObject(configText);
|
|
@@ -26945,7 +27024,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26945
27024
|
unresolved(`non-enumerable ${label} require a current settings review`);
|
|
26946
27025
|
};
|
|
26947
27026
|
if (hasHooks)
|
|
26948
|
-
verifyCurrentOpaqueSettings("hooks");
|
|
27027
|
+
reviewRemedy(canonical9(configPath), () => verifyCurrentOpaqueSettings("hooks"));
|
|
26949
27028
|
if (agent === "claude") {
|
|
26950
27029
|
if (config.env !== undefined) {
|
|
26951
27030
|
if (!object6(config.env) || Object.keys(config.env).length > 512)
|
|
@@ -26958,7 +27037,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26958
27037
|
if (hasFunctionHookEnvironment) {
|
|
26959
27038
|
if (!["0", "1"].includes(config.env.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS))
|
|
26960
27039
|
unresolved("malformed Claude env.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS flag");
|
|
26961
|
-
verifyCurrentOpaqueSettings("function-hook environment controls");
|
|
27040
|
+
reviewRemedy(canonical9(configPath), () => verifyCurrentOpaqueSettings("function-hook environment controls"));
|
|
26962
27041
|
}
|
|
26963
27042
|
}
|
|
26964
27043
|
if (config.pluginConfigs !== undefined) {
|
|
@@ -26985,7 +27064,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
26985
27064
|
}
|
|
26986
27065
|
hasPluginConfigs = Object.keys(config.pluginConfigs).length > 0;
|
|
26987
27066
|
if (hasPluginConfigs)
|
|
26988
|
-
verifyCurrentOpaqueSettings("plugin configurations");
|
|
27067
|
+
reviewRemedy(canonical9(configPath), () => verifyCurrentOpaqueSettings("plugin configurations"));
|
|
26989
27068
|
}
|
|
26990
27069
|
if (config.enabledPlugins !== undefined && !object6(config.enabledPlugins))
|
|
26991
27070
|
unresolved("enabledPlugins must be an object");
|
|
@@ -27097,7 +27176,7 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27097
27176
|
if (Object.keys(config.enabledPlugins ?? config.plugins ?? config.extensions ?? {}).length > 512)
|
|
27098
27177
|
unresolved("unbounded native registrations");
|
|
27099
27178
|
if (hasHooks || hasPluginConfigs || hasFunctionHookEnvironment)
|
|
27100
|
-
verifyOpaqueClosure([]);
|
|
27179
|
+
reviewRemedy(canonical9(configPath), () => verifyOpaqueClosure([]));
|
|
27101
27180
|
}
|
|
27102
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))
|
|
27103
27182
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
@@ -27350,40 +27429,41 @@ function resolveDiscovery(options, exclusion) {
|
|
|
27350
27429
|
if (exclusion && Object.keys(manifest).some((key2) => /skill|plugin|hook|marketplace|extension|instruction/i.test(key2) && !["skills", "hooks"].includes(key2)))
|
|
27351
27430
|
unresolved("unknown plugin discovery controls");
|
|
27352
27431
|
const hooks = witness(join41(root2, "hooks/hooks.json"));
|
|
27353
|
-
if (manifest.hooks !== undefined || hooks !== null)
|
|
27354
|
-
|
|
27355
|
-
|
|
27356
|
-
|
|
27357
|
-
|
|
27358
|
-
|
|
27359
|
-
if (
|
|
27360
|
-
|
|
27361
|
-
|
|
27362
|
-
|
|
27363
|
-
|
|
27364
|
-
|
|
27365
|
-
|
|
27366
|
-
|
|
27367
|
-
|
|
27368
|
-
|
|
27369
|
-
|
|
27370
|
-
|
|
27371
|
-
|
|
27372
|
-
scoped2
|
|
27373
|
-
|
|
27374
|
-
|
|
27375
|
-
|
|
27376
|
-
scoped2
|
|
27377
|
-
|
|
27378
|
-
|
|
27379
|
-
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
|
|
27383
|
-
|
|
27384
|
-
|
|
27385
|
-
|
|
27386
|
-
|
|
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
|
+
});
|
|
27387
27467
|
const commands = join41(root2, "commands");
|
|
27388
27468
|
safe6(commands);
|
|
27389
27469
|
if (agent === "claude" && (manifest.commands !== undefined || lstatSync34(commands, { throwIfNoEntry: false })?.isDirectory() && entries(commands).length))
|
|
@@ -27522,6 +27602,11 @@ function physicalPath(path) {
|
|
|
27522
27602
|
}
|
|
27523
27603
|
return resolve29(realpathSync14(cursor), ...suffix);
|
|
27524
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
|
+
}
|
|
27525
27610
|
function contains2(root2, path) {
|
|
27526
27611
|
const boundary = root2.endsWith(sep16) ? root2 : root2 + sep16;
|
|
27527
27612
|
return path === root2 || path.startsWith(boundary);
|
|
@@ -27622,11 +27707,18 @@ function assertPortableAuthoringPath(path, options = {}) {
|
|
|
27622
27707
|
if (native) {
|
|
27623
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.`);
|
|
27624
27709
|
}
|
|
27625
|
-
const roots =
|
|
27626
|
-
|
|
27627
|
-
|
|
27628
|
-
|
|
27629
|
-
|
|
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;
|
|
27630
27722
|
}
|
|
27631
27723
|
for (const root2 of roots) {
|
|
27632
27724
|
if (candidates.some((candidate2) => [resolve29(root2), physicalPath(root2)].some((nativeRoot) => contains2(nativeRoot, candidate2) || contains2(candidate2, nativeRoot)))) {
|
|
@@ -65,6 +65,19 @@ export interface AgentDiscoveryBinding {
|
|
|
65
65
|
export declare const DISCOVERY_ROOT_UNRESOLVED = "DISCOVERY_ROOT_UNRESOLVED";
|
|
66
66
|
export declare function discoveryRootUnresolved(agent: IntegrationAgent, command: string, detail: string): Error;
|
|
67
67
|
export declare function isDiscoveryRootUnresolved(error: unknown): error is Error;
|
|
68
|
+
/** A native configuration Skills cannot resolve. The message names the
|
|
69
|
+
* --discovery-inputs option of the native commands that accept it (hook
|
|
70
|
+
* install, migrate native). Portable authoring takes no such option and
|
|
71
|
+
* rewrites the remedy from these fields: `reviewable` marks a hook, plugin hook
|
|
72
|
+
* or plugin option check that a current discovery review satisfies, and
|
|
73
|
+
* `source` names the configuration or hook file it checked (a path only). */
|
|
74
|
+
export declare class NativeDiscoveryUnresolved extends Error {
|
|
75
|
+
readonly agent: IntegrationAgent;
|
|
76
|
+
readonly detail: string;
|
|
77
|
+
source?: string | undefined;
|
|
78
|
+
reviewable: boolean;
|
|
79
|
+
constructor(agent: IntegrationAgent, detail: string, source?: string | undefined);
|
|
80
|
+
}
|
|
68
81
|
/** The runtime executable a discovery review resolved through PATH, with the
|
|
69
82
|
* realpath its launcher led to. The policy stores it beside the binding
|
|
70
83
|
* (bridge.discoveryExecutables), not inside it, so a consumer that compares
|