@hasna/hooks 0.6.8 → 0.7.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 (33) hide show
  1. package/README.md +1 -1
  2. package/bin/index.js +886 -418
  3. package/bin/serve.js +613 -148
  4. package/dist/config.d.ts +7 -5
  5. package/dist/db/pg-migrations.d.ts +1 -1
  6. package/dist/index.js +788 -326
  7. package/dist/openapi.d.ts +216 -0
  8. package/dist/storage.js +34 -34
  9. package/hooks/codewith-native-common.test.ts +25 -25
  10. package/hooks/codewith-native-common.ts +1 -1
  11. package/hooks/hook-autoformat/package.json +1 -1
  12. package/hooks/hook-desktopnotify/package.json +1 -1
  13. package/hooks/hook-envsetup/package.json +1 -1
  14. package/hooks/hook-knowledge-context/README.md +1 -1
  15. package/hooks/hook-knowledge-context/src/hook.test.ts +1 -1
  16. package/hooks/hook-permissionguard/package.json +1 -1
  17. package/hooks/hook-protectfiles/package.json +1 -1
  18. package/hooks/hook-spiral-detector/LICENSE +191 -0
  19. package/hooks/hook-spiral-detector/README.md +23 -0
  20. package/hooks/hook-spiral-detector/package.json +12 -0
  21. package/hooks/hook-spiral-detector/src/hook.test.ts +72 -0
  22. package/hooks/hook-spiral-detector/src/hook.ts +93 -0
  23. package/hooks/hook-workspace-repos-guard/README.md +4 -3
  24. package/hooks/hook-workspace-repos-guard/src/hook.test.ts +7 -7
  25. package/hooks/hook-workspace-repos-guard/src/hook.ts +5 -3
  26. package/hooks/mention-context/src/hasna-mention-context.py +2 -2
  27. package/hooks/mention-context/src/hasna-mention-warm.py +1 -1
  28. package/hooks/pre-bash/README.md +1 -1
  29. package/hooks/prompt-guard/README.md +1 -1
  30. package/hooks/session-start/README.md +1 -1
  31. package/hooks/stop-sync/README.md +1 -1
  32. package/hooks/worktree-guard/README.md +1 -1
  33. package/package.json +10 -6
package/bin/index.js CHANGED
@@ -20,12 +20,14 @@ var __toESM = (mod, isNodeMode, target) => {
20
20
  }
21
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
22
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
- for (let key of __getOwnPropNames(mod))
24
- if (!__hasOwnProp.call(to, key))
25
- __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
27
- enumerable: true
28
- });
23
+ if (mod && typeof mod === "object" || typeof mod === "function") {
24
+ for (let key of __getOwnPropNames(mod))
25
+ if (!__hasOwnProp.call(to, key))
26
+ __defProp(to, key, {
27
+ get: __accessProp.bind(mod, key),
28
+ enumerable: true
29
+ });
30
+ }
29
31
  if (canCache)
30
32
  cache.set(mod, to);
31
33
  return to;
@@ -48,7 +50,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
48
50
  var __require = import.meta.require;
49
51
 
