@hasna/hooks 0.7.10 → 0.8.0

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.
Files changed (49) hide show
  1. package/README.md +32 -6
  2. package/bin/index.js +1872 -1300
  3. package/bin/serve.js +730 -551
  4. package/dist/config.d.ts +25 -19
  5. package/dist/index.d.ts +3 -2
  6. package/dist/index.js +1198 -712
  7. package/dist/lib/app-home.d.ts +9 -0
  8. package/dist/lib/installer.d.ts +0 -9
  9. package/dist/lib/local-opt-in.d.ts +80 -0
  10. package/dist/lib/profiles.d.ts +10 -0
  11. package/dist/lib/resolver-types.d.ts +45 -0
  12. package/dist/lib/sync.d.ts +13 -8
  13. package/dist/lib/transport.d.ts +87 -0
  14. package/dist/openapi.d.ts +1 -1
  15. package/dist/storage.js +54 -63
  16. package/hooks/hook-affected-tests/package.json +1 -1
  17. package/hooks/hook-agent-rules-version-check/package.json +1 -1
  18. package/hooks/hook-agentmessages/package.json +1 -1
  19. package/hooks/hook-announce-start/package.json +1 -1
  20. package/hooks/hook-announce-stop/package.json +1 -1
  21. package/hooks/hook-autoformat/package.json +1 -1
  22. package/hooks/hook-branchprotect/package.json +1 -1
  23. package/hooks/hook-checkbugs/package.json +1 -1
  24. package/hooks/hook-checkdocs/package.json +1 -1
  25. package/hooks/hook-checkfiles/package.json +1 -1
  26. package/hooks/hook-checklint/package.json +1 -1
  27. package/hooks/hook-checkpoint/package.json +1 -1
  28. package/hooks/hook-checksecurity/package.json +1 -1
  29. package/hooks/hook-checktasks/package.json +1 -1
  30. package/hooks/hook-checktests/package.json +1 -1
  31. package/hooks/hook-conflict-detect/package.json +1 -1
  32. package/hooks/hook-contextrefresh/package.json +1 -1
  33. package/hooks/hook-desktopnotify/package.json +1 -1
  34. package/hooks/hook-dm-inject/package.json +1 -1
  35. package/hooks/hook-envsetup/package.json +1 -1
  36. package/hooks/hook-failure-to-task/package.json +1 -1
  37. package/hooks/hook-filelock/package.json +1 -1
  38. package/hooks/hook-fleet-blockers-gate/package.json +1 -1
  39. package/hooks/hook-fleet-catchup/package.json +1 -1
  40. package/hooks/hook-gitguard/package.json +1 -1
  41. package/hooks/hook-packageage/package.json +1 -1
  42. package/hooks/hook-permissionguard/package.json +1 -1
  43. package/hooks/hook-phonenotify/package.json +1 -1
  44. package/hooks/hook-precompact/package.json +1 -1
  45. package/hooks/hook-protectfiles/package.json +1 -1
  46. package/hooks/hook-stylescheck/package.json +1 -1
  47. package/hooks/hook-typecheck-gate/package.json +1 -1
  48. package/package.json +4 -9
  49. package/scripts/ensure-profiles-dir.mjs +52 -1
package/README.md CHANGED
@@ -116,22 +116,33 @@ hooks update # re-register hooks and refresh pins
116
116
  **Registry server.** `hooks serve` exposes the local store over HTTP — catalog, artifacts, and the published lock:
117
117
 
118
118
  ```bash
119
- hooks serve --port 39428 # publish key resolves from HASNA_HOOKS_API_KEY / HOOKS_API_KEY only
119
+ hooks serve --port 39428 # publish key resolves from the @hasna/contracts chain per request
120
120
  # GET /health, GET /api/v1/catalog, GET /api/v1/hooks/:name/:version,
121
121
  # PUT /api/v1/hooks (publish, requires the key), GET /api/v1/lock
122
122
  ```
123
123
 
