@hasna/skills 0.9.15 → 0.9.16
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/README.md +7 -0
- package/bin/index.js +58 -31
- package/bin/mcp.js +17 -3
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +31 -4
- package/dist/lib/agent-discovery.d.ts +1 -0
- package/dist/lib/agent-integration.d.ts +5 -0
- package/dist/sdk/index.js +34 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -373,6 +373,13 @@ system configuration, process-specific overrides, and alternate agent home
|
|
|
373
373
|
directories are outside automatic coverage and require their own integration
|
|
374
374
|
review before declaring a station migrated.
|
|
375
375
|
|
|
376
|
+
When `hook install` omits `--discovery-inputs`, it reuses an existing reviewed
|
|
377
|
+
binding only after rechecking its sources, directory membership, configuration
|
|
378
|
+
coverage, managed bridge and native trust. Unchanged reviews need no new input
|
|
379
|
+
file; drift still requires a fresh explicit review. Explicit discovery inputs
|
|
380
|
+
take precedence, including when their `agents` array omits a previously reviewed
|
|
381
|
+
agent. The default `--agent all` still includes every supported adapter.
|
|
382
|
+
|
|
376
383
|
For reviewed Claude plugins, `skills integration plugin plan`, `admit` and
|
|
377
384
|
`resolve` provide a command-source admission boundary before native discovery.
|
|
378
385
|
Original packages and migrated skill/command prompts remain in private versioned
|
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.9.
|
|
36871
|
+
version: "0.9.16",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -51146,7 +51146,15 @@ function planAgentIntegration(options) {
|
|
|
51146
51146
|
validateProfile(profileId2);
|
|
51147
51147
|
return [agent, { command, profileId: profileId2 }];
|
|
51148
51148
|
}));
|
|
51149
|
-
const
|
|
51149
|
+
const retainedAgents = [];
|
|
51150
|
+
const discoveries = [...new Set(options.agents)].map((agent) => {
|
|
51151
|
+
const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? policy.bridge.discovery[agent] : undefined;
|
|
51152
|
+
if (retainedReview) {
|
|
51153
|
+
assertManagedAgentBridge(agent, { home, dataDir: dataDir3, projectDir: options.projectDir ?? home });
|
|
51154
|
+
retainedAgents.push(agent);
|
|
51155
|
+
}
|
|
51156
|
+
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
51157
|
+
});
|
|
51150
51158
|
const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases });
|
|
51151
51159
|
const changes = [];
|
|
51152
51160
|
for (const agent of [...new Set(options.agents)]) {
|
|
@@ -51231,7 +51239,7 @@ function planAgentIntegration(options) {
|
|
|
51231
51239
|
if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
|
|
51232
51240
|
changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
|
|
51233
51241
|
recheckRootAliases(aliases);
|
|
51234
|
-
return { dataDir: dataDir3, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
51242
|
+
return { dataDir: dataDir3, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
|
|
51235
51243
|
}
|
|
51236
51244
|
function verifyCoordinatedDiscovery(binding, home, aliases) {
|
|
51237
51245
|
try {
|
|
@@ -51269,6 +51277,8 @@ function applyAgentIntegration(plan) {
|
|
|
51269
51277
|
parseManagedSkillPolicy(change.after);
|
|
51270
51278
|
const aliases = plan.rootAliases ?? [];
|
|
51271
51279
|
recheckRootAliases(aliases);
|
|
51280
|
+
for (const agent of plan.retainedReviewChecks?.agents ?? [])
|
|
51281
|
+
assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
|
|
51272
51282
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
51273
51283
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
51274
51284
|
for (const binding of plan.discoveryBefore ?? []) {
|
|
@@ -54741,7 +54751,7 @@ function reviewNativeHookAdditions(options) {
|
|
|
54741
54751
|
need4(Array.isArray(groups), "RECONCILE_ADDITIONS_DECLARATIONS");
|
|
54742
54752
|
const key = `${options.nativeHooksPath}:${matchedEvents[event][1]}:${groups.length}:0`;
|
|
54743
54753
|
need4(hook.key === key && !seen.has(key) && !options.recordedNativeHooks.some((originalHook) => originalHook.key === key) && !Object.hasOwn(expectedConfig.hooks.state, key), "RECONCILE_ADDITIONS_IDENTITY");
|
|
54744
|
-
need4(hook.eventName === matchedEvents[event][0] && hook.handlerType === "command" && text2(hook.command, 16384) && !commands.has(hook.command) && Number.isInteger(hook.timeoutSec) && hook.timeoutSec > 0 && hook.timeoutSec <= 60 && (event === "PreToolUse" ? text2(hook.matcher) : hook.matcher === null) && hook.async === false && hook.statusMessage === null && hook.additionalContextLimit === null && hook.sourcePath === options.nativeHooksPath && hook.source === "user" && hook.pluginId === null && hook.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash) && hook.enabled === true && hook.trustStatus === "trusted", "RECONCILE_ADDITIONS_IDENTITY");
|
|
54754
|
+
need4(hook.eventName === matchedEvents[event][0] && hook.handlerType === "command" && (review.version === 2 ? v2Command(hook.command) : text2(hook.command, 16384)) && !commands.has(hook.command) && Number.isInteger(hook.timeoutSec) && hook.timeoutSec > 0 && hook.timeoutSec <= 60 && (event === "PreToolUse" ? text2(hook.matcher) : hook.matcher === null) && hook.async === false && hook.statusMessage === null && hook.additionalContextLimit === null && hook.sourcePath === options.nativeHooksPath && hook.source === "user" && hook.pluginId === null && hook.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash) && hook.enabled === true && hook.trustStatus === "trusted", "RECONCILE_ADDITIONS_IDENTITY");
|
|
54745
54755
|
let trustState = { enabled: true, trusted_hash: hook.currentHash };
|
|
54746
54756
|
if (review.version === 2) {
|
|
54747
54757
|
const state = addition.trustState;
|
|
@@ -54769,7 +54779,7 @@ function reviewNativeHookAdditions(options) {
|
|
|
54769
54779
|
proofs.set(trustProof, { before: options.configBefore, current: options.configCurrent, journalHooks: options.journalHooks.map(({ key, currentHash }) => ({ key, currentHash })), states: Object.freeze(states), recheck });
|
|
54770
54780
|
return { hooks: added, recheck, trustProof, receipt: { ...review.version === 2 ? { version: 2 } : {}, reviewPath: reviewFile.file, reviewSha256: reviewFile.sha256, reason: review.reason, producerEvidence: review.producerEvidence, producerBefore: { configPath: producerConfig.file, configSha256: producerConfig.sha256, hooksPath: producerHooks.file, hooksSha256: producerHooks.sha256 }, additions: review.version === 1 ? added : review.additions } };
|
|
54771
54781
|
}
|
|
54772
|
-
var events, matchedEvents, hash2 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), text2 = (value, max2 = 2048) => typeof value === "string" && value.trim().length > 0 && value.length <= max2 && !/[\0\r\n]/.test(value), structure = (value) => JSON.parse(JSON.stringify(Bun.TOML.parse(value))), nativeKeys, proofs;
|
|
54782
|
+
var events, matchedEvents, hash2 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), text2 = (value, max2 = 2048) => typeof value === "string" && value.trim().length > 0 && value.length <= max2 && !/[\0\r\n]/.test(value), v2Command = (value) => typeof value === "string" && value.trim().length > 0 && value.length <= 16384 && !/[\0\r]/.test(value), structure = (value) => JSON.parse(JSON.stringify(Bun.TOML.parse(value))), nativeKeys, proofs;
|
|
54773
54783
|
var init_codex_hook_additions_review = __esm(() => {
|
|
54774
54784
|
init_codex_hook_trust_files();
|
|
54775
54785
|
init_codex_hook_trust_layout();
|
|
@@ -54843,6 +54853,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
|
|
|
54843
54853
|
import { createHash as createHash16 } from "crypto";
|
|
54844
54854
|
import { lstatSync as lstatSync20, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync4 } from "fs";
|
|
54845
54855
|
import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute15, join as join31, resolve as resolve15, sep as sep5 } from "path";
|
|
54856
|
+
import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
|
|
54846
54857
|
function parseConfig(text3, path, toml = false) {
|
|
54847
54858
|
try {
|
|
54848
54859
|
const value = toml ? Bun.TOML.parse(text3) : JSON.parse(text3);
|
|
@@ -54952,7 +54963,10 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
54952
54963
|
const budget = discoveryByteBudget();
|
|
54953
54964
|
return { ...binding, sources: binding.sources.map((source) => {
|
|
54954
54965
|
assertMarketplaceBinding(binding, source);
|
|
54955
|
-
|
|
54966
|
+
const sha2562 = projected(source, changes, budget);
|
|
54967
|
+
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
54968
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
54969
|
+
return { ...source, sha256: sha2562 };
|
|
54956
54970
|
}) };
|
|
54957
54971
|
}
|
|
54958
54972
|
function assertProjectDiscovery(agent, directories, home, canonical4 = resolve15, reviewed) {
|
|
@@ -55064,6 +55078,19 @@ function resolveAgentDiscovery(options) {
|
|
|
55064
55078
|
}
|
|
55065
55079
|
}
|
|
55066
55080
|
}
|
|
55081
|
+
if (options.reviewed === undefined && options.retainedReview !== undefined) {
|
|
55082
|
+
const retained = options.retainedReview;
|
|
55083
|
+
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
55084
|
+
unresolved("invalid retained review");
|
|
55085
|
+
verifyAgentDiscovery(retained);
|
|
55086
|
+
if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
|
|
55087
|
+
unresolved("retained review is missing the full agent configuration source");
|
|
55088
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2") && source.path !== canonical4(configPath)))
|
|
55089
|
+
unresolved("retained settings witness names another configuration source");
|
|
55090
|
+
if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual7(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
55091
|
+
unresolved("retained review is missing current runtime discovery coverage");
|
|
55092
|
+
return retained;
|
|
55093
|
+
}
|
|
55067
55094
|
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))
|
|
55068
55095
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
55069
55096
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
@@ -84538,7 +84565,7 @@ var init_execution_grants = __esm(() => {
|
|
|
84538
84565
|
});
|
|
84539
84566
|
|
|
84540
84567
|
// src/lib/execution-grant-client.ts
|
|
84541
|
-
import { isDeepStrictEqual as
|
|
84568
|
+
import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
|
|
84542
84569
|
function id(value) {
|
|
84543
84570
|
if (!grantIdentifier(value))
|
|
84544
84571
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -84590,7 +84617,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
|
|
|
84590
84617
|
headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
|
|
84591
84618
|
body: JSON.stringify({ grants: snapshot2 })
|
|
84592
84619
|
}));
|
|
84593
|
-
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !
|
|
84620
|
+
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual8(policy.grants, snapshot2)) {
|
|
84594
84621
|
throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
|
|
84595
84622
|
}
|
|
84596
84623
|
return policy;
|
|
@@ -84601,7 +84628,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
84601
84628
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
84602
84629
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
84603
84630
|
}
|
|
84604
|
-
if (!
|
|
84631
|
+
if (!isDeepStrictEqual8(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual8(value.bindings.consumer, snapshot2.consumer)) {
|
|
84605
84632
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
84606
84633
|
}
|
|
84607
84634
|
return value;
|
|
@@ -108595,7 +108622,7 @@ var init_codex_hook_rpc = __esm(() => {
|
|
|
108595
108622
|
// src/lib/codex-hook-trust-identity.ts
|
|
108596
108623
|
import { realpathSync as realpathSync13 } from "fs";
|
|
108597
108624
|
import { dirname as dirname30, isAbsolute as isAbsolute25, join as join56 } from "path";
|
|
108598
|
-
import { isDeepStrictEqual as
|
|
108625
|
+
import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
|
|
108599
108626
|
function inspectRecordedSkillsCli(command, expected) {
|
|
108600
108627
|
need4(isAbsolute25(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
|
|
108601
108628
|
const resolved = realpathSync13(command);
|
|
@@ -108605,7 +108632,7 @@ function inspectRecordedSkillsCli(command, expected) {
|
|
|
108605
108632
|
need4(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join56(dirname30(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
108606
108633
|
need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
108607
108634
|
const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
|
|
108608
|
-
need4(
|
|
108635
|
+
need4(isDeepStrictEqual9(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
108609
108636
|
return { receipt, recheck() {
|
|
108610
108637
|
need4(realpathSync13(command) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
108611
108638
|
unchanged2(cli);
|
|
@@ -108641,19 +108668,19 @@ var init_codex_hook_trust_identity = __esm(() => {
|
|
|
108641
108668
|
});
|
|
108642
108669
|
|
|
108643
108670
|
// src/lib/codex-hook-policy-recovery.ts
|
|
108644
|
-
import { isDeepStrictEqual as
|
|
108671
|
+
import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
|
|
108645
108672
|
function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
|
|
108646
108673
|
need4(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
|
|
108647
108674
|
need4(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
108648
108675
|
const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
|
|
108649
108676
|
const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
|
|
108650
108677
|
need4(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
|
|
108651
|
-
const changes = ["roots", "sources"].filter((key) => !
|
|
108678
|
+
const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual10(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
|
|
108652
108679
|
need4(changes.length > 0, "RECONCILE_POLICY_CHANGED");
|
|
108653
108680
|
const compared = structuredClone(current);
|
|
108654
108681
|
compared.bridge.discovery.claude.roots = oldDiscovery.roots;
|
|
108655
108682
|
compared.bridge.discovery.claude.sources = oldDiscovery.sources;
|
|
108656
|
-
need4(
|
|
108683
|
+
need4(isDeepStrictEqual10(before2, compared), "RECONCILE_POLICY_CHANGED");
|
|
108657
108684
|
verifyAgentDiscovery(newDiscovery);
|
|
108658
108685
|
return { reason: options.reason, changes };
|
|
108659
108686
|
}
|
|
@@ -108669,7 +108696,7 @@ import { createHash as createHash27, randomUUID as randomUUID10 } from "crypto";
|
|
|
108669
108696
|
import { execFileSync } from "child_process";
|
|
108670
108697
|
import { dirname as dirname31, isAbsolute as isAbsolute26, join as join57, resolve as resolve23 } from "path";
|
|
108671
108698
|
import { homedir as homedir12 } from "os";
|
|
108672
|
-
import { isDeepStrictEqual as
|
|
108699
|
+
import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
|
|
108673
108700
|
function verifyReleaseArtifact(proof, skillsCli) {
|
|
108674
108701
|
need4(isAbsolute26(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
108675
108702
|
const tarPath = realpathSync14(proof.packageTarPath);
|
|
@@ -108711,7 +108738,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108711
108738
|
const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
|
|
108712
108739
|
const digestInput = { version: 1, home: intent.home ?? home, configPath: intent.configPath, ...intent.nativeConfigPath !== undefined ? { nativeConfigPath: intent.nativeConfigPath } : {}, configSha256, configVersion, policySha256: intent.policySha256, hooksSha256: intent.hooksSha256, skillsCli: intent.skillsCli, nativeVersion: intent.nativeVersion, declarations, admitted };
|
|
108713
108740
|
need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
|
|
108714
|
-
need4(
|
|
108741
|
+
need4(isDeepStrictEqual11(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
|
|
108715
108742
|
need4(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
|
|
108716
108743
|
const policyCurrent = snapshot(join57(dataDir3, "agent-policy.json"), true);
|
|
108717
108744
|
const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
|
|
@@ -108724,7 +108751,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108724
108751
|
if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
|
|
108725
108752
|
need4(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
|
|
108726
108753
|
const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
|
|
108727
|
-
need4(
|
|
108754
|
+
need4(isDeepStrictEqual11(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
|
|
108728
108755
|
supersededBinding = true;
|
|
108729
108756
|
}
|
|
108730
108757
|
if (supersededBinding || recovery) {
|
|
@@ -108760,7 +108787,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108760
108787
|
need4(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
|
|
108761
108788
|
}
|
|
108762
108789
|
} else
|
|
108763
|
-
need4(
|
|
108790
|
+
need4(isDeepStrictEqual11(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
|
|
108764
108791
|
const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
|
|
108765
108792
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
|
|
108766
108793
|
const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
|
|
@@ -108769,14 +108796,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108769
108796
|
const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
108770
108797
|
if (supersededBinding)
|
|
108771
108798
|
verifyReleaseArtifact(options.releaseProof, skillsCli);
|
|
108772
|
-
const skillsBindingChanged = !
|
|
108799
|
+
const skillsBindingChanged = !isDeepStrictEqual11(skillsCli.receipt, intent.skillsCli);
|
|
108773
108800
|
need4(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
108774
108801
|
if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
|
|
108775
108802
|
need4(supersededBinding, "RECONCILE_HOOKS_CHANGED");
|
|
108776
108803
|
const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
|
|
108777
108804
|
for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
|
|
108778
108805
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
108779
|
-
need4(
|
|
108806
|
+
need4(isDeepStrictEqual11(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
108780
108807
|
}
|
|
108781
108808
|
}
|
|
108782
108809
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
@@ -108799,9 +108826,9 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108799
108826
|
delete identity4.command;
|
|
108800
108827
|
return identity4;
|
|
108801
108828
|
};
|
|
108802
|
-
need4(
|
|
108829
|
+
need4(isDeepStrictEqual11(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
108803
108830
|
if (!plannedKeys.has(expected.key))
|
|
108804
|
-
need4(
|
|
108831
|
+
need4(isDeepStrictEqual11(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
|
|
108805
108832
|
}
|
|
108806
108833
|
for (const wanted of intent.hooks) {
|
|
108807
108834
|
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join57(home, ".codex/hooks.json"), hooksPath));
|
|
@@ -108818,12 +108845,12 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108818
108845
|
}
|
|
108819
108846
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
108820
108847
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
108821
|
-
need4(layers?.length === 1 && !layers[0].disabledReason &&
|
|
108848
|
+
need4(layers?.length === 1 && !layers[0].disabledReason && isDeepStrictEqual11(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
|
|
108822
108849
|
const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
|
|
108823
|
-
need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks ||
|
|
108850
|
+
need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual11(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
|
|
108824
108851
|
const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
108825
108852
|
const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
108826
|
-
need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason &&
|
|
108853
|
+
need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason && isDeepStrictEqual11(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
|
|
108827
108854
|
if (recovery) {
|
|
108828
108855
|
for (const response of [config2, finalConfig]) {
|
|
108829
108856
|
const layerIndex = response.layers.findIndex((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
@@ -108881,7 +108908,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108881
108908
|
const groups = declarations[event];
|
|
108882
108909
|
need4(Array.isArray(groups), "DECLARATION_CHANGED");
|
|
108883
108910
|
const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
|
|
108884
|
-
need4(matches2.length === 1 &&
|
|
108911
|
+
need4(matches2.length === 1 && isDeepStrictEqual11(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
|
|
108885
108912
|
expected.push({ event: nativeEvent, key: `${join57(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
|
|
108886
108913
|
}
|
|
108887
108914
|
codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
|
|
@@ -108905,7 +108932,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108905
108932
|
const discovered = await list2(), admitted = admit(discovered);
|
|
108906
108933
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
108907
108934
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
108908
|
-
need4(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason &&
|
|
108935
|
+
need4(layers?.length === 1 && /^sha256:[a-f0-9]{64}$/.test(layers[0].version) && !layers[0].disabledReason && isDeepStrictEqual11(layers[0].config, original), "NATIVE_CONFIG_MISMATCH");
|
|
108909
108936
|
need4(config2.config && config2.config.features?.hooks !== false, "NATIVE_HOOKS_DISABLED");
|
|
108910
108937
|
need4(config2.layers.slice(0, config2.layers.indexOf(layers[0])).every((layer) => layer.config?.hooks === undefined && layer.config?.features?.hooks === undefined), "NATIVE_CONFIG_OVERRIDDEN");
|
|
108911
108938
|
const keys3 = admitted.map((h) => h.key), planned = admitted.filter((h) => !h.enabled || h.trustStatus !== "trusted");
|
|
@@ -108944,7 +108971,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108944
108971
|
save(join57(journal, "hooks.before.json"), hooksFile.bytes);
|
|
108945
108972
|
save(join57(journal, "policy.before.json"), policyFile.bytes);
|
|
108946
108973
|
save(join57(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
|
|
108947
|
-
need4(
|
|
108974
|
+
need4(isDeepStrictEqual11(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
|
|
108948
108975
|
checkInputs();
|
|
108949
108976
|
let result2;
|
|
108950
108977
|
try {
|
|
@@ -108958,7 +108985,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108958
108985
|
expectedConfig.hooks.state ??= {};
|
|
108959
108986
|
for (const h of planned)
|
|
108960
108987
|
expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
|
|
108961
|
-
need4(
|
|
108988
|
+
need4(isDeepStrictEqual11(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
|
|
108962
108989
|
const savedConfig = snapshot(join57(journal, "config.before.toml"), true);
|
|
108963
108990
|
need4(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
|
|
108964
108991
|
const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
|
|
@@ -108966,8 +108993,8 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108966
108993
|
unchanged2(hooksFile);
|
|
108967
108994
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
|
|
108968
108995
|
const current = await list2(), confirmed = admit(current);
|
|
108969
|
-
need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") &&
|
|
108970
|
-
need4(
|
|
108996
|
+
need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual11(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
|
|
108997
|
+
need4(isDeepStrictEqual11(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
|
|
108971
108998
|
unchanged2(after2);
|
|
108972
108999
|
unchanged2(savedConfig);
|
|
108973
109000
|
unchanged2(hooksFile);
|
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.9.
|
|
20022
|
+
version: "0.9.16",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -48834,6 +48834,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
|
|
|
48834
48834
|
import { createHash as createHash14 } from "crypto";
|
|
48835
48835
|
import { lstatSync as lstatSync16, readFileSync as readFileSync14, readdirSync as readdirSync6, realpathSync as realpathSync3 } from "fs";
|
|
48836
48836
|
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute13, join as join23, resolve as resolve13, sep as sep4 } from "path";
|
|
48837
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
48837
48838
|
function parseConfig(text2, path, toml = false) {
|
|
48838
48839
|
try {
|
|
48839
48840
|
const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
|
|
@@ -49008,6 +49009,19 @@ function resolveAgentDiscovery(options) {
|
|
|
49008
49009
|
}
|
|
49009
49010
|
}
|
|
49010
49011
|
}
|
|
49012
|
+
if (options.reviewed === undefined && options.retainedReview !== undefined) {
|
|
49013
|
+
const retained = options.retainedReview;
|
|
49014
|
+
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
49015
|
+
unresolved("invalid retained review");
|
|
49016
|
+
verifyAgentDiscovery(retained);
|
|
49017
|
+
if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
|
|
49018
|
+
unresolved("retained review is missing the full agent configuration source");
|
|
49019
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2") && source.path !== canonical4(configPath)))
|
|
49020
|
+
unresolved("retained settings witness names another configuration source");
|
|
49021
|
+
if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual2(saved, source))) || [...roots].some((root) => !retained.roots.includes(root)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
49022
|
+
unresolved("retained review is missing current runtime discovery coverage");
|
|
49023
|
+
return retained;
|
|
49024
|
+
}
|
|
49011
49025
|
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))
|
|
49012
49026
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
49013
49027
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
@@ -57344,7 +57358,7 @@ var init_execution_grants = __esm(() => {
|
|
|
57344
57358
|
});
|
|
57345
57359
|
|
|
57346
57360
|
// src/lib/execution-grant-client.ts
|
|
57347
|
-
import { isDeepStrictEqual as
|
|
57361
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
57348
57362
|
function id(value) {
|
|
57349
57363
|
if (!grantIdentifier(value))
|
|
57350
57364
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -57387,7 +57401,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
57387
57401
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
57388
57402
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
57389
57403
|
}
|
|
57390
|
-
if (!
|
|
57404
|
+
if (!isDeepStrictEqual3(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual3(value.bindings.consumer, snapshot2.consumer)) {
|
|
57391
57405
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
57392
57406
|
}
|
|
57393
57407
|
return value;
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
|
|
|
24122
24122
|
// package.json
|
|
24123
24123
|
var package_default = {
|
|
24124
24124
|
name: "@hasna/skills",
|
|
24125
|
-
version: "0.9.
|
|
24125
|
+
version: "0.9.16",
|
|
24126
24126
|
description: "Skills library for AI coding agents",
|
|
24127
24127
|
type: "module",
|
|
24128
24128
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24125,7 +24125,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24125
24125
|
// package.json
|
|
24126
24126
|
var package_default = {
|
|
24127
24127
|
name: "@hasna/skills",
|
|
24128
|
-
version: "0.9.
|
|
24128
|
+
version: "0.9.16",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -9093,6 +9093,7 @@ function assertAgentPolicyCollections(policy) {
|
|
|
9093
9093
|
import { createHash as createHash16 } from "crypto";
|
|
9094
9094
|
import { lstatSync as lstatSync20, readFileSync as readFileSync16, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
|
|
9095
9095
|
import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute14, join as join26, resolve as resolve14, sep as sep5 } from "path";
|
|
9096
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
9096
9097
|
|
|
9097
9098
|
// node_modules/yaml/dist/index.js
|
|
9098
9099
|
var composer = require_composer();
|
|
@@ -9610,7 +9611,7 @@ import { isAbsolute as isAbsolute11, join as join23, resolve as resolve11 } from
|
|
|
9610
9611
|
// package.json
|
|
9611
9612
|
var package_default = {
|
|
9612
9613
|
name: "@hasna/skills",
|
|
9613
|
-
version: "0.9.
|
|
9614
|
+
version: "0.9.16",
|
|
9614
9615
|
description: "Skills library for AI coding agents",
|
|
9615
9616
|
type: "module",
|
|
9616
9617
|
bin: {
|
|
@@ -13369,7 +13370,15 @@ function planAgentIntegration(options) {
|
|
|
13369
13370
|
validateProfile(profileId2);
|
|
13370
13371
|
return [agent, { command, profileId: profileId2 }];
|
|
13371
13372
|
}));
|
|
13372
|
-
const
|
|
13373
|
+
const retainedAgents = [];
|
|
13374
|
+
const discoveries = [...new Set(options.agents)].map((agent) => {
|
|
13375
|
+
const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? policy.bridge.discovery[agent] : undefined;
|
|
13376
|
+
if (retainedReview) {
|
|
13377
|
+
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
|
|
13378
|
+
retainedAgents.push(agent);
|
|
13379
|
+
}
|
|
13380
|
+
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
13381
|
+
});
|
|
13373
13382
|
const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases });
|
|
13374
13383
|
const changes = [];
|
|
13375
13384
|
for (const agent of [...new Set(options.agents)]) {
|
|
@@ -13454,7 +13463,7 @@ function planAgentIntegration(options) {
|
|
|
13454
13463
|
if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
|
|
13455
13464
|
changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
|
|
13456
13465
|
recheckRootAliases(aliases);
|
|
13457
|
-
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
13466
|
+
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
|
|
13458
13467
|
}
|
|
13459
13468
|
function planClaudeStopHookUpdate(options) {
|
|
13460
13469
|
return planClaudeHookEventsUpdate({ ...options, events: ["Stop"] });
|
|
@@ -13573,6 +13582,8 @@ function applyAgentIntegration(plan) {
|
|
|
13573
13582
|
parseManagedSkillPolicy(change.after);
|
|
13574
13583
|
const aliases = plan.rootAliases ?? [];
|
|
13575
13584
|
recheckRootAliases(aliases);
|
|
13585
|
+
for (const agent of plan.retainedReviewChecks?.agents ?? [])
|
|
13586
|
+
assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
|
|
13576
13587
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
13577
13588
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
13578
13589
|
for (const binding of plan.discoveryBefore ?? []) {
|
|
@@ -16573,7 +16584,10 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
16573
16584
|
const budget = discoveryByteBudget();
|
|
16574
16585
|
return { ...binding, sources: binding.sources.map((source) => {
|
|
16575
16586
|
assertMarketplaceBinding(binding, source);
|
|
16576
|
-
|
|
16587
|
+
const sha2562 = projected(source, changes, budget);
|
|
16588
|
+
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
16589
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
16590
|
+
return { ...source, sha256: sha2562 };
|
|
16577
16591
|
}) };
|
|
16578
16592
|
}
|
|
16579
16593
|
function captureDiscoveryByteSources(paths) {
|
|
@@ -16697,6 +16711,19 @@ function resolveAgentDiscovery(options) {
|
|
|
16697
16711
|
}
|
|
16698
16712
|
}
|
|
16699
16713
|
}
|
|
16714
|
+
if (options.reviewed === undefined && options.retainedReview !== undefined) {
|
|
16715
|
+
const retained = options.retainedReview;
|
|
16716
|
+
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
16717
|
+
unresolved("invalid retained review");
|
|
16718
|
+
verifyAgentDiscovery(retained);
|
|
16719
|
+
if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
|
|
16720
|
+
unresolved("retained review is missing the full agent configuration source");
|
|
16721
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2") && source.path !== canonical4(configPath)))
|
|
16722
|
+
unresolved("retained settings witness names another configuration source");
|
|
16723
|
+
if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual3(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
16724
|
+
unresolved("retained review is missing current runtime discovery coverage");
|
|
16725
|
+
return retained;
|
|
16726
|
+
}
|
|
16700
16727
|
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))
|
|
16701
16728
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
16702
16729
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
@@ -56,6 +56,11 @@ export interface AgentIntegrationPlan {
|
|
|
56
56
|
home: string;
|
|
57
57
|
agents: IntegrationAgent[];
|
|
58
58
|
};
|
|
59
|
+
retainedReviewChecks?: {
|
|
60
|
+
home: string;
|
|
61
|
+
projectDir: string;
|
|
62
|
+
agents: IntegrationAgent[];
|
|
63
|
+
};
|
|
59
64
|
}
|
|
60
65
|
/** Parse the reviewed exact-target binding used by native migration. Paths are
|
|
61
66
|
* deliberately project-relative so a manifest cannot silently select another
|
package/dist/sdk/index.js
CHANGED
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.9.
|
|
32376
|
+
version: "0.9.16",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -37704,6 +37704,7 @@ function isOwnedCliBridge(path, expectedPaths) {
|
|
|
37704
37704
|
import { createHash as createHash15 } from "crypto";
|
|
37705
37705
|
import { lstatSync as lstatSync17, readFileSync as readFileSync12, readdirSync as readdirSync6, realpathSync as realpathSync3 } from "fs";
|
|
37706
37706
|
import { basename as basename6, dirname as dirname13, isAbsolute as isAbsolute14, join as join20, resolve as resolve13, sep as sep3 } from "path";
|
|
37707
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
37707
37708
|
|
|
37708
37709
|
// node_modules/yaml/dist/index.js
|
|
37709
37710
|
var composer = require_composer();
|
|
@@ -40401,7 +40402,10 @@ function rebindAgentDiscovery(binding, changes) {
|
|
|
40401
40402
|
const budget = discoveryByteBudget();
|
|
40402
40403
|
return { ...binding, sources: binding.sources.map((source) => {
|
|
40403
40404
|
assertMarketplaceBinding(binding, source);
|
|
40404
|
-
|
|
40405
|
+
const sha2562 = projected(source, changes, budget);
|
|
40406
|
+
if (!changes.has(source.path) && sha2562 !== source.sha256)
|
|
40407
|
+
throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
|
|
40408
|
+
return { ...source, sha256: sha2562 };
|
|
40405
40409
|
}) };
|
|
40406
40410
|
}
|
|
40407
40411
|
function captureDiscoveryByteSources(paths) {
|
|
@@ -40525,6 +40529,19 @@ function resolveAgentDiscovery(options) {
|
|
|
40525
40529
|
}
|
|
40526
40530
|
}
|
|
40527
40531
|
}
|
|
40532
|
+
if (options.reviewed === undefined && options.retainedReview !== undefined) {
|
|
40533
|
+
const retained = options.retainedReview;
|
|
40534
|
+
if (retained.agent !== agent || retained.method !== "reviewed")
|
|
40535
|
+
unresolved("invalid retained review");
|
|
40536
|
+
verifyAgentDiscovery(retained);
|
|
40537
|
+
if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
|
|
40538
|
+
unresolved("retained review is missing the full agent configuration source");
|
|
40539
|
+
if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2") && source.path !== canonical4(configPath)))
|
|
40540
|
+
unresolved("retained settings witness names another configuration source");
|
|
40541
|
+
if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual3(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
|
|
40542
|
+
unresolved("retained review is missing current runtime discovery coverage");
|
|
40543
|
+
return retained;
|
|
40544
|
+
}
|
|
40528
40545
|
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))
|
|
40529
40546
|
throw new Error("Invalid --discovery-inputs version or agents");
|
|
40530
40547
|
const review = options.reviewed?.agents.find((item) => item.agent === agent);
|
|
@@ -40718,7 +40735,7 @@ export default async ({ directory }) => {
|
|
|
40718
40735
|
}
|
|
40719
40736
|
|
|
40720
40737
|
// src/lib/agent-codex.ts
|
|
40721
|
-
import { isDeepStrictEqual as
|
|
40738
|
+
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
40722
40739
|
var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
|
|
40723
40740
|
function assertCodexPathConfigEditable(text2) {
|
|
40724
40741
|
const before = Bun.TOML.parse(text2);
|
|
@@ -40728,7 +40745,7 @@ function assertCodexPathConfigEditable(text2) {
|
|
|
40728
40745
|
delete expected.skills.config;
|
|
40729
40746
|
try {
|
|
40730
40747
|
const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
40731
|
-
if (
|
|
40748
|
+
if (isDeepStrictEqual4(withoutSections, expected))
|
|
40732
40749
|
return;
|
|
40733
40750
|
} catch {}
|
|
40734
40751
|
throw new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables; preserve and convert inline or dotted config arrays before running skills hook install");
|
|
@@ -40766,7 +40783,7 @@ function disableCodexBundledSkills(text2) {
|
|
|
40766
40783
|
const candidates = new Set;
|
|
40767
40784
|
const consider = (candidate2) => {
|
|
40768
40785
|
try {
|
|
40769
|
-
if (
|
|
40786
|
+
if (isDeepStrictEqual4(Bun.TOML.parse(candidate2), expected))
|
|
40770
40787
|
candidates.add(candidate2);
|
|
40771
40788
|
} catch {}
|
|
40772
40789
|
};
|
|
@@ -41224,7 +41241,15 @@ function planAgentIntegration(options) {
|
|
|
41224
41241
|
validateProfile(profileId2);
|
|
41225
41242
|
return [agent, { command, profileId: profileId2 }];
|
|
41226
41243
|
}));
|
|
41227
|
-
const
|
|
41244
|
+
const retainedAgents = [];
|
|
41245
|
+
const discoveries = [...new Set(options.agents)].map((agent) => {
|
|
41246
|
+
const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? policy.bridge.discovery[agent] : undefined;
|
|
41247
|
+
if (retainedReview) {
|
|
41248
|
+
assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
|
|
41249
|
+
retainedAgents.push(agent);
|
|
41250
|
+
}
|
|
41251
|
+
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
41252
|
+
});
|
|
41228
41253
|
const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases });
|
|
41229
41254
|
const changes = [];
|
|
41230
41255
|
for (const agent of [...new Set(options.agents)]) {
|
|
@@ -41309,7 +41334,7 @@ function planAgentIntegration(options) {
|
|
|
41309
41334
|
if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
|
|
41310
41335
|
changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
|
|
41311
41336
|
recheckRootAliases(aliases);
|
|
41312
|
-
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
41337
|
+
return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
|
|
41313
41338
|
}
|
|
41314
41339
|
function planClaudeStopHookUpdate(options) {
|
|
41315
41340
|
return planClaudeHookEventsUpdate({ ...options, events: ["Stop"] });
|
|
@@ -41428,6 +41453,8 @@ function applyAgentIntegration(plan) {
|
|
|
41428
41453
|
parseManagedSkillPolicy(change.after);
|
|
41429
41454
|
const aliases = plan.rootAliases ?? [];
|
|
41430
41455
|
recheckRootAliases(aliases);
|
|
41456
|
+
for (const agent of plan.retainedReviewChecks?.agents ?? [])
|
|
41457
|
+
assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
|
|
41431
41458
|
for (const agent of plan.managedAgentChecks?.agents ?? [])
|
|
41432
41459
|
assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
|
|
41433
41460
|
for (const binding of plan.discoveryBefore ?? []) {
|