50
52
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/error.js
51
- var require_error = __commonJS((exports) => {
53
+ var require_error = __commonJS(function(exports) {
52
54
  class CommanderError extends Error {
53
55
  constructor(exitCode, code, message) {
54
56
  super(message);
@@ -72,7 +74,7 @@ var require_error = __commonJS((exports) => {
72
74
  });
73
75
 
74
76
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/argument.js
75
- var require_argument = __commonJS((exports) => {
77
+ var require_argument = __commonJS(function(exports) {
76
78
  var { InvalidArgumentError } = require_error();
77
79
 
78
80
  class Argument {
@@ -151,7 +153,7 @@ var require_argument = __commonJS((exports) => {
151
153
  });
152
154
 
153
155
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/help.js
154
- var require_help = __commonJS((exports) => {
156
+ var require_help = __commonJS(function(exports) {
155
157
  var { humanReadableArgName } = require_argument();
156
158
 
157
159
  class Help {
@@ -400,7 +402,7 @@ var require_help = __commonJS((exports) => {
400
402
  });
401
403
 
402
404
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/option.js
403
- var require_option = __commonJS((exports) => {
405
+ var require_option = __commonJS(function(exports) {
404
406
  var { InvalidArgumentError } = require_error();
405
407
 
406
408
  class Option {
@@ -551,7 +553,7 @@ var require_option = __commonJS((exports) => {
551
553
  });
552
554
 
553
555
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/suggestSimilar.js
554
- var require_suggestSimilar = __commonJS((exports) => {
556
+ var require_suggestSimilar = __commonJS(function(exports) {
555
557
  var maxDistance = 3;
556
558
  function editDistance(a, b) {
557
559
  if (Math.abs(a.length - b.length) > maxDistance)
@@ -624,7 +626,7 @@ var require_suggestSimilar = __commonJS((exports) => {
624
626
  });
625
627
 
626
628
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/command.js
627
- var require_command = __commonJS((exports) => {
629
+ var require_command = __commonJS(function(exports) {
628
630
  var EventEmitter = __require("events").EventEmitter;
629
631
  var childProcess = __require("child_process");
630
632
  var path = __require("path");
@@ -1867,7 +1869,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1867
1869
  });
1868
1870
 
1869
1871
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/index.js
1870
- var require_commander = __commonJS((exports) => {
1872
+ var require_commander = __commonJS(function(exports) {
1871
1873
  var { Argument } = require_argument();
1872
1874
  var { Command } = require_command();
1873
1875
  var { CommanderError, InvalidArgumentError } = require_error();
@@ -2266,6 +2268,16 @@ var init_registry = __esm(() => {
2266
2268
  matcher: "",
2267
2269
  tags: ["errors", "failures", "logging", "debugging"]
2268
2270
  },
2271
+ {
2272
+ name: "spiral-detector",
2273
+ displayName: "Spiral Detector",
2274
+ description: "Interrupts sessions after five consecutive identical command failures",
2275
+ version: "0.1.0",
2276
+ category: "Observability",
2277
+ event: "PostToolUse",
2278
+ matcher: "",
2279
+ tags: ["spiral", "failures", "retries", "safety", "interrupt"]
2280
+ },
2269
2281
  {
2270
2282
  name: "stylescheck",
2271
2283
  displayName: "Styles Check",
@@ -2420,7 +2432,7 @@ var init_registry = __esm(() => {
2420
2432
  });
2421
2433
 
2422
2434
  // ../../node_modules/.bun/cli-spinners@2.9.2/node_modules/cli-spinners/spinners.json
2423
- var require_spinners = __commonJS((exports, module) => {
2435
+ var require_spinners = __commonJS(function(exports, module) {
2424
2436
  module.exports = {
2425
2437
  dots: {
2426
2438
  interval: 80,
@@ -4046,7 +4058,7 @@ var require_spinners = __commonJS((exports, module) => {
4046
4058
  });
4047
4059
 
4048
4060
  // ../../node_modules/.bun/cli-spinners@2.9.2/node_modules/cli-spinners/index.js
4049
- var require_cli_spinners = __commonJS((exports, module) => {
4061
+ var require_cli_spinners = __commonJS(function(exports, module) {
4050
4062
  var spinners = Object.assign({}, require_spinners());
4051
4063
  var spinnersList = Object.keys(spinners);
4052
4064
  Object.defineProperty(spinners, "random", {
@@ -4059,55 +4071,329 @@ var require_cli_spinners = __commonJS((exports, module) => {
4059
4071
  module.exports = spinners;
4060
4072
  });
4061
4073
 
4074
+ // ../contracts/dist/client/transport.js
4075
+ import { readFileSync, statSync } from "fs";
4076
+ import { join as join2 } from "path";
4077
+ function envToken(name) {
4078
+ return name.toUpperCase().replace(/-/g, "_");
4079
+ }
4080
+ function clientTransportEnvKeys(name) {
4081
+ const envSegment = envToken(name);
4082
+ return {
4083
+ apiUrlKeys: [`HASNA_${envSegment}_API_URL`, `${envSegment}_API_URL`],
4084
+ apiKeyKeys: [`HASNA_${envSegment}_API_KEY`, `${envSegment}_API_KEY`]
4085
+ };
4086
+ }
4087
+ function credentialOverrideEnvKey(name) {
4088
+ return `HASNA_${envToken(name)}_API_KEY_OVERRIDE`;
4089
+ }
4090
+ function homeDir(env) {
4091
+ const home = env.HOME?.trim();
4092
+ return home ? home : null;
4093
+ }
4094
+ function credentialDiskSources(name, env) {
4095
+ return profileDiskSources(name, env, null);
4096
+ }
4097
+ function profileDiskSources(name, env, profile) {
4098
+ const home = homeDir(env);
4099
+ if (!home || !SAFE_APP_SLUG.test(name))
4100
+ return [];
4101
+ const stem = profile ? `${name}.${profile}` : name;
4102
+ const configStem = profile ? `${name}-${profile}` : name;
4103
+ return [
4104
+ join2(home, HASNA_STATE_DIR, FLEET_CREDENTIAL_DIR, `${stem}.env`),
4105
+ join2(home, CONFIG_DIR, CONFIG_NAMESPACE, `${configStem}-cloud.env`)
4106
+ ];
4107
+ }
4108
+ function parseEnvFile(text) {
4109
+ const values = new Map;
4110
+ for (const rawLine of text.split(/\r?\n/)) {
4111
+ const line = rawLine.trim();
4112
+ if (line.length === 0 || line.startsWith("#"))
4113
+ continue;
4114
+ const withoutExport = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
4115
+ const equals = withoutExport.indexOf("=");
4116
+ if (equals <= 0)
4117
+ continue;
4118
+ const key = withoutExport.slice(0, equals).trim();
4119
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
4120
+ continue;
4121
+ let value = withoutExport.slice(equals + 1).trim();
4122
+ const quote = value[0];
4123
+ if (quote === '"' || quote === "'") {
4124
+ if (value.length < 2 || !value.endsWith(quote))
4125
+ continue;
4126
+ value = value.slice(1, -1);
4127
+ }
4128
+ if (value.length === 0)
4129
+ continue;
4130
+ values.set(key, value);
4131
+ }
4132
+ return values;
4133
+ }
4134
+ function readAppConfigFile(path) {
4135
+ let text;
4136
+ try {
4137
+ const stats = statSync(path);
4138
+ if (!stats.isFile() || stats.size > MAX_CREDENTIAL_FILE_BYTES)
4139
+ return null;
4140
+ text = readFileSync(path, "utf8");
4141
+ } catch {
4142
+ return null;
4143
+ }
4144
+ return parseEnvFile(text);
4145
+ }
4146
+ function readCredentialFile(path, apiKeyKeys) {
4147
+ const values = readAppConfigFile(path);
4148
+ if (!values)
4149
+ return null;
4150
+ for (const key of apiKeyKeys) {
4151
+ const value = values.get(key)?.trim();
4152
+ if (value)
4153
+ return value;
4154
+ }
4155
+ return null;
4156
+ }
4157
+ function assertUsableCredential(appName, source, value) {
4158
+ if (!ILLEGAL_IN_HEADER_VALUE.test(value))
4159
+ return;
4160
+ throw new CredentialResolutionError(appName, `The credential from ${source} contains characters that cannot be sent in an HTTP header ` + `(a control character or non-ASCII byte). A file written with CR-only line endings is the usual ` + `cause. Rewrite that credential file with one LF-terminated KEY=value line. ` + `The value is not shown here, and is deliberately never logged.`, [source]);
4161
+ }
4162
+ function sealCredential(fields) {
4163
+ const { apiKey } = fields;
4164
+ const visible = {
4165
+ tier: fields.tier,
4166
+ source: fields.source,
4167
+ deliberate: fields.deliberate,
4168
+ deprecated: fields.deprecated,
4169
+ diskCandidates: Object.freeze([...fields.diskCandidates]),
4170
+ warning: fields.warning
4171
+ };
4172
+ const sealed = { ...visible };
4173
+ Object.defineProperty(sealed, "apiKey", {
4174
+ value: apiKey,
4175
+ enumerable: false,
4176
+ writable: false,
4177
+ configurable: false
4178
+ });
4179
+ Object.defineProperty(sealed, INSPECT_CUSTOM, {
4180
+ value: () => ({ ...visible, apiKey: "[redacted]" }),
4181
+ enumerable: false,
4182
+ writable: false,
4183
+ configurable: false
4184
+ });
4185
+ Object.defineProperty(sealed, CREDENTIAL_SEAL, {
4186
+ value: true,
4187
+ enumerable: false,
4188
+ writable: false,
4189
+ configurable: false
4190
+ });
4191
+ return Object.freeze(sealed);
4192
+ }
4193
+ function firstEnvValue(env, keys) {
4194
+ for (const key of keys) {
4195
+ const value = env[key]?.trim();
4196
+ if (value)
4197
+ return { key, value };
4198
+ }
4199
+ return null;
4200
+ }
4201
+ function deprecationNotified() {
4202
+ const host = globalThis;
4203
+ const existing = host[DEPRECATION_REGISTRY];
4204
+ if (existing instanceof Set)
4205
+ return existing;
4206
+ const created = new Set;
4207
+ host[DEPRECATION_REGISTRY] = created;
4208
+ return created;
4209
+ }
4210
+ function defaultDeprecationSink(message) {
4211
+ if (typeof process !== "undefined" && process.stderr) {
4212
+ process.stderr.write(`${message}
4213
+ `);
4214
+ }
4215
+ }
4216
+ function resolveCredential(name, env, options = {}) {
4217
+ const { apiKeyKeys } = clientTransportEnvKeys(name);
4218
+ const diskPaths = credentialDiskSources(name, env);
4219
+ const explicitKey = options.apiKey?.trim();
4220
+ if (explicitKey) {
4221
+ assertUsableCredential(name, "the explicit apiKey argument", explicitKey);
4222
+ return sealCredential({
4223
+ apiKey: explicitKey,
4224
+ tier: "argument",
4225
+ source: "explicit apiKey argument",
4226
+ deliberate: true,
4227
+ deprecated: false,
4228
+ diskCandidates: diskPaths,
4229
+ warning: null
4230
+ });
4231
+ }
4232
+ const overrideKeyName = credentialOverrideEnvKey(name);
4233
+ const overrideRaw = env[overrideKeyName];
4234
+ if (overrideRaw !== undefined) {
4235
+ const override = overrideRaw.trim();
4236
+ if (!override) {
4237
+ throw new CredentialResolutionError(name, `${overrideKeyName} is set but empty. It is a deliberate override, so it is not resolved around: ` + `either give it a real key or unset it to fall back to the credential on disk.`, [overrideKeyName]);
4238
+ }
4239
+ assertUsableCredential(name, overrideKeyName, override);
4240
+ return sealCredential({
4241
+ apiKey: override,
4242
+ tier: "override",
4243
+ source: overrideKeyName,
4244
+ deliberate: true,
4245
+ deprecated: false,
4246
+ diskCandidates: diskPaths,
4247
+ warning: null
4248
+ });
4249
+ }
4250
+ const profile = options.profile?.trim() || env[CREDENTIAL_PROFILE_ENV_KEY]?.trim();
4251
+ if (profile) {
4252
+ const profileSource = options.profile?.trim() ? "explicit profile argument" : CREDENTIAL_PROFILE_ENV_KEY;
4253
+ if (!SAFE_PROFILE.test(profile)) {
4254
+ throw new CredentialResolutionError(name, `Profile name from ${profileSource} is not usable in a path. ` + `Use letters, digits, dot, dash, or underscore.`, [profileSource]);
4255
+ }
4256
+ const paths = profileDiskSources(name, env, profile);
4257
+ for (const path of paths) {
4258
+ const value = readCredentialFile(path, apiKeyKeys);
4259
+ if (value) {
4260
+ assertUsableCredential(name, path, value);
4261
+ return sealCredential({
4262
+ apiKey: value,
4263
+ tier: "profile",
4264
+ source: path,
4265
+ deliberate: true,
4266
+ deprecated: false,
4267
+ diskCandidates: paths,
4268
+ warning: null
4269
+ });
4270
+ }
4271
+ }
4272
+ throw new CredentialResolutionError(name, `Profile '${profile}' (from ${profileSource}) has no ${apiKeyKeys[0]} for '${name}'. ` + `Looked in: ${paths.join(", ") || "<no HOME in this environment>"}. ` + `A profile names WHICH identity to use, so it is never resolved around \u2014 ` + `create the profile's credential file or unset ${CREDENTIAL_PROFILE_ENV_KEY}.`, paths);
4273
+ }
4274
+ const diskHits = diskPaths.map((path) => ({ path, value: readCredentialFile(path, apiKeyKeys) })).filter((hit) => hit.value !== null);
4275
+ if (diskHits.length > 0) {
4276
+ const winner = diskHits[0];
4277
+ assertUsableCredential(name, winner.path, winner.value);
4278
+ const divergentSources = [
4279
+ ...diskHits.slice(1).filter((hit) => hit.value !== winner.value).map((hit) => hit.path),
4280
+ ...(() => {
4281
+ const legacyHit = firstEnvValue(env, apiKeyKeys);
4282
+ return legacyHit && legacyHit.value !== winner.value ? [legacyHit.key] : [];
4283
+ })()
4284
+ ];
4285
+ const warning = divergentSources.length > 0 ? `Credential sources disagree for '${name}': ${winner.path} and ` + `${divergentSources.join(", ")} hold different keys. ${winner.path} wins, because a file on ` + `disk is re-read on every call while an environment variable is a snapshot. Reconcile them \u2014 ` + `a rotation that updated only one leaves the other to fail 401 wherever it is loaded first.` : null;
4286
+ return sealCredential({
4287
+ apiKey: winner.value,
4288
+ tier: "disk",
4289
+ source: winner.path,
4290
+ deliberate: false,
4291
+ deprecated: false,
4292
+ diskCandidates: diskPaths,
4293
+ warning
4294
+ });
4295
+ }
4296
+ const legacy = firstEnvValue(env, apiKeyKeys);
4297
+ if (legacy) {
4298
+ assertUsableCredential(name, legacy.key, legacy.value);
4299
+ const where = diskPaths.length > 0 ? `Put the current key in ${diskPaths[0]} \u2014 it is re-read on every call, so rotations take effect immediately.` : `This environment has no HOME, so no credential file could be consulted at all; the disk tier is ` + `unavailable here and this process will keep using the environment snapshot.`;
4300
+ const message = `[${name}] DEPRECATED: the API key came from ${legacy.key} in this process's environment. ` + `Environment variables are a snapshot taken when this process started, so a shell that started ` + `before a key rotation keeps using the old key until it exits. ${where}`;
4301
+ const sink = options.onDeprecation ?? defaultDeprecationSink;
4302
+ const notified = deprecationNotified();
4303
+ if (!notified.has(name)) {
4304
+ notified.add(name);
4305
+ sink(message);
4306
+ }
4307
+ return sealCredential({
4308
+ apiKey: legacy.value,
4309
+ tier: "legacy-env",
4310
+ source: legacy.key,
4311
+ deliberate: false,
4312
+ deprecated: true,
4313
+ diskCandidates: diskPaths,
4314
+ warning: message
4315
+ });
4316
+ }
4317
+ return null;
4318
+ }
4319
+ var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE", CredentialResolutionError, HASNA_STATE_DIR = ".hasna", FLEET_CREDENTIAL_DIR = "cloud", CONFIG_DIR = ".config", CONFIG_NAMESPACE = "hasna", MAX_CREDENTIAL_FILE_BYTES, SAFE_APP_SLUG, SAFE_PROFILE, ILLEGAL_IN_HEADER_VALUE, INSPECT_CUSTOM, CREDENTIAL_SEAL, DEPRECATION_REGISTRY, IDEMPOTENT_METHODS, AUTHORITY_OVERRIDE_HEADERS;
4320
+ var init_transport = __esm(() => {
4321
+ CredentialResolutionError = class CredentialResolutionError extends Error {
4322
+ appName;
4323
+ attempted;
4324
+ constructor(appName, message, attempted) {
4325
+ super(message);
4326
+ this.name = "CredentialResolutionError";
4327
+ this.appName = appName;
4328
+ this.attempted = attempted;
4329
+ }
4330
+ };
4331
+ MAX_CREDENTIAL_FILE_BYTES = 64 * 1024;
4332
+ SAFE_APP_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
4333
+ SAFE_PROFILE = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
4334
+ ILLEGAL_IN_HEADER_VALUE = /[^\t\x20-\x7e]/;
4335
+ INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
4336
+ CREDENTIAL_SEAL = Symbol.for("hasna:contracts:sealedCredential");
4337
+ DEPRECATION_REGISTRY = Symbol.for("hasna:contracts:credentialDeprecationNotices");
4338
+ IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
4339
+ AUTHORITY_OVERRIDE_HEADERS = new Set([
4340
+ "host",
4341
+ ":authority",
4342
+ "forwarded",
4343
+ "x-forwarded-host",
4344
+ "x-original-host"
4345
+ ]);
4346
+ });
4347
+
4062
4348
  // src/config.ts
4063
4349
  var exports_config = {};
4064
4350
  __export(exports_config, {
4065
- writeConfig: () => writeConfig,
4066
- resolveApiUrl: () => resolveApiUrl,
4067
- resolveApiKey: () => resolveApiKey,
4068
- readConfig: () => readConfig,
4069
- getLockPath: () => getLockPath,
4070
- getHooksDataDir: () => getHooksDataDir,
4351
+ getConfigPath: () => getConfigPath,
4071
4352
  getCustomHooksDir: () => getCustomHooksDir,
4072
- getConfigPath: () => getConfigPath
4353
+ getHooksDataDir: () => getHooksDataDir,
4354
+ getLockPath: () => getLockPath,
4355
+ readConfig: () => readConfig,
4356
+ resolveApiKey: () => resolveApiKey,
4357
+ resolveApiUrl: () => resolveApiUrl,
4358
+ writeConfig: () => writeConfig
4073
4359
  });
4074
- import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
4075
- import { join as join2 } from "path";
4360
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
4361
+ import { join as join3 } from "path";
4076
4362
  import { homedir as homedir2 } from "os";
4077
4363
  function getHooksDataDir() {
4078
4364
  const explicit = process.env.HASNA_HOOKS_DATA_DIR ?? process.env.HOOKS_DATA_DIR;
4079
4365
  if (explicit)
4080
4366
  return explicit;
4081
- return join2(homedir2(), ".hasna", "hooks");
4367
+ return join3(homedir2(), ".hasna", "hooks");
4082
4368
  }
4083
4369
  function getCustomHooksDir() {
4084
- return join2(getHooksDataDir(), "hooks");
4370
+ return join3(getHooksDataDir(), "hooks");
4085
4371
  }
4086
4372
  function getLockPath() {
4087
4373
  const explicit = process.env.HASNA_HOOKS_LOCK_PATH ?? process.env.HOOKS_LOCK_PATH;
4088
4374
  if (explicit)
4089
4375
  return explicit;
4090
- return join2(getHooksDataDir(), "hooks.lock");
4376
+ return join3(getHooksDataDir(), "hooks.lock");
4091
4377
  }
4092
4378
  function getConfigPath() {
4093
4379
  const explicit = process.env.HASNA_HOOKS_CONFIG_PATH ?? process.env.HOOKS_CONFIG_PATH;
4094
4380
  if (explicit)
4095
4381
  return explicit;
4096
- return join2(getHooksDataDir(), "config.json");
4382
+ return join3(getHooksDataDir(), "config.json");
4097
4383
  }
4098
4384
  function readConfig() {
4099
4385
  try {
4100
4386
  const path = getConfigPath();
4101
4387
  if (!existsSync2(path))
4102
4388
  return {};
4103
- return JSON.parse(readFileSync(path, "utf-8"));
4389
+ return JSON.parse(readFileSync2(path, "utf-8"));
4104
4390
  } catch {
4105
4391
  return {};
4106
4392
  }
4107
4393
  }
4108
4394
  function writeConfig(config) {
4109
4395
  const path = getConfigPath();
4110
- mkdirSync(join2(path, ".."), { recursive: true });
4396
+ mkdirSync(join3(path, ".."), { recursive: true });
4111
4397
  writeFileSync(path, JSON.stringify(config, null, 2) + `
4112
4398
  `, "utf-8");
4113
4399
  return path;
@@ -4120,9 +4406,11 @@ function resolveApiUrl() {
4120
4406
  return cfg.api_url ? cfg.api_url.replace(/\/+$/, "") : undefined;
4121
4407
  }
4122
4408
  function resolveApiKey() {
4123
- return process.env.HASNA_HOOKS_API_KEY ?? process.env.HOOKS_API_KEY;
4409
+ return resolveCredential("hooks", process.env)?.apiKey;
4124
4410
  }
4125
- var init_config = () => {};
4411
+ var init_config = __esm(() => {
4412
+ init_transport();
4413
+ });
4126
4414
 
4127
4415
  // src/lib/semver.ts
4128
4416
  var NUMERIC_ID = "(0|[1-9]\\d{0,15})", ALNUM_ID = "\\d*[A-Za-z-][0-9A-Za-z-]*", SEMVER_PATTERN;
@@ -4133,23 +4421,23 @@ var init_semver = __esm(() => {
4133
4421
  // src/lib/manifest.ts
4134
4422
  var exports_manifest = {};
4135
4423
  __export(exports_manifest, {
4136
- writeCustomHook: () => writeCustomHook,
4137
- shortManifestName: () => shortManifestName,
4138
- scriptRelFor: () => scriptRelFor,
4139
- resolveScript: () => resolveScript,
4140
- readCustomManifest: () => readCustomManifest,
4141
- parseManifest: () => parseManifest,
4142
- normalizeManifestName: () => normalizeManifestName,
4143
- manifestSchema: () => manifestSchema,
4144
- listCustomHooks: () => listCustomHooks,
4145
- isInlineScript: () => isInlineScript,
4146
- customHookDir: () => customHookDir,
4147
- assertContained: () => assertContained,
4424
+ HOOK_NAME_RE: () => HOOK_NAME_RE,
4148
4425
  ScriptContainmentError: () => ScriptContainmentError,
4149
- HOOK_NAME_RE: () => HOOK_NAME_RE
4426
+ assertContained: () => assertContained,
4427
+ customHookDir: () => customHookDir,
4428
+ isInlineScript: () => isInlineScript,
4429
+ listCustomHooks: () => listCustomHooks,
4430
+ manifestSchema: () => manifestSchema,
4431
+ normalizeManifestName: () => normalizeManifestName,
4432
+ parseManifest: () => parseManifest,
4433
+ readCustomManifest: () => readCustomManifest,
4434
+ resolveScript: () => resolveScript,
4435
+ scriptRelFor: () => scriptRelFor,
4436
+ shortManifestName: () => shortManifestName,
4437
+ writeCustomHook: () => writeCustomHook
4150
4438
  });
4151
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync, realpathSync, writeFileSync as writeFileSync2 } from "fs";
4152
- import { basename, dirname, isAbsolute, join as join3, normalize, resolve, sep } from "path";
4439
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync, realpathSync, writeFileSync as writeFileSync2 } from "fs";
4440
+ import { basename, dirname, isAbsolute, join as join4, normalize, resolve, sep } from "path";
4153
4441
  import { z } from "zod";
4154
4442
  function parseManifest(json) {
4155
4443
  const parsed = JSON.parse(json);
@@ -4182,7 +4470,7 @@ function realpathNearest(path) {
4182
4470
  tail.unshift(basename(cur));
4183
4471
  cur = parent;
4184
4472
  }
4185
- return join3(realpathSync(cur), ...tail);
4473
+ return join4(realpathSync(cur), ...tail);
4186
4474
  }
4187
4475
  function assertContainedReal(root, target, raw) {
4188
4476
  const rootReal = realpathSync(root);
@@ -4215,25 +4503,25 @@ function resolveScript(manifest, manifestDir) {
4215
4503
  if (isInlineScript(manifest))
4216
4504
  return { path: scriptRelFor(manifest), content: manifest.script };
4217
4505
  const rel = scriptRelFor(manifest);
4218
- const resolved = assertContained(join3(manifestDir, rel), manifestDir, manifest.script);
4506
+ const resolved = assertContained(join4(manifestDir, rel), manifestDir, manifest.script);
4219
4507
  if (!existsSync3(resolved)) {
4220
4508
  throw new Error(`manifest script file not found: ${resolved}`);
4221
4509
  }
4222
- return { path: rel, content: readFileSync2(resolved, "utf-8") };
4510
+ return { path: rel, content: readFileSync3(resolved, "utf-8") };
4223
4511
  }
4224
4512
  function customHookDir(name) {
4225
- return join3(getCustomHooksDir(), shortManifestName(name));
4513
+ return join4(getCustomHooksDir(), shortManifestName(name));
4226
4514
  }
4227
4515
  function readCustomManifest(name) {
4228
4516
  const dir = customHookDir(name);
4229
- const manifestPath = join3(dir, "manifest.json");
4517
+ const manifestPath = join4(dir, "manifest.json");
4230
4518
  if (!existsSync3(manifestPath))
4231
4519
  return;
4232
4520
  try {
4233
- const manifest = parseManifest(readFileSync2(manifestPath, "utf-8"));
4521
+ const manifest = parseManifest(readFileSync3(manifestPath, "utf-8"));
4234
4522
  const script = resolveScript(manifest, dir);
4235
4523
  const scriptIsInline = isInlineScript(manifest);
4236
- return { manifest, scriptPath: join3(dir, script.path), scriptContent: script.content, scriptIsInline };
4524
+ return { manifest, scriptPath: join4(dir, script.path), scriptContent: script.content, scriptIsInline };
4237
4525
  } catch (err) {
4238
4526
  if (err instanceof ScriptContainmentError)
4239
4527
  throw err;
@@ -4257,7 +4545,7 @@ function writeCustomHook(name, manifest, scriptContent, scriptRelPath) {
4257
4545
  if (isAbsolute(scriptRelPath)) {
4258
4546
  throw new ScriptContainmentError(scriptRelPath, dir);
4259
4547
  }
4260
- const scriptPath = scriptRelPath.includes("/") ? join3(dir, scriptRelPath) : join3(dir, scriptRelPath);
4548
+ const scriptPath = scriptRelPath.includes("/") ? join4(dir, scriptRelPath) : join4(dir, scriptRelPath);
4261
4549
  assertContainedLexical(scriptPath, dir, scriptRelPath);
4262
4550
  mkdirSync2(dir, { recursive: true });
4263
4551
  assertContainedReal(dir, scriptPath, scriptRelPath);
@@ -4265,7 +4553,7 @@ function writeCustomHook(name, manifest, scriptContent, scriptRelPath) {
4265
4553
  if (scriptDir !== dir)
4266
4554
  mkdirSync2(scriptDir, { recursive: true });
4267
4555
  writeFileSync2(scriptPath, scriptContent, "utf-8");
4268
- writeFileSync2(join3(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + `
4556
+ writeFileSync2(join4(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + `
4269
4557
  `, "utf-8");
4270
4558
  return { dir, scriptPath };
4271
4559
  }
@@ -4303,20 +4591,20 @@ var init_manifest = __esm(() => {
4303
4591
  // src/lib/resolve.ts
4304
4592
  var exports_resolve = {};
4305
4593
  __export(exports_resolve, {
4306
- resolveScriptPath: () => resolveScriptPath,
4307
- resolveHookMeta: () => resolveHookMeta,
4308
- resolveHookDir: () => resolveHookDir,
4594
+ resolveBundledHooksDir: () => resolveBundledHooksDir,
4309
4595
  resolveHook: () => resolveHook,
4310
- resolveBundledHooksDir: () => resolveBundledHooksDir
4596
+ resolveHookDir: () => resolveHookDir,
4597
+ resolveHookMeta: () => resolveHookMeta,
4598
+ resolveScriptPath: () => resolveScriptPath
4311
4599
  });
4312
- import { dirname as dirname2, join as join4 } from "path";
4600
+ import { dirname as dirname2, join as join5 } from "path";
4313
4601
  import { existsSync as existsSync4 } from "fs";
4314
4602
  import { fileURLToPath } from "url";
4315
4603
  function resolveBundledHooksDir(moduleDir = dirname2(fileURLToPath(import.meta.url))) {
4316
- const sourceLayout = join4(moduleDir, "..", "..", "hooks");
4317
- if (existsSync4(join4(sourceLayout, "hook-gitguard")))
4604
+ const sourceLayout = join5(moduleDir, "..", "..", "hooks");
4605
+ if (existsSync4(join5(sourceLayout, "hook-gitguard")))
4318
4606
  return sourceLayout;
4319
- return join4(moduleDir, "..", "hooks");
4607
+ return join5(moduleDir, "..", "hooks");
4320
4608
  }
4321
4609
  function resolveHookDir(name) {
4322
4610
  const custom = readCustomManifest(name);
@@ -4327,10 +4615,10 @@ function resolveHookDir(name) {
4327
4615
  return;
4328
4616
  const short = shortManifestName(name);
4329
4617
  const bundledBase = resolveBundledHooksDir();
4330
- const direct = join4(bundledBase, short);
4618
+ const direct = join5(bundledBase, short);
4331
4619
  if (existsSync4(direct))
4332
4620
  return direct;
4333
- const prefixed = join4(bundledBase, `hook-${short}`);
4621
+ const prefixed = join5(bundledBase, `hook-${short}`);
4334
4622
  return existsSync4(prefixed) ? prefixed : undefined;
4335
4623
  }
4336
4624
  function resolveScriptPath(name) {
@@ -4340,7 +4628,7 @@ function resolveScriptPath(name) {
4340
4628
  const dir = resolveHookDir(name);
4341
4629
  if (!dir)
4342
4630
  return;
4343
- const script = join4(dir, "src", "hook.ts");
4631
+ const script = join5(dir, "src", "hook.ts");
4344
4632
  return existsSync4(script) ? script : undefined;
4345
4633
  }
4346
4634
  function resolveHookMeta(name) {
@@ -4430,7 +4718,7 @@ function up(db) {
4430
4718
  db.exec(idx);
4431
4719
  }
4432
4720
  }
4433
- var init_001_initial = __esm(() => {
4721
+ var init__001_initial = __esm(() => {
4434
4722
  init_schema();
4435
4723
  });
4436
4724
 
@@ -4459,7 +4747,7 @@ function up2(db) {
4459
4747
  throw err;
4460
4748
  }
4461
4749
  }
4462
- var init_002_session_events = __esm(() => {
4750
+ var init__002_session_events = __esm(() => {
4463
4751
  init_schema();
4464
4752
  });
4465
4753
 
@@ -4488,7 +4776,7 @@ function up3(db) {
4488
4776
  throw err;
4489
4777
  }
4490
4778
  }
4491
- var init_003_user_prompt_submit_event = __esm(() => {
4779
+ var init__003_user_prompt_submit_event = __esm(() => {
4492
4780
  init_schema();
4493
4781
  });
4494
4782
 
@@ -4512,7 +4800,7 @@ var CREATE_HOOKS_TABLE = `
4512
4800
  last_verified_at TEXT
4513
4801
  )
4514
4802
  `, CREATE_HOOKS_INDEXES;
4515
- var init_004_hooks_table = __esm(() => {
4803
+ var init__004_hooks_table = __esm(() => {
4516
4804
  CREATE_HOOKS_INDEXES = [
4517
4805
  `CREATE INDEX IF NOT EXISTS idx_hooks_name ON hooks (name)`
4518
4806
  ];
@@ -4543,7 +4831,7 @@ function up5(db) {
4543
4831
  throw err;
4544
4832
  }
4545
4833
  }
4546
- var init_005_subagent_start_event = __esm(() => {
4834
+ var init__005_subagent_start_event = __esm(() => {
4547
4835
  init_schema();
4548
4836
  });
4549
4837
 
@@ -4575,11 +4863,11 @@ function runMigrations(db) {
4575
4863
  }
4576
4864
  var MIGRATIONS;
4577
4865
  var init_migrations = __esm(() => {
4578
- init_001_initial();
4579
- init_002_session_events();
4580
- init_003_user_prompt_submit_event();
4581
- init_004_hooks_table();
4582
- init_005_subagent_start_event();
4866
+ init__001_initial();
4867
+ init__002_session_events();
4868
+ init__003_user_prompt_submit_event();
4869
+ init__004_hooks_table();
4870
+ init__005_subagent_start_event();
4583
4871
  MIGRATIONS = [
4584
4872
  { version: "001_initial", up },
4585
4873
  { version: "002_session_events", up: up2 },
@@ -4590,8 +4878,8 @@ var init_migrations = __esm(() => {
4590
4878
  });
4591
4879
 
4592
4880
  // src/db/legacy-import.ts
4593
- import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
4594
- import { join as join5 } from "path";
4881
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
4882
+ import { join as join6 } from "path";
4595
4883
  import { homedir as homedir3 } from "os";
4596
4884
  function ensureMetaTable(db) {
4597
4885
  db.exec(`
@@ -4615,7 +4903,7 @@ function nanoid() {
4615
4903
  function importJsonlFile(db, filePath) {
4616
4904
  let count = 0;
4617
4905
  try {
4618
- const lines = readFileSync3(filePath, "utf-8").split(`
4906
+ const lines = readFileSync4(filePath, "utf-8").split(`
4619
4907
  `).filter(Boolean);
4620
4908
  for (const line of lines) {
4621
4909
  try {
@@ -4641,7 +4929,7 @@ function importJsonlFile(db, filePath) {
4641
4929
  function importErrorsLog(db, filePath) {
4642
4930
  let count = 0;
4643
4931
  try {
4644
- const lines = readFileSync3(filePath, "utf-8").split(`
4932
+ const lines = readFileSync4(filePath, "utf-8").split(`
4645
4933
  `).filter(Boolean);
4646
4934
  const linePattern = /^\[(.+?)\]\s+(?:\[session:(\S+)\]\s+)?(.+?)\s+\u2014\s+(.+)$/;
4647
4935
  for (const line of lines) {
@@ -4666,25 +4954,25 @@ function importErrorsLog(db, filePath) {
4666
4954
  } catch {}
4667
4955
  return count;
4668
4956
  }
4669
- function runLegacyImport(db, homeDir = homedir3()) {
4957
+ function runLegacyImport(db, homeDir2 = homedir3()) {
4670
4958
  try {
4671
4959
  if (isAlreadyDone(db))
4672
4960
  return;
4673
4961
  let total = 0;
4674
- const claudeProjectsDir = join5(homeDir, ".claude", "projects");
4962
+ const claudeProjectsDir = join6(homeDir2, ".claude", "projects");
4675
4963
  if (existsSync5(claudeProjectsDir)) {
4676
4964
  try {
4677
4965
  const projectDirs = readdirSync2(claudeProjectsDir);
4678
4966
  for (const dir of projectDirs) {
4679
- const projectDir = join5(claudeProjectsDir, dir);
4967
+ const projectDir = join6(claudeProjectsDir, dir);
4680
4968
  try {
4681
4969
  const files = readdirSync2(projectDir);
4682
4970
  for (const file of files) {
4683
4971
  if (file.match(/^session-log-\d{4}-\d{2}-\d{2}\.jsonl$/)) {
4684
- total += importJsonlFile(db, join5(projectDir, file));
4972
+ total += importJsonlFile(db, join6(projectDir, file));
4685
4973
  }
4686
4974
  if (file === "errors.log") {
4687
- total += importErrorsLog(db, join5(projectDir, file));
4975
+ total += importErrorsLog(db, join6(projectDir, file));
4688
4976
  }
4689
4977
  }
4690
4978
  } catch {}
@@ -4721,23 +5009,23 @@ function runRetention(db, days) {
4721
5009
  // src/db/index.ts
4722
5010
  var exports_db = {};
4723
5011
  __export(exports_db, {
4724
- getDbPath: () => getDbPath,
4725
- getDb: () => getDb,
5012
+ closeDb: () => closeDb,
4726
5013
  createTestDb: () => createTestDb,
4727
- closeDb: () => closeDb
5014
+ getDb: () => getDb,
5015
+ getDbPath: () => getDbPath
4728
5016
  });
4729
5017
  import { Database } from "bun:sqlite";
4730
5018
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, cpSync } from "fs";
4731
- import { join as join6 } from "path";
5019
+ import { join as join7 } from "path";
4732
5020
  import { homedir as homedir4 } from "os";
4733
5021
  function resolveDataDir() {
4734
5022
  const explicit = process.env.HASNA_HOOKS_DATA_DIR ?? process.env.HOOKS_DATA_DIR;
4735
5023
  if (explicit)
4736
5024
  return explicit;
4737
- const newDir = join6(homedir4(), ".hasna", "hooks");
4738
- const oldDir = join6(homedir4(), ".hooks");
5025
+ const newDir = join7(homedir4(), ".hasna", "hooks");
5026
+ const oldDir = join7(homedir4(), ".hooks");
4739
5027
  if (!existsSync6(newDir) && existsSync6(oldDir)) {
4740
- mkdirSync3(join6(homedir4(), ".hasna"), { recursive: true });
5028
+ mkdirSync3(join7(homedir4(), ".hasna"), { recursive: true });
4741
5029
  cpSync(oldDir, newDir, { recursive: true });
4742
5030
  }
4743
5031
  return newDir;
@@ -4747,7 +5035,7 @@ function getDbPath() {
4747
5035
  if (explicitDb)
4748
5036
  return explicitDb;
4749
5037
  const dataDir = resolveDataDir();
4750
- return join6(dataDir, "hooks.db");
5038
+ return join7(dataDir, "hooks.db");
4751
5039
  }
4752
5040
  function ensureDir(dbPath) {
4753
5041
  const dir = dbPath.substring(0, dbPath.lastIndexOf("/"));
@@ -4802,26 +5090,26 @@ var init_db = __esm(() => {
4802
5090
  // src/lib/store.ts
4803
5091
  var exports_store = {};
4804
5092
  __export(exports_store, {
4805
- writeLock: () => writeLock,
4806
- verifyScriptHash: () => verifyScriptHash,
4807
- upsertHookRecord: () => upsertHookRecord,
4808
- sha256Of: () => sha256Of,
4809
- sha256File: () => sha256File,
4810
- setPinnedHook: () => setPinnedHook,
4811
- retrustHook: () => retrustHook,
4812
- removePinnedHook: () => removePinnedHook,
4813
- removeHookRecord: () => removeHookRecord,
4814
- removeHookFromStore: () => removeHookFromStore,
4815
- readLock: () => readLock,
4816
- pinInstalledHook: () => pinInstalledHook,
4817
- listHookRecords: () => listHookRecords,
4818
- getPinnedHook: () => getPinnedHook,
4819
- getHookRecord: () => getHookRecord,
5093
+ LockFileError: () => LockFileError,
4820
5094
  checkScriptHash: () => checkScriptHash,
4821
- LockFileError: () => LockFileError
5095
+ getHookRecord: () => getHookRecord,
5096
+ getPinnedHook: () => getPinnedHook,
5097
+ listHookRecords: () => listHookRecords,
5098
+ pinInstalledHook: () => pinInstalledHook,
5099
+ readLock: () => readLock,
5100
+ removeHookFromStore: () => removeHookFromStore,
5101
+ removeHookRecord: () => removeHookRecord,
5102
+ removePinnedHook: () => removePinnedHook,
5103
+ retrustHook: () => retrustHook,
5104
+ setPinnedHook: () => setPinnedHook,
5105
+ sha256File: () => sha256File,
5106
+ sha256Of: () => sha256Of,
5107
+ upsertHookRecord: () => upsertHookRecord,
5108
+ verifyScriptHash: () => verifyScriptHash,
5109
+ writeLock: () => writeLock
4822
5110
  });
4823
5111
  import { createHash } from "crypto";
4824
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync } from "fs";
5112
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync } from "fs";
4825
5113
  import { readFile as readFile2 } from "fs/promises";
4826
5114
  import { randomBytes } from "crypto";
4827
5115
  function sha256Of(input) {
@@ -4887,7 +5175,7 @@ function readLock() {
4887
5175
  return { hooks: {} };
4888
5176
  let raw;
4889
5177
  try {
4890
- raw = readFileSync4(path, "utf-8");
5178
+ raw = readFileSync5(path, "utf-8");
4891
5179
  } catch {
4892
5180
  throw new LockFileError(`hooks.lock (${path}) could not be read. Repair: fix permissions or move the file aside and run 'hooks sync'.`);
4893
5181
  }
@@ -4984,7 +5272,7 @@ async function verifyScriptHash(name, scriptPath) {
4984
5272
  return checkScriptHash(name, await sha256File(scriptPath));
4985
5273
  }
4986
5274
  function retrustHook(name, scriptPath, version, source) {
4987
- const actual = sha256Of(readFileSync4(scriptPath));
5275
+ const actual = sha256Of(readFileSync5(scriptPath));
4988
5276
  const db = getDb();
4989
5277
  upsertHookRecord(db, {
4990
5278
  name,
@@ -5011,22 +5299,22 @@ var init_store = __esm(() => {
5011
5299
  // src/lib/installer.ts
5012
5300
  var exports_installer = {};
5013
5301
  __export(exports_installer, {
5014
- uninstallHook: () => uninstallHook,
5015
- removeHook: () => removeHook,
5016
- removeCodewithHookEntry: () => removeCodewithHookEntry,
5017
- isEventSupported: () => isEventSupported,
5018
- installHooks: () => installHooks,
5019
- installHook: () => installHook,
5020
- hookExists: () => hookExists,
5021
- getSettingsPath: () => getSettingsPath,
5022
- getRegisteredHooksForTarget: () => getRegisteredHooksForTarget,
5023
- getRegisteredHooks: () => getRegisteredHooks,
5024
- getInstalledHooks: () => getInstalledHooks,
5302
+ buildCodewithTomlFragment: () => buildCodewithTomlFragment,
5025
5303
  getHookPath: () => getHookPath,
5026
- buildCodewithTomlFragment: () => buildCodewithTomlFragment
5304
+ getInstalledHooks: () => getInstalledHooks,
5305
+ getRegisteredHooks: () => getRegisteredHooks,
5306
+ getRegisteredHooksForTarget: () => getRegisteredHooksForTarget,
5307
+ getSettingsPath: () => getSettingsPath,
5308
+ hookExists: () => hookExists,
5309
+ installHook: () => installHook,
5310
+ installHooks: () => installHooks,
5311
+ isEventSupported: () => isEventSupported,
5312
+ removeCodewithHookEntry: () => removeCodewithHookEntry,
5313
+ removeHook: () => removeHook,
5314
+ uninstallHook: () => uninstallHook
5027
5315
  });
5028
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
5029
- import { join as join7, dirname as dirname3 } from "path";
5316
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync4 } from "fs";
5317
+ import { join as join8, dirname as dirname3 } from "path";
5030
5318
  import { homedir as homedir5 } from "os";
5031
5319
  import { fileURLToPath as fileURLToPath2 } from "url";
5032
5320
  function normalizeHookName(name) {
@@ -5066,16 +5354,16 @@ function getSettingsPath(scope = "global", target = "claude", codewithConfigPath
5066
5354
  return globalOverride;
5067
5355
  const dir = getTargetSettingsDir(target);
5068
5356
  if (scope === "project") {
5069
- return target === "codewith" ? join7(process.cwd(), dir, "config.toml") : join7(process.cwd(), dir, "settings.json");
5357
+ return target === "codewith" ? join8(process.cwd(), dir, "config.toml") : join8(process.cwd(), dir, "settings.json");
5070
5358
  }
5071
- return target === "codewith" ? join7(homedir5(), dir, "config.toml") : join7(homedir5(), dir, "settings.json");
5359
+ return target === "codewith" ? join8(homedir5(), dir, "config.toml") : join8(homedir5(), dir, "settings.json");
5072
5360
  }
5073
5361
  function getHookPath(name) {
5074
5362
  const shortName = shortHookName(name);
5075
5363
  const resolved = resolveHookDir(shortName);
5076
5364
  if (resolved)
5077
5365
  return resolved;
5078
- return join7(HOOKS_DIR, normalizeHookName(shortName));
5366
+ return join8(HOOKS_DIR, normalizeHookName(shortName));
5079
5367
  }
5080
5368
  function hookExists(name) {
5081
5369
  return existsSync8(getHookPath(name));
@@ -5084,7 +5372,7 @@ function readSettings(scope = "global", target = "claude") {
5084
5372
  const path = getSettingsPath(scope, target);
5085
5373
  try {
5086
5374
  if (existsSync8(path)) {
5087
- return JSON.parse(readFileSync5(path, "utf-8"));
5375
+ return JSON.parse(readFileSync6(path, "utf-8"));
5088
5376
  }
5089
5377
  } catch (error) {
5090
5378
  console.warn(`[hooks] Failed to read settings at ${path}: ${error instanceof Error ? error.message : String(error)}`);
@@ -5183,7 +5471,7 @@ function buildCodewithTomlFragment(name, profile) {
5183
5471
  function readCodewithConfig(scope, configPath) {
5184
5472
  const path = getSettingsPath(scope, "codewith", configPath);
5185
5473
  try {
5186
- return existsSync8(path) ? readFileSync5(path, "utf-8") : "";
5474
+ return existsSync8(path) ? readFileSync6(path, "utf-8") : "";
5187
5475
  } catch {
5188
5476
  return "";
5189
5477
  }
@@ -5193,7 +5481,7 @@ function appendCodewithFragment(fragment, scope, configPath) {
5193
5481
  const dir = dirname3(path);
5194
5482
  if (!existsSync8(dir))
5195
5483
  mkdirSync5(dir, { recursive: true });
5196
- const existing = existsSync8(path) ? readFileSync5(path, "utf-8") : "";
5484
+ const existing = existsSync8(path) ? readFileSync6(path, "utf-8") : "";
5197
5485
  const sep2 = existing.trim() ? `
5198
5486
 
5199
5487
  ` : "";
@@ -5238,7 +5526,7 @@ function installForTarget(name, scope, overwrite, target, profile, codewithMode
5238
5526
  target,
5239
5527
  fragment,
5240
5528
  applied: false,
5241
- note: "Codewith install is fragment-only by default; open-configs should own applying this TOML."
5529
+ note: "Codewith install is fragment-only by default; configs should own applying this TOML."
5242
5530
  };
5243
5531
  }
5244
5532
  if (!codewithConfigPath) {
@@ -5265,7 +5553,7 @@ function installForTarget(name, scope, overwrite, target, profile, codewithMode
5265
5553
  fragment,
5266
5554
  applied: true,
5267
5555
  configPath: path,
5268
- note: "Direct Codewith config write was explicitly requested; prefer open-configs for managed machines."
5556
+ note: "Direct Codewith config write was explicitly requested; prefer configs for managed machines."
5269
5557
  };
5270
5558
  } catch (error) {
5271
5559
  return {
@@ -5496,7 +5784,7 @@ function codewithHasHookEntry(name, scope = "global") {
5496
5784
  if (!existsSync8(path))
5497
5785
  return false;
5498
5786
  try {
5499
- return codewithHasHookEntryInText(readFileSync5(path, "utf-8"), name);
5787
+ return codewithHasHookEntryInText(readFileSync6(path, "utf-8"), name);
5500
5788
  } catch {
5501
5789
  return false;
5502
5790
  }
@@ -5528,7 +5816,7 @@ function uninstallHook(name, scope = "global", target = "claude") {
5528
5816
  if (target === "codewith" || target === "all") {
5529
5817
  const codewithPath = getSettingsPath(scope, "codewith");
5530
5818
  if (existsSync8(codewithPath)) {
5531
- const before = readFileSync5(codewithPath, "utf-8");
5819
+ const before = readFileSync6(codewithPath, "utf-8");
5532
5820
  const after = removeCodewithHookEntry(before, shortName);
5533
5821
  if (after.removed) {
5534
5822
  writeFileSync4(codewithPath, after.text, "utf-8");
@@ -5574,7 +5862,7 @@ var init_installer = __esm(() => {
5574
5862
  init_manifest();
5575
5863
  init_store();
5576
5864
  __dirname2 = dirname3(fileURLToPath2(import.meta.url));
5577
- HOOKS_DIR = existsSync8(join7(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join7(__dirname2, "..", "..", "hooks") : join7(__dirname2, "..", "hooks");
5865
+ HOOKS_DIR = existsSync8(join8(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join8(__dirname2, "..", "..", "hooks") : join8(__dirname2, "..", "hooks");
5578
5866
  EVENT_MAP = {
5579
5867
  claude: {
5580
5868
  PreToolUse: "PreToolUse",
@@ -5721,14 +6009,14 @@ var init_redact = __esm(() => {
5721
6009
  });
5722
6010
 
5723
6011
  // src/lib/profiles.ts
5724
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5, readdirSync as readdirSync3, rmSync as rmSync2, cpSync as cpSync2 } from "fs";
5725
- import { join as join8 } from "path";
6012
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync3, rmSync as rmSync2, cpSync as cpSync2 } from "fs";
6013
+ import { join as join9 } from "path";
5726
6014
  import { homedir as homedir6 } from "os";
5727
6015
  function resolveProfilesDir() {
5728
- const newDir = join8(homedir6(), ".hasna", "hooks", "profiles");
5729
- const oldDir = join8(homedir6(), ".hooks", "profiles");
6016
+ const newDir = join9(homedir6(), ".hasna", "hooks", "profiles");
6017
+ const oldDir = join9(homedir6(), ".hooks", "profiles");
5730
6018
  if (!existsSync9(newDir) && existsSync9(oldDir)) {
5731
- mkdirSync6(join8(homedir6(), ".hasna", "hooks"), { recursive: true });
6019
+ mkdirSync6(join9(homedir6(), ".hasna", "hooks"), { recursive: true });
5732
6020
  cpSync2(oldDir, newDir, { recursive: true });
5733
6021
  }
5734
6022
  return newDir;
@@ -5739,7 +6027,7 @@ function ensureProfilesDir() {
5739
6027
  }
5740
6028
  }
5741
6029
  function profilePath(id) {
5742
- return join8(PROFILES_DIR, `${id}.json`);
6030
+ return join9(PROFILES_DIR, `${id}.json`);
5743
6031
  }
5744
6032
  function shortUuid() {
5745
6033
  return crypto.randomUUID().slice(0, 8);
@@ -5767,7 +6055,7 @@ function getProfile(id) {
5767
6055
  try {
5768
6056
  if (!existsSync9(path))
5769
6057
  return null;
5770
- return JSON.parse(readFileSync6(path, "utf-8"));
6058
+ return JSON.parse(readFileSync7(path, "utf-8"));
5771
6059
  } catch {
5772
6060
  return null;
5773
6061
  }
@@ -5780,7 +6068,7 @@ function listProfiles() {
5780
6068
  const profiles = [];
5781
6069
  for (const file of files) {
5782
6070
  try {
5783
- const content = readFileSync6(join8(PROFILES_DIR, file), "utf-8");
6071
+ const content = readFileSync7(join9(PROFILES_DIR, file), "utf-8");
5784
6072
  profiles.push(JSON.parse(content));
5785
6073
  } catch {}
5786
6074
  }
@@ -5828,10 +6116,10 @@ var init_profiles = __esm(() => {
5828
6116
  // src/lib/db-writer.ts
5829
6117
  var exports_db_writer = {};
5830
6118
  __export(exports_db_writer, {
5831
- writeHookEvent: () => writeHookEvent,
5832
- resolveEventType: () => resolveEventType,
6119
+ normalizeEventType: () => normalizeEventType,
5833
6120
  recordHookRun: () => recordHookRun,
5834
- normalizeEventType: () => normalizeEventType
6121
+ resolveEventType: () => resolveEventType,
6122
+ writeHookEvent: () => writeHookEvent
5835
6123
  });
5836
6124
  function nanoid2() {
5837
6125
  return crypto.randomUUID().replace(/-/g, "").slice(0, 21);
@@ -5923,7 +6211,7 @@ var init_db_writer = __esm(() => {
5923
6211
 
5924
6212
  // src/lib/hook-env.ts
5925
6213
  import { dirname as dirname4, isAbsolute as isAbsolute2 } from "path";
5926
- import { existsSync as existsSync10, statSync } from "fs";
6214
+ import { existsSync as existsSync10, statSync as statSync2 } from "fs";
5927
6215
  function isDeniedEnvName(name) {
5928
6216
  const upper = name.toUpperCase();
5929
6217
  if (DENY_PREFIXES.some((prefix) => upper.startsWith(prefix)))
@@ -5950,7 +6238,7 @@ function isUnsafePathEntry(entry, home) {
5950
6238
  if (entry === "/var/tmp" || entry.startsWith("/var/tmp/"))
5951
6239
  return true;
5952
6240
  try {
5953
- if ((statSync(entry).mode & 2) !== 0)
6241
+ if ((statSync2(entry).mode & 2) !== 0)
5954
6242
  return true;
5955
6243
  } catch {}
5956
6244
  return false;
@@ -6078,12 +6366,12 @@ var init_hook_env = __esm(() => {
6078
6366
  // src/lib/run.ts
6079
6367
  var exports_run = {};
6080
6368
  __export(exports_run, {
6081
- executeVerifiedScript: () => executeVerifiedScript,
6082
- HookTimeoutError: () => HookTimeoutError
6369
+ HookTimeoutError: () => HookTimeoutError,
6370
+ executeVerifiedScript: () => executeVerifiedScript
6083
6371
  });
6084
6372
  import { randomBytes as randomBytes2 } from "crypto";
6085
6373
  import { rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
6086
- import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute3, join as join9 } from "path";
6374
+ import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute3, join as join10 } from "path";
6087
6375
  function extensionOf(scriptPath) {
6088
6376
  const base = basename2(scriptPath);
6089
6377
  const dot = base.lastIndexOf(".");
@@ -6154,7 +6442,7 @@ async function readPipeWithDeadline(stream, deadlineMs) {
6154
6442
  async function executeVerifiedScript(options) {
6155
6443
  const scriptDir = dirname5(options.scriptPath);
6156
6444
  const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
6157
- const tempPath = join9(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
6445
+ const tempPath = join10(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
6158
6446
  let proc = null;
6159
6447
  let timedOut = false;
6160
6448
  const timeoutTimer = options.timeout ? setTimeout(() => {
@@ -6241,19 +6529,19 @@ var init_run = __esm(() => {
6241
6529
  // src/lib/custom-install.ts
6242
6530
  var exports_custom_install = {};
6243
6531
  __export(exports_custom_install, {
6244
- isCustomSource: () => isCustomSource,
6532
+ ensureCustomDir: () => ensureCustomDir,
6245
6533
  installCustomSource: () => installCustomSource,
6246
- ensureCustomDir: () => ensureCustomDir
6534
+ isCustomSource: () => isCustomSource
6247
6535
  });
6248
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, mkdtempSync, readFileSync as readFileSync7, rmSync as rmSync4 } from "fs";
6249
- import { join as join10 } from "path";
6536
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, mkdtempSync, readFileSync as readFileSync8, rmSync as rmSync4 } from "fs";
6537
+ import { join as join11 } from "path";
6250
6538
  import { tmpdir } from "os";
6251
6539
  function manifestAt(dir) {
6252
- const path = join10(dir, "manifest.json");
6540
+ const path = join11(dir, "manifest.json");
6253
6541
  if (!existsSync11(path))
6254
6542
  return;
6255
6543
  try {
6256
- return parseManifest(readFileSync7(path, "utf-8"));
6544
+ return parseManifest(readFileSync8(path, "utf-8"));
6257
6545
  } catch {
6258
6546
  return;
6259
6547
  }
@@ -6272,8 +6560,8 @@ function writeFromManifest(manifest, srcDir, source, kind) {
6272
6560
  return { name, version: manifest.version, kind, source, dir, scriptPath };
6273
6561
  }
6274
6562
  async function cloneToTemp(gitUrl) {
6275
- const tmp = mkdtempSync(join10(tmpdir(), "hooks-install-"));
6276
- const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl, join10(tmp, "repo")], {
6563
+ const tmp = mkdtempSync(join11(tmpdir(), "hooks-install-"));
6564
+ const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl, join11(tmp, "repo")], {
6277
6565
  stdout: "pipe",
6278
6566
  stderr: "pipe"
6279
6567
  });
@@ -6286,7 +6574,7 @@ async function cloneToTemp(gitUrl) {
6286
6574
  rmSync4(tmp, { recursive: true, force: true });
6287
6575
  throw new Error(`git clone failed (${exitCode}): ${stderr.trim()}`);
6288
6576
  }
6289
- return join10(tmp, "repo");
6577
+ return join11(tmp, "repo");
6290
6578
  }
6291
6579
  function isGitUrl(value) {
6292
6580
  return value.startsWith("git@") || value.startsWith("ssh://") || value.startsWith("file://") || value.endsWith(".git");
@@ -6314,7 +6602,7 @@ async function installCustomSource(source) {
6314
6602
  throw new Error(`No such local path: ${source}`);
6315
6603
  }
6316
6604
  if (source.endsWith("manifest.json")) {
6317
- const manifest = parseManifest(readFileSync7(source, "utf-8"));
6605
+ const manifest = parseManifest(readFileSync8(source, "utf-8"));
6318
6606
  return writeFromManifest(manifest, source.substring(0, source.lastIndexOf("/")), source, "local");
6319
6607
  }
6320
6608
  return copyManifestDir(source, source, "local");
@@ -6356,19 +6644,19 @@ var init_custom_install = __esm(() => {
6356
6644
  // src/lib/sync.ts
6357
6645
  var exports_sync = {};
6358
6646
  __export(exports_sync, {
6359
- syncHooks: () => syncHooks,
6360
- stageSyncArtifacts: () => stageSyncArtifacts,
6361
- planSync: () => planSync,
6647
+ commitSyncArtifacts: () => commitSyncArtifacts,
6362
6648
  fetchPinnedHook: () => fetchPinnedHook,
6363
- commitSyncArtifacts: () => commitSyncArtifacts
6649
+ planSync: () => planSync,
6650
+ stageSyncArtifacts: () => stageSyncArtifacts,
6651
+ syncHooks: () => syncHooks
6364
6652
  });
6365
6653
  import { createHash as createHash2 } from "crypto";
6366
- import { readFileSync as readFileSync8 } from "fs";
6654
+ import { readFileSync as readFileSync9 } from "fs";
6367
6655
  function sha256OfText(text) {
6368
6656
  return createHash2("sha256").update(text).digest("hex");
6369
6657
  }
6370
6658
  function sha256OfPath(path) {
6371
- return createHash2("sha256").update(readFileSync8(path)).digest("hex");
6659
+ return createHash2("sha256").update(readFileSync9(path)).digest("hex");
6372
6660
  }
6373
6661
  function collectBundledCatalog() {
6374
6662
  const out = [];
@@ -7069,31 +7357,31 @@ var init_storage_sync = __esm(() => {
7069
7357
  // src/storage.ts
7070
7358
  var exports_storage = {};
7071
7359
  __export(exports_storage, {
7072
- storageSync: () => storageSync,
7073
- storagePush: () => storagePush,
7074
- storagePull: () => storagePull,
7075
- runStorageMigrations: () => runStorageMigrations,
7076
- resolveTables: () => resolveTables,
7077
- parseStorageTables: () => parseStorageTables,
7078
- getSyncMetaAll: () => getSyncMetaAll,
7079
- getStorageStatus: () => getStorageStatus,
7080
- getStoragePg: () => getStoragePg,
7081
- getStorageDatabaseUrl: () => getStorageDatabaseUrl,
7082
- getStorageDatabaseEnvName: () => getStorageDatabaseEnvName,
7083
- getStorageDatabaseEnv: () => getStorageDatabaseEnv,
7084
- getStorageBackend: () => getStorageBackend,
7085
- STORAGE_TABLES: () => STORAGE_TABLES,
7086
- STORAGE_DATABASE_ENV: () => STORAGE_DATABASE_ENV,
7087
- STORAGE_BACKEND_ENV: () => STORAGE_BACKEND_ENV,
7088
- STORAGE_BACKENDS: () => STORAGE_BACKENDS,
7089
- RETIRED_STORAGE_MODE_ENV: () => RETIRED_STORAGE_MODE_ENV,
7090
- PgAdapterAsync: () => PgAdapterAsync,
7091
- PG_MIGRATIONS: () => PG_MIGRATIONS,
7092
- HOOKS_STORAGE_TABLES: () => HOOKS_STORAGE_TABLES,
7093
- HOOKS_STORAGE_FALLBACK_ENV: () => HOOKS_STORAGE_FALLBACK_ENV,
7094
- HOOKS_STORAGE_ENV: () => HOOKS_STORAGE_ENV,
7360
+ HOOKS_STORAGE_BACKEND_ENV: () => HOOKS_STORAGE_BACKEND_ENV,
7095
7361
  HOOKS_STORAGE_BACKEND_FALLBACK_ENV: () => HOOKS_STORAGE_BACKEND_FALLBACK_ENV,
7096
- HOOKS_STORAGE_BACKEND_ENV: () => HOOKS_STORAGE_BACKEND_ENV
7362
+ HOOKS_STORAGE_ENV: () => HOOKS_STORAGE_ENV,
7363
+ HOOKS_STORAGE_FALLBACK_ENV: () => HOOKS_STORAGE_FALLBACK_ENV,
7364
+ HOOKS_STORAGE_TABLES: () => HOOKS_STORAGE_TABLES,
7365
+ PG_MIGRATIONS: () => PG_MIGRATIONS,
7366
+ PgAdapterAsync: () => PgAdapterAsync,
7367
+ RETIRED_STORAGE_MODE_ENV: () => RETIRED_STORAGE_MODE_ENV,
7368
+ STORAGE_BACKENDS: () => STORAGE_BACKENDS,
7369
+ STORAGE_BACKEND_ENV: () => STORAGE_BACKEND_ENV,
7370
+ STORAGE_DATABASE_ENV: () => STORAGE_DATABASE_ENV,
7371
+ STORAGE_TABLES: () => STORAGE_TABLES,
7372
+ getStorageBackend: () => getStorageBackend,
7373
+ getStorageDatabaseEnv: () => getStorageDatabaseEnv,
7374
+ getStorageDatabaseEnvName: () => getStorageDatabaseEnvName,
7375
+ getStorageDatabaseUrl: () => getStorageDatabaseUrl,
7376
+ getStoragePg: () => getStoragePg,
7377
+ getStorageStatus: () => getStorageStatus,
7378
+ getSyncMetaAll: () => getSyncMetaAll,
7379
+ parseStorageTables: () => parseStorageTables,
7380
+ resolveTables: () => resolveTables,
7381
+ runStorageMigrations: () => runStorageMigrations,
7382
+ storagePull: () => storagePull,
7383
+ storagePush: () => storagePush,
7384
+ storageSync: () => storageSync
7097
7385
  });
7098
7386
  var init_storage = __esm(() => {
7099
7387
  init_storage_sync();
@@ -7114,15 +7402,180 @@ function secureEqual(a, b) {
7114
7402
  return diff === 0;
7115
7403
  }
7116
7404
 
7405
+ // src/openapi.ts
7406
+ var openApiDocument;
7407
+ var init_openapi = __esm(() => {
7408
+ openApiDocument = {
7409
+ openapi: "3.1.0",
7410
+ info: {
7411
+ title: "Hooks Registry API",
7412
+ version: "0.1.0",
7413
+ description: "Local registry HTTP API for @hasna/hooks: catalog, lock, and artifact reads are open; publishing (PUT /api/v1/hooks) requires the API key (authorization: Bearer or x-api-key)."
7414
+ },
7415
+ servers: [{ url: "/" }],
7416
+ paths: {
7417
+ "/health": {
7418
+ get: {
7419
+ summary: "Liveness probe",
7420
+ operationId: "getHealth",
7421
+ responses: {
7422
+ "200": {
7423
+ description: "Service is alive",
7424
+ content: {
7425
+ "application/json": {
7426
+ schema: { type: "object", additionalProperties: true }
7427
+ }
7428
+ }
7429
+ }
7430
+ }
7431
+ }
7432
+ },
7433
+ "/ready": {
7434
+ get: {
7435
+ summary: "Readiness probe \u2014 the hooks.lock store is readable",
7436
+ operationId: "getReady",
7437
+ responses: {
7438
+ "200": { description: "Lock store is readable" },
7439
+ "503": { description: "Lock store is not readable" }
7440
+ }
7441
+ }
7442
+ },
7443
+ "/version": {
7444
+ get: {
7445
+ summary: "Package version",
7446
+ operationId: "getVersion",
7447
+ responses: {
7448
+ "200": {
7449
+ description: "The installed @hasna/hooks version",
7450
+ content: {
7451
+ "application/json": {
7452
+ schema: { type: "object", properties: { version: { type: "string" } } }
7453
+ }
7454
+ }
7455
+ }
7456
+ }
7457
+ }
7458
+ },
7459
+ "/openapi.json": {
7460
+ get: {
7461
+ summary: "This OpenAPI document",
7462
+ operationId: "getOpenApi",
7463
+ responses: { "200": { description: "OpenAPI 3.1 document" } }
7464
+ }
7465
+ },
7466
+ "/api/v1/catalog": {
7467
+ get: {
7468
+ summary: "List the full hook catalog with versions and sha256",
7469
+ operationId: "getCatalog",
7470
+ responses: {
7471
+ "200": {
7472
+ description: "Catalog entries",
7473
+ content: {
7474
+ "application/json": {
7475
+ schema: {
7476
+ type: "object",
7477
+ properties: {
7478
+ hooks: {
7479
+ type: "array",
7480
+ items: {
7481
+ type: "object",
7482
+ properties: {
7483
+ name: { type: "string" },
7484
+ version: { type: "string" },
7485
+ sha256: { type: "string" },
7486
+ events: { type: "array", items: { type: "string" } }
7487
+ }
7488
+ }
7489
+ }
7490
+ }
7491
+ }
7492
+ }
7493
+ }
7494
+ }
7495
+ }
7496
+ }
7497
+ },
7498
+ "/api/v1/lock": {
7499
+ get: {
7500
+ summary: "Read the published hooks.lock with per-hook versions",
7501
+ operationId: "getLock",
7502
+ responses: { "200": { description: "The lock file" } }
7503
+ }
7504
+ },
7505
+ "/api/v1/hooks/{name}/{version}": {
7506
+ get: {
7507
+ summary: "Fetch one hook's manifest and script",
7508
+ operationId: "getHookArtifact",
7509
+ parameters: [
7510
+ { name: "name", in: "path", required: true, schema: { type: "string" } },
7511
+ { name: "version", in: "path", required: true, schema: { type: "string" } }
7512
+ ],
7513
+ responses: {
7514
+ "200": {
7515
+ description: "Hook artifact; x-hook-sha256 header carries the script hash"
7516
+ },
7517
+ "400": { description: "Invalid URL encoding or semver version" },
7518
+ "404": { description: "Hook not found locally" }
7519
+ }
7520
+ }
7521
+ },
7522
+ "/api/v1/hooks": {
7523
+ put: {
7524
+ summary: "Publish/retrust a hook (requires the API key)",
7525
+ operationId: "putHook",
7526
+ requestBody: {
7527
+ required: true,
7528
+ content: {
7529
+ "application/json": {
7530
+ schema: {
7531
+ type: "object",
7532
+ properties: { name: { type: "string" }, version: { type: "string" } }
7533
+ }
7534
+ }
7535
+ }
7536
+ },
7537
+ responses: {
7538
+ "200": { description: "Hook retrusted and pin updated" },
7539
+ "401": { description: "Missing or invalid API key" },
7540
+ "404": { description: "Hook not found in local store" },
7541
+ "409": { description: "Version mismatch with the local store" }
7542
+ },
7543
+ security: [{ apiKey: [] }]
7544
+ }
7545
+ }
7546
+ },
7547
+ components: {
7548
+ securitySchemes: {
7549
+ apiKey: {
7550
+ type: "http",
7551
+ scheme: "bearer",
7552
+ description: "The registry API key (HASNA_HOOKS_API_KEY / HOOKS_API_KEY)."
7553
+ }
7554
+ }
7555
+ }
7556
+ };
7557
+ });
7558
+
7117
7559
  // src/serve.ts
7118
7560
  var exports_serve = {};
7119
7561
  __export(exports_serve, {
7120
- startServeServer: () => startServeServer,
7121
- handleServeRequest: () => handleServeRequest,
7122
- SERVE_SERVICE_NAME: () => SERVE_SERVICE_NAME,
7562
+ DEFAULT_SERVE_PORT: () => DEFAULT_SERVE_PORT,
7123
7563
  SERVE_HOST: () => SERVE_HOST,
7124
- DEFAULT_SERVE_PORT: () => DEFAULT_SERVE_PORT
7564
+ SERVE_SERVICE_NAME: () => SERVE_SERVICE_NAME,
7565
+ handleServeRequest: () => handleServeRequest,
7566
+ startServeServer: () => startServeServer
7125
7567
  });
7568
+ import { readFileSync as readFileSync10 } from "fs";
7569
+ import { dirname as dirname6, join as join12 } from "path";
7570
+ import { fileURLToPath as fileURLToPath3 } from "url";
7571
+ function packageVersion() {
7572
+ try {
7573
+ const pkgPath = join12(dirname6(fileURLToPath3(import.meta.url)), "..", "package.json");
7574
+ return JSON.parse(readFileSync10(pkgPath, "utf-8")).version || "0.0.0";
7575
+ } catch {
7576
+ return "0.0.0";
7577
+ }
7578
+ }
7126
7579
  async function buildCatalog() {
7127
7580
  const byName = new Map;
7128
7581
  for (const meta of HOOKS) {
@@ -7195,6 +7648,20 @@ function handleServeRequest(req, apiKey) {
7195
7648
  if (url.pathname === "/health" && req.method === "GET") {
7196
7649
  return Promise.resolve(json({ status: "ok", name: SERVE_SERVICE_NAME }));
7197
7650
  }
7651
+ if (url.pathname === "/ready" && req.method === "GET") {
7652
+ try {
7653
+ readLock();
7654
+ return Promise.resolve(json({ status: "ready", name: SERVE_SERVICE_NAME }));
7655
+ } catch {
7656
+ return Promise.resolve(json({ status: "not-ready", name: SERVE_SERVICE_NAME }, 503));
7657
+ }
7658
+ }
7659
+ if (url.pathname === "/version" && req.method === "GET") {
7660
+ return Promise.resolve(json({ version: packageVersion() }));
7661
+ }
7662
+ if (url.pathname === "/openapi.json" && req.method === "GET") {
7663
+ return Promise.resolve(json(openApiDocument));
7664
+ }
7198
7665
  if (url.pathname === "/api/v1/catalog" && req.method === "GET") {
7199
7666
  return buildCatalog().then((catalog) => json({ hooks: catalog }));
7200
7667
  }
@@ -7273,6 +7740,7 @@ var init_serve = __esm(() => {
7273
7740
  init_store();
7274
7741
  init_resolve();
7275
7742
  init_config();
7743
+ init_openapi();
7276
7744
  init_semver();
7277
7745
  if (false) {}
7278
7746
  });
@@ -7361,21 +7829,21 @@ var CF_API = "https://api.cloudflare.com/client/v4";
7361
7829
  // src/mcp/server.ts
7362
7830
  var exports_server = {};
7363
7831
  __export(exports_server, {
7364
- startStdioServer: () => startStdioServer,
7365
- startSSEServer: () => startSSEServer,
7366
- sseAuthToken: () => sseAuthToken,
7367
- createHooksServer: () => createHooksServer,
7832
+ MCP_PORT: () => MCP_PORT,
7368
7833
  MCP_SSE_HOST: () => MCP_SSE_HOST,
7369
- MCP_PORT: () => MCP_PORT
7834
+ createHooksServer: () => createHooksServer,
7835
+ sseAuthToken: () => sseAuthToken,
7836
+ startSSEServer: () => startSSEServer,
7837
+ startStdioServer: () => startStdioServer
7370
7838
  });
7371
7839
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7372
7840
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
7373
7841
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7374
7842
  import { z as z2 } from "zod";
7375
7843
  import { createServer } from "http";
7376
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
7377
- import { join as join11, dirname as dirname6 } from "path";
7378
- import { fileURLToPath as fileURLToPath3 } from "url";
7844
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
7845
+ import { join as join13, dirname as dirname7 } from "path";
7846
+ import { fileURLToPath as fileURLToPath4 } from "url";
7379
7847
  async function runVerifiedHook(name, stdin, timeoutMs) {
7380
7848
  const { resolveHook: resolveHook2, resolveScriptPath: resolveScriptPath2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
7381
7849
  const resolved = resolveHook2(name);
@@ -7383,7 +7851,7 @@ async function runVerifiedHook(name, stdin, timeoutMs) {
7383
7851
  if (!resolved || !hookScript || !existsSync12(hookScript)) {
7384
7852
  throw new Error(`Hook '${name}' not found`);
7385
7853
  }
7386
- const content = readFileSync9(hookScript);
7854
+ const content = readFileSync11(hookScript);
7387
7855
  const sha = sha256Of(content);
7388
7856
  const check = checkScriptHash(name, sha);
7389
7857
  if (!check.ok) {
@@ -7608,14 +8076,14 @@ function createHooksServer() {
7608
8076
  continue;
7609
8077
  }
7610
8078
  const hookDir = getHookPath(name);
7611
- if (!existsSync12(join11(hookDir, "src", "hook.ts"))) {
8079
+ if (!existsSync12(join13(hookDir, "src", "hook.ts"))) {
7612
8080
  issues.push({ hook: name, issue: "Missing src/hook.ts in package", severity: "error" });
7613
8081
  hookHealthy = false;
7614
8082
  }
7615
8083
  }
7616
8084
  if (meta && settingsExist) {
7617
8085
  try {
7618
- const settings = JSON.parse(readFileSync9(settingsPath, "utf-8"));
8086
+ const settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
7619
8087
  if (!hookRegisteredInSettings(settings, name, meta.event, meta.matcher)) {
7620
8088
  const eventName = meta.event.split(":")[0];
7621
8089
  issues.push({ hook: name, issue: `Not registered under correct event (${eventName})`, severity: "error" });
@@ -7645,10 +8113,10 @@ function createHooksServer() {
7645
8113
  return { content: [{ type: "text", text: JSON.stringify({ error: `Hook '${name}' not found` }) }] };
7646
8114
  }
7647
8115
  const hookPath = getHookPath(name);
7648
- const readmePath = join11(hookPath, "README.md");
8116
+ const readmePath = join13(hookPath, "README.md");
7649
8117
  let readme = "";
7650
8118
  if (existsSync12(readmePath)) {
7651
- readme = readFileSync9(readmePath, "utf-8");
8119
+ readme = readFileSync11(readmePath, "utf-8");
7652
8120
  }
7653
8121
  if (verbose) {
7654
8122
  return { content: [{ type: "text", text: JSON.stringify({ ...meta, readme }) }] };
@@ -7978,7 +8446,7 @@ function createHooksServer() {
7978
8446
  let settings = {};
7979
8447
  try {
7980
8448
  if (existsSync12(settingsPath))
7981
- settings = JSON.parse(readFileSync9(settingsPath, "utf-8"));
8449
+ settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
7982
8450
  } catch {}
7983
8451
  if (!settings.hooks)
7984
8452
  settings.hooks = {};
@@ -7987,8 +8455,8 @@ function createHooksServer() {
7987
8455
  disabled.push(name);
7988
8456
  settings.hooks.__disabled = disabled;
7989
8457
  const { writeFileSync: writeFileSync7, mkdirSync: mkdirSync8 } = await import("fs");
7990
- const { dirname: dirname7 } = await import("path");
7991
- mkdirSync8(dirname7(settingsPath), { recursive: true });
8458
+ const { dirname: dirname8 } = await import("path");
8459
+ mkdirSync8(dirname8(settingsPath), { recursive: true });
7992
8460
  writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + `
7993
8461
  `);
7994
8462
  return { content: [{ type: "text", text: JSON.stringify({ hook: name, disabled: true, scope }) }] };
@@ -8001,7 +8469,7 @@ function createHooksServer() {
8001
8469
  let settings = {};
8002
8470
  try {
8003
8471
  if (existsSync12(settingsPath))
8004
- settings = JSON.parse(readFileSync9(settingsPath, "utf-8"));
8472
+ settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
8005
8473
  } catch {}
8006
8474
  if (settings.hooks?.__disabled) {
8007
8475
  settings.hooks.__disabled = settings.hooks.__disabled.filter((n) => n !== name);
@@ -8361,13 +8829,13 @@ var init_server = __esm(() => {
8361
8829
  init_store();
8362
8830
  init_redact();
8363
8831
  init_storage();
8364
- __dirname3 = dirname6(fileURLToPath3(import.meta.url));
8832
+ __dirname3 = dirname7(fileURLToPath4(import.meta.url));
8365
8833
  pkg = { name: "@hasna/hooks", version: "0.0.0" };
8366
8834
  try {
8367
8835
  for (const rel of ["../../package.json", "../package.json", "../../../package.json"]) {
8368
- const p = join11(__dirname3, rel);
8836
+ const p = join13(__dirname3, rel);
8369
8837
  if (existsSync12(p)) {
8370
- pkg = JSON.parse(readFileSync9(p, "utf-8"));
8838
+ pkg = JSON.parse(readFileSync11(p, "utf-8"));
8371
8839
  break;
8372
8840
  }
8373
8841
  }
@@ -8379,14 +8847,14 @@ var init_server = __esm(() => {
8379
8847
  // src/mcp/http.ts
8380
8848
  var exports_http = {};
8381
8849
  __export(exports_http, {
8382
- startMcpHttpServer: () => startMcpHttpServer,
8383
- resolveMcpHttpPort: () => resolveMcpHttpPort,
8384
- isHttpMode: () => isHttpMode,
8385
- healthPayload: () => healthPayload,
8386
- handleMcpRequest: () => handleMcpRequest,
8387
- MCP_SERVICE_NAME: () => MCP_SERVICE_NAME,
8850
+ DEFAULT_MCP_HTTP_PORT: () => DEFAULT_MCP_HTTP_PORT,
8388
8851
  MCP_HTTP_HOST: () => MCP_HTTP_HOST,
8389
- DEFAULT_MCP_HTTP_PORT: () => DEFAULT_MCP_HTTP_PORT
8852
+ MCP_SERVICE_NAME: () => MCP_SERVICE_NAME,
8853
+ handleMcpRequest: () => handleMcpRequest,
8854
+ healthPayload: () => healthPayload,
8855
+ isHttpMode: () => isHttpMode,
8856
+ resolveMcpHttpPort: () => resolveMcpHttpPort,
8857
+ startMcpHttpServer: () => startMcpHttpServer
8390
8858
  });
8391
8859
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
8392
8860
  function isHttpMode(args) {
@@ -9638,9 +10106,9 @@ var {
9638
10106
 
9639
10107
  // src/cli/index.tsx
9640
10108
  import chalk2 from "chalk";
9641
- import { existsSync as existsSync13, readFileSync as readFileSync10, rmSync as rmSync5 } from "fs";
9642
- import { join as join12, dirname as dirname7 } from "path";
9643
- import { fileURLToPath as fileURLToPath4 } from "url";
10109
+ import { existsSync as existsSync13, readFileSync as readFileSync12, rmSync as rmSync5 } from "fs";
10110
+ import { join as join14, dirname as dirname8 } from "path";
10111
+ import { fileURLToPath as fileURLToPath5 } from "url";
9644
10112
 
9645
10113
  // src/cli/components/App.tsx
9646
10114
  import { useState as useState7 } from "react";
@@ -10051,29 +10519,29 @@ function SelectInput({ items = [], isFocused = true, initialIndex = 0, indicator
10051
10519
  var SelectInput_default = SelectInput;
10052
10520
  // src/cli/components/Header.tsx
10053
10521
  import { Box as Box3, Text as Text3 } from "ink";
10054
- import { jsxDEV } from "react/jsx-dev-runtime";
10522
+ import { jsx, jsxs } from "react/jsx-runtime";
10055
10523
  function Header({ title = "Hooks", subtitle }) {
10056
- return /* @__PURE__ */ jsxDEV(Box3, {
10524
+ return /* @__PURE__ */ jsxs(Box3, {
10057
10525
  flexDirection: "column",
10058
10526
  marginBottom: 1,
10059
10527
  children: [
10060
- /* @__PURE__ */ jsxDEV(Text3, {
10528
+ /* @__PURE__ */ jsx(Text3, {
10061
10529
  bold: true,
10062
10530
  color: "cyan",
10063
10531
  children: title
10064
- }, undefined, false, undefined, this),
10065
- subtitle && /* @__PURE__ */ jsxDEV(Text3, {
10532
+ }),
10533
+ subtitle && /* @__PURE__ */ jsx(Text3, {
10066
10534
  dimColor: true,
10067
10535
  children: subtitle
10068
- }, undefined, false, undefined, this)
10536
+ })
10069
10537
  ]
10070
- }, undefined, true, undefined, this);
10538
+ });
10071
10539
  }
10072
10540
 
10073
10541
  // src/cli/components/CategorySelect.tsx
10074
10542
  import { Box as Box4, Text as Text4 } from "ink";
10075
10543
  init_registry();
10076
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
10544
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
10077
10545
  function CategorySelect({ onSelect, onBack }) {
10078
10546
  const items = CATEGORIES.map((cat) => ({
10079
10547
  label: `${cat} (${getHooksByCategory(cat).length})`,
@@ -10089,28 +10557,28 @@ function CategorySelect({ onSelect, onBack }) {
10089
10557
  onSelect(item.value);
10090
10558
  }
10091
10559
  };
10092
- return /* @__PURE__ */ jsxDEV2(Box4, {
10560
+ return /* @__PURE__ */ jsxs2(Box4, {
10093
10561
  flexDirection: "column",
10094
10562
  children: [
10095
- /* @__PURE__ */ jsxDEV2(Box4, {
10563
+ /* @__PURE__ */ jsx2(Box4, {
10096
10564
  marginBottom: 1,
10097
- children: /* @__PURE__ */ jsxDEV2(Text4, {
10565
+ children: /* @__PURE__ */ jsx2(Text4, {
10098
10566
  bold: true,
10099
10567
  children: "Select a category:"
10100
- }, undefined, false, undefined, this)
10101
- }, undefined, false, undefined, this),
10102
- /* @__PURE__ */ jsxDEV2(SelectInput_default, {
10568
+ })
10569
+ }),
10570
+ /* @__PURE__ */ jsx2(SelectInput_default, {
10103
10571
  items,
10104
10572
  onSelect: handleSelect
10105
- }, undefined, false, undefined, this)
10573
+ })
10106
10574
  ]
10107
- }, undefined, true, undefined, this);
10575
+ });
10108
10576
  }
10109
10577
 
10110
10578
  // src/cli/components/DataTable.tsx
10111
10579
  import { useState as useState2 } from "react";
10112
10580
  import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
10113
- import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
10581
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
10114
10582
  var COL_CHECK = 5;
10115
10583
  var COL_NAME = 18;
10116
10584
  var COL_VERSION = 9;
@@ -10145,18 +10613,18 @@ function DataTable({
10145
10613
  onBack();
10146
10614
  }
10147
10615
  });
10148
- return /* @__PURE__ */ jsxDEV3(Box5, {
10616
+ return /* @__PURE__ */ jsxs3(Box5, {
10149
10617
  flexDirection: "column",
10150
10618
  children: [
10151
- /* @__PURE__ */ jsxDEV3(Box5, {
10619
+ /* @__PURE__ */ jsx3(Box5, {
10152
10620
  marginBottom: 1,
10153
- children: /* @__PURE__ */ jsxDEV3(Text5, {
10621
+ children: /* @__PURE__ */ jsx3(Text5, {
10154
10622
  bold: true,
10155
10623
  children: "Select hooks (up/down to navigate, space/enter to toggle, esc to go back):"
10156
- }, undefined, false, undefined, this)
10157
- }, undefined, false, undefined, this),
10158
- /* @__PURE__ */ jsxDEV3(Box5, {
10159
- children: /* @__PURE__ */ jsxDEV3(Text5, {
10624
+ })
10625
+ }),
10626
+ /* @__PURE__ */ jsx3(Box5, {
10627
+ children: /* @__PURE__ */ jsxs3(Text5, {
10160
10628
  dimColor: true,
10161
10629
  children: [
10162
10630
  pad("", COL_CHECK),
@@ -10165,11 +10633,11 @@ function DataTable({
10165
10633
  pad("Event", COL_EVENT),
10166
10634
  "Description"
10167
10635
  ]
10168
- }, undefined, true, undefined, this)
10169
- }, undefined, false, undefined, this),
10170
- /* @__PURE__ */ jsxDEV3(Box5, {
10636
+ })
10637
+ }),
10638
+ /* @__PURE__ */ jsx3(Box5, {
10171
10639
  marginBottom: 0,
10172
- children: /* @__PURE__ */ jsxDEV3(Text5, {
10640
+ children: /* @__PURE__ */ jsxs3(Text5, {
10173
10641
  dimColor: true,
10174
10642
  children: [
10175
10643
  pad("\u2500".repeat(COL_CHECK - 1), COL_CHECK),
@@ -10178,14 +10646,14 @@ function DataTable({
10178
10646
  pad("\u2500".repeat(COL_EVENT - 1), COL_EVENT),
10179
10647
  "\u2500".repeat(COL_DESC)
10180
10648
  ]
10181
- }, undefined, true, undefined, this)
10182
- }, undefined, false, undefined, this),
10649
+ })
10650
+ }),
10183
10651
  hooks.map((hook, i) => {
10184
10652
  const isHighlighted = i === cursor;
10185
10653
  const isSelected = selected.has(hook.name);
10186
10654
  const check = isSelected ? "[x]" : "[ ]";
10187
- return /* @__PURE__ */ jsxDEV3(Box5, {
10188
- children: /* @__PURE__ */ jsxDEV3(Text5, {
10655
+ return /* @__PURE__ */ jsx3(Box5, {
10656
+ children: /* @__PURE__ */ jsxs3(Text5, {
10189
10657
  bold: isHighlighted,
10190
10658
  inverse: isHighlighted,
10191
10659
  children: [
@@ -10195,62 +10663,62 @@ function DataTable({
10195
10663
  pad(hook.event, COL_EVENT),
10196
10664
  truncate2(hook.description, COL_DESC)
10197
10665
  ]
10198
- }, undefined, true, undefined, this)
10199
- }, hook.name, false, undefined, this);
10666
+ })
10667
+ }, hook.name);
10200
10668
  }),
10201
- /* @__PURE__ */ jsxDEV3(Box5, {
10669
+ /* @__PURE__ */ jsxs3(Box5, {
10202
10670
  marginTop: 1,
10203
10671
  flexDirection: "row",
10204
10672
  gap: 2,
10205
10673
  children: [
10206
- /* @__PURE__ */ jsxDEV3(Text5, {
10674
+ /* @__PURE__ */ jsx3(Text5, {
10207
10675
  dimColor: true,
10208
10676
  children: "[esc] Back"
10209
- }, undefined, false, undefined, this),
10210
- selected.size > 0 && /* @__PURE__ */ jsxDEV3(Text5, {
10677
+ }),
10678
+ selected.size > 0 && /* @__PURE__ */ jsxs3(Text5, {
10211
10679
  color: "green",
10212
10680
  bold: true,
10213
10681
  children: [
10214
10682
  selected.size,
10215
10683
  " selected"
10216
10684
  ]
10217
- }, undefined, true, undefined, this)
10685
+ })
10218
10686
  ]
10219
- }, undefined, true, undefined, this),
10220
- selected.size > 0 && /* @__PURE__ */ jsxDEV3(Box5, {
10687
+ }),
10688
+ selected.size > 0 && /* @__PURE__ */ jsx3(Box5, {
10221
10689
  marginTop: 0,
10222
- children: /* @__PURE__ */ jsxDEV3(Text5, {
10690
+ children: /* @__PURE__ */ jsxs3(Text5, {
10223
10691
  dimColor: true,
10224
10692
  children: [
10225
10693
  "Selected: ",
10226
10694
  Array.from(selected).join(", ")
10227
10695
  ]
10228
- }, undefined, true, undefined, this)
10229
- }, undefined, false, undefined, this),
10230
- selected.size > 0 && /* @__PURE__ */ jsxDEV3(Box5, {
10696
+ })
10697
+ }),
10698
+ selected.size > 0 && /* @__PURE__ */ jsx3(Box5, {
10231
10699
  marginTop: 1,
10232
- children: /* @__PURE__ */ jsxDEV3(Text5, {
10700
+ children: /* @__PURE__ */ jsxs3(Text5, {
10233
10701
  children: [
10234
10702
  "Press",
10235
10703
  " ",
10236
- /* @__PURE__ */ jsxDEV3(Text5, {
10704
+ /* @__PURE__ */ jsx3(Text5, {
10237
10705
  bold: true,
10238
10706
  color: "cyan",
10239
10707
  children: "i"
10240
- }, undefined, false, undefined, this),
10708
+ }),
10241
10709
  " ",
10242
10710
  "to install selected (",
10243
10711
  selected.size,
10244
10712
  ")"
10245
10713
  ]
10246
- }, undefined, true, undefined, this)
10247
- }, undefined, false, undefined, this)
10714
+ })
10715
+ })
10248
10716
  ]
10249
- }, undefined, true, undefined, this);
10717
+ });
10250
10718
  }
10251
10719
 
10252
10720
  // src/cli/components/HookSelect.tsx
10253
- import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
10721
+ import { jsx as jsx4 } from "react/jsx-runtime";
10254
10722
  function HookSelect({
10255
10723
  hooks,
10256
10724
  selected,
@@ -10258,13 +10726,13 @@ function HookSelect({
10258
10726
  onConfirm,
10259
10727
  onBack
10260
10728
  }) {
10261
- return /* @__PURE__ */ jsxDEV4(DataTable, {
10729
+ return /* @__PURE__ */ jsx4(DataTable, {
10262
10730
  hooks,
10263
10731
  selected,
10264
10732
  onToggle,
10265
10733
  onConfirm,
10266
10734
  onBack
10267
- }, undefined, false, undefined, this);
10735
+ });
10268
10736
  }
10269
10737
 
10270
10738
  // src/cli/components/SearchView.tsx
@@ -10365,7 +10833,7 @@ var build_default = TextInput;
10365
10833
 
10366
10834
  // src/cli/components/SearchView.tsx
10367
10835
  init_registry();
10368
- import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
10836
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
10369
10837
  function SearchView({
10370
10838
  selected,
10371
10839
  onToggle,
@@ -10394,55 +10862,55 @@ function SearchView({
10394
10862
  setMode("select");
10395
10863
  }
10396
10864
  });
10397
- return /* @__PURE__ */ jsxDEV5(Box6, {
10865
+ return /* @__PURE__ */ jsxs4(Box6, {
10398
10866
  flexDirection: "column",
10399
10867
  children: [
10400
- /* @__PURE__ */ jsxDEV5(Box6, {
10868
+ /* @__PURE__ */ jsxs4(Box6, {
10401
10869
  marginBottom: 1,
10402
10870
  children: [
10403
- /* @__PURE__ */ jsxDEV5(Text7, {
10871
+ /* @__PURE__ */ jsx5(Text7, {
10404
10872
  bold: true,
10405
10873
  children: "Search: "
10406
- }, undefined, false, undefined, this),
10407
- mode === "search" && /* @__PURE__ */ jsxDEV5(build_default, {
10874
+ }),
10875
+ mode === "search" && /* @__PURE__ */ jsx5(build_default, {
10408
10876
  value: query,
10409
10877
  onChange: setQuery,
10410
10878
  placeholder: "Type to search hooks..."
10411
- }, undefined, false, undefined, this),
10412
- mode === "select" && /* @__PURE__ */ jsxDEV5(Text7, {
10879
+ }),
10880
+ mode === "select" && /* @__PURE__ */ jsx5(Text7, {
10413
10881
  children: query
10414
- }, undefined, false, undefined, this)
10882
+ })
10415
10883
  ]
10416
- }, undefined, true, undefined, this),
10417
- query.length < 2 && /* @__PURE__ */ jsxDEV5(Text7, {
10884
+ }),
10885
+ query.length < 2 && /* @__PURE__ */ jsx5(Text7, {
10418
10886
  dimColor: true,
10419
10887
  children: "Type at least 2 characters to search"
10420
- }, undefined, false, undefined, this),
10421
- query.length >= 2 && results.length === 0 && /* @__PURE__ */ jsxDEV5(Text7, {
10888
+ }),
10889
+ query.length >= 2 && results.length === 0 && /* @__PURE__ */ jsxs4(Text7, {
10422
10890
  dimColor: true,
10423
10891
  children: [
10424
10892
  'No hooks found for "',
10425
10893
  query,
10426
10894
  '"'
10427
10895
  ]
10428
- }, undefined, true, undefined, this),
10429
- results.length > 0 && mode === "search" && /* @__PURE__ */ jsxDEV5(Text7, {
10896
+ }),
10897
+ results.length > 0 && mode === "search" && /* @__PURE__ */ jsxs4(Text7, {
10430
10898
  dimColor: true,
10431
10899
  children: [
10432
10900
  "Found ",
10433
10901
  results.length,
10434
10902
  " hook(s) \u2014 press down arrow to browse results"
10435
10903
  ]
10436
- }, undefined, true, undefined, this),
10437
- results.length > 0 && mode === "select" && /* @__PURE__ */ jsxDEV5(DataTable, {
10904
+ }),
10905
+ results.length > 0 && mode === "select" && /* @__PURE__ */ jsx5(DataTable, {
10438
10906
  hooks: results,
10439
10907
  selected,
10440
10908
  onToggle,
10441
10909
  onConfirm,
10442
10910
  onBack: () => setMode("search")
10443
- }, undefined, false, undefined, this)
10911
+ })
10444
10912
  ]
10445
- }, undefined, true, undefined, this);
10913
+ });
10446
10914
  }
10447
10915
 
10448
10916
  // src/cli/components/InstallProgress.tsx
@@ -10474,7 +10942,7 @@ var build_default2 = Spinner;
10474
10942
  // src/cli/components/InstallProgress.tsx
10475
10943
  init_installer();
10476
10944
  init_registry();
10477
- import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
10945
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
10478
10946
  function InstallProgress({
10479
10947
  hooks,
10480
10948
  overwrite = false,
@@ -10498,55 +10966,55 @@ function InstallProgress({
10498
10966
  };
10499
10967
  install();
10500
10968
  }, [hooks, overwrite]);
10501
- return /* @__PURE__ */ jsxDEV6(Box7, {
10969
+ return /* @__PURE__ */ jsxs5(Box7, {
10502
10970
  flexDirection: "column",
10503
10971
  children: [
10504
- /* @__PURE__ */ jsxDEV6(Box7, {
10972
+ /* @__PURE__ */ jsx6(Box7, {
10505
10973
  marginBottom: 1,
10506
- children: /* @__PURE__ */ jsxDEV6(Text9, {
10974
+ children: /* @__PURE__ */ jsx6(Text9, {
10507
10975
  bold: true,
10508
10976
  children: installing ? `Installing hooks (${current + 1}/${hooks.length})...` : "Installation complete!"
10509
- }, undefined, false, undefined, this)
10510
- }, undefined, false, undefined, this),
10977
+ })
10978
+ }),
10511
10979
  hooks.map((name, i) => {
10512
10980
  const result = results[i];
10513
10981
  const isCurrent = i === current && installing;
10514
10982
  const meta = getHook(name);
10515
- return /* @__PURE__ */ jsxDEV6(Box7, {
10983
+ return /* @__PURE__ */ jsxs5(Box7, {
10516
10984
  flexDirection: "column",
10517
10985
  children: [
10518
- isCurrent && !result && /* @__PURE__ */ jsxDEV6(Text9, {
10986
+ isCurrent && !result && /* @__PURE__ */ jsxs5(Text9, {
10519
10987
  color: "cyan",
10520
10988
  children: [
10521
- /* @__PURE__ */ jsxDEV6(build_default2, {
10989
+ /* @__PURE__ */ jsx6(build_default2, {
10522
10990
  type: "dots"
10523
- }, undefined, false, undefined, this),
10991
+ }),
10524
10992
  " ",
10525
10993
  name
10526
10994
  ]
10527
- }, undefined, true, undefined, this),
10528
- result?.success && /* @__PURE__ */ jsxDEV6(Box7, {
10995
+ }),
10996
+ result?.success && /* @__PURE__ */ jsxs5(Box7, {
10529
10997
  flexDirection: "column",
10530
10998
  children: [
10531
- /* @__PURE__ */ jsxDEV6(Text9, {
10999
+ /* @__PURE__ */ jsxs5(Text9, {
10532
11000
  color: "green",
10533
11001
  children: [
10534
11002
  "\u2713",
10535
11003
  " ",
10536
11004
  name
10537
11005
  ]
10538
- }, undefined, true, undefined, this),
10539
- meta && /* @__PURE__ */ jsxDEV6(Text9, {
11006
+ }),
11007
+ meta && /* @__PURE__ */ jsxs5(Text9, {
10540
11008
  dimColor: true,
10541
11009
  children: [
10542
11010
  " ",
10543
11011
  meta.event,
10544
11012
  meta.matcher ? ` [${meta.matcher}]` : ""
10545
11013
  ]
10546
- }, undefined, true, undefined, this)
11014
+ })
10547
11015
  ]
10548
- }, undefined, true, undefined, this),
10549
- result && !result.success && /* @__PURE__ */ jsxDEV6(Text9, {
11016
+ }),
11017
+ result && !result.success && /* @__PURE__ */ jsxs5(Text9, {
10550
11018
  color: "red",
10551
11019
  children: [
10552
11020
  "\u2717",
@@ -10555,54 +11023,54 @@ function InstallProgress({
10555
11023
  " - ",
10556
11024
  result.error
10557
11025
  ]
10558
- }, undefined, true, undefined, this),
10559
- !isCurrent && !result && /* @__PURE__ */ jsxDEV6(Text9, {
11026
+ }),
11027
+ !isCurrent && !result && /* @__PURE__ */ jsxs5(Text9, {
10560
11028
  dimColor: true,
10561
11029
  children: [
10562
11030
  "\u25CB",
10563
11031
  " ",
10564
11032
  name
10565
11033
  ]
10566
- }, undefined, true, undefined, this)
11034
+ })
10567
11035
  ]
10568
- }, name, true, undefined, this);
11036
+ }, name);
10569
11037
  }),
10570
- !installing && /* @__PURE__ */ jsxDEV6(Box7, {
11038
+ !installing && /* @__PURE__ */ jsxs5(Box7, {
10571
11039
  marginTop: 1,
10572
11040
  flexDirection: "column",
10573
11041
  children: [
10574
- /* @__PURE__ */ jsxDEV6(Text9, {
11042
+ /* @__PURE__ */ jsxs5(Text9, {
10575
11043
  children: [
10576
- /* @__PURE__ */ jsxDEV6(Text9, {
11044
+ /* @__PURE__ */ jsxs5(Text9, {
10577
11045
  color: "green",
10578
11046
  children: [
10579
11047
  results.filter((r) => r.success).length,
10580
11048
  " installed"
10581
11049
  ]
10582
- }, undefined, true, undefined, this),
10583
- results.some((r) => !r.success) && /* @__PURE__ */ jsxDEV6(Text9, {
11050
+ }),
11051
+ results.some((r) => !r.success) && /* @__PURE__ */ jsxs5(Text9, {
10584
11052
  color: "red",
10585
11053
  children: [
10586
11054
  ", ",
10587
11055
  results.filter((r) => !r.success).length,
10588
11056
  " failed"
10589
11057
  ]
10590
- }, undefined, true, undefined, this)
11058
+ })
10591
11059
  ]
10592
- }, undefined, true, undefined, this),
10593
- /* @__PURE__ */ jsxDEV6(Text9, {
11060
+ }),
11061
+ /* @__PURE__ */ jsx6(Text9, {
10594
11062
  dimColor: true,
10595
11063
  children: "Hooks installed to .hooks/ and registered in ~/.claude/settings.json"
10596
- }, undefined, false, undefined, this)
11064
+ })
10597
11065
  ]
10598
- }, undefined, true, undefined, this)
11066
+ })
10599
11067
  ]
10600
- }, undefined, true, undefined, this);
11068
+ });
10601
11069
  }
10602
11070
 
10603
11071
  // src/cli/components/App.tsx
10604
11072
  init_registry();
10605
- import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
11073
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
10606
11074
  function App({ initialHooks, overwrite = false }) {
10607
11075
  const { exit } = useApp();
10608
11076
  const [view, setView] = useState7(initialHooks?.length ? "installing" : "main");
@@ -10649,44 +11117,44 @@ function App({ initialHooks, overwrite = false }) {
10649
11117
  setView(item.value);
10650
11118
  }
10651
11119
  };
10652
- return /* @__PURE__ */ jsxDEV7(Box8, {
11120
+ return /* @__PURE__ */ jsxs6(Box8, {
10653
11121
  flexDirection: "column",
10654
11122
  padding: 1,
10655
11123
  children: [
10656
- /* @__PURE__ */ jsxDEV7(Header, {
11124
+ /* @__PURE__ */ jsx7(Header, {
10657
11125
  title: "Hooks",
10658
11126
  subtitle: "Install Claude Code hooks for your project"
10659
- }, undefined, false, undefined, this),
10660
- view === "main" && /* @__PURE__ */ jsxDEV7(Box8, {
11127
+ }),
11128
+ view === "main" && /* @__PURE__ */ jsxs6(Box8, {
10661
11129
  flexDirection: "column",
10662
11130
  children: [
10663
- /* @__PURE__ */ jsxDEV7(Box8, {
11131
+ /* @__PURE__ */ jsx7(Box8, {
10664
11132
  marginBottom: 1,
10665
- children: /* @__PURE__ */ jsxDEV7(Text10, {
11133
+ children: /* @__PURE__ */ jsx7(Text10, {
10666
11134
  children: "What would you like to do?"
10667
- }, undefined, false, undefined, this)
10668
- }, undefined, false, undefined, this),
10669
- /* @__PURE__ */ jsxDEV7(SelectInput_default, {
11135
+ })
11136
+ }),
11137
+ /* @__PURE__ */ jsx7(SelectInput_default, {
10670
11138
  items: mainMenuItems,
10671
11139
  onSelect: handleMainSelect
10672
- }, undefined, false, undefined, this),
10673
- /* @__PURE__ */ jsxDEV7(Box8, {
11140
+ }),
11141
+ /* @__PURE__ */ jsx7(Box8, {
10674
11142
  marginTop: 1,
10675
- children: /* @__PURE__ */ jsxDEV7(Text10, {
11143
+ children: /* @__PURE__ */ jsx7(Text10, {
10676
11144
  dimColor: true,
10677
11145
  children: "Press q to quit"
10678
- }, undefined, false, undefined, this)
10679
- }, undefined, false, undefined, this)
11146
+ })
11147
+ })
10680
11148
  ]
10681
- }, undefined, true, undefined, this),
10682
- view === "browse" && !category && /* @__PURE__ */ jsxDEV7(CategorySelect, {
11149
+ }),
11150
+ view === "browse" && !category && /* @__PURE__ */ jsx7(CategorySelect, {
10683
11151
  onSelect: (cat) => {
10684
11152
  setCategory(cat);
10685
11153
  setView("hooks");
10686
11154
  },
10687
11155
  onBack: () => setView("main")
10688
- }, undefined, false, undefined, this),
10689
- view === "hooks" && category && /* @__PURE__ */ jsxDEV7(HookSelect, {
11156
+ }),
11157
+ view === "hooks" && category && /* @__PURE__ */ jsx7(HookSelect, {
10690
11158
  hooks: getHooksByCategory(category),
10691
11159
  selected,
10692
11160
  onToggle: handleToggle,
@@ -10695,57 +11163,57 @@ function App({ initialHooks, overwrite = false }) {
10695
11163
  setCategory(null);
10696
11164
  setView("browse");
10697
11165
  }
10698
- }, undefined, false, undefined, this),
10699
- view === "search" && /* @__PURE__ */ jsxDEV7(SearchView, {
11166
+ }),
11167
+ view === "search" && /* @__PURE__ */ jsx7(SearchView, {
10700
11168
  selected,
10701
11169
  onToggle: handleToggle,
10702
11170
  onConfirm: handleConfirm,
10703
11171
  onBack: () => setView("main")
10704
- }, undefined, false, undefined, this),
10705
- view === "installing" && /* @__PURE__ */ jsxDEV7(InstallProgress, {
11172
+ }),
11173
+ view === "installing" && /* @__PURE__ */ jsx7(InstallProgress, {
10706
11174
  hooks: Array.from(selected),
10707
11175
  overwrite,
10708
11176
  onComplete: handleComplete
10709
- }, undefined, false, undefined, this),
10710
- view === "done" && /* @__PURE__ */ jsxDEV7(Box8, {
11177
+ }),
11178
+ view === "done" && /* @__PURE__ */ jsxs6(Box8, {
10711
11179
  flexDirection: "column",
10712
11180
  children: [
10713
- /* @__PURE__ */ jsxDEV7(Box8, {
11181
+ /* @__PURE__ */ jsx7(Box8, {
10714
11182
  marginBottom: 1,
10715
- children: /* @__PURE__ */ jsxDEV7(Text10, {
11183
+ children: /* @__PURE__ */ jsx7(Text10, {
10716
11184
  bold: true,
10717
11185
  color: "green",
10718
11186
  children: "Installation complete!"
10719
- }, undefined, false, undefined, this)
10720
- }, undefined, false, undefined, this),
10721
- results.filter((r) => r.success).length > 0 && /* @__PURE__ */ jsxDEV7(Box8, {
11187
+ })
11188
+ }),
11189
+ results.filter((r) => r.success).length > 0 && /* @__PURE__ */ jsxs6(Box8, {
10722
11190
  flexDirection: "column",
10723
11191
  marginBottom: 1,
10724
11192
  children: [
10725
- /* @__PURE__ */ jsxDEV7(Text10, {
11193
+ /* @__PURE__ */ jsx7(Text10, {
10726
11194
  bold: true,
10727
11195
  children: "Installed:"
10728
- }, undefined, false, undefined, this),
10729
- results.filter((r) => r.success).map((r) => /* @__PURE__ */ jsxDEV7(Text10, {
11196
+ }),
11197
+ results.filter((r) => r.success).map((r) => /* @__PURE__ */ jsxs6(Text10, {
10730
11198
  color: "green",
10731
11199
  children: [
10732
11200
  "\u2713",
10733
11201
  " ",
10734
11202
  r.hook
10735
11203
  ]
10736
- }, r.hook, true, undefined, this))
11204
+ }, r.hook))
10737
11205
  ]
10738
- }, undefined, true, undefined, this),
10739
- results.filter((r) => !r.success).length > 0 && /* @__PURE__ */ jsxDEV7(Box8, {
11206
+ }),
11207
+ results.filter((r) => !r.success).length > 0 && /* @__PURE__ */ jsxs6(Box8, {
10740
11208
  flexDirection: "column",
10741
11209
  marginBottom: 1,
10742
11210
  children: [
10743
- /* @__PURE__ */ jsxDEV7(Text10, {
11211
+ /* @__PURE__ */ jsx7(Text10, {
10744
11212
  bold: true,
10745
11213
  color: "red",
10746
11214
  children: "Failed:"
10747
- }, undefined, false, undefined, this),
10748
- results.filter((r) => !r.success).map((r) => /* @__PURE__ */ jsxDEV7(Text10, {
11215
+ }),
11216
+ results.filter((r) => !r.success).map((r) => /* @__PURE__ */ jsxs6(Text10, {
10749
11217
  color: "red",
10750
11218
  children: [
10751
11219
  "\u2717",
@@ -10754,61 +11222,61 @@ function App({ initialHooks, overwrite = false }) {
10754
11222
  ": ",
10755
11223
  r.error
10756
11224
  ]
10757
- }, r.hook, true, undefined, this))
11225
+ }, r.hook))
10758
11226
  ]
10759
- }, undefined, true, undefined, this),
10760
- /* @__PURE__ */ jsxDEV7(Box8, {
11227
+ }),
11228
+ /* @__PURE__ */ jsxs6(Box8, {
10761
11229
  marginTop: 1,
10762
11230
  flexDirection: "column",
10763
11231
  children: [
10764
- /* @__PURE__ */ jsxDEV7(Text10, {
11232
+ /* @__PURE__ */ jsx7(Text10, {
10765
11233
  bold: true,
10766
11234
  children: "What happened:"
10767
- }, undefined, false, undefined, this),
10768
- /* @__PURE__ */ jsxDEV7(Text10, {
11235
+ }),
11236
+ /* @__PURE__ */ jsx7(Text10, {
10769
11237
  children: "1. Hook source copied to .hooks/"
10770
- }, undefined, false, undefined, this),
10771
- /* @__PURE__ */ jsxDEV7(Text10, {
11238
+ }),
11239
+ /* @__PURE__ */ jsx7(Text10, {
10772
11240
  children: "2. Hook registered in ~/.claude/settings.json"
10773
- }, undefined, false, undefined, this),
10774
- /* @__PURE__ */ jsxDEV7(Text10, {
11241
+ }),
11242
+ /* @__PURE__ */ jsx7(Text10, {
10775
11243
  children: "3. Ready to use in Claude Code sessions"
10776
- }, undefined, false, undefined, this)
11244
+ })
10777
11245
  ]
10778
- }, undefined, true, undefined, this),
10779
- /* @__PURE__ */ jsxDEV7(Box8, {
11246
+ }),
11247
+ /* @__PURE__ */ jsxs6(Box8, {
10780
11248
  marginTop: 1,
10781
11249
  flexDirection: "column",
10782
11250
  children: [
10783
- /* @__PURE__ */ jsxDEV7(Text10, {
11251
+ /* @__PURE__ */ jsx7(Text10, {
10784
11252
  bold: true,
10785
11253
  children: "Next steps:"
10786
- }, undefined, false, undefined, this),
10787
- /* @__PURE__ */ jsxDEV7(Text10, {
11254
+ }),
11255
+ /* @__PURE__ */ jsx7(Text10, {
10788
11256
  dimColor: true,
10789
11257
  children: " hooks list --registered Check active hooks"
10790
- }, undefined, false, undefined, this),
10791
- /* @__PURE__ */ jsxDEV7(Text10, {
11258
+ }),
11259
+ /* @__PURE__ */ jsxs6(Text10, {
10792
11260
  dimColor: true,
10793
11261
  children: [
10794
11262
  " hooks info ",
10795
11263
  "<name>",
10796
11264
  " View hook details"
10797
11265
  ]
10798
- }, undefined, true, undefined, this)
11266
+ })
10799
11267
  ]
10800
- }, undefined, true, undefined, this),
10801
- /* @__PURE__ */ jsxDEV7(Box8, {
11268
+ }),
11269
+ /* @__PURE__ */ jsx7(Box8, {
10802
11270
  marginTop: 1,
10803
- children: /* @__PURE__ */ jsxDEV7(Text10, {
11271
+ children: /* @__PURE__ */ jsx7(Text10, {
10804
11272
  dimColor: true,
10805
11273
  children: "Press q to exit"
10806
- }, undefined, false, undefined, this)
10807
- }, undefined, false, undefined, this)
11274
+ })
11275
+ })
10808
11276
  ]
10809
- }, undefined, true, undefined, this)
11277
+ })
10810
11278
  ]
10811
- }, undefined, true, undefined, this);
11279
+ });
10812
11280
  }
10813
11281
 
10814
11282
  // src/cli/index.tsx
@@ -10820,10 +11288,10 @@ init_manifest();
10820
11288
  init_resolve();
10821
11289
  init_store();
10822
11290
  init_semver();
10823
- import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
10824
- var __dirname4 = dirname7(fileURLToPath4(import.meta.url));
10825
- var pkgPath = existsSync13(join12(__dirname4, "..", "package.json")) ? join12(__dirname4, "..", "package.json") : join12(__dirname4, "..", "..", "package.json");
10826
- var pkg2 = JSON.parse(readFileSync10(pkgPath, "utf-8"));
11291
+ import { jsx as jsx8 } from "react/jsx-runtime";
11292
+ var __dirname4 = dirname8(fileURLToPath5(import.meta.url));
11293
+ var pkgPath = existsSync13(join14(__dirname4, "..", "package.json")) ? join14(__dirname4, "..", "package.json") : join14(__dirname4, "..", "..", "package.json");
11294
+ var pkg2 = JSON.parse(readFileSync12(pkgPath, "utf-8"));
10827
11295
  var program2 = new Command;
10828
11296
  function resolveScope(options) {
10829
11297
  if (options.project)
@@ -10857,7 +11325,7 @@ function projectLogRows2(rows) {
10857
11325
  function readToken(tokenFile) {
10858
11326
  if (tokenFile) {
10859
11327
  try {
10860
- return readFileSync10(tokenFile, "utf-8").trim();
11328
+ return readFileSync12(tokenFile, "utf-8").trim();
10861
11329
  } catch {
10862
11330
  return;
10863
11331
  }
@@ -10918,7 +11386,7 @@ function suggestHooks(name, max = 3) {
10918
11386
  }
10919
11387
  program2.name("hooks").description("Install hooks for AI coding agents").version(pkg2.version);
10920
11388
  program2.command("interactive", { isDefault: true }).alias("i").description("Interactive hook browser").action(() => {
10921
- render(/* @__PURE__ */ jsxDEV8(App, {}, undefined, false, undefined, this));
11389
+ render(/* @__PURE__ */ jsx8(App, {}));
10922
11390
  });
10923
11391
  program2.command("init").description("Register a new agent profile with a unique ID").option("-a, --agent <type>", "Agent type: claude, gemini, custom", "claude").option("-n, --name <name>", "Optional display name for the agent").option("-j, --json", "Output as JSON", false).option("--cloudflare", "Configure a remote registry (Cloudflare worker)", false).option("--api-url <url>", "Remote registry API URL (with --cloudflare)").option("--api-key <ref>", "Vault key NAME for the registry API key (never the value; with --cloudflare)").action(async (options) => {
10924
11392
  if (options.cloudflare) {
@@ -10994,7 +11462,7 @@ program2.command("run").argument("<hook>", "Hook to run").option("--profile <id>
10994
11462
  console.error(JSON.stringify({ error: `Hook script not found: ${hookScript ?? hook}` }));
10995
11463
  process.exit(1);
10996
11464
  }
10997
- const content = readFileSync10(hookScript);
11465
+ const content = readFileSync12(hookScript);
10998
11466
  const sha = sha256Of2(content);
10999
11467
  const check = checkScriptHash2(hook, sha);
11000
11468
  if (!check.ok) {
@@ -11100,7 +11568,7 @@ program2.command("run").argument("<hook>", "Hook to run").option("--profile <id>
11100
11568
  process.stderr.write(stderr);
11101
11569
  process.exit(exitCode);
11102
11570
  });
11103
- program2.command("install").alias("add").argument("[hooks...]", "Hooks to install").option("-o, --overwrite", "Overwrite existing hooks", false).option("-a, --all", "Install all available hooks", false).option("-c, --category <category>", "Install all hooks in a category").option("-g, --global", "Install globally (~/.claude/settings.json)", false).option("-p, --project", "Install for current project (.claude/settings.json)", false).option("-t, --target <target>", "Agent target: claude, gemini, codewith, all (default: claude)", "claude").option("--profile <id>", "Agent profile ID to scope hooks to").option("--dry-run", "Preview what would be installed without writing to settings", false).option("--apply-codewith", "Explicitly append Codewith TOML to a config file (prefer open-configs for managed configs)", false).option("--codewith-config <path>", "Explicit Codewith config path required with --apply-codewith").option("-j, --json", "Output as JSON", false).description("Install one or more hooks (registry names, local paths, git URLs, manifest URLs, or <name>@<version> pinned installs)").action(async (hooks, options) => {
11571
+ program2.command("install").alias("add").argument("[hooks...]", "Hooks to install").option("-o, --overwrite", "Overwrite existing hooks", false).option("-a, --all", "Install all available hooks", false).option("-c, --category <category>", "Install all hooks in a category").option("-g, --global", "Install globally (~/.claude/settings.json)", false).option("-p, --project", "Install for current project (.claude/settings.json)", false).option("-t, --target <target>", "Agent target: claude, gemini, codewith, all (default: claude)", "claude").option("--profile <id>", "Agent profile ID to scope hooks to").option("--dry-run", "Preview what would be installed without writing to settings", false).option("--apply-codewith", "Explicitly append Codewith TOML to a config file (prefer configs for managed configs)", false).option("--codewith-config <path>", "Explicit Codewith config path required with --apply-codewith").option("-j, --json", "Output as JSON", false).description("Install one or more hooks (registry names, local paths, git URLs, manifest URLs, or <name>@<version> pinned installs)").action(async (hooks, options) => {
11104
11572
  const scope = resolveScope(options);
11105
11573
  const target = resolveTarget(options);
11106
11574
  let toInstall = hooks;
@@ -11121,7 +11589,7 @@ program2.command("install").alias("add").argument("[hooks...]", "Hooks to instal
11121
11589
  toInstall = getHooksByCategory(category).map((h) => h.name);
11122
11590
  }
11123
11591
  if (toInstall.length === 0) {
11124
- render(/* @__PURE__ */ jsxDEV8(App, {}, undefined, false, undefined, this));
11592
+ render(/* @__PURE__ */ jsx8(App, {}));
11125
11593
  return;
11126
11594
  }
11127
11595
  if (options.applyCodewith && (target === "codewith" || target === "all") && !options.codewithConfig) {
@@ -11288,7 +11756,7 @@ Dry run \u2014 would install (${scope}, ${target}):
11288
11756
  return;
11289
11757
  }
11290
11758
  const successCount = results.filter((r) => r.success).length;
11291
- const settingsFile = target === "codewith" ? options.applyCodewith ? options.codewithConfig : "TOML fragment only (open-configs should apply)" : scope === "project" ? ".claude/settings.json" : "~/.claude/settings.json";
11759
+ const settingsFile = target === "codewith" ? options.applyCodewith ? options.codewithConfig : "TOML fragment only (configs should apply)" : scope === "project" ? ".claude/settings.json" : "~/.claude/settings.json";
11292
11760
  console.log(chalk2.bold(`
11293
11761
  Installing hooks (${scope}, ${target})...
11294
11762
  `));
@@ -11570,7 +12038,7 @@ program2.command("doctor").option("-g, --global", "Check global settings", false
11570
12038
  let wiringCount = 0;
11571
12039
  if (settingsExist) {
11572
12040
  try {
11573
- const settings = JSON.parse(readFileSync10(settingsPath, "utf-8"));
12041
+ const settings = JSON.parse(readFileSync12(settingsPath, "utf-8"));
11574
12042
  wiringCount = countSettingsWiring(settings);
11575
12043
  } catch {}
11576
12044
  }
@@ -11590,7 +12058,7 @@ program2.command("doctor").option("-g, --global", "Check global settings", false
11590
12058
  continue;
11591
12059
  }
11592
12060
  const hookDir = getHookPath(name);
11593
- const hookScript = join12(hookDir, "src", "hook.ts");
12061
+ const hookScript = join14(hookDir, "src", "hook.ts");
11594
12062
  if (!existsSync13(hookScript)) {
11595
12063
  issues.push({ hook: name, issue: "Missing src/hook.ts in package", severity: "error" });
11596
12064
  hookHealthy = false;
@@ -11598,7 +12066,7 @@ program2.command("doctor").option("-g, --global", "Check global settings", false
11598
12066
  }
11599
12067
  if (meta && settingsExist) {
11600
12068
  try {
11601
- const settings = JSON.parse(readFileSync10(settingsPath, "utf-8"));
12069
+ const settings = JSON.parse(readFileSync12(settingsPath, "utf-8"));
11602
12070
  if (!hookRegisteredInSettings(settings, name, meta.event, meta.matcher)) {
11603
12071
  const eventName = meta.event.split(":")[0];
11604
12072
  issues.push({ hook: name, issue: `Not registered under correct event (${eventName})`, severity: "error" });
@@ -11765,10 +12233,10 @@ program2.command("docs").argument("[hook]", "Hook name (shows general docs if om
11765
12233
  return;
11766
12234
  }
11767
12235
  const hookPath = getHookPath(hook);
11768
- const readmePath = join12(hookPath, "README.md");
12236
+ const readmePath = join14(hookPath, "README.md");
11769
12237
  let readme = "";
11770
12238
  if (existsSync13(readmePath)) {
11771
- readme = readFileSync10(readmePath, "utf-8");
12239
+ readme = readFileSync12(readmePath, "utf-8");
11772
12240
  }
11773
12241
  if (options.json) {
11774
12242
  console.log(JSON.stringify({ ...meta, readme }));
@@ -11822,7 +12290,7 @@ ${meta.displayName} v${meta.version}
11822
12290
  installation: {
11823
12291
  global: "hooks install gitguard",
11824
12292
  project: "hooks install gitguard --project",
11825
- codewith: "hooks install session-start --target codewith # emits TOML for open-configs to apply",
12293
+ codewith: "hooks install session-start --target codewith # emits TOML for configs to apply",
11826
12294
  category: 'hooks install --category "Git Safety"',
11827
12295
  all: "hooks install --all"
11828
12296
  },
@@ -11965,9 +12433,9 @@ program2.command("profile-import").argument("<file>", "JSON file to import profi
11965
12433
  if (file === "-") {
11966
12434
  raw = await new Response(Bun.stdin.stream()).text();
11967
12435
  } else {
11968
- const { readFileSync: readFileSync11 } = await import("fs");
12436
+ const { readFileSync: readFileSync13 } = await import("fs");
11969
12437
  try {
11970
- raw = readFileSync11(file, "utf-8");
12438
+ raw = readFileSync13(file, "utf-8");
11971
12439
  } catch {
11972
12440
  if (options.json) {
11973
12441
  console.log(JSON.stringify({ error: `Cannot read file: ${file}` }));