124
- **Cloudflare registry (opt-in).** Presence of an API URL selects the remote registry; absence means local. There is no mode concept.
124
+ **Registry selection (fail closed, strict pair).** The remote-registry authority and its credential resolve through the ONE `@hasna/contracts` client chain (2026-09-04 adoption, hasna/apps#1720), fresh on every call, as a STRICT pair: a URL without a key is a refusal, never half-open progress, and a key alone is a complete configuration (it resolves the fleet gateway `https://api.hasna.com/hooks`). The chain, in order: an explicit `--api-key`/`--profile` argument; `HASNA_HOOKS_API_KEY_OVERRIDE` / `HASNA_PROFILE` / `HASNA_HOOKS_API_KEY_REF`; the macOS Keychain items `hasna.credentials.hooks.api-key` / `.api-url`; `~/.hasna/hooks/config/credentials` (owner-only 0400/0600); then `HASNA_HOOKS_API_URL` / `HASNA_HOOKS_API_KEY` (the unprefixed `HOOKS_*` spellings remain only as the resolver's silent alias fallback). The retired locations (`~/.hasna/fleet-env`, `~/.hasna/cloud`, `~/.config/hasna`, `$XDG_CONFIG_HOME`) and the retired `~/.hasna/hooks/config.json` key store are never read, and no `*_MODE` switch exists. Without a resolved credential, the CLI **fails closed**: registry commands refuse to run rather than silently serving the bundled catalog and local SQLite store, and they name every tier they consulted in the error. Local mode (bundled registry + local store) is an explicit opt-in:
125
+
126
+ ```bash
127
+ HASNA_HOOKS_LOCAL=1 hooks list # canonical local opt-in
128
+ HOOKS_LOCAL=1 hooks list # accepted alias
129
+ ```
130
+
131
+ Local mode is answered BEFORE the resolver runs (so an unhosted run touches neither the Keychain nor the credential file) and says so on stderr, once per process.
132
+
133
+ Surfaces that are local, runtime, or operator-only by design never need either setting: `run`, `serve`, `mcp`, `cf`, `migrate`, `init`, `profile-export`/`profile-import`, `channels`, `events`, and `--help`/`--version`.
125
134
 
126
135
  ```bash
127
136
  hooks init --cloudflare --api-url https://registry.example.com --api-key <vault-key-name>
128
- hooks sync # fetch catalog + lock from the API, verify sha256, update the local store
129
- hooks sync --dry-run # print the plan without changing anything
137
+ HASNA_HOOKS_LOCAL=1 hooks sync # local workflow: bundled catalog into the local store
138
+ HASNA_HOOKS_LOCAL=1 hooks sync --dry-run # print the plan without changing anything
130
139
  ```
131
140
 
132
- `hooks init --cloudflare` stores the API URL and a vault key NAME in `~/.hasna/hooks/config.json` — never the key value. Serve with the key resolved from the vault:
141
+ `hooks init --cloudflare` no longer writes a config file — `config.json` is retired (hasna/apps#1720). It prints the exact configuration to apply: set `HASNA_HOOKS_API_URL` and `HASNA_HOOKS_API_KEY_REF` (the vault key NAME, never the value) in the environment, or store the Keychain items / credentials file:
133
142
 
134
143
  ```bash
144
+ export HASNA_HOOKS_API_URL=https://registry.example.com
145
+ export HASNA_HOOKS_API_KEY_REF=<vault-key-name> # resolved through the vault at request time
135
146
  secrets exec <vault-key-name> --as HASNA_HOOKS_API_KEY -- hooks serve
136
147
  ```
137
148
 
@@ -185,9 +196,24 @@ naming the replacement variable and the backend to use (`local` became `sqlite`,
185
196
  everything else became `postgresql`). Deployment location was never a property of
186
197
  the data layer, so it is no longer expressed as one.
187
198
 
199
+ ## Environment variables
200
+
201
+ | Variable | Purpose |
202
+ | --- | --- |
203
+ | `HASNA_HOOKS_API_URL` | Registry URL (strict pair — requires a credential with it). Fallback aliases are not read; the unprefixed `HOOKS_API_URL` survives only as the resolver's silent alias. `HASNA_HOOKS_REGISTRY_URL` / `HOOKS_REGISTRY_URL` are retired. |
204
+ | `HASNA_HOOKS_API_KEY` | Registry key, resolved through the @hasna/contracts chain (below disk, above nothing else). Unprefixed `HOOKS_API_KEY` is the silent alias. |
205
+ | `HASNA_HOOKS_API_KEY_OVERRIDE` | Deliberate per-run key override (tier 1). |
206
+ | `HASNA_HOOKS_API_KEY_REF` | Vault ITEM KEY name; resolved through `@hasna/secrets` at request time. Never a value. |
207
+ | `HASNA_PROFILE` | Selects which identity profile the chain reads. |
208
+ | `HASNA_HOOKS_LOCAL` / `HOOKS_LOCAL` | Explicit opt-in to local mode (bundled registry + local store); answered before the resolver runs and printed on stderr. |
209
+ | `HASNA_STATION` | Keychain account when reading `hasna.credentials.hooks.*`. |
210
+ | `HASNA_HOME` / `HASNA_CONFIG_HOME` | Move the disk credential root (`<…>/hooks/config/credentials`). `~/.hasna/fleet-env`, `~/.hasna/cloud`, `~/.config/hasna` and `$XDG_CONFIG_HOME` are never read. |
211
+ | `HASNA_HOOKS_DATA_DIR` / `HOOKS_DATA_DIR`, `HASNA_HOOKS_HOME` / `HOOKS_HOME`, `HASNA_HOOKS_DB_PATH` / `HOOKS_DB_PATH`, `HASNA_HOOKS_LOCK_PATH` / `HOOKS_LOCK_PATH` | Local store locations (data root, DB, lock). |
212
+ | `HASNA_HOOKS_DATABASE_URL` / `HOOKS_DATABASE_URL`, `HASNA_HOOKS_STORAGE_BACKEND` / `HOOKS_STORAGE_BACKEND` | Optional PostgreSQL data backend (see Storage). `HASNA_HOOKS_STORAGE_MODE` / `HOOKS_STORAGE_MODE` are retired and raise an error. |
213
+
188
214
  ## Runtime model
189
215
 
190
- This package is an npm CLI, MCP server, and static dashboard package. Installing
216
+ This package is an npm CLI and MCP server. Installing
191
217
  and running hooks needs nothing deployed anywhere — the SQLite backend is the
192
218
  default and requires no server.
193
219