@hasna/hooks 0.10.2 → 0.10.4

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 CHANGED
@@ -82,6 +82,16 @@ or in-place edits. A missing or unauthenticated Trash client refuses the rewritt
82
82
  operation; it never falls back to raw removal. Configure hosted Trash and verify a
83
83
  disposable capture/restore before relying on recoverable deletion.
84
84
 
85
+ `hooks safety inspect` is a small, read-only installation check. It returns
86
+ compact JSON with the verified Trash path and version, or a fixed failure code
87
+ such as `unsafe_executable`, `unsafe_directory`, `unsafe_manifest` or
88
+ `identity_failed`, and exits nonzero on failure. It checks package identity,
89
+ ownership, permissions and a bounded credential-free protocol probe; it does not
90
+ contact the registry or Trash API. Use it after setup and package upgrades.
91
+ Repair unsafe installation permissions with a verified package installer, then
92
+ check again. This check alone does not prove hosted authentication, native hook
93
+ trust or a running agent's interception.
94
+
85
95
  ## Optional Mementos prompt context
86
96
 
87
97
  `hooks install mementos-context --target codex` registers a native prompt hook.
@@ -300,6 +310,37 @@ store is never a silent default: every client surface either resolves a registry
300
310
  credential (hosted route — no local store is opened) or is told `HASNA_HOOKS_LOCAL=1`
301
311
  (local mode — announced on stderr); with neither it exits non-zero and creates nothing.
302
312
 
313
+ ## Required Sumi safety binding
314
+
315
+ `hooks safety install trash-guard --target sumi` registers the bundled composite
316
+ guard for Sumi's required native adapter. `hooks safety verify --target sumi`
317
+ checks the exact saved supervisor by classifying a synthetic removal without
318
+ creating a file or executing the rewritten command. Both
319
+ commands are credential-free and return compact JSON. Verification explicitly
320
+ reports `nativeAdoptionVerified: false`: a fresh native Sumi tool invocation is
321
+ still required to prove that the installed harness uses this binding.
322
+
323
+ The owner-only `~/.hasna/hooks/native/sumi-trash-guard.json` pins the supervisor's
324
+ runtime, worker, home and PATH. Identical setup is a no-op. An upgrade requires
325
+ `--expected-binding-sha256 <current-digest>` and preserves the previous record.
326
+ An interrupted lock or unexpected current record requires reconciliation; setup
327
+ does not overwrite it. This file is native configuration, not a Hooks app store.
328
+ Setup checks the supervisor's bound PATH before registration; verification checks
329
+ the same readback digest before and after the probe. Package-manager bin file
330
+ symlinks are supported, but symlinked or writable PATH directory ancestry refuses
331
+ instead of hiding an unsafe parent through realpath resolution. The published
332
+ Trash script's Bun interpreter and all preceding PATH directories are checked
333
+ before its identity probe runs; unsafe or unsupported interpreter resolution
334
+ reports `unsafe_interpreter` without executing it.
335
+
336
+ Native adapters import `@hasna/hooks/native-safety`, pass the final shell/patch
337
+ operation to `evaluateNativeSafety`, and enforce its continue, rewrite or deny
338
+ verdict before execution. Missing or unhealthy bindings reject the call. The
339
+ adapter must preserve cancellation and prevent later hooks from changing admitted
340
+ inputs. This covers supported shell removal and whole-file patch deletion; it
341
+ does not claim universal interception of arbitrary filesystem writes or plugin
342
+ executor replacements. Installing the record alone does not activate a harness.
343
+
303
344
  ## Data Directory
304
345
 
305
346
  Data is stored in `~/.hasna/hooks/`.
package/bin/hooks-mcp.js CHANGED
@@ -1419,7 +1419,7 @@ var init_redact = __esm(() => {
1419
1419
  ];
1420
1420
  });
1421
1421
 
1422
- // ../hooks0102-standalone-lock/node_modules/@hasna/contracts/dist/client/transport.js
1422
+ // ../../node_modules/.bun/@hasna+contracts@1.2.2+944b86d8024e79fa/node_modules/@hasna/contracts/dist/client/transport.js
1423
1423
  import { isIP } from "net";
1424
1424
  import { spawnSync } from "child_process";
1425
1425
  import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
@@ -2781,6 +2781,9 @@ function buildNativeSafetyCommand(binding) {
2781
2781
  return prefix + [encoded, binding.runtime, binding.home, binding.path].map(quote).join(" ");
2782
2782
  }
2783
2783
  function nativeSafetyRegistration(command) {
2784
+ return nativeSafetyCommandBinding(command)?.name;
2785
+ }
2786
+ function nativeSafetyCommandBinding(command) {
2784
2787
  if (!command.startsWith(prefix))
2785
2788
  return;
2786
2789
  const encoded = command.slice(prefix.length).match(/^'([A-Za-z0-9_-]+)' /)?.[1];
@@ -2788,7 +2791,7 @@ function nativeSafetyRegistration(command) {
2788
2791
  return;
2789
2792
  try {
2790
2793
  const binding = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
2791
- return buildNativeSafetyCommand(binding) === command ? binding.name : undefined;
2794
+ return buildNativeSafetyCommand(binding) === command ? binding : undefined;
2792
2795
  } catch {
2793
2796
  return;
2794
2797
  }
@@ -3170,7 +3173,7 @@ function writeCodexSettings(path, settings) {
3170
3173
  // src/lib/claude-skills-coordination.ts
3171
3174
  import { createHash as createHash5 } from "crypto";
3172
3175
 
3173
- // ../hooks0102-standalone-lock/node_modules/@hasna/skills/dist/index.js
3176
+ // ../../node_modules/.bun/@hasna+skills@0.9.14+a00d3064559565bb/node_modules/@hasna/skills/dist/index.js
3174
3177
  import { existsSync as existsSync9 } from "fs";
3175
3178
  import { homedir as homedir4 } from "os";
3176
3179
  import { join as join8, resolve as resolve3 } from "path";