@hasna/skills 0.9.13 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/mcp.js CHANGED
@@ -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.13",
20022
+ version: "0.9.15",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
@@ -47083,7 +47083,7 @@ function canonical2(value) {
47083
47083
  function inferencePreference(key, child) {
47084
47084
  if (key === "skipDangerousModePermissionPrompt") {
47085
47085
  need2(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid dialog acknowledgment");
47086
- return true;
47086
+ return false;
47087
47087
  }
47088
47088
  if (key === "effortLevel") {
47089
47089
  need2(child.kind === "string" && PERSISTED_EFFORT_LEVELS.has(child.value), "contains an invalid persisted effort level");
@@ -48796,13 +48796,24 @@ var init_codex_hook_trust_files = () => {};
48796
48796
  // src/lib/codex-hook-trust-layout.ts
48797
48797
  var init_codex_hook_trust_layout = () => {};
48798
48798
 
48799
+ // src/lib/codex-hook-additions-review.ts
48800
+ var events, matchedEvents, proofs;
48801
+ var init_codex_hook_additions_review = __esm(() => {
48802
+ init_codex_hook_trust_files();
48803
+ init_codex_hook_trust_layout();
48804
+ events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
48805
+ matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
48806
+ proofs = new WeakMap;
48807
+ });
48808
+
48799
48809
  // src/lib/codex-hook-discovery-recovery.ts
48800
48810
  import { createHash as createHash13 } from "crypto";
48801
48811
  function assertCodexHookDiscoveryRecovery(binding, witness) {
48802
- const proof = proofs.get(witness);
48812
+ const proof = proofs2.get(witness);
48803
48813
  need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
48804
48814
  unchanged2(proof.before);
48805
48815
  unchanged2(proof.current);
48816
+ proof.recheckAdditions();
48806
48817
  }
48807
48818
  function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
48808
48819
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
@@ -48811,11 +48822,12 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
48811
48822
  need4(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
48812
48823
  return true;
48813
48824
  }
48814
- var sha = (text2) => createHash13("sha256").update(text2).digest("hex"), proofs;
48825
+ var sha = (text2) => createHash13("sha256").update(text2).digest("hex"), proofs2;
48815
48826
  var init_codex_hook_discovery_recovery = __esm(() => {
48816
48827
  init_codex_hook_trust_files();
48817
48828
  init_codex_hook_trust_layout();
48818
- proofs = new WeakMap;
48829
+ init_codex_hook_additions_review();
48830
+ proofs2 = new WeakMap;
48819
48831
  });
48820
48832
 
48821
48833
  // src/lib/agent-discovery.ts
@@ -59783,17 +59795,17 @@ var WSContext = class {
59783
59795
  if (typeof args[0] === "function") {
59784
59796
  const [createEvents, options] = args;
59785
59797
  return async function upgradeWebSocket(c, next) {
59786
- const events = await createEvents(c);
59787
- const result = await handler(c, events, options);
59798
+ const events2 = await createEvents(c);
59799
+ const result = await handler(c, events2, options);
59788
59800
  if (result) {
59789
59801
  return result;
59790
59802
  }
59791
59803
  await next();
59792
59804
  };
59793
59805
  } else {
59794
- const [c, events, options] = args;
59806
+ const [c, events2, options] = args;
59795
59807
  return (async () => {
59796
- const upgraded = await handler(c, events, options);
59808
+ const upgraded = await handler(c, events2, options);
59797
59809
  if (!upgraded) {
59798
59810
  throw new Error("Failed to upgrade WebSocket");
59799
59811
  }
@@ -60990,7 +61002,7 @@ var init_dist2 = __esm(() => {
60990
61002
  "sec-websocket-extensions",
60991
61003
  "sec-websocket-protocol"
60992
61004
  ]);
60993
- upgradeWebSocket = defineWebSocketHelper(async (c, events, options) => {
61005
+ upgradeWebSocket = defineWebSocketHelper(async (c, events2, options) => {
60994
61006
  if (c.req.header("upgrade")?.toLowerCase() !== "websocket")
60995
61007
  return;
60996
61008
  const env = c.env;
@@ -61027,7 +61039,7 @@ var init_dist2 = __esm(() => {
61027
61039
  url: new URL(c.req.url)
61028
61040
  };
61029
61041
  try {
61030
- events?.onOpen?.(new Event("open"), ctx);
61042
+ events2?.onOpen?.(new Event("open"), ctx);
61031
61043
  } catch (e2) {
61032
61044
  (options?.onError ?? console.error)(e2);
61033
61045
  }
@@ -61035,7 +61047,7 @@ var init_dist2 = __esm(() => {
61035
61047
  const datas = Array.isArray(data) ? data : [data];
61036
61048
  for (const data2 of datas)
61037
61049
  try {
61038
- events?.onMessage?.(new MessageEvent("message", { data: isBinary ? data2 instanceof ArrayBuffer ? data2 : data2.buffer.slice(data2.byteOffset, data2.byteOffset + data2.byteLength) : typeof data2 === "string" ? data2 : Buffer.from(data2).toString("utf-8") }), ctx);
61050
+ events2?.onMessage?.(new MessageEvent("message", { data: isBinary ? data2 instanceof ArrayBuffer ? data2 : data2.buffer.slice(data2.byteOffset, data2.byteOffset + data2.byteLength) : typeof data2 === "string" ? data2 : Buffer.from(data2).toString("utf-8") }), ctx);
61039
61051
  } catch (e2) {
61040
61052
  (options?.onError ?? console.error)(e2);
61041
61053
  }
@@ -61048,7 +61060,7 @@ var init_dist2 = __esm(() => {
61048
61060
  });
61049
61061
  ws.on("close", (code2, reason) => {
61050
61062
  try {
61051
- events?.onClose?.(new CloseEvent("close", {
61063
+ events2?.onClose?.(new CloseEvent("close", {
61052
61064
  code: code2,
61053
61065
  reason: reason.toString()
61054
61066
  }), ctx);
@@ -61058,7 +61070,7 @@ var init_dist2 = __esm(() => {
61058
61070
  });
61059
61071
  ws.on("error", (error61) => {
61060
61072
  try {
61061
- events?.onError?.(new ErrorEvent("error", { error: error61 }), ctx);
61073
+ events2?.onError?.(new ErrorEvent("error", { error: error61 }), ctx);
61062
61074
  } catch (e2) {
61063
61075
  (options?.onError ?? console.error)(e2);
61064
61076
  }
package/bin/migrate.js CHANGED
@@ -7,7 +7,7 @@ import { join as join5 } from "path";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@hasna/skills",
10
- version: "0.9.13",
10
+ version: "0.9.15",
11
11
  description: "Skills library for AI coding agents",
12
12
  type: "module",
13
13
  bin: {
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.13",
24125
+ version: "0.9.15",
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.13",
24128
+ version: "0.9.15",
24129
24129
  description: "Skills library for AI coding agents",
24130
24130
  type: "module",
24131
24131
  bin: {
package/dist/index.d.ts CHANGED
@@ -60,7 +60,7 @@ export type { SkillSelection, SkillProfile, ResolvedSkillSelection, ResolvedSkil
60
60
  export { buildSkillContext } from "./lib/skill-context.js";
61
61
  export { syncSelectionProfile } from "./lib/selection-resolver.js";
62
62
  export { inspectSkillSession, reconcileSkillSession, type SessionReconciliationInput, type SessionReconciliationOptions } from "./lib/session-reconciliation.js";
63
- export { planAgentIntegration, planClaudeStopHookUpdate, applyAgentIntegration, inventoryNativeSkills, archiveNativeSkills } from "./lib/agent-integration.js";
63
+ export { planAgentIntegration, planClaudeStopHookUpdate, planClaudePreToolUseHookUpdate, planClaudeHookEventsUpdate, applyAgentIntegration, inventoryNativeSkills, archiveNativeSkills } from "./lib/agent-integration.js";
64
64
  export { captureDiscoveryDirectories, captureDiscoveryByteSources, captureDiscoveryPathSources, type DiscoveryDirectory, type DiscoverySource } from "./lib/agent-discovery.js";
65
65
  export { captureClaudeMarketplaceRegistry, captureClaudeMarketplaceRegistryV2 } from "./lib/claude-marketplace-registry.js";
66
66
  export { captureClaudeSettings, captureClaudeSettingsV2 } from "./lib/claude-settings-witness.js";
@@ -69,3 +69,4 @@ export { planPluginAdmission, admitPlugin, resolveAdmittedPlugin, pluginResolver
69
69
  export { captureManagedPluginRegistry, type ManagedPluginRegistrationWitness } from "./lib/plugin-discovery.js";
70
70
  export type { RemoteCreditCheckoutOptions, RemoteCreditCheckout, RemoteCreditCheckoutErrorCode } from "./lib/remote-credit-checkout.js";
71
71
  export { assertPortableAuthoringPath, type PortableAuthoringPathOptions } from "./lib/authoring-path.js";
72
+ export { CLAUDE_COORDINATED_HOOK_EVENTS, type ClaudeCoordinatedHookEvent } from "./lib/claude-settings-witness.js";
package/dist/index.js CHANGED
@@ -9610,7 +9610,7 @@ import { isAbsolute as isAbsolute11, join as join23, resolve as resolve11 } from
9610
9610
  // package.json
9611
9611
  var package_default = {
9612
9612
  name: "@hasna/skills",
9613
- version: "0.9.13",
9613
+ version: "0.9.15",
9614
9614
  description: "Skills library for AI coding agents",
9615
9615
  type: "module",
9616
9616
  bin: {
@@ -12846,32 +12846,37 @@ function canonical2(value) {
12846
12846
  return { kind: "array", items: value.items.map(canonical2) };
12847
12847
  return value;
12848
12848
  }
12849
- function assertClaudeStopHookReplacement(before, replacement) {
12849
+ var CLAUDE_COORDINATED_HOOK_EVENTS = ["PreToolUse", "PostToolUse", "Stop", "Notification", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart"];
12850
+ function assertClaudeHookEventsReplacement(before, replacement, events) {
12851
+ need2(Array.isArray(events) && events.length > 0 && events.length <= CLAUDE_COORDINATED_HOOK_EVENTS.length && new Set(events).size === events.length && events.every((event) => CLAUDE_COORDINATED_HOOK_EVENTS.includes(event)), "requires explicit supported hook events");
12852
+ const label = events.join(",");
12850
12853
  function bounded(text2) {
12851
- need2(typeof text2 === "string" && Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "Stop update exceeds its byte limit");
12854
+ need2(typeof text2 === "string" && Buffer.byteLength(text2) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, `${label} update exceeds its byte limit`);
12852
12855
  return parse3(text2);
12853
12856
  }
12854
12857
  const original = bounded(before ?? "{}"), next = bounded(replacement);
12855
12858
  const hadHooks = original.entries.some(([key]) => key === "hooks");
12856
- function withoutStop(value) {
12859
+ function withoutOwnedEvents(value) {
12857
12860
  const hooks = value.entries.find(([key]) => key === "hooks")?.[1];
12858
12861
  if (hooks !== undefined) {
12859
- need2(hooks.kind === "object", "Stop update requires an object of hooks");
12860
- const stop = hooks.entries.find(([key]) => key === "Stop")?.[1];
12861
- need2(stop === undefined || stop.kind === "array", "Stop update requires a Stop array");
12862
- hooks.entries = hooks.entries.filter(([key]) => key !== "Stop");
12862
+ need2(hooks.kind === "object", `${label} update requires an object of hooks`);
12863
+ for (const event of events) {
12864
+ const selected = hooks.entries.find(([key]) => key === event)?.[1];
12865
+ need2(selected === undefined || selected.kind === "array", `${event} update requires a ${event} array`);
12866
+ }
12867
+ hooks.entries = hooks.entries.filter(([key]) => !events.includes(key));
12863
12868
  if (!hadHooks && hooks.entries.length === 0)
12864
12869
  value.entries = value.entries.filter(([key]) => key !== "hooks");
12865
12870
  }
12866
12871
  return canonical2(value);
12867
12872
  }
12868
- need2(JSON.stringify(withoutStop(original)) === JSON.stringify(withoutStop(next)), "Stop update changed settings outside hooks.Stop");
12873
+ need2(JSON.stringify(withoutOwnedEvents(original)) === JSON.stringify(withoutOwnedEvents(next)), `${label} update changed settings outside hooks.${label}`);
12869
12874
  }
12870
12875
  var PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
12871
12876
  function inferencePreference(key, child) {
12872
12877
  if (key === "skipDangerousModePermissionPrompt") {
12873
12878
  need2(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid dialog acknowledgment");
12874
- return true;
12879
+ return false;
12875
12880
  }
12876
12881
  if (key === "effortLevel") {
12877
12882
  need2(child.kind === "string" && PERSISTED_EFFORT_LEVELS.has(child.value), "contains an invalid persisted effort level");
@@ -13452,6 +13457,34 @@ function planAgentIntegration(options) {
13452
13457
  return { dataDir: dataDir2, profileId, changes, nativeSkills, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {} };
13453
13458
  }
13454
13459
  function planClaudeStopHookUpdate(options) {
13460
+ return planClaudeHookEventsUpdate({ ...options, events: ["Stop"] });
13461
+ }
13462
+ function planClaudePreToolUseHookUpdate(options) {
13463
+ return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
13464
+ }
13465
+ function claudeSettingsConsumers(home, path, discoveries, aliases) {
13466
+ const canonical3 = (value) => canonicalAgentPath(value, aliases);
13467
+ const containsSettings = (root) => {
13468
+ const distance = relative4(canonical3(root), path);
13469
+ return distance === "" || distance !== ".." && !distance.startsWith(`..${sep4}`) && !isAbsolute9(distance);
13470
+ };
13471
+ return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical3(join16(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical3(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
13472
+ }
13473
+ function verifyCoordinatedDiscovery(binding, home, aliases) {
13474
+ try {
13475
+ if (binding.method !== "automatic" && binding.method !== "reviewed")
13476
+ throw new Error("Invalid managed discovery method");
13477
+ verifyAgentDiscovery(binding);
13478
+ if (binding.method === "automatic") {
13479
+ const current = resolveAgentDiscovery({ home, agent: binding.agent, canonical: (path) => canonicalAgentPath(path, aliases) });
13480
+ if (JSON.stringify(current) !== JSON.stringify(binding))
13481
+ throw new Error("Configured native discovery roots changed during Claude update");
13482
+ }
13483
+ } catch (error) {
13484
+ throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
13485
+ }
13486
+ }
13487
+ function planClaudeHookEventsUpdate(options) {
13455
13488
  const home = resolve9(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
13456
13489
  const policyPath = join16(dataDir2, "agent-policy.json");
13457
13490
  assertSafePath(policyPath);
@@ -13469,31 +13502,42 @@ function planClaudeStopHookUpdate(options) {
13469
13502
  const before = lstatSync14(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
13470
13503
  if ((before === null ? "absent" : sha(before)) !== options.expectedSettingsSha256)
13471
13504
  throw new Error("Claude settings preimage changed");
13472
- assertClaudeStopHookReplacement(before, options.replacement);
13505
+ assertClaudeHookEventsReplacement(before, options.replacement, options.events);
13473
13506
  const agents = bridge.agents;
13474
13507
  const discoveries = Object.entries(bridge.discovery).map(([agent, binding]) => {
13475
- if (!agents.includes(agent) || binding?.agent !== agent)
13476
- throw new Error("Invalid managed discovery owner");
13508
+ if (!agents.includes(agent) || binding?.agent !== agent || typeof bridge.commands?.[agent] !== "string" || typeof bridge.profiles?.[agent] !== "string")
13509
+ throw new Error("Invalid managed discovery owner or command/profile binding");
13477
13510
  return binding;
13478
13511
  });
13479
- if (discoveries.length !== agents.length)
13480
- throw new Error("Missing managed discovery coverage");
13481
- for (const agent of agents)
13512
+ if (discoveries.length !== agents.length || !agents.includes("claude"))
13513
+ throw new Error("Missing managed Claude discovery coverage");
13514
+ for (const binding of discoveries)
13515
+ verifyCoordinatedDiscovery(binding, home, aliases);
13516
+ const consumers = claudeSettingsConsumers(home, path, discoveries, aliases);
13517
+ for (const agent of consumers)
13482
13518
  assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: home });
13483
13519
  const replacements = new Map(before === options.replacement ? [] : [[path, options.replacement]]);
13484
- const discoveryAfter = discoveries.map((binding) => rebindAgentDiscovery(binding, replacements));
13520
+ const discoveryAfter = discoveries.map((binding) => {
13521
+ const next = rebindAgentDiscovery(binding, replacements);
13522
+ for (const [index, source] of binding.sources.entries()) {
13523
+ if (source.path !== path && JSON.stringify(next.sources[index]) !== JSON.stringify(source)) {
13524
+ throw new Error(`Unrelated native discovery input changed during Claude update: ${source.path}`);
13525
+ }
13526
+ }
13527
+ return JSON.stringify(next) === JSON.stringify(binding) ? binding : next;
13528
+ });
13485
13529
  const nextPolicy = { ...policy, bridge: { ...bridge, discovery: Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) } };
13486
13530
  const changes = before === options.replacement ? [] : [{ path, before, after: options.replacement }];
13487
13531
  if (JSON.stringify(nextPolicy) !== JSON.stringify(policy))
13488
13532
  changes.push({ path: policyPath, before: snapshot.text, after: serializeManagedSkillPolicy(nextPolicy) });
13489
13533
  recheckRootAliases(aliases);
13490
13534
  const nativeSkills = inventoryNativeSkills(home, {
13491
- agents,
13535
+ agents: consumers,
13492
13536
  projectDir: home,
13493
13537
  includeVendor: true,
13494
- guardHermes: agents.includes("hermes"),
13538
+ guardHermes: consumers.includes("hermes"),
13495
13539
  allowRootAliases: aliases.length > 0,
13496
- agentRoots: discoveries.flatMap((binding) => binding.roots.map((path2) => ({ agent: binding.agent, path: path2 })))
13540
+ agentRoots: discoveries.filter((binding) => consumers.includes(binding.agent)).flatMap((binding) => binding.roots.map((path2) => ({ agent: binding.agent, path: path2 })))
13497
13541
  });
13498
13542
  return {
13499
13543
  dataDir: dataDir2,
@@ -13504,7 +13548,7 @@ function planClaudeStopHookUpdate(options) {
13504
13548
  discoveryBefore: discoveries,
13505
13549
  discoveryAfter,
13506
13550
  rootAliases: aliases,
13507
- managedAgentChecks: { home, agents: [...agents] }
13551
+ managedAgentChecks: { home, agents: consumers }
13508
13552
  };
13509
13553
  }
13510
13554
  function atomicWrite2(path, content) {
@@ -13531,8 +13575,12 @@ function applyAgentIntegration(plan) {
13531
13575
  recheckRootAliases(aliases);
13532
13576
  for (const agent of plan.managedAgentChecks?.agents ?? [])
13533
13577
  assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
13534
- for (const binding of plan.discoveryBefore ?? [])
13535
- verifyAgentDiscovery(binding);
13578
+ for (const binding of plan.discoveryBefore ?? []) {
13579
+ if (plan.managedAgentChecks)
13580
+ verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
13581
+ else
13582
+ verifyAgentDiscovery(binding);
13583
+ }
13536
13584
  if (plan.observedPolicy && currentText(plan.observedPolicy.path) !== plan.observedPolicy.before)
13537
13585
  throw new Error("Agent policy changed after planning");
13538
13586
  for (const change of plan.changes)
@@ -13564,8 +13612,12 @@ function applyAgentIntegration(plan) {
13564
13612
  written.push({ path: change.path, before: change.before, after });
13565
13613
  }
13566
13614
  recheckRootAliases(aliases);
13567
- for (const binding of plan.discoveryAfter ?? [])
13568
- verifyAgentDiscovery(binding);
13615
+ for (const binding of plan.discoveryAfter ?? []) {
13616
+ if (plan.managedAgentChecks)
13617
+ verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
13618
+ else
13619
+ verifyAgentDiscovery(binding);
13620
+ }
13569
13621
  for (const agent of plan.managedAgentChecks?.agents ?? [])
13570
13622
  assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
13571
13623
  atomicWrite2(join16(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha(change.before), afterHash: sha(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
@@ -16387,14 +16439,20 @@ function unchanged2(before) {
16387
16439
  need4(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
16388
16440
  }
16389
16441
 
16442
+ // src/lib/codex-hook-additions-review.ts
16443
+ var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
16444
+ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
16445
+ var proofs = new WeakMap;
16446
+
16390
16447
  // src/lib/codex-hook-discovery-recovery.ts
16391
16448
  var sha2 = (text2) => createHash15("sha256").update(text2).digest("hex");
16392
- var proofs = new WeakMap;
16449
+ var proofs2 = new WeakMap;
16393
16450
  function assertCodexHookDiscoveryRecovery(binding, witness) {
16394
- const proof = proofs.get(witness);
16451
+ const proof = proofs2.get(witness);
16395
16452
  need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha2(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
16396
16453
  unchanged2(proof.before);
16397
16454
  unchanged2(proof.current);
16455
+ proof.recheckAdditions();
16398
16456
  }
16399
16457
  function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witness) {
16400
16458
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
@@ -43658,6 +43716,8 @@ export {
43658
43716
  planSkillsS3SnapshotUpload,
43659
43717
  planPluginAdmission,
43660
43718
  planClaudeStopHookUpdate,
43719
+ planClaudePreToolUseHookUpdate,
43720
+ planClaudeHookEventsUpdate,
43661
43721
  planAgentIntegration,
43662
43722
  pinSkill,
43663
43723
  pinProjectSkill,
@@ -43861,6 +43921,7 @@ export {
43861
43921
  DEFAULT_EXPORT_DIR,
43862
43922
  ContentHashInputError,
43863
43923
  CONTENT_HASH_LIMITS,
43924
+ CLAUDE_COORDINATED_HOOK_EVENTS,
43864
43925
  CATEGORIES,
43865
43926
  BASIC_SKILL_NAMES,
43866
43927
  ARTICLE_GENERATION_SLUG,
@@ -71,7 +71,8 @@ export declare function reconcileCodexNativeHooks(options: CodexNativeHookReconc
71
71
  hooksPath: string;
72
72
  hooksSha256: string;
73
73
  };
74
- additions: any[];
74
+ additions: any;
75
+ version?: number | undefined;
75
76
  } | undefined;
76
77
  configDiscoveryRecovery?: import("./codex-hook-discovery-recovery.js").CodexHookDiscoveryRecovery | undefined;
77
78
  version: number;
@@ -1,6 +1,7 @@
1
1
  import { type AgentDiscoveryBinding, type ReviewedDiscoveryInputs } from "./agent-discovery.js";
2
2
  import { type IntegrationAgent } from "./agent-adapters.js";
3
3
  import type { CodexHookDiscoveryRecovery } from "./codex-hook-discovery-recovery.js";
4
+ import { type ClaudeCoordinatedHookEvent } from "./claude-settings-witness.js";
4
5
  export type { IntegrationAgent } from "./agent-adapters.js";
5
6
  export type ContextHookEvent = "UserPromptSubmit" | "SessionStart" | "SubagentStart";
6
7
  export interface AgentRootAlias {
@@ -96,11 +97,18 @@ export declare function planAgentIntegration(options: {
96
97
  * every other settings change and owns discovery witnesses and guarded writes.
97
98
  * No policy means this installer has no Skills integration to update.
98
99
  */
99
- export declare function planClaudeStopHookUpdate(options: {
100
+ export interface ClaudeHookUpdateOptions {
100
101
  home?: string;
101
102
  dataDir?: string;
102
103
  expectedSettingsSha256: string;
103
104
  replacement: string;
105
+ }
106
+ export declare function planClaudeStopHookUpdate(options: ClaudeHookUpdateOptions): AgentIntegrationPlan | null;
107
+ /** Cooperating safety installers may replace only PreToolUse. The caller owns
108
+ * its exact hook authorization; Skills retains discovery and native guards. */
109
+ export declare function planClaudePreToolUseHookUpdate(options: ClaudeHookUpdateOptions): AgentIntegrationPlan | null;
110
+ export declare function planClaudeHookEventsUpdate(options: ClaudeHookUpdateOptions & {
111
+ events: readonly ClaudeCoordinatedHookEvent[];
104
112
  }): AgentIntegrationPlan | null;
105
113
  export declare function applyAgentIntegration(plan: AgentIntegrationPlan): {
106
114
  changed: string[];
@@ -9,12 +9,16 @@ export declare const CLAUDE_SETTINGS_WITNESS_LIMITS: Readonly<{
9
9
  export interface ClaudeSettingsWitnessBudget {
10
10
  remaining: number;
11
11
  }
12
- /** Cooperating installers may replace Stop only. Compare the strict syntax
12
+ /** Cooperating installers may replace only their explicitly selected event. Compare the strict syntax
13
13
  * trees, not JSON.parse values or the preference-tolerant witness digest: all
14
14
  * other hooks, discovery settings, unknown values and number spellings remain
15
15
  * identical. Whitespace and object-key order have no configuration meaning.
16
16
  */
17
+ export declare const CLAUDE_COORDINATED_HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "Stop", "Notification", "SessionStart", "SessionEnd", "UserPromptSubmit", "SubagentStart"];
18
+ export type ClaudeCoordinatedHookEvent = typeof CLAUDE_COORDINATED_HOOK_EVENTS[number];
19
+ export declare function assertClaudeHookEventsReplacement(before: string | null, replacement: string, events: readonly ClaudeCoordinatedHookEvent[]): void;
17
20
  export declare function assertClaudeStopHookReplacement(before: string | null, replacement: string): void;
21
+ export declare function assertClaudePreToolUseHookReplacement(before: string | null, replacement: string): void;
18
22
  /** Hash only reviewed installer-rendered settings after the disk preimage has been verified. */
19
23
  export declare function hashClaudeSettingsReplacement(text: string, budget: ClaudeSettingsWitnessBudget): string;
20
24
  /** Explicit capture never converts a stored legacy raw witness or changes settings. */
@@ -4,6 +4,25 @@ export interface NativeHookAdditionsRecovery {
4
4
  path: string;
5
5
  reviewedPlanDigest?: string;
6
6
  }
7
+ /** Internal capability, not a caller-supplied config override. */
8
+ export interface ReviewedNativeHookAdditions {
9
+ readonly reviewSha256: string;
10
+ }
11
+ type JournalHook = {
12
+ key: string;
13
+ currentHash: string;
14
+ };
15
+ type TrustState = Readonly<{
16
+ trusted_hash: string;
17
+ enabled?: true;
18
+ }>;
19
+ type AddedTrustState = Readonly<{
20
+ key: string;
21
+ state: TrustState;
22
+ }>;
23
+ /** Rebind the proof to this exact journal transition and recheck every source.
24
+ * A serialized/cloned receipt can never create a discovery exception. */
25
+ export declare function readReviewedAdditionTrustStates(proof: ReviewedNativeHookAdditions, before: Snapshot, current: Snapshot, journalHooks: JournalHook[]): readonly AddedTrustState[];
7
26
  /** An explicit, private review of a later producer's append-only hook change.
8
27
  * The producer backups must themselves describe exactly the completed journal
9
28
  * operation. Neither a claimed receipt nor a matching raw hash grants authority
@@ -25,6 +44,7 @@ export declare function reviewNativeHookAdditions(options: {
25
44
  }): {
26
45
  hooks: any[];
27
46
  recheck: () => void;
47
+ trustProof: ReviewedNativeHookAdditions;
28
48
  receipt: {
29
49
  reviewPath: string;
30
50
  reviewSha256: string;
@@ -36,7 +56,8 @@ export declare function reviewNativeHookAdditions(options: {
36
56
  hooksPath: string;
37
57
  hooksSha256: string;
38
58
  };
39
- additions: any[];
59
+ additions: any;
60
+ version?: number | undefined;
40
61
  };
41
62
  };
42
63
  export {};
@@ -1,5 +1,6 @@
1
1
  import type { AgentDiscoveryBinding, DiscoverySource } from "./agent-discovery.js";
2
2
  import { type snapshot } from "./codex-hook-trust-files.js";
3
+ import { type ReviewedNativeHookAdditions } from "./codex-hook-additions-review.js";
3
4
  type Snapshot = ReturnType<typeof snapshot>;
4
5
  /** A receipt projection, not an override supplied by a CLI caller. Only this
5
6
  * module can create the in-memory proof used by discovery verification. */
@@ -17,7 +18,7 @@ export interface CodexHookDiscoveryRecovery {
17
18
  export declare function createCodexHookDiscoveryRecovery(binding: AgentDiscoveryBinding, before: Snapshot, current: Snapshot, hooks: Array<{
18
19
  key: string;
19
20
  currentHash: string;
20
- }>): CodexHookDiscoveryRecovery | undefined;
21
+ }>, additions?: ReviewedNativeHookAdditions): CodexHookDiscoveryRecovery | undefined;
21
22
  export declare function assertCodexHookDiscoveryRecovery(binding: AgentDiscoveryBinding, witness: CodexHookDiscoveryRecovery): void;
22
23
  /** Verify this one historical byte witness using the already-validated native
23
24
  * transition. Every other source still uses its ordinary current-file hash. */