@hasna/hooks 0.6.6 → 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.
- package/README.md +1 -1
- package/bin/index.js +1024 -461
- package/bin/serve.js +626 -158
- package/dist/config.d.ts +7 -5
- package/dist/db/pg-migrations.d.ts +1 -1
- package/dist/index.js +898 -340
- package/dist/lib/hook-env.d.ts +60 -4
- package/dist/lib/manifest.d.ts +11 -0
- package/dist/lib/semver.d.ts +27 -0
- package/dist/openapi.d.ts +216 -0
- package/dist/storage.js +34 -34
- package/hooks/codewith-native-common.test.ts +25 -25
- package/hooks/codewith-native-common.ts +1 -1
- package/hooks/hook-autoformat/package.json +1 -1
- package/hooks/hook-desktopnotify/package.json +1 -1
- package/hooks/hook-envsetup/package.json +1 -1
- package/hooks/hook-knowledge-context/README.md +1 -1
- package/hooks/hook-knowledge-context/src/hook.test.ts +1 -1
- package/hooks/hook-permissionguard/package.json +1 -1
- package/hooks/hook-protectfiles/package.json +1 -1
- package/hooks/hook-spiral-detector/LICENSE +191 -0
- package/hooks/hook-spiral-detector/README.md +23 -0
- package/hooks/hook-spiral-detector/package.json +12 -0
- package/hooks/hook-spiral-detector/src/hook.test.ts +72 -0
- package/hooks/hook-spiral-detector/src/hook.ts +93 -0
- package/hooks/hook-workspace-repos-guard/README.md +4 -3
- package/hooks/hook-workspace-repos-guard/src/hook.test.ts +7 -7
- package/hooks/hook-workspace-repos-guard/src/hook.ts +5 -3
- package/hooks/mention-context/src/hasna-mention-context.py +2 -2
- package/hooks/mention-context/src/hasna-mention-warm.py +1 -1
- package/hooks/pre-bash/README.md +1 -1
- package/hooks/prompt-guard/README.md +1 -1
- package/hooks/session-start/README.md +1 -1
- package/hooks/stop-sync/README.md +1 -1
- package/hooks/worktree-guard/README.md +1 -1
- 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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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;
|
|
@@ -47,8 +49,8 @@ var __export = (target, all) => {
|
|
|
47
49
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
48
50
|
var __require = import.meta.require;
|
|
49
51
|
|
|
50
|
-
// node_modules/commander/lib/error.js
|
|
51
|
-
var require_error = __commonJS((exports)
|
|
52
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/error.js
|
|
53
|
+
var require_error = __commonJS(function(exports) {
|
|
52
54
|
class CommanderError extends Error {
|
|
53
55
|
constructor(exitCode, code, message) {
|
|
54
56
|
super(message);
|
|
@@ -71,8 +73,8 @@ var require_error = __commonJS((exports) => {
|
|
|
71
73
|
exports.InvalidArgumentError = InvalidArgumentError;
|
|
72
74
|
});
|
|
73
75
|
|
|
74
|
-
// node_modules/commander/lib/argument.js
|
|
75
|
-
var require_argument = __commonJS((exports)
|
|
76
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/argument.js
|
|
77
|
+
var require_argument = __commonJS(function(exports) {
|
|
76
78
|
var { InvalidArgumentError } = require_error();
|
|
77
79
|
|
|
78
80
|
class Argument {
|
|
@@ -150,8 +152,8 @@ var require_argument = __commonJS((exports) => {
|
|
|
150
152
|
exports.humanReadableArgName = humanReadableArgName;
|
|
151
153
|
});
|
|
152
154
|
|
|
153
|
-
// node_modules/commander/lib/help.js
|
|
154
|
-
var require_help = __commonJS((exports)
|
|
155
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/help.js
|
|
156
|
+
var require_help = __commonJS(function(exports) {
|
|
155
157
|
var { humanReadableArgName } = require_argument();
|
|
156
158
|
|
|
157
159
|
class Help {
|
|
@@ -399,8 +401,8 @@ var require_help = __commonJS((exports) => {
|
|
|
399
401
|
exports.Help = Help;
|
|
400
402
|
});
|
|
401
403
|
|
|
402
|
-
// node_modules/commander/lib/option.js
|
|
403
|
-
var require_option = __commonJS((exports)
|
|
404
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/option.js
|
|
405
|
+
var require_option = __commonJS(function(exports) {
|
|
404
406
|
var { InvalidArgumentError } = require_error();
|
|
405
407
|
|
|
406
408
|
class Option {
|
|
@@ -550,8 +552,8 @@ var require_option = __commonJS((exports) => {
|
|
|
550
552
|
exports.DualOptions = DualOptions;
|
|
551
553
|
});
|
|
552
554
|
|
|
553
|
-
// node_modules/commander/lib/suggestSimilar.js
|
|
554
|
-
var require_suggestSimilar = __commonJS((exports)
|
|
555
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/suggestSimilar.js
|
|
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)
|
|
@@ -623,8 +625,8 @@ var require_suggestSimilar = __commonJS((exports) => {
|
|
|
623
625
|
exports.suggestSimilar = suggestSimilar;
|
|
624
626
|
});
|
|
625
627
|
|
|
626
|
-
// node_modules/commander/lib/command.js
|
|
627
|
-
var require_command = __commonJS((exports)
|
|
628
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/command.js
|
|
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");
|
|
@@ -1866,8 +1868,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1866
1868
|
exports.Command = Command;
|
|
1867
1869
|
});
|
|
1868
1870
|
|
|
1869
|
-
// node_modules/commander/index.js
|
|
1870
|
-
var require_commander = __commonJS((exports)
|
|
1871
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/index.js
|
|
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",
|
|
@@ -2419,8 +2431,8 @@ var init_registry = __esm(() => {
|
|
|
2419
2431
|
];
|
|
2420
2432
|
});
|
|
2421
2433
|
|
|
2422
|
-
// node_modules/cli-spinners/spinners.json
|
|
2423
|
-
var require_spinners = __commonJS((exports, module)
|
|
2434
|
+
// ../../node_modules/.bun/cli-spinners@2.9.2/node_modules/cli-spinners/spinners.json
|
|
2435
|
+
var require_spinners = __commonJS(function(exports, module) {
|
|
2424
2436
|
module.exports = {
|
|
2425
2437
|
dots: {
|
|
2426
2438
|
interval: 80,
|
|
@@ -4045,8 +4057,8 @@ var require_spinners = __commonJS((exports, module) => {
|
|
|
4045
4057
|
};
|
|
4046
4058
|
});
|
|
4047
4059
|
|
|
4048
|
-
// node_modules/cli-spinners/index.js
|
|
4049
|
-
var require_cli_spinners = __commonJS((exports, module)
|
|
4060
|
+
// ../../node_modules/.bun/cli-spinners@2.9.2/node_modules/cli-spinners/index.js
|
|
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
|
-
|
|
4066
|
-
resolveApiUrl: () => resolveApiUrl,
|
|
4067
|
-
resolveApiKey: () => resolveApiKey,
|
|
4068
|
-
readConfig: () => readConfig,
|
|
4069
|
-
getLockPath: () => getLockPath,
|
|
4070
|
-
getHooksDataDir: () => getHooksDataDir,
|
|
4351
|
+
getConfigPath: () => getConfigPath,
|
|
4071
4352
|
getCustomHooksDir: () => getCustomHooksDir,
|
|
4072
|
-
|
|
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
|
|
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
|
|
4367
|
+
return join3(homedir2(), ".hasna", "hooks");
|
|
4082
4368
|
}
|
|
4083
4369
|
function getCustomHooksDir() {
|
|
4084
|
-
return
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
4396
|
+
mkdirSync(join3(path, ".."), { recursive: true });
|
|
4111
4397
|
writeFileSync(path, JSON.stringify(config, null, 2) + `
|
|
4112
4398
|
`, "utf-8");
|
|
4113
4399
|
return path;
|
|
@@ -4120,36 +4406,38 @@ function resolveApiUrl() {
|
|
|
4120
4406
|
return cfg.api_url ? cfg.api_url.replace(/\/+$/, "") : undefined;
|
|
4121
4407
|
}
|
|
4122
4408
|
function resolveApiKey() {
|
|
4123
|
-
return
|
|
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
|
-
var SEMVER_PATTERN;
|
|
4416
|
+
var NUMERIC_ID = "(0|[1-9]\\d{0,15})", ALNUM_ID = "\\d*[A-Za-z-][0-9A-Za-z-]*", SEMVER_PATTERN;
|
|
4129
4417
|
var init_semver = __esm(() => {
|
|
4130
|
-
SEMVER_PATTERN =
|
|
4418
|
+
SEMVER_PATTERN = new RegExp(`^${NUMERIC_ID}\\.${NUMERIC_ID}\\.${NUMERIC_ID}` + `(?:-(?:${NUMERIC_ID}|${ALNUM_ID})(?:\\.(?:${NUMERIC_ID}|${ALNUM_ID}))*)?` + `(?:\\+[0-9A-Za-z.-]+)?$`);
|
|
4131
4419
|
});
|
|
4132
4420
|
|
|
4133
4421
|
// src/lib/manifest.ts
|
|
4134
4422
|
var exports_manifest = {};
|
|
4135
4423
|
__export(exports_manifest, {
|
|
4136
|
-
|
|
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
|
-
|
|
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
|
|
4152
|
-
import { basename, dirname, isAbsolute, join as
|
|
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
|
|
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(
|
|
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:
|
|
4510
|
+
return { path: rel, content: readFileSync3(resolved, "utf-8") };
|
|
4223
4511
|
}
|
|
4224
4512
|
function customHookDir(name) {
|
|
4225
|
-
return
|
|
4513
|
+
return join4(getCustomHooksDir(), shortManifestName(name));
|
|
4226
4514
|
}
|
|
4227
4515
|
function readCustomManifest(name) {
|
|
4228
4516
|
const dir = customHookDir(name);
|
|
4229
|
-
const manifestPath =
|
|
4517
|
+
const manifestPath = join4(dir, "manifest.json");
|
|
4230
4518
|
if (!existsSync3(manifestPath))
|
|
4231
4519
|
return;
|
|
4232
4520
|
try {
|
|
4233
|
-
const manifest = parseManifest(
|
|
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:
|
|
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("/") ?
|
|
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(
|
|
4556
|
+
writeFileSync2(join4(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + `
|
|
4269
4557
|
`, "utf-8");
|
|
4270
4558
|
return { dir, scriptPath };
|
|
4271
4559
|
}
|
|
@@ -4289,7 +4577,8 @@ var init_manifest = __esm(() => {
|
|
|
4289
4577
|
script: z.string().min(1, "script must name a relative path or inline content"),
|
|
4290
4578
|
script_kind: z.enum(["inline", "file"]).optional(),
|
|
4291
4579
|
args: z.array(z.string()).optional(),
|
|
4292
|
-
timeout_ms: z.number().int().positive().optional()
|
|
4580
|
+
timeout_ms: z.number().int().positive().optional(),
|
|
4581
|
+
env: z.record(z.string(), z.string()).optional()
|
|
4293
4582
|
});
|
|
4294
4583
|
ScriptContainmentError = class ScriptContainmentError extends Error {
|
|
4295
4584
|
constructor(path, hookDir, raw) {
|
|
@@ -4302,20 +4591,20 @@ var init_manifest = __esm(() => {
|
|
|
4302
4591
|
// src/lib/resolve.ts
|
|
4303
4592
|
var exports_resolve = {};
|
|
4304
4593
|
__export(exports_resolve, {
|
|
4305
|
-
|
|
4306
|
-
resolveHookMeta: () => resolveHookMeta,
|
|
4307
|
-
resolveHookDir: () => resolveHookDir,
|
|
4594
|
+
resolveBundledHooksDir: () => resolveBundledHooksDir,
|
|
4308
4595
|
resolveHook: () => resolveHook,
|
|
4309
|
-
|
|
4596
|
+
resolveHookDir: () => resolveHookDir,
|
|
4597
|
+
resolveHookMeta: () => resolveHookMeta,
|
|
4598
|
+
resolveScriptPath: () => resolveScriptPath
|
|
4310
4599
|
});
|
|
4311
|
-
import { dirname as dirname2, join as
|
|
4600
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
4312
4601
|
import { existsSync as existsSync4 } from "fs";
|
|
4313
4602
|
import { fileURLToPath } from "url";
|
|
4314
4603
|
function resolveBundledHooksDir(moduleDir = dirname2(fileURLToPath(import.meta.url))) {
|
|
4315
|
-
const sourceLayout =
|
|
4316
|
-
if (existsSync4(
|
|
4604
|
+
const sourceLayout = join5(moduleDir, "..", "..", "hooks");
|
|
4605
|
+
if (existsSync4(join5(sourceLayout, "hook-gitguard")))
|
|
4317
4606
|
return sourceLayout;
|
|
4318
|
-
return
|
|
4607
|
+
return join5(moduleDir, "..", "hooks");
|
|
4319
4608
|
}
|
|
4320
4609
|
function resolveHookDir(name) {
|
|
4321
4610
|
const custom = readCustomManifest(name);
|
|
@@ -4326,10 +4615,10 @@ function resolveHookDir(name) {
|
|
|
4326
4615
|
return;
|
|
4327
4616
|
const short = shortManifestName(name);
|
|
4328
4617
|
const bundledBase = resolveBundledHooksDir();
|
|
4329
|
-
const direct =
|
|
4618
|
+
const direct = join5(bundledBase, short);
|
|
4330
4619
|
if (existsSync4(direct))
|
|
4331
4620
|
return direct;
|
|
4332
|
-
const prefixed =
|
|
4621
|
+
const prefixed = join5(bundledBase, `hook-${short}`);
|
|
4333
4622
|
return existsSync4(prefixed) ? prefixed : undefined;
|
|
4334
4623
|
}
|
|
4335
4624
|
function resolveScriptPath(name) {
|
|
@@ -4339,7 +4628,7 @@ function resolveScriptPath(name) {
|
|
|
4339
4628
|
const dir = resolveHookDir(name);
|
|
4340
4629
|
if (!dir)
|
|
4341
4630
|
return;
|
|
4342
|
-
const script =
|
|
4631
|
+
const script = join5(dir, "src", "hook.ts");
|
|
4343
4632
|
return existsSync4(script) ? script : undefined;
|
|
4344
4633
|
}
|
|
4345
4634
|
function resolveHookMeta(name) {
|
|
@@ -4429,7 +4718,7 @@ function up(db) {
|
|
|
4429
4718
|
db.exec(idx);
|
|
4430
4719
|
}
|
|
4431
4720
|
}
|
|
4432
|
-
var
|
|
4721
|
+
var init__001_initial = __esm(() => {
|
|
4433
4722
|
init_schema();
|
|
4434
4723
|
});
|
|
4435
4724
|
|
|
@@ -4458,7 +4747,7 @@ function up2(db) {
|
|
|
4458
4747
|
throw err;
|
|
4459
4748
|
}
|
|
4460
4749
|
}
|
|
4461
|
-
var
|
|
4750
|
+
var init__002_session_events = __esm(() => {
|
|
4462
4751
|
init_schema();
|
|
4463
4752
|
});
|
|
4464
4753
|
|
|
@@ -4487,7 +4776,7 @@ function up3(db) {
|
|
|
4487
4776
|
throw err;
|
|
4488
4777
|
}
|
|
4489
4778
|
}
|
|
4490
|
-
var
|
|
4779
|
+
var init__003_user_prompt_submit_event = __esm(() => {
|
|
4491
4780
|
init_schema();
|
|
4492
4781
|
});
|
|
4493
4782
|
|
|
@@ -4511,7 +4800,7 @@ var CREATE_HOOKS_TABLE = `
|
|
|
4511
4800
|
last_verified_at TEXT
|
|
4512
4801
|
)
|
|
4513
4802
|
`, CREATE_HOOKS_INDEXES;
|
|
4514
|
-
var
|
|
4803
|
+
var init__004_hooks_table = __esm(() => {
|
|
4515
4804
|
CREATE_HOOKS_INDEXES = [
|
|
4516
4805
|
`CREATE INDEX IF NOT EXISTS idx_hooks_name ON hooks (name)`
|
|
4517
4806
|
];
|
|
@@ -4542,7 +4831,7 @@ function up5(db) {
|
|
|
4542
4831
|
throw err;
|
|
4543
4832
|
}
|
|
4544
4833
|
}
|
|
4545
|
-
var
|
|
4834
|
+
var init__005_subagent_start_event = __esm(() => {
|
|
4546
4835
|
init_schema();
|
|
4547
4836
|
});
|
|
4548
4837
|
|
|
@@ -4574,11 +4863,11 @@ function runMigrations(db) {
|
|
|
4574
4863
|
}
|
|
4575
4864
|
var MIGRATIONS;
|
|
4576
4865
|
var init_migrations = __esm(() => {
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
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();
|
|
4582
4871
|
MIGRATIONS = [
|
|
4583
4872
|
{ version: "001_initial", up },
|
|
4584
4873
|
{ version: "002_session_events", up: up2 },
|
|
@@ -4589,8 +4878,8 @@ var init_migrations = __esm(() => {
|
|
|
4589
4878
|
});
|
|
4590
4879
|
|
|
4591
4880
|
// src/db/legacy-import.ts
|
|
4592
|
-
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as
|
|
4593
|
-
import { join as
|
|
4881
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
4882
|
+
import { join as join6 } from "path";
|
|
4594
4883
|
import { homedir as homedir3 } from "os";
|
|
4595
4884
|
function ensureMetaTable(db) {
|
|
4596
4885
|
db.exec(`
|
|
@@ -4614,7 +4903,7 @@ function nanoid() {
|
|
|
4614
4903
|
function importJsonlFile(db, filePath) {
|
|
4615
4904
|
let count = 0;
|
|
4616
4905
|
try {
|
|
4617
|
-
const lines =
|
|
4906
|
+
const lines = readFileSync4(filePath, "utf-8").split(`
|
|
4618
4907
|
`).filter(Boolean);
|
|
4619
4908
|
for (const line of lines) {
|
|
4620
4909
|
try {
|
|
@@ -4640,7 +4929,7 @@ function importJsonlFile(db, filePath) {
|
|
|
4640
4929
|
function importErrorsLog(db, filePath) {
|
|
4641
4930
|
let count = 0;
|
|
4642
4931
|
try {
|
|
4643
|
-
const lines =
|
|
4932
|
+
const lines = readFileSync4(filePath, "utf-8").split(`
|
|
4644
4933
|
`).filter(Boolean);
|
|
4645
4934
|
const linePattern = /^\[(.+?)\]\s+(?:\[session:(\S+)\]\s+)?(.+?)\s+\u2014\s+(.+)$/;
|
|
4646
4935
|
for (const line of lines) {
|
|
@@ -4665,25 +4954,25 @@ function importErrorsLog(db, filePath) {
|
|
|
4665
4954
|
} catch {}
|
|
4666
4955
|
return count;
|
|
4667
4956
|
}
|
|
4668
|
-
function runLegacyImport(db,
|
|
4957
|
+
function runLegacyImport(db, homeDir2 = homedir3()) {
|
|
4669
4958
|
try {
|
|
4670
4959
|
if (isAlreadyDone(db))
|
|
4671
4960
|
return;
|
|
4672
4961
|
let total = 0;
|
|
4673
|
-
const claudeProjectsDir =
|
|
4962
|
+
const claudeProjectsDir = join6(homeDir2, ".claude", "projects");
|
|
4674
4963
|
if (existsSync5(claudeProjectsDir)) {
|
|
4675
4964
|
try {
|
|
4676
4965
|
const projectDirs = readdirSync2(claudeProjectsDir);
|
|
4677
4966
|
for (const dir of projectDirs) {
|
|
4678
|
-
const projectDir =
|
|
4967
|
+
const projectDir = join6(claudeProjectsDir, dir);
|
|
4679
4968
|
try {
|
|
4680
4969
|
const files = readdirSync2(projectDir);
|
|
4681
4970
|
for (const file of files) {
|
|
4682
4971
|
if (file.match(/^session-log-\d{4}-\d{2}-\d{2}\.jsonl$/)) {
|
|
4683
|
-
total += importJsonlFile(db,
|
|
4972
|
+
total += importJsonlFile(db, join6(projectDir, file));
|
|
4684
4973
|
}
|
|
4685
4974
|
if (file === "errors.log") {
|
|
4686
|
-
total += importErrorsLog(db,
|
|
4975
|
+
total += importErrorsLog(db, join6(projectDir, file));
|
|
4687
4976
|
}
|
|
4688
4977
|
}
|
|
4689
4978
|
} catch {}
|
|
@@ -4720,23 +5009,23 @@ function runRetention(db, days) {
|
|
|
4720
5009
|
// src/db/index.ts
|
|
4721
5010
|
var exports_db = {};
|
|
4722
5011
|
__export(exports_db, {
|
|
4723
|
-
|
|
4724
|
-
getDb: () => getDb,
|
|
5012
|
+
closeDb: () => closeDb,
|
|
4725
5013
|
createTestDb: () => createTestDb,
|
|
4726
|
-
|
|
5014
|
+
getDb: () => getDb,
|
|
5015
|
+
getDbPath: () => getDbPath
|
|
4727
5016
|
});
|
|
4728
5017
|
import { Database } from "bun:sqlite";
|
|
4729
5018
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, cpSync } from "fs";
|
|
4730
|
-
import { join as
|
|
5019
|
+
import { join as join7 } from "path";
|
|
4731
5020
|
import { homedir as homedir4 } from "os";
|
|
4732
5021
|
function resolveDataDir() {
|
|
4733
5022
|
const explicit = process.env.HASNA_HOOKS_DATA_DIR ?? process.env.HOOKS_DATA_DIR;
|
|
4734
5023
|
if (explicit)
|
|
4735
5024
|
return explicit;
|
|
4736
|
-
const newDir =
|
|
4737
|
-
const oldDir =
|
|
5025
|
+
const newDir = join7(homedir4(), ".hasna", "hooks");
|
|
5026
|
+
const oldDir = join7(homedir4(), ".hooks");
|
|
4738
5027
|
if (!existsSync6(newDir) && existsSync6(oldDir)) {
|
|
4739
|
-
mkdirSync3(
|
|
5028
|
+
mkdirSync3(join7(homedir4(), ".hasna"), { recursive: true });
|
|
4740
5029
|
cpSync(oldDir, newDir, { recursive: true });
|
|
4741
5030
|
}
|
|
4742
5031
|
return newDir;
|
|
@@ -4746,7 +5035,7 @@ function getDbPath() {
|
|
|
4746
5035
|
if (explicitDb)
|
|
4747
5036
|
return explicitDb;
|
|
4748
5037
|
const dataDir = resolveDataDir();
|
|
4749
|
-
return
|
|
5038
|
+
return join7(dataDir, "hooks.db");
|
|
4750
5039
|
}
|
|
4751
5040
|
function ensureDir(dbPath) {
|
|
4752
5041
|
const dir = dbPath.substring(0, dbPath.lastIndexOf("/"));
|
|
@@ -4801,26 +5090,26 @@ var init_db = __esm(() => {
|
|
|
4801
5090
|
// src/lib/store.ts
|
|
4802
5091
|
var exports_store = {};
|
|
4803
5092
|
__export(exports_store, {
|
|
4804
|
-
|
|
4805
|
-
verifyScriptHash: () => verifyScriptHash,
|
|
4806
|
-
upsertHookRecord: () => upsertHookRecord,
|
|
4807
|
-
sha256Of: () => sha256Of,
|
|
4808
|
-
sha256File: () => sha256File,
|
|
4809
|
-
setPinnedHook: () => setPinnedHook,
|
|
4810
|
-
retrustHook: () => retrustHook,
|
|
4811
|
-
removePinnedHook: () => removePinnedHook,
|
|
4812
|
-
removeHookRecord: () => removeHookRecord,
|
|
4813
|
-
removeHookFromStore: () => removeHookFromStore,
|
|
4814
|
-
readLock: () => readLock,
|
|
4815
|
-
pinInstalledHook: () => pinInstalledHook,
|
|
4816
|
-
listHookRecords: () => listHookRecords,
|
|
4817
|
-
getPinnedHook: () => getPinnedHook,
|
|
4818
|
-
getHookRecord: () => getHookRecord,
|
|
5093
|
+
LockFileError: () => LockFileError,
|
|
4819
5094
|
checkScriptHash: () => checkScriptHash,
|
|
4820
|
-
|
|
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
|
|
4821
5110
|
});
|
|
4822
5111
|
import { createHash } from "crypto";
|
|
4823
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as
|
|
5112
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync } from "fs";
|
|
4824
5113
|
import { readFile as readFile2 } from "fs/promises";
|
|
4825
5114
|
import { randomBytes } from "crypto";
|
|
4826
5115
|
function sha256Of(input) {
|
|
@@ -4886,7 +5175,7 @@ function readLock() {
|
|
|
4886
5175
|
return { hooks: {} };
|
|
4887
5176
|
let raw;
|
|
4888
5177
|
try {
|
|
4889
|
-
raw =
|
|
5178
|
+
raw = readFileSync5(path, "utf-8");
|
|
4890
5179
|
} catch {
|
|
4891
5180
|
throw new LockFileError(`hooks.lock (${path}) could not be read. Repair: fix permissions or move the file aside and run 'hooks sync'.`);
|
|
4892
5181
|
}
|
|
@@ -4983,7 +5272,7 @@ async function verifyScriptHash(name, scriptPath) {
|
|
|
4983
5272
|
return checkScriptHash(name, await sha256File(scriptPath));
|
|
4984
5273
|
}
|
|
4985
5274
|
function retrustHook(name, scriptPath, version, source) {
|
|
4986
|
-
const actual = sha256Of(
|
|
5275
|
+
const actual = sha256Of(readFileSync5(scriptPath));
|
|
4987
5276
|
const db = getDb();
|
|
4988
5277
|
upsertHookRecord(db, {
|
|
4989
5278
|
name,
|
|
@@ -5010,22 +5299,22 @@ var init_store = __esm(() => {
|
|
|
5010
5299
|
// src/lib/installer.ts
|
|
5011
5300
|
var exports_installer = {};
|
|
5012
5301
|
__export(exports_installer, {
|
|
5013
|
-
|
|
5014
|
-
removeHook: () => removeHook,
|
|
5015
|
-
removeCodewithHookEntry: () => removeCodewithHookEntry,
|
|
5016
|
-
isEventSupported: () => isEventSupported,
|
|
5017
|
-
installHooks: () => installHooks,
|
|
5018
|
-
installHook: () => installHook,
|
|
5019
|
-
hookExists: () => hookExists,
|
|
5020
|
-
getSettingsPath: () => getSettingsPath,
|
|
5021
|
-
getRegisteredHooksForTarget: () => getRegisteredHooksForTarget,
|
|
5022
|
-
getRegisteredHooks: () => getRegisteredHooks,
|
|
5023
|
-
getInstalledHooks: () => getInstalledHooks,
|
|
5302
|
+
buildCodewithTomlFragment: () => buildCodewithTomlFragment,
|
|
5024
5303
|
getHookPath: () => getHookPath,
|
|
5025
|
-
|
|
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
|
|
5026
5315
|
});
|
|
5027
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as
|
|
5028
|
-
import { join as
|
|
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";
|
|
5029
5318
|
import { homedir as homedir5 } from "os";
|
|
5030
5319
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5031
5320
|
function normalizeHookName(name) {
|
|
@@ -5065,16 +5354,16 @@ function getSettingsPath(scope = "global", target = "claude", codewithConfigPath
|
|
|
5065
5354
|
return globalOverride;
|
|
5066
5355
|
const dir = getTargetSettingsDir(target);
|
|
5067
5356
|
if (scope === "project") {
|
|
5068
|
-
return target === "codewith" ?
|
|
5357
|
+
return target === "codewith" ? join8(process.cwd(), dir, "config.toml") : join8(process.cwd(), dir, "settings.json");
|
|
5069
5358
|
}
|
|
5070
|
-
return target === "codewith" ?
|
|
5359
|
+
return target === "codewith" ? join8(homedir5(), dir, "config.toml") : join8(homedir5(), dir, "settings.json");
|
|
5071
5360
|
}
|
|
5072
5361
|
function getHookPath(name) {
|
|
5073
5362
|
const shortName = shortHookName(name);
|
|
5074
5363
|
const resolved = resolveHookDir(shortName);
|
|
5075
5364
|
if (resolved)
|
|
5076
5365
|
return resolved;
|
|
5077
|
-
return
|
|
5366
|
+
return join8(HOOKS_DIR, normalizeHookName(shortName));
|
|
5078
5367
|
}
|
|
5079
5368
|
function hookExists(name) {
|
|
5080
5369
|
return existsSync8(getHookPath(name));
|
|
@@ -5083,7 +5372,7 @@ function readSettings(scope = "global", target = "claude") {
|
|
|
5083
5372
|
const path = getSettingsPath(scope, target);
|
|
5084
5373
|
try {
|
|
5085
5374
|
if (existsSync8(path)) {
|
|
5086
|
-
return JSON.parse(
|
|
5375
|
+
return JSON.parse(readFileSync6(path, "utf-8"));
|
|
5087
5376
|
}
|
|
5088
5377
|
} catch (error) {
|
|
5089
5378
|
console.warn(`[hooks] Failed to read settings at ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -5182,7 +5471,7 @@ function buildCodewithTomlFragment(name, profile) {
|
|
|
5182
5471
|
function readCodewithConfig(scope, configPath) {
|
|
5183
5472
|
const path = getSettingsPath(scope, "codewith", configPath);
|
|
5184
5473
|
try {
|
|
5185
|
-
return existsSync8(path) ?
|
|
5474
|
+
return existsSync8(path) ? readFileSync6(path, "utf-8") : "";
|
|
5186
5475
|
} catch {
|
|
5187
5476
|
return "";
|
|
5188
5477
|
}
|
|
@@ -5192,7 +5481,7 @@ function appendCodewithFragment(fragment, scope, configPath) {
|
|
|
5192
5481
|
const dir = dirname3(path);
|
|
5193
5482
|
if (!existsSync8(dir))
|
|
5194
5483
|
mkdirSync5(dir, { recursive: true });
|
|
5195
|
-
const existing = existsSync8(path) ?
|
|
5484
|
+
const existing = existsSync8(path) ? readFileSync6(path, "utf-8") : "";
|
|
5196
5485
|
const sep2 = existing.trim() ? `
|
|
5197
5486
|
|
|
5198
5487
|
` : "";
|
|
@@ -5237,7 +5526,7 @@ function installForTarget(name, scope, overwrite, target, profile, codewithMode
|
|
|
5237
5526
|
target,
|
|
5238
5527
|
fragment,
|
|
5239
5528
|
applied: false,
|
|
5240
|
-
note: "Codewith install is fragment-only by default;
|
|
5529
|
+
note: "Codewith install is fragment-only by default; configs should own applying this TOML."
|
|
5241
5530
|
};
|
|
5242
5531
|
}
|
|
5243
5532
|
if (!codewithConfigPath) {
|
|
@@ -5264,7 +5553,7 @@ function installForTarget(name, scope, overwrite, target, profile, codewithMode
|
|
|
5264
5553
|
fragment,
|
|
5265
5554
|
applied: true,
|
|
5266
5555
|
configPath: path,
|
|
5267
|
-
note: "Direct Codewith config write was explicitly requested; prefer
|
|
5556
|
+
note: "Direct Codewith config write was explicitly requested; prefer configs for managed machines."
|
|
5268
5557
|
};
|
|
5269
5558
|
} catch (error) {
|
|
5270
5559
|
return {
|
|
@@ -5495,7 +5784,7 @@ function codewithHasHookEntry(name, scope = "global") {
|
|
|
5495
5784
|
if (!existsSync8(path))
|
|
5496
5785
|
return false;
|
|
5497
5786
|
try {
|
|
5498
|
-
return codewithHasHookEntryInText(
|
|
5787
|
+
return codewithHasHookEntryInText(readFileSync6(path, "utf-8"), name);
|
|
5499
5788
|
} catch {
|
|
5500
5789
|
return false;
|
|
5501
5790
|
}
|
|
@@ -5527,7 +5816,7 @@ function uninstallHook(name, scope = "global", target = "claude") {
|
|
|
5527
5816
|
if (target === "codewith" || target === "all") {
|
|
5528
5817
|
const codewithPath = getSettingsPath(scope, "codewith");
|
|
5529
5818
|
if (existsSync8(codewithPath)) {
|
|
5530
|
-
const before =
|
|
5819
|
+
const before = readFileSync6(codewithPath, "utf-8");
|
|
5531
5820
|
const after = removeCodewithHookEntry(before, shortName);
|
|
5532
5821
|
if (after.removed) {
|
|
5533
5822
|
writeFileSync4(codewithPath, after.text, "utf-8");
|
|
@@ -5573,7 +5862,7 @@ var init_installer = __esm(() => {
|
|
|
5573
5862
|
init_manifest();
|
|
5574
5863
|
init_store();
|
|
5575
5864
|
__dirname2 = dirname3(fileURLToPath2(import.meta.url));
|
|
5576
|
-
HOOKS_DIR = existsSync8(
|
|
5865
|
+
HOOKS_DIR = existsSync8(join8(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join8(__dirname2, "..", "..", "hooks") : join8(__dirname2, "..", "hooks");
|
|
5577
5866
|
EVENT_MAP = {
|
|
5578
5867
|
claude: {
|
|
5579
5868
|
PreToolUse: "PreToolUse",
|
|
@@ -5720,14 +6009,14 @@ var init_redact = __esm(() => {
|
|
|
5720
6009
|
});
|
|
5721
6010
|
|
|
5722
6011
|
// src/lib/profiles.ts
|
|
5723
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as
|
|
5724
|
-
import { join as
|
|
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";
|
|
5725
6014
|
import { homedir as homedir6 } from "os";
|
|
5726
6015
|
function resolveProfilesDir() {
|
|
5727
|
-
const newDir =
|
|
5728
|
-
const oldDir =
|
|
6016
|
+
const newDir = join9(homedir6(), ".hasna", "hooks", "profiles");
|
|
6017
|
+
const oldDir = join9(homedir6(), ".hooks", "profiles");
|
|
5729
6018
|
if (!existsSync9(newDir) && existsSync9(oldDir)) {
|
|
5730
|
-
mkdirSync6(
|
|
6019
|
+
mkdirSync6(join9(homedir6(), ".hasna", "hooks"), { recursive: true });
|
|
5731
6020
|
cpSync2(oldDir, newDir, { recursive: true });
|
|
5732
6021
|
}
|
|
5733
6022
|
return newDir;
|
|
@@ -5738,7 +6027,7 @@ function ensureProfilesDir() {
|
|
|
5738
6027
|
}
|
|
5739
6028
|
}
|
|
5740
6029
|
function profilePath(id) {
|
|
5741
|
-
return
|
|
6030
|
+
return join9(PROFILES_DIR, `${id}.json`);
|
|
5742
6031
|
}
|
|
5743
6032
|
function shortUuid() {
|
|
5744
6033
|
return crypto.randomUUID().slice(0, 8);
|
|
@@ -5766,7 +6055,7 @@ function getProfile(id) {
|
|
|
5766
6055
|
try {
|
|
5767
6056
|
if (!existsSync9(path))
|
|
5768
6057
|
return null;
|
|
5769
|
-
return JSON.parse(
|
|
6058
|
+
return JSON.parse(readFileSync7(path, "utf-8"));
|
|
5770
6059
|
} catch {
|
|
5771
6060
|
return null;
|
|
5772
6061
|
}
|
|
@@ -5779,7 +6068,7 @@ function listProfiles() {
|
|
|
5779
6068
|
const profiles = [];
|
|
5780
6069
|
for (const file of files) {
|
|
5781
6070
|
try {
|
|
5782
|
-
const content =
|
|
6071
|
+
const content = readFileSync7(join9(PROFILES_DIR, file), "utf-8");
|
|
5783
6072
|
profiles.push(JSON.parse(content));
|
|
5784
6073
|
} catch {}
|
|
5785
6074
|
}
|
|
@@ -5827,10 +6116,10 @@ var init_profiles = __esm(() => {
|
|
|
5827
6116
|
// src/lib/db-writer.ts
|
|
5828
6117
|
var exports_db_writer = {};
|
|
5829
6118
|
__export(exports_db_writer, {
|
|
5830
|
-
|
|
5831
|
-
resolveEventType: () => resolveEventType,
|
|
6119
|
+
normalizeEventType: () => normalizeEventType,
|
|
5832
6120
|
recordHookRun: () => recordHookRun,
|
|
5833
|
-
|
|
6121
|
+
resolveEventType: () => resolveEventType,
|
|
6122
|
+
writeHookEvent: () => writeHookEvent
|
|
5834
6123
|
});
|
|
5835
6124
|
function nanoid2() {
|
|
5836
6125
|
return crypto.randomUUID().replace(/-/g, "").slice(0, 21);
|
|
@@ -5921,6 +6210,8 @@ var init_db_writer = __esm(() => {
|
|
|
5921
6210
|
});
|
|
5922
6211
|
|
|
5923
6212
|
// src/lib/hook-env.ts
|
|
6213
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2 } from "path";
|
|
6214
|
+
import { existsSync as existsSync10, statSync as statSync2 } from "fs";
|
|
5924
6215
|
function isDeniedEnvName(name) {
|
|
5925
6216
|
const upper = name.toUpperCase();
|
|
5926
6217
|
if (DENY_PREFIXES.some((prefix) => upper.startsWith(prefix)))
|
|
@@ -5929,13 +6220,74 @@ function isDeniedEnvName(name) {
|
|
|
5929
6220
|
return true;
|
|
5930
6221
|
return DENY_CONTAINS.some((token) => upper.includes(token));
|
|
5931
6222
|
}
|
|
6223
|
+
function isInterpreterInjectionEnvName(name) {
|
|
6224
|
+
if (INTERPRETER_INJECTION.has(name))
|
|
6225
|
+
return true;
|
|
6226
|
+
return INTERPRETER_INJECTION_PREFIXES.some((prefix) => name.startsWith(prefix));
|
|
6227
|
+
}
|
|
6228
|
+
function isBashFunctionEnvName(name) {
|
|
6229
|
+
return name.startsWith("BASH_FUNC_");
|
|
6230
|
+
}
|
|
6231
|
+
function isUnsafePathEntry(entry, home) {
|
|
6232
|
+
if (entry === "" || !isAbsolute2(entry))
|
|
6233
|
+
return true;
|
|
6234
|
+
if (home && (entry === home || entry.startsWith(home + "/")))
|
|
6235
|
+
return true;
|
|
6236
|
+
if (entry === "/tmp" || entry.startsWith("/tmp/"))
|
|
6237
|
+
return true;
|
|
6238
|
+
if (entry === "/var/tmp" || entry.startsWith("/var/tmp/"))
|
|
6239
|
+
return true;
|
|
6240
|
+
try {
|
|
6241
|
+
if ((statSync2(entry).mode & 2) !== 0)
|
|
6242
|
+
return true;
|
|
6243
|
+
} catch {}
|
|
6244
|
+
return false;
|
|
6245
|
+
}
|
|
6246
|
+
function sanitizeHookPath(pathValue, home) {
|
|
6247
|
+
const baseline = PATH_BASELINE.slice();
|
|
6248
|
+
if (process.platform === "darwin" && existsSync10("/opt/homebrew/bin"))
|
|
6249
|
+
baseline.push("/opt/homebrew/bin");
|
|
6250
|
+
const bunDir = dirname4(process.execPath);
|
|
6251
|
+
const seen = new Set;
|
|
6252
|
+
const out = [];
|
|
6253
|
+
for (const dir of baseline) {
|
|
6254
|
+
if (seen.has(dir))
|
|
6255
|
+
continue;
|
|
6256
|
+
seen.add(dir);
|
|
6257
|
+
out.push(dir);
|
|
6258
|
+
}
|
|
6259
|
+
if (!seen.has(bunDir)) {
|
|
6260
|
+
seen.add(bunDir);
|
|
6261
|
+
out.push(bunDir);
|
|
6262
|
+
}
|
|
6263
|
+
for (const entry of pathValue.split(":")) {
|
|
6264
|
+
if (seen.has(entry))
|
|
6265
|
+
continue;
|
|
6266
|
+
if (isUnsafePathEntry(entry, home))
|
|
6267
|
+
continue;
|
|
6268
|
+
seen.add(entry);
|
|
6269
|
+
out.push(entry);
|
|
6270
|
+
}
|
|
6271
|
+
return out.join(":");
|
|
6272
|
+
}
|
|
5932
6273
|
function buildHookEnv(source, extra) {
|
|
5933
6274
|
const out = {};
|
|
5934
6275
|
for (const name of ALLOWLIST) {
|
|
6276
|
+
if (name === "PATH")
|
|
6277
|
+
continue;
|
|
5935
6278
|
const value = source[name] ?? extra?.[name];
|
|
5936
6279
|
if (value !== undefined && value !== null)
|
|
5937
6280
|
out[name] = value;
|
|
5938
6281
|
}
|
|
6282
|
+
const pathOverride = extra?.PATH;
|
|
6283
|
+
if (pathOverride !== undefined && pathOverride !== null) {
|
|
6284
|
+
out.PATH = pathOverride;
|
|
6285
|
+
} else {
|
|
6286
|
+
const parentPath = source.PATH;
|
|
6287
|
+
if (parentPath !== undefined && parentPath !== null) {
|
|
6288
|
+
out.PATH = sanitizeHookPath(parentPath, source.HOME ?? extra?.HOME);
|
|
6289
|
+
}
|
|
6290
|
+
}
|
|
5939
6291
|
for (const [from, to] of CONFIG_PROJECTIONS) {
|
|
5940
6292
|
const value = source[from] ?? extra?.[from];
|
|
5941
6293
|
if (value !== undefined && value !== null)
|
|
@@ -5947,14 +6299,44 @@ function buildHookEnv(source, extra) {
|
|
|
5947
6299
|
continue;
|
|
5948
6300
|
if (isDeniedEnvName(name))
|
|
5949
6301
|
continue;
|
|
6302
|
+
if (isInterpreterInjectionEnvName(name))
|
|
6303
|
+
continue;
|
|
6304
|
+
if (isBashFunctionEnvName(name))
|
|
6305
|
+
continue;
|
|
5950
6306
|
if (value !== undefined && value !== null)
|
|
5951
6307
|
out[name] = value;
|
|
5952
6308
|
}
|
|
5953
6309
|
return out;
|
|
5954
6310
|
}
|
|
5955
|
-
var ALLOWLIST, DENY_SUFFIXES, DENY_PREFIXES, DENY_CONTAINS, CONFIG_PROJECTIONS;
|
|
6311
|
+
var ALLOWLIST, INTERPRETER_INJECTION, INTERPRETER_INJECTION_PREFIXES, DENY_SUFFIXES, DENY_PREFIXES, DENY_CONTAINS, PATH_BASELINE, CONFIG_PROJECTIONS;
|
|
5956
6312
|
var init_hook_env = __esm(() => {
|
|
5957
6313
|
ALLOWLIST = new Set(["PATH", "HOME", "LANG", "TZ", "SHELL", "TERM", "USER", "PWD"]);
|
|
6314
|
+
INTERPRETER_INJECTION = new Set([
|
|
6315
|
+
"BASH_ENV",
|
|
6316
|
+
"ENV",
|
|
6317
|
+
"BASHOPTS",
|
|
6318
|
+
"SHELLOPTS",
|
|
6319
|
+
"NODE_OPTIONS",
|
|
6320
|
+
"NODE_PATH",
|
|
6321
|
+
"PYTHONSTARTUP",
|
|
6322
|
+
"PYTHONINSPECT",
|
|
6323
|
+
"PYTHONPATH",
|
|
6324
|
+
"PYTHONHOME",
|
|
6325
|
+
"LD_PRELOAD",
|
|
6326
|
+
"LD_LIBRARY_PATH",
|
|
6327
|
+
"GCONV_PATH",
|
|
6328
|
+
"LOCPATH",
|
|
6329
|
+
"PERL5OPT",
|
|
6330
|
+
"RUBYOPT",
|
|
6331
|
+
"NODE_EXTRA_CA_CERTS",
|
|
6332
|
+
"NODE_TLS_REJECT_UNAUTHORIZED",
|
|
6333
|
+
"SSL_CERT_FILE",
|
|
6334
|
+
"SSL_CERT_DIR",
|
|
6335
|
+
"CURL_CA_BUNDLE",
|
|
6336
|
+
"GIT_SSL_CAINFO",
|
|
6337
|
+
"AWS_CA_BUNDLE"
|
|
6338
|
+
]);
|
|
6339
|
+
INTERPRETER_INJECTION_PREFIXES = ["GIT_CONFIG_"];
|
|
5958
6340
|
DENY_SUFFIXES = ["KEY", "TOKEN", "SECRET", "PASSWORD", "PASSWD", "CREDENTIAL", "CREDENTIALS", "URL", "URI"];
|
|
5959
6341
|
DENY_PREFIXES = [
|
|
5960
6342
|
"HASNA_",
|
|
@@ -5972,6 +6354,7 @@ var init_hook_env = __esm(() => {
|
|
|
5972
6354
|
"MEMENTOS_"
|
|
5973
6355
|
];
|
|
5974
6356
|
DENY_CONTAINS = ["DATABASE_URL"];
|
|
6357
|
+
PATH_BASELINE = ["/usr/bin", "/bin", "/usr/local/bin", "/sbin", "/usr/sbin"];
|
|
5975
6358
|
CONFIG_PROJECTIONS = [
|
|
5976
6359
|
["HASNA_HOOKS_DATA_DIR", "HOOKS_DATA_DIR"],
|
|
5977
6360
|
["HASNA_HOOKS_DB_PATH", "HOOKS_DB_PATH"],
|
|
@@ -5983,12 +6366,12 @@ var init_hook_env = __esm(() => {
|
|
|
5983
6366
|
// src/lib/run.ts
|
|
5984
6367
|
var exports_run = {};
|
|
5985
6368
|
__export(exports_run, {
|
|
5986
|
-
|
|
5987
|
-
|
|
6369
|
+
HookTimeoutError: () => HookTimeoutError,
|
|
6370
|
+
executeVerifiedScript: () => executeVerifiedScript
|
|
5988
6371
|
});
|
|
5989
6372
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
5990
6373
|
import { rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
5991
|
-
import { basename as basename2, dirname as
|
|
6374
|
+
import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute3, join as join10 } from "path";
|
|
5992
6375
|
function extensionOf(scriptPath) {
|
|
5993
6376
|
const base = basename2(scriptPath);
|
|
5994
6377
|
const dot = base.lastIndexOf(".");
|
|
@@ -6015,13 +6398,13 @@ function interpreterFor(name, scriptPath, content) {
|
|
|
6015
6398
|
if (interp === "bash" || interp === "sh")
|
|
6016
6399
|
return { command: ["/bin/bash"], tempExt: "sh" };
|
|
6017
6400
|
if (interp === "node" || interp === "bun")
|
|
6018
|
-
return { command: [
|
|
6401
|
+
return { command: [process.execPath, "run"], tempExt: BUN_EXTENSIONS.has(ext) ? ext : "ts" };
|
|
6019
6402
|
throw new Error(`Refusing to run hook '${name}': shebang '${firstLine}' is not a recognized interpreter (supported: bash/sh, node/bun)`);
|
|
6020
6403
|
}
|
|
6021
6404
|
if (BASH_EXTENSIONS.has(ext))
|
|
6022
6405
|
return { command: ["/bin/bash"], tempExt: "sh" };
|
|
6023
6406
|
if (BUN_EXTENSIONS.has(ext))
|
|
6024
|
-
return { command: [
|
|
6407
|
+
return { command: [process.execPath, "run"], tempExt: ext };
|
|
6025
6408
|
throw new Error(`Refusing to run hook '${name}': unsupported script extension '.${ext}' (supported: .sh .bash .ts .tsx .js .jsx .mjs .cjs .mts .cts, or a recognized shebang)`);
|
|
6026
6409
|
}
|
|
6027
6410
|
async function readPipeWithDeadline(stream, deadlineMs) {
|
|
@@ -6057,9 +6440,9 @@ async function readPipeWithDeadline(stream, deadlineMs) {
|
|
|
6057
6440
|
return chunks.join("");
|
|
6058
6441
|
}
|
|
6059
6442
|
async function executeVerifiedScript(options) {
|
|
6060
|
-
const scriptDir =
|
|
6443
|
+
const scriptDir = dirname5(options.scriptPath);
|
|
6061
6444
|
const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
|
|
6062
|
-
const tempPath =
|
|
6445
|
+
const tempPath = join10(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
|
|
6063
6446
|
let proc = null;
|
|
6064
6447
|
let timedOut = false;
|
|
6065
6448
|
const timeoutTimer = options.timeout ? setTimeout(() => {
|
|
@@ -6068,7 +6451,7 @@ async function executeVerifiedScript(options) {
|
|
|
6068
6451
|
}, options.timeout) : null;
|
|
6069
6452
|
try {
|
|
6070
6453
|
writeFileSync6(tempPath, options.content, { flag: "wx", mode: 384 });
|
|
6071
|
-
proc = Bun.spawn([...interpreter.command,
|
|
6454
|
+
proc = Bun.spawn([...interpreter.command, isAbsolute3(tempPath) ? tempPath : `./${tempPath}`, ...options.args ?? []], {
|
|
6072
6455
|
stdin: new Response(options.stdin),
|
|
6073
6456
|
stdout: "pipe",
|
|
6074
6457
|
stderr: "pipe",
|
|
@@ -6146,19 +6529,19 @@ var init_run = __esm(() => {
|
|
|
6146
6529
|
// src/lib/custom-install.ts
|
|
6147
6530
|
var exports_custom_install = {};
|
|
6148
6531
|
__export(exports_custom_install, {
|
|
6149
|
-
|
|
6532
|
+
ensureCustomDir: () => ensureCustomDir,
|
|
6150
6533
|
installCustomSource: () => installCustomSource,
|
|
6151
|
-
|
|
6534
|
+
isCustomSource: () => isCustomSource
|
|
6152
6535
|
});
|
|
6153
|
-
import { existsSync as
|
|
6154
|
-
import { join as
|
|
6536
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync7, mkdtempSync, readFileSync as readFileSync8, rmSync as rmSync4 } from "fs";
|
|
6537
|
+
import { join as join11 } from "path";
|
|
6155
6538
|
import { tmpdir } from "os";
|
|
6156
6539
|
function manifestAt(dir) {
|
|
6157
|
-
const path =
|
|
6158
|
-
if (!
|
|
6540
|
+
const path = join11(dir, "manifest.json");
|
|
6541
|
+
if (!existsSync11(path))
|
|
6159
6542
|
return;
|
|
6160
6543
|
try {
|
|
6161
|
-
return parseManifest(
|
|
6544
|
+
return parseManifest(readFileSync8(path, "utf-8"));
|
|
6162
6545
|
} catch {
|
|
6163
6546
|
return;
|
|
6164
6547
|
}
|
|
@@ -6177,8 +6560,8 @@ function writeFromManifest(manifest, srcDir, source, kind) {
|
|
|
6177
6560
|
return { name, version: manifest.version, kind, source, dir, scriptPath };
|
|
6178
6561
|
}
|
|
6179
6562
|
async function cloneToTemp(gitUrl) {
|
|
6180
|
-
const tmp = mkdtempSync(
|
|
6181
|
-
const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl,
|
|
6563
|
+
const tmp = mkdtempSync(join11(tmpdir(), "hooks-install-"));
|
|
6564
|
+
const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl, join11(tmp, "repo")], {
|
|
6182
6565
|
stdout: "pipe",
|
|
6183
6566
|
stderr: "pipe"
|
|
6184
6567
|
});
|
|
@@ -6191,7 +6574,7 @@ async function cloneToTemp(gitUrl) {
|
|
|
6191
6574
|
rmSync4(tmp, { recursive: true, force: true });
|
|
6192
6575
|
throw new Error(`git clone failed (${exitCode}): ${stderr.trim()}`);
|
|
6193
6576
|
}
|
|
6194
|
-
return
|
|
6577
|
+
return join11(tmp, "repo");
|
|
6195
6578
|
}
|
|
6196
6579
|
function isGitUrl(value) {
|
|
6197
6580
|
return value.startsWith("git@") || value.startsWith("ssh://") || value.startsWith("file://") || value.endsWith(".git");
|
|
@@ -6201,7 +6584,7 @@ function isCustomSource(value) {
|
|
|
6201
6584
|
return true;
|
|
6202
6585
|
if (value.startsWith("http://") || value.startsWith("https://"))
|
|
6203
6586
|
return true;
|
|
6204
|
-
return
|
|
6587
|
+
return existsSync11(value);
|
|
6205
6588
|
}
|
|
6206
6589
|
async function installCustomSource(source) {
|
|
6207
6590
|
if (isGitUrl(source)) {
|
|
@@ -6215,11 +6598,11 @@ async function installCustomSource(source) {
|
|
|
6215
6598
|
if (source.startsWith("http://") || source.startsWith("https://")) {
|
|
6216
6599
|
return installManifestUrl(source);
|
|
6217
6600
|
}
|
|
6218
|
-
if (!
|
|
6601
|
+
if (!existsSync11(source)) {
|
|
6219
6602
|
throw new Error(`No such local path: ${source}`);
|
|
6220
6603
|
}
|
|
6221
6604
|
if (source.endsWith("manifest.json")) {
|
|
6222
|
-
const manifest = parseManifest(
|
|
6605
|
+
const manifest = parseManifest(readFileSync8(source, "utf-8"));
|
|
6223
6606
|
return writeFromManifest(manifest, source.substring(0, source.lastIndexOf("/")), source, "local");
|
|
6224
6607
|
}
|
|
6225
6608
|
return copyManifestDir(source, source, "local");
|
|
@@ -6261,19 +6644,19 @@ var init_custom_install = __esm(() => {
|
|
|
6261
6644
|
// src/lib/sync.ts
|
|
6262
6645
|
var exports_sync = {};
|
|
6263
6646
|
__export(exports_sync, {
|
|
6264
|
-
|
|
6265
|
-
stageSyncArtifacts: () => stageSyncArtifacts,
|
|
6266
|
-
planSync: () => planSync,
|
|
6647
|
+
commitSyncArtifacts: () => commitSyncArtifacts,
|
|
6267
6648
|
fetchPinnedHook: () => fetchPinnedHook,
|
|
6268
|
-
|
|
6649
|
+
planSync: () => planSync,
|
|
6650
|
+
stageSyncArtifacts: () => stageSyncArtifacts,
|
|
6651
|
+
syncHooks: () => syncHooks
|
|
6269
6652
|
});
|
|
6270
6653
|
import { createHash as createHash2 } from "crypto";
|
|
6271
|
-
import { readFileSync as
|
|
6654
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
6272
6655
|
function sha256OfText(text) {
|
|
6273
6656
|
return createHash2("sha256").update(text).digest("hex");
|
|
6274
6657
|
}
|
|
6275
6658
|
function sha256OfPath(path) {
|
|
6276
|
-
return createHash2("sha256").update(
|
|
6659
|
+
return createHash2("sha256").update(readFileSync9(path)).digest("hex");
|
|
6277
6660
|
}
|
|
6278
6661
|
function collectBundledCatalog() {
|
|
6279
6662
|
const out = [];
|
|
@@ -6974,31 +7357,31 @@ var init_storage_sync = __esm(() => {
|
|
|
6974
7357
|
// src/storage.ts
|
|
6975
7358
|
var exports_storage = {};
|
|
6976
7359
|
__export(exports_storage, {
|
|
6977
|
-
|
|
6978
|
-
storagePush: () => storagePush,
|
|
6979
|
-
storagePull: () => storagePull,
|
|
6980
|
-
runStorageMigrations: () => runStorageMigrations,
|
|
6981
|
-
resolveTables: () => resolveTables,
|
|
6982
|
-
parseStorageTables: () => parseStorageTables,
|
|
6983
|
-
getSyncMetaAll: () => getSyncMetaAll,
|
|
6984
|
-
getStorageStatus: () => getStorageStatus,
|
|
6985
|
-
getStoragePg: () => getStoragePg,
|
|
6986
|
-
getStorageDatabaseUrl: () => getStorageDatabaseUrl,
|
|
6987
|
-
getStorageDatabaseEnvName: () => getStorageDatabaseEnvName,
|
|
6988
|
-
getStorageDatabaseEnv: () => getStorageDatabaseEnv,
|
|
6989
|
-
getStorageBackend: () => getStorageBackend,
|
|
6990
|
-
STORAGE_TABLES: () => STORAGE_TABLES,
|
|
6991
|
-
STORAGE_DATABASE_ENV: () => STORAGE_DATABASE_ENV,
|
|
6992
|
-
STORAGE_BACKEND_ENV: () => STORAGE_BACKEND_ENV,
|
|
6993
|
-
STORAGE_BACKENDS: () => STORAGE_BACKENDS,
|
|
6994
|
-
RETIRED_STORAGE_MODE_ENV: () => RETIRED_STORAGE_MODE_ENV,
|
|
6995
|
-
PgAdapterAsync: () => PgAdapterAsync,
|
|
6996
|
-
PG_MIGRATIONS: () => PG_MIGRATIONS,
|
|
6997
|
-
HOOKS_STORAGE_TABLES: () => HOOKS_STORAGE_TABLES,
|
|
6998
|
-
HOOKS_STORAGE_FALLBACK_ENV: () => HOOKS_STORAGE_FALLBACK_ENV,
|
|
6999
|
-
HOOKS_STORAGE_ENV: () => HOOKS_STORAGE_ENV,
|
|
7360
|
+
HOOKS_STORAGE_BACKEND_ENV: () => HOOKS_STORAGE_BACKEND_ENV,
|
|
7000
7361
|
HOOKS_STORAGE_BACKEND_FALLBACK_ENV: () => HOOKS_STORAGE_BACKEND_FALLBACK_ENV,
|
|
7001
|
-
|
|
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
|
|
7002
7385
|
});
|
|
7003
7386
|
var init_storage = __esm(() => {
|
|
7004
7387
|
init_storage_sync();
|
|
@@ -7019,15 +7402,180 @@ function secureEqual(a, b) {
|
|
|
7019
7402
|
return diff === 0;
|
|
7020
7403
|
}
|
|
7021
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
|
+
|
|
7022
7559
|
// src/serve.ts
|
|
7023
7560
|
var exports_serve = {};
|
|
7024
7561
|
__export(exports_serve, {
|
|
7025
|
-
|
|
7026
|
-
handleServeRequest: () => handleServeRequest,
|
|
7027
|
-
SERVE_SERVICE_NAME: () => SERVE_SERVICE_NAME,
|
|
7562
|
+
DEFAULT_SERVE_PORT: () => DEFAULT_SERVE_PORT,
|
|
7028
7563
|
SERVE_HOST: () => SERVE_HOST,
|
|
7029
|
-
|
|
7564
|
+
SERVE_SERVICE_NAME: () => SERVE_SERVICE_NAME,
|
|
7565
|
+
handleServeRequest: () => handleServeRequest,
|
|
7566
|
+
startServeServer: () => startServeServer
|
|
7030
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
|
+
}
|
|
7031
7579
|
async function buildCatalog() {
|
|
7032
7580
|
const byName = new Map;
|
|
7033
7581
|
for (const meta of HOOKS) {
|
|
@@ -7100,6 +7648,20 @@ function handleServeRequest(req, apiKey) {
|
|
|
7100
7648
|
if (url.pathname === "/health" && req.method === "GET") {
|
|
7101
7649
|
return Promise.resolve(json({ status: "ok", name: SERVE_SERVICE_NAME }));
|
|
7102
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
|
+
}
|
|
7103
7665
|
if (url.pathname === "/api/v1/catalog" && req.method === "GET") {
|
|
7104
7666
|
return buildCatalog().then((catalog) => json({ hooks: catalog }));
|
|
7105
7667
|
}
|
|
@@ -7178,6 +7740,7 @@ var init_serve = __esm(() => {
|
|
|
7178
7740
|
init_store();
|
|
7179
7741
|
init_resolve();
|
|
7180
7742
|
init_config();
|
|
7743
|
+
init_openapi();
|
|
7181
7744
|
init_semver();
|
|
7182
7745
|
if (false) {}
|
|
7183
7746
|
});
|
|
@@ -7266,29 +7829,29 @@ var CF_API = "https://api.cloudflare.com/client/v4";
|
|
|
7266
7829
|
// src/mcp/server.ts
|
|
7267
7830
|
var exports_server = {};
|
|
7268
7831
|
__export(exports_server, {
|
|
7269
|
-
|
|
7270
|
-
startSSEServer: () => startSSEServer,
|
|
7271
|
-
sseAuthToken: () => sseAuthToken,
|
|
7272
|
-
createHooksServer: () => createHooksServer,
|
|
7832
|
+
MCP_PORT: () => MCP_PORT,
|
|
7273
7833
|
MCP_SSE_HOST: () => MCP_SSE_HOST,
|
|
7274
|
-
|
|
7834
|
+
createHooksServer: () => createHooksServer,
|
|
7835
|
+
sseAuthToken: () => sseAuthToken,
|
|
7836
|
+
startSSEServer: () => startSSEServer,
|
|
7837
|
+
startStdioServer: () => startStdioServer
|
|
7275
7838
|
});
|
|
7276
7839
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7277
7840
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
7278
7841
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7279
7842
|
import { z as z2 } from "zod";
|
|
7280
7843
|
import { createServer } from "http";
|
|
7281
|
-
import { existsSync as
|
|
7282
|
-
import { join as
|
|
7283
|
-
import { fileURLToPath as
|
|
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";
|
|
7284
7847
|
async function runVerifiedHook(name, stdin, timeoutMs) {
|
|
7285
7848
|
const { resolveHook: resolveHook2, resolveScriptPath: resolveScriptPath2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
|
|
7286
7849
|
const resolved = resolveHook2(name);
|
|
7287
7850
|
const hookScript = resolveScriptPath2(name);
|
|
7288
|
-
if (!resolved || !hookScript || !
|
|
7851
|
+
if (!resolved || !hookScript || !existsSync12(hookScript)) {
|
|
7289
7852
|
throw new Error(`Hook '${name}' not found`);
|
|
7290
7853
|
}
|
|
7291
|
-
const content =
|
|
7854
|
+
const content = readFileSync11(hookScript);
|
|
7292
7855
|
const sha = sha256Of(content);
|
|
7293
7856
|
const check = checkScriptHash(name, sha);
|
|
7294
7857
|
if (!check.ok) {
|
|
@@ -7493,7 +8056,7 @@ function createHooksServer() {
|
|
|
7493
8056
|
const settingsPath = getSettingsPath(scope);
|
|
7494
8057
|
const issues = [];
|
|
7495
8058
|
const healthy = [];
|
|
7496
|
-
const settingsExist =
|
|
8059
|
+
const settingsExist = existsSync12(settingsPath);
|
|
7497
8060
|
if (!settingsExist) {
|
|
7498
8061
|
issues.push({ hook: "(settings)", issue: `${settingsPath} not found`, severity: "warning" });
|
|
7499
8062
|
}
|
|
@@ -7503,7 +8066,7 @@ function createHooksServer() {
|
|
|
7503
8066
|
const meta = custom ? resolveHookMeta(name) : getHook(name);
|
|
7504
8067
|
let hookHealthy = true;
|
|
7505
8068
|
if (custom) {
|
|
7506
|
-
if (!
|
|
8069
|
+
if (!existsSync12(custom.scriptPath)) {
|
|
7507
8070
|
issues.push({ hook: name, issue: `Custom hook script missing: ${custom.scriptPath}`, severity: "error" });
|
|
7508
8071
|
hookHealthy = false;
|
|
7509
8072
|
}
|
|
@@ -7513,14 +8076,14 @@ function createHooksServer() {
|
|
|
7513
8076
|
continue;
|
|
7514
8077
|
}
|
|
7515
8078
|
const hookDir = getHookPath(name);
|
|
7516
|
-
if (!
|
|
8079
|
+
if (!existsSync12(join13(hookDir, "src", "hook.ts"))) {
|
|
7517
8080
|
issues.push({ hook: name, issue: "Missing src/hook.ts in package", severity: "error" });
|
|
7518
8081
|
hookHealthy = false;
|
|
7519
8082
|
}
|
|
7520
8083
|
}
|
|
7521
8084
|
if (meta && settingsExist) {
|
|
7522
8085
|
try {
|
|
7523
|
-
const settings = JSON.parse(
|
|
8086
|
+
const settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
|
|
7524
8087
|
if (!hookRegisteredInSettings(settings, name, meta.event, meta.matcher)) {
|
|
7525
8088
|
const eventName = meta.event.split(":")[0];
|
|
7526
8089
|
issues.push({ hook: name, issue: `Not registered under correct event (${eventName})`, severity: "error" });
|
|
@@ -7550,10 +8113,10 @@ function createHooksServer() {
|
|
|
7550
8113
|
return { content: [{ type: "text", text: JSON.stringify({ error: `Hook '${name}' not found` }) }] };
|
|
7551
8114
|
}
|
|
7552
8115
|
const hookPath = getHookPath(name);
|
|
7553
|
-
const readmePath =
|
|
8116
|
+
const readmePath = join13(hookPath, "README.md");
|
|
7554
8117
|
let readme = "";
|
|
7555
|
-
if (
|
|
7556
|
-
readme =
|
|
8118
|
+
if (existsSync12(readmePath)) {
|
|
8119
|
+
readme = readFileSync11(readmePath, "utf-8");
|
|
7557
8120
|
}
|
|
7558
8121
|
if (verbose) {
|
|
7559
8122
|
return { content: [{ type: "text", text: JSON.stringify({ ...meta, readme }) }] };
|
|
@@ -7736,7 +8299,7 @@ function createHooksServer() {
|
|
|
7736
8299
|
const ctx = {
|
|
7737
8300
|
scope,
|
|
7738
8301
|
settings_path: settingsPath,
|
|
7739
|
-
settings_exists:
|
|
8302
|
+
settings_exists: existsSync12(settingsPath),
|
|
7740
8303
|
registered_hooks: hooks,
|
|
7741
8304
|
hook_count: hooks.length,
|
|
7742
8305
|
healthy,
|
|
@@ -7882,8 +8445,8 @@ function createHooksServer() {
|
|
|
7882
8445
|
const settingsPath = getSettingsPath(scope);
|
|
7883
8446
|
let settings = {};
|
|
7884
8447
|
try {
|
|
7885
|
-
if (
|
|
7886
|
-
settings = JSON.parse(
|
|
8448
|
+
if (existsSync12(settingsPath))
|
|
8449
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
|
|
7887
8450
|
} catch {}
|
|
7888
8451
|
if (!settings.hooks)
|
|
7889
8452
|
settings.hooks = {};
|
|
@@ -7892,8 +8455,8 @@ function createHooksServer() {
|
|
|
7892
8455
|
disabled.push(name);
|
|
7893
8456
|
settings.hooks.__disabled = disabled;
|
|
7894
8457
|
const { writeFileSync: writeFileSync7, mkdirSync: mkdirSync8 } = await import("fs");
|
|
7895
|
-
const { dirname:
|
|
7896
|
-
mkdirSync8(
|
|
8458
|
+
const { dirname: dirname8 } = await import("path");
|
|
8459
|
+
mkdirSync8(dirname8(settingsPath), { recursive: true });
|
|
7897
8460
|
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + `
|
|
7898
8461
|
`);
|
|
7899
8462
|
return { content: [{ type: "text", text: JSON.stringify({ hook: name, disabled: true, scope }) }] };
|
|
@@ -7905,8 +8468,8 @@ function createHooksServer() {
|
|
|
7905
8468
|
const settingsPath = getSettingsPath(scope);
|
|
7906
8469
|
let settings = {};
|
|
7907
8470
|
try {
|
|
7908
|
-
if (
|
|
7909
|
-
settings = JSON.parse(
|
|
8471
|
+
if (existsSync12(settingsPath))
|
|
8472
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
|
|
7910
8473
|
} catch {}
|
|
7911
8474
|
if (settings.hooks?.__disabled) {
|
|
7912
8475
|
settings.hooks.__disabled = settings.hooks.__disabled.filter((n) => n !== name);
|
|
@@ -8266,13 +8829,13 @@ var init_server = __esm(() => {
|
|
|
8266
8829
|
init_store();
|
|
8267
8830
|
init_redact();
|
|
8268
8831
|
init_storage();
|
|
8269
|
-
__dirname3 =
|
|
8832
|
+
__dirname3 = dirname7(fileURLToPath4(import.meta.url));
|
|
8270
8833
|
pkg = { name: "@hasna/hooks", version: "0.0.0" };
|
|
8271
8834
|
try {
|
|
8272
8835
|
for (const rel of ["../../package.json", "../package.json", "../../../package.json"]) {
|
|
8273
|
-
const p =
|
|
8274
|
-
if (
|
|
8275
|
-
pkg = JSON.parse(
|
|
8836
|
+
const p = join13(__dirname3, rel);
|
|
8837
|
+
if (existsSync12(p)) {
|
|
8838
|
+
pkg = JSON.parse(readFileSync11(p, "utf-8"));
|
|
8276
8839
|
break;
|
|
8277
8840
|
}
|
|
8278
8841
|
}
|
|
@@ -8284,14 +8847,14 @@ var init_server = __esm(() => {
|
|
|
8284
8847
|
// src/mcp/http.ts
|
|
8285
8848
|
var exports_http = {};
|
|
8286
8849
|
__export(exports_http, {
|
|
8287
|
-
|
|
8288
|
-
resolveMcpHttpPort: () => resolveMcpHttpPort,
|
|
8289
|
-
isHttpMode: () => isHttpMode,
|
|
8290
|
-
healthPayload: () => healthPayload,
|
|
8291
|
-
handleMcpRequest: () => handleMcpRequest,
|
|
8292
|
-
MCP_SERVICE_NAME: () => MCP_SERVICE_NAME,
|
|
8850
|
+
DEFAULT_MCP_HTTP_PORT: () => DEFAULT_MCP_HTTP_PORT,
|
|
8293
8851
|
MCP_HTTP_HOST: () => MCP_HTTP_HOST,
|
|
8294
|
-
|
|
8852
|
+
MCP_SERVICE_NAME: () => MCP_SERVICE_NAME,
|
|
8853
|
+
handleMcpRequest: () => handleMcpRequest,
|
|
8854
|
+
healthPayload: () => healthPayload,
|
|
8855
|
+
isHttpMode: () => isHttpMode,
|
|
8856
|
+
resolveMcpHttpPort: () => resolveMcpHttpPort,
|
|
8857
|
+
startMcpHttpServer: () => startMcpHttpServer
|
|
8295
8858
|
});
|
|
8296
8859
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
8297
8860
|
function isHttpMode(args) {
|
|
@@ -8340,7 +8903,7 @@ function startMcpHttpServer(options) {
|
|
|
8340
8903
|
var DEFAULT_MCP_HTTP_PORT = 8847, MCP_HTTP_HOST = "127.0.0.1", MCP_SERVICE_NAME = "hooks";
|
|
8341
8904
|
var init_http = () => {};
|
|
8342
8905
|
|
|
8343
|
-
//
|
|
8906
|
+
// ../events/dist/commander.js
|
|
8344
8907
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
8345
8908
|
import { Buffer as Buffer2 } from "buffer";
|
|
8346
8909
|
import { existsSync } from "fs";
|
|
@@ -9525,7 +10088,7 @@ function replaySummary(events, deliveries, nextCursor) {
|
|
|
9525
10088
|
// src/cli/index.tsx
|
|
9526
10089
|
import { render } from "ink";
|
|
9527
10090
|
|
|
9528
|
-
// node_modules/commander/esm.mjs
|
|
10091
|
+
// ../../node_modules/.bun/commander@12.1.0/node_modules/commander/esm.mjs
|
|
9529
10092
|
var import__ = __toESM(require_commander(), 1);
|
|
9530
10093
|
var {
|
|
9531
10094
|
program,
|
|
@@ -9543,19 +10106,19 @@ var {
|
|
|
9543
10106
|
|
|
9544
10107
|
// src/cli/index.tsx
|
|
9545
10108
|
import chalk2 from "chalk";
|
|
9546
|
-
import { existsSync as
|
|
9547
|
-
import { join as
|
|
9548
|
-
import { fileURLToPath as
|
|
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";
|
|
9549
10112
|
|
|
9550
10113
|
// src/cli/components/App.tsx
|
|
9551
10114
|
import { useState as useState7 } from "react";
|
|
9552
10115
|
import { Box as Box8, Text as Text10, useApp, useInput as useInput5 } from "ink";
|
|
9553
10116
|
|
|
9554
|
-
// node_modules/ink-select-input/build/Indicator.js
|
|
10117
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/Indicator.js
|
|
9555
10118
|
import React from "react";
|
|
9556
10119
|
import { Box, Text } from "ink";
|
|
9557
10120
|
|
|
9558
|
-
// node_modules/is-unicode-supported/index.js
|
|
10121
|
+
// ../../node_modules/.bun/is-unicode-supported@2.1.0/node_modules/is-unicode-supported/index.js
|
|
9559
10122
|
import process2 from "process";
|
|
9560
10123
|
function isUnicodeSupported() {
|
|
9561
10124
|
const { env } = process2;
|
|
@@ -9566,7 +10129,7 @@ function isUnicodeSupported() {
|
|
|
9566
10129
|
return Boolean(env.WT_SESSION) || Boolean(env.TERMINUS_SUBLIME) || env.ConEmuTask === "{cmd::Cmder}" || TERM_PROGRAM === "Terminus-Sublime" || TERM_PROGRAM === "vscode" || TERM === "xterm-256color" || TERM === "alacritty" || TERM === "rxvt-unicode" || TERM === "rxvt-unicode-256color" || env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
9567
10130
|
}
|
|
9568
10131
|
|
|
9569
|
-
// node_modules/figures/index.js
|
|
10132
|
+
// ../../node_modules/.bun/figures@6.1.0/node_modules/figures/index.js
|
|
9570
10133
|
var common = {
|
|
9571
10134
|
circleQuestionMark: "(?)",
|
|
9572
10135
|
questionMarkPrefix: "(?)",
|
|
@@ -9842,23 +10405,23 @@ var figures = shouldUseMain ? mainSymbols : fallbackSymbols;
|
|
|
9842
10405
|
var figures_default = figures;
|
|
9843
10406
|
var replacements = Object.entries(specialMainSymbols);
|
|
9844
10407
|
|
|
9845
|
-
// node_modules/ink-select-input/build/Indicator.js
|
|
10408
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/Indicator.js
|
|
9846
10409
|
function Indicator({ isSelected = false }) {
|
|
9847
10410
|
return React.createElement(Box, { marginRight: 1 }, isSelected ? React.createElement(Text, { color: "blue" }, figures_default.pointer) : React.createElement(Text, null, " "));
|
|
9848
10411
|
}
|
|
9849
10412
|
var Indicator_default = Indicator;
|
|
9850
|
-
// node_modules/ink-select-input/build/Item.js
|
|
10413
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/Item.js
|
|
9851
10414
|
import * as React2 from "react";
|
|
9852
10415
|
import { Text as Text2 } from "ink";
|
|
9853
10416
|
function Item({ isSelected = false, label }) {
|
|
9854
10417
|
return React2.createElement(Text2, { color: isSelected ? "blue" : undefined }, label);
|
|
9855
10418
|
}
|
|
9856
10419
|
var Item_default = Item;
|
|
9857
|
-
// node_modules/ink-select-input/build/SelectInput.js
|
|
10420
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/SelectInput.js
|
|
9858
10421
|
import { isDeepStrictEqual } from "util";
|
|
9859
10422
|
import React3, { useState, useEffect, useRef, useCallback } from "react";
|
|
9860
10423
|
|
|
9861
|
-
// node_modules/to-rotated/index.js
|
|
10424
|
+
// ../../node_modules/.bun/to-rotated@1.0.0/node_modules/to-rotated/index.js
|
|
9862
10425
|
function toRotated(array, steps) {
|
|
9863
10426
|
if (!Array.isArray(array)) {
|
|
9864
10427
|
throw new TypeError(`Expected an array, got \`${typeof array}\`.`);
|
|
@@ -9880,7 +10443,7 @@ function toRotated(array, steps) {
|
|
|
9880
10443
|
];
|
|
9881
10444
|
}
|
|
9882
10445
|
|
|
9883
|
-
// node_modules/ink-select-input/build/SelectInput.js
|
|
10446
|
+
// ../../node_modules/.bun/ink-select-input@6.2.0+fa875fef9f949ebd/node_modules/ink-select-input/build/SelectInput.js
|
|
9884
10447
|
import { Box as Box2, useInput } from "ink";
|
|
9885
10448
|
function SelectInput({ items = [], isFocused = true, initialIndex = 0, indicatorComponent = Indicator_default, itemComponent = Item_default, limit: customLimit, onSelect, onHighlight }) {
|
|
9886
10449
|
const hasLimit = typeof customLimit === "number" && items.length > customLimit;
|
|
@@ -9956,29 +10519,29 @@ function SelectInput({ items = [], isFocused = true, initialIndex = 0, indicator
|
|
|
9956
10519
|
var SelectInput_default = SelectInput;
|
|
9957
10520
|
// src/cli/components/Header.tsx
|
|
9958
10521
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
9959
|
-
import {
|
|
10522
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9960
10523
|
function Header({ title = "Hooks", subtitle }) {
|
|
9961
|
-
return /* @__PURE__ */
|
|
10524
|
+
return /* @__PURE__ */ jsxs(Box3, {
|
|
9962
10525
|
flexDirection: "column",
|
|
9963
10526
|
marginBottom: 1,
|
|
9964
10527
|
children: [
|
|
9965
|
-
/* @__PURE__ */
|
|
10528
|
+
/* @__PURE__ */ jsx(Text3, {
|
|
9966
10529
|
bold: true,
|
|
9967
10530
|
color: "cyan",
|
|
9968
10531
|
children: title
|
|
9969
|
-
}
|
|
9970
|
-
subtitle && /* @__PURE__ */
|
|
10532
|
+
}),
|
|
10533
|
+
subtitle && /* @__PURE__ */ jsx(Text3, {
|
|
9971
10534
|
dimColor: true,
|
|
9972
10535
|
children: subtitle
|
|
9973
|
-
}
|
|
10536
|
+
})
|
|
9974
10537
|
]
|
|
9975
|
-
}
|
|
10538
|
+
});
|
|
9976
10539
|
}
|
|
9977
10540
|
|
|
9978
10541
|
// src/cli/components/CategorySelect.tsx
|
|
9979
10542
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
9980
10543
|
init_registry();
|
|
9981
|
-
import {
|
|
10544
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
9982
10545
|
function CategorySelect({ onSelect, onBack }) {
|
|
9983
10546
|
const items = CATEGORIES.map((cat) => ({
|
|
9984
10547
|
label: `${cat} (${getHooksByCategory(cat).length})`,
|
|
@@ -9994,28 +10557,28 @@ function CategorySelect({ onSelect, onBack }) {
|
|
|
9994
10557
|
onSelect(item.value);
|
|
9995
10558
|
}
|
|
9996
10559
|
};
|
|
9997
|
-
return /* @__PURE__ */
|
|
10560
|
+
return /* @__PURE__ */ jsxs2(Box4, {
|
|
9998
10561
|
flexDirection: "column",
|
|
9999
10562
|
children: [
|
|
10000
|
-
/* @__PURE__ */
|
|
10563
|
+
/* @__PURE__ */ jsx2(Box4, {
|
|
10001
10564
|
marginBottom: 1,
|
|
10002
|
-
children: /* @__PURE__ */
|
|
10565
|
+
children: /* @__PURE__ */ jsx2(Text4, {
|
|
10003
10566
|
bold: true,
|
|
10004
10567
|
children: "Select a category:"
|
|
10005
|
-
}
|
|
10006
|
-
}
|
|
10007
|
-
/* @__PURE__ */
|
|
10568
|
+
})
|
|
10569
|
+
}),
|
|
10570
|
+
/* @__PURE__ */ jsx2(SelectInput_default, {
|
|
10008
10571
|
items,
|
|
10009
10572
|
onSelect: handleSelect
|
|
10010
|
-
}
|
|
10573
|
+
})
|
|
10011
10574
|
]
|
|
10012
|
-
}
|
|
10575
|
+
});
|
|
10013
10576
|
}
|
|
10014
10577
|
|
|
10015
10578
|
// src/cli/components/DataTable.tsx
|
|
10016
10579
|
import { useState as useState2 } from "react";
|
|
10017
10580
|
import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
|
|
10018
|
-
import {
|
|
10581
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
10019
10582
|
var COL_CHECK = 5;
|
|
10020
10583
|
var COL_NAME = 18;
|
|
10021
10584
|
var COL_VERSION = 9;
|
|
@@ -10050,18 +10613,18 @@ function DataTable({
|
|
|
10050
10613
|
onBack();
|
|
10051
10614
|
}
|
|
10052
10615
|
});
|
|
10053
|
-
return /* @__PURE__ */
|
|
10616
|
+
return /* @__PURE__ */ jsxs3(Box5, {
|
|
10054
10617
|
flexDirection: "column",
|
|
10055
10618
|
children: [
|
|
10056
|
-
/* @__PURE__ */
|
|
10619
|
+
/* @__PURE__ */ jsx3(Box5, {
|
|
10057
10620
|
marginBottom: 1,
|
|
10058
|
-
children: /* @__PURE__ */
|
|
10621
|
+
children: /* @__PURE__ */ jsx3(Text5, {
|
|
10059
10622
|
bold: true,
|
|
10060
10623
|
children: "Select hooks (up/down to navigate, space/enter to toggle, esc to go back):"
|
|
10061
|
-
}
|
|
10062
|
-
}
|
|
10063
|
-
/* @__PURE__ */
|
|
10064
|
-
children: /* @__PURE__ */
|
|
10624
|
+
})
|
|
10625
|
+
}),
|
|
10626
|
+
/* @__PURE__ */ jsx3(Box5, {
|
|
10627
|
+
children: /* @__PURE__ */ jsxs3(Text5, {
|
|
10065
10628
|
dimColor: true,
|
|
10066
10629
|
children: [
|
|
10067
10630
|
pad("", COL_CHECK),
|
|
@@ -10070,11 +10633,11 @@ function DataTable({
|
|
|
10070
10633
|
pad("Event", COL_EVENT),
|
|
10071
10634
|
"Description"
|
|
10072
10635
|
]
|
|
10073
|
-
}
|
|
10074
|
-
}
|
|
10075
|
-
/* @__PURE__ */
|
|
10636
|
+
})
|
|
10637
|
+
}),
|
|
10638
|
+
/* @__PURE__ */ jsx3(Box5, {
|
|
10076
10639
|
marginBottom: 0,
|
|
10077
|
-
children: /* @__PURE__ */
|
|
10640
|
+
children: /* @__PURE__ */ jsxs3(Text5, {
|
|
10078
10641
|
dimColor: true,
|
|
10079
10642
|
children: [
|
|
10080
10643
|
pad("\u2500".repeat(COL_CHECK - 1), COL_CHECK),
|
|
@@ -10083,14 +10646,14 @@ function DataTable({
|
|
|
10083
10646
|
pad("\u2500".repeat(COL_EVENT - 1), COL_EVENT),
|
|
10084
10647
|
"\u2500".repeat(COL_DESC)
|
|
10085
10648
|
]
|
|
10086
|
-
}
|
|
10087
|
-
}
|
|
10649
|
+
})
|
|
10650
|
+
}),
|
|
10088
10651
|
hooks.map((hook, i) => {
|
|
10089
10652
|
const isHighlighted = i === cursor;
|
|
10090
10653
|
const isSelected = selected.has(hook.name);
|
|
10091
10654
|
const check = isSelected ? "[x]" : "[ ]";
|
|
10092
|
-
return /* @__PURE__ */
|
|
10093
|
-
children: /* @__PURE__ */
|
|
10655
|
+
return /* @__PURE__ */ jsx3(Box5, {
|
|
10656
|
+
children: /* @__PURE__ */ jsxs3(Text5, {
|
|
10094
10657
|
bold: isHighlighted,
|
|
10095
10658
|
inverse: isHighlighted,
|
|
10096
10659
|
children: [
|
|
@@ -10100,62 +10663,62 @@ function DataTable({
|
|
|
10100
10663
|
pad(hook.event, COL_EVENT),
|
|
10101
10664
|
truncate2(hook.description, COL_DESC)
|
|
10102
10665
|
]
|
|
10103
|
-
}
|
|
10104
|
-
}, hook.name
|
|
10666
|
+
})
|
|
10667
|
+
}, hook.name);
|
|
10105
10668
|
}),
|
|
10106
|
-
/* @__PURE__ */
|
|
10669
|
+
/* @__PURE__ */ jsxs3(Box5, {
|
|
10107
10670
|
marginTop: 1,
|
|
10108
10671
|
flexDirection: "row",
|
|
10109
10672
|
gap: 2,
|
|
10110
10673
|
children: [
|
|
10111
|
-
/* @__PURE__ */
|
|
10674
|
+
/* @__PURE__ */ jsx3(Text5, {
|
|
10112
10675
|
dimColor: true,
|
|
10113
10676
|
children: "[esc] Back"
|
|
10114
|
-
}
|
|
10115
|
-
selected.size > 0 && /* @__PURE__ */
|
|
10677
|
+
}),
|
|
10678
|
+
selected.size > 0 && /* @__PURE__ */ jsxs3(Text5, {
|
|
10116
10679
|
color: "green",
|
|
10117
10680
|
bold: true,
|
|
10118
10681
|
children: [
|
|
10119
10682
|
selected.size,
|
|
10120
10683
|
" selected"
|
|
10121
10684
|
]
|
|
10122
|
-
}
|
|
10685
|
+
})
|
|
10123
10686
|
]
|
|
10124
|
-
}
|
|
10125
|
-
selected.size > 0 && /* @__PURE__ */
|
|
10687
|
+
}),
|
|
10688
|
+
selected.size > 0 && /* @__PURE__ */ jsx3(Box5, {
|
|
10126
10689
|
marginTop: 0,
|
|
10127
|
-
children: /* @__PURE__ */
|
|
10690
|
+
children: /* @__PURE__ */ jsxs3(Text5, {
|
|
10128
10691
|
dimColor: true,
|
|
10129
10692
|
children: [
|
|
10130
10693
|
"Selected: ",
|
|
10131
10694
|
Array.from(selected).join(", ")
|
|
10132
10695
|
]
|
|
10133
|
-
}
|
|
10134
|
-
}
|
|
10135
|
-
selected.size > 0 && /* @__PURE__ */
|
|
10696
|
+
})
|
|
10697
|
+
}),
|
|
10698
|
+
selected.size > 0 && /* @__PURE__ */ jsx3(Box5, {
|
|
10136
10699
|
marginTop: 1,
|
|
10137
|
-
children: /* @__PURE__ */
|
|
10700
|
+
children: /* @__PURE__ */ jsxs3(Text5, {
|
|
10138
10701
|
children: [
|
|
10139
10702
|
"Press",
|
|
10140
10703
|
" ",
|
|
10141
|
-
/* @__PURE__ */
|
|
10704
|
+
/* @__PURE__ */ jsx3(Text5, {
|
|
10142
10705
|
bold: true,
|
|
10143
10706
|
color: "cyan",
|
|
10144
10707
|
children: "i"
|
|
10145
|
-
}
|
|
10708
|
+
}),
|
|
10146
10709
|
" ",
|
|
10147
10710
|
"to install selected (",
|
|
10148
10711
|
selected.size,
|
|
10149
10712
|
")"
|
|
10150
10713
|
]
|
|
10151
|
-
}
|
|
10152
|
-
}
|
|
10714
|
+
})
|
|
10715
|
+
})
|
|
10153
10716
|
]
|
|
10154
|
-
}
|
|
10717
|
+
});
|
|
10155
10718
|
}
|
|
10156
10719
|
|
|
10157
10720
|
// src/cli/components/HookSelect.tsx
|
|
10158
|
-
import {
|
|
10721
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
10159
10722
|
function HookSelect({
|
|
10160
10723
|
hooks,
|
|
10161
10724
|
selected,
|
|
@@ -10163,20 +10726,20 @@ function HookSelect({
|
|
|
10163
10726
|
onConfirm,
|
|
10164
10727
|
onBack
|
|
10165
10728
|
}) {
|
|
10166
|
-
return /* @__PURE__ */
|
|
10729
|
+
return /* @__PURE__ */ jsx4(DataTable, {
|
|
10167
10730
|
hooks,
|
|
10168
10731
|
selected,
|
|
10169
10732
|
onToggle,
|
|
10170
10733
|
onConfirm,
|
|
10171
10734
|
onBack
|
|
10172
|
-
}
|
|
10735
|
+
});
|
|
10173
10736
|
}
|
|
10174
10737
|
|
|
10175
10738
|
// src/cli/components/SearchView.tsx
|
|
10176
10739
|
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
10177
10740
|
import { Box as Box6, Text as Text7, useInput as useInput4 } from "ink";
|
|
10178
10741
|
|
|
10179
|
-
// node_modules/ink-text-input/build/index.js
|
|
10742
|
+
// ../../node_modules/.bun/ink-text-input@6.0.0+fa875fef9f949ebd/node_modules/ink-text-input/build/index.js
|
|
10180
10743
|
import React5, { useState as useState3, useEffect as useEffect2 } from "react";
|
|
10181
10744
|
import { Text as Text6, useInput as useInput3 } from "ink";
|
|
10182
10745
|
import chalk from "chalk";
|
|
@@ -10270,7 +10833,7 @@ var build_default = TextInput;
|
|
|
10270
10833
|
|
|
10271
10834
|
// src/cli/components/SearchView.tsx
|
|
10272
10835
|
init_registry();
|
|
10273
|
-
import {
|
|
10836
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
10274
10837
|
function SearchView({
|
|
10275
10838
|
selected,
|
|
10276
10839
|
onToggle,
|
|
@@ -10299,62 +10862,62 @@ function SearchView({
|
|
|
10299
10862
|
setMode("select");
|
|
10300
10863
|
}
|
|
10301
10864
|
});
|
|
10302
|
-
return /* @__PURE__ */
|
|
10865
|
+
return /* @__PURE__ */ jsxs4(Box6, {
|
|
10303
10866
|
flexDirection: "column",
|
|
10304
10867
|
children: [
|
|
10305
|
-
/* @__PURE__ */
|
|
10868
|
+
/* @__PURE__ */ jsxs4(Box6, {
|
|
10306
10869
|
marginBottom: 1,
|
|
10307
10870
|
children: [
|
|
10308
|
-
/* @__PURE__ */
|
|
10871
|
+
/* @__PURE__ */ jsx5(Text7, {
|
|
10309
10872
|
bold: true,
|
|
10310
10873
|
children: "Search: "
|
|
10311
|
-
}
|
|
10312
|
-
mode === "search" && /* @__PURE__ */
|
|
10874
|
+
}),
|
|
10875
|
+
mode === "search" && /* @__PURE__ */ jsx5(build_default, {
|
|
10313
10876
|
value: query,
|
|
10314
10877
|
onChange: setQuery,
|
|
10315
10878
|
placeholder: "Type to search hooks..."
|
|
10316
|
-
}
|
|
10317
|
-
mode === "select" && /* @__PURE__ */
|
|
10879
|
+
}),
|
|
10880
|
+
mode === "select" && /* @__PURE__ */ jsx5(Text7, {
|
|
10318
10881
|
children: query
|
|
10319
|
-
}
|
|
10882
|
+
})
|
|
10320
10883
|
]
|
|
10321
|
-
}
|
|
10322
|
-
query.length < 2 && /* @__PURE__ */
|
|
10884
|
+
}),
|
|
10885
|
+
query.length < 2 && /* @__PURE__ */ jsx5(Text7, {
|
|
10323
10886
|
dimColor: true,
|
|
10324
10887
|
children: "Type at least 2 characters to search"
|
|
10325
|
-
}
|
|
10326
|
-
query.length >= 2 && results.length === 0 && /* @__PURE__ */
|
|
10888
|
+
}),
|
|
10889
|
+
query.length >= 2 && results.length === 0 && /* @__PURE__ */ jsxs4(Text7, {
|
|
10327
10890
|
dimColor: true,
|
|
10328
10891
|
children: [
|
|
10329
10892
|
'No hooks found for "',
|
|
10330
10893
|
query,
|
|
10331
10894
|
'"'
|
|
10332
10895
|
]
|
|
10333
|
-
}
|
|
10334
|
-
results.length > 0 && mode === "search" && /* @__PURE__ */
|
|
10896
|
+
}),
|
|
10897
|
+
results.length > 0 && mode === "search" && /* @__PURE__ */ jsxs4(Text7, {
|
|
10335
10898
|
dimColor: true,
|
|
10336
10899
|
children: [
|
|
10337
10900
|
"Found ",
|
|
10338
10901
|
results.length,
|
|
10339
10902
|
" hook(s) \u2014 press down arrow to browse results"
|
|
10340
10903
|
]
|
|
10341
|
-
}
|
|
10342
|
-
results.length > 0 && mode === "select" && /* @__PURE__ */
|
|
10904
|
+
}),
|
|
10905
|
+
results.length > 0 && mode === "select" && /* @__PURE__ */ jsx5(DataTable, {
|
|
10343
10906
|
hooks: results,
|
|
10344
10907
|
selected,
|
|
10345
10908
|
onToggle,
|
|
10346
10909
|
onConfirm,
|
|
10347
10910
|
onBack: () => setMode("search")
|
|
10348
|
-
}
|
|
10911
|
+
})
|
|
10349
10912
|
]
|
|
10350
|
-
}
|
|
10913
|
+
});
|
|
10351
10914
|
}
|
|
10352
10915
|
|
|
10353
10916
|
// src/cli/components/InstallProgress.tsx
|
|
10354
10917
|
import { useState as useState6, useEffect as useEffect5 } from "react";
|
|
10355
10918
|
import { Box as Box7, Text as Text9 } from "ink";
|
|
10356
10919
|
|
|
10357
|
-
// node_modules/ink-spinner/build/index.js
|
|
10920
|
+
// ../../node_modules/.bun/ink-spinner@5.0.0+fa875fef9f949ebd/node_modules/ink-spinner/build/index.js
|
|
10358
10921
|
var import_cli_spinners = __toESM(require_cli_spinners(), 1);
|
|
10359
10922
|
import React7, { useState as useState5, useEffect as useEffect4 } from "react";
|
|
10360
10923
|
import { Text as Text8 } from "ink";
|
|
@@ -10379,7 +10942,7 @@ var build_default2 = Spinner;
|
|
|
10379
10942
|
// src/cli/components/InstallProgress.tsx
|
|
10380
10943
|
init_installer();
|
|
10381
10944
|
init_registry();
|
|
10382
|
-
import {
|
|
10945
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
10383
10946
|
function InstallProgress({
|
|
10384
10947
|
hooks,
|
|
10385
10948
|
overwrite = false,
|
|
@@ -10403,55 +10966,55 @@ function InstallProgress({
|
|
|
10403
10966
|
};
|
|
10404
10967
|
install();
|
|
10405
10968
|
}, [hooks, overwrite]);
|
|
10406
|
-
return /* @__PURE__ */
|
|
10969
|
+
return /* @__PURE__ */ jsxs5(Box7, {
|
|
10407
10970
|
flexDirection: "column",
|
|
10408
10971
|
children: [
|
|
10409
|
-
/* @__PURE__ */
|
|
10972
|
+
/* @__PURE__ */ jsx6(Box7, {
|
|
10410
10973
|
marginBottom: 1,
|
|
10411
|
-
children: /* @__PURE__ */
|
|
10974
|
+
children: /* @__PURE__ */ jsx6(Text9, {
|
|
10412
10975
|
bold: true,
|
|
10413
10976
|
children: installing ? `Installing hooks (${current + 1}/${hooks.length})...` : "Installation complete!"
|
|
10414
|
-
}
|
|
10415
|
-
}
|
|
10977
|
+
})
|
|
10978
|
+
}),
|
|
10416
10979
|
hooks.map((name, i) => {
|
|
10417
10980
|
const result = results[i];
|
|
10418
10981
|
const isCurrent = i === current && installing;
|
|
10419
10982
|
const meta = getHook(name);
|
|
10420
|
-
return /* @__PURE__ */
|
|
10983
|
+
return /* @__PURE__ */ jsxs5(Box7, {
|
|
10421
10984
|
flexDirection: "column",
|
|
10422
10985
|
children: [
|
|
10423
|
-
isCurrent && !result && /* @__PURE__ */
|
|
10986
|
+
isCurrent && !result && /* @__PURE__ */ jsxs5(Text9, {
|
|
10424
10987
|
color: "cyan",
|
|
10425
10988
|
children: [
|
|
10426
|
-
/* @__PURE__ */
|
|
10989
|
+
/* @__PURE__ */ jsx6(build_default2, {
|
|
10427
10990
|
type: "dots"
|
|
10428
|
-
}
|
|
10991
|
+
}),
|
|
10429
10992
|
" ",
|
|
10430
10993
|
name
|
|
10431
10994
|
]
|
|
10432
|
-
}
|
|
10433
|
-
result?.success && /* @__PURE__ */
|
|
10995
|
+
}),
|
|
10996
|
+
result?.success && /* @__PURE__ */ jsxs5(Box7, {
|
|
10434
10997
|
flexDirection: "column",
|
|
10435
10998
|
children: [
|
|
10436
|
-
/* @__PURE__ */
|
|
10999
|
+
/* @__PURE__ */ jsxs5(Text9, {
|
|
10437
11000
|
color: "green",
|
|
10438
11001
|
children: [
|
|
10439
11002
|
"\u2713",
|
|
10440
11003
|
" ",
|
|
10441
11004
|
name
|
|
10442
11005
|
]
|
|
10443
|
-
}
|
|
10444
|
-
meta && /* @__PURE__ */
|
|
11006
|
+
}),
|
|
11007
|
+
meta && /* @__PURE__ */ jsxs5(Text9, {
|
|
10445
11008
|
dimColor: true,
|
|
10446
11009
|
children: [
|
|
10447
11010
|
" ",
|
|
10448
11011
|
meta.event,
|
|
10449
11012
|
meta.matcher ? ` [${meta.matcher}]` : ""
|
|
10450
11013
|
]
|
|
10451
|
-
}
|
|
11014
|
+
})
|
|
10452
11015
|
]
|
|
10453
|
-
}
|
|
10454
|
-
result && !result.success && /* @__PURE__ */
|
|
11016
|
+
}),
|
|
11017
|
+
result && !result.success && /* @__PURE__ */ jsxs5(Text9, {
|
|
10455
11018
|
color: "red",
|
|
10456
11019
|
children: [
|
|
10457
11020
|
"\u2717",
|
|
@@ -10460,54 +11023,54 @@ function InstallProgress({
|
|
|
10460
11023
|
" - ",
|
|
10461
11024
|
result.error
|
|
10462
11025
|
]
|
|
10463
|
-
}
|
|
10464
|
-
!isCurrent && !result && /* @__PURE__ */
|
|
11026
|
+
}),
|
|
11027
|
+
!isCurrent && !result && /* @__PURE__ */ jsxs5(Text9, {
|
|
10465
11028
|
dimColor: true,
|
|
10466
11029
|
children: [
|
|
10467
11030
|
"\u25CB",
|
|
10468
11031
|
" ",
|
|
10469
11032
|
name
|
|
10470
11033
|
]
|
|
10471
|
-
}
|
|
11034
|
+
})
|
|
10472
11035
|
]
|
|
10473
|
-
}, name
|
|
11036
|
+
}, name);
|
|
10474
11037
|
}),
|
|
10475
|
-
!installing && /* @__PURE__ */
|
|
11038
|
+
!installing && /* @__PURE__ */ jsxs5(Box7, {
|
|
10476
11039
|
marginTop: 1,
|
|
10477
11040
|
flexDirection: "column",
|
|
10478
11041
|
children: [
|
|
10479
|
-
/* @__PURE__ */
|
|
11042
|
+
/* @__PURE__ */ jsxs5(Text9, {
|
|
10480
11043
|
children: [
|
|
10481
|
-
/* @__PURE__ */
|
|
11044
|
+
/* @__PURE__ */ jsxs5(Text9, {
|
|
10482
11045
|
color: "green",
|
|
10483
11046
|
children: [
|
|
10484
11047
|
results.filter((r) => r.success).length,
|
|
10485
11048
|
" installed"
|
|
10486
11049
|
]
|
|
10487
|
-
}
|
|
10488
|
-
results.some((r) => !r.success) && /* @__PURE__ */
|
|
11050
|
+
}),
|
|
11051
|
+
results.some((r) => !r.success) && /* @__PURE__ */ jsxs5(Text9, {
|
|
10489
11052
|
color: "red",
|
|
10490
11053
|
children: [
|
|
10491
11054
|
", ",
|
|
10492
11055
|
results.filter((r) => !r.success).length,
|
|
10493
11056
|
" failed"
|
|
10494
11057
|
]
|
|
10495
|
-
}
|
|
11058
|
+
})
|
|
10496
11059
|
]
|
|
10497
|
-
}
|
|
10498
|
-
/* @__PURE__ */
|
|
11060
|
+
}),
|
|
11061
|
+
/* @__PURE__ */ jsx6(Text9, {
|
|
10499
11062
|
dimColor: true,
|
|
10500
11063
|
children: "Hooks installed to .hooks/ and registered in ~/.claude/settings.json"
|
|
10501
|
-
}
|
|
11064
|
+
})
|
|
10502
11065
|
]
|
|
10503
|
-
}
|
|
11066
|
+
})
|
|
10504
11067
|
]
|
|
10505
|
-
}
|
|
11068
|
+
});
|
|
10506
11069
|
}
|
|
10507
11070
|
|
|
10508
11071
|
// src/cli/components/App.tsx
|
|
10509
11072
|
init_registry();
|
|
10510
|
-
import {
|
|
11073
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
10511
11074
|
function App({ initialHooks, overwrite = false }) {
|
|
10512
11075
|
const { exit } = useApp();
|
|
10513
11076
|
const [view, setView] = useState7(initialHooks?.length ? "installing" : "main");
|
|
@@ -10554,44 +11117,44 @@ function App({ initialHooks, overwrite = false }) {
|
|
|
10554
11117
|
setView(item.value);
|
|
10555
11118
|
}
|
|
10556
11119
|
};
|
|
10557
|
-
return /* @__PURE__ */
|
|
11120
|
+
return /* @__PURE__ */ jsxs6(Box8, {
|
|
10558
11121
|
flexDirection: "column",
|
|
10559
11122
|
padding: 1,
|
|
10560
11123
|
children: [
|
|
10561
|
-
/* @__PURE__ */
|
|
11124
|
+
/* @__PURE__ */ jsx7(Header, {
|
|
10562
11125
|
title: "Hooks",
|
|
10563
11126
|
subtitle: "Install Claude Code hooks for your project"
|
|
10564
|
-
}
|
|
10565
|
-
view === "main" && /* @__PURE__ */
|
|
11127
|
+
}),
|
|
11128
|
+
view === "main" && /* @__PURE__ */ jsxs6(Box8, {
|
|
10566
11129
|
flexDirection: "column",
|
|
10567
11130
|
children: [
|
|
10568
|
-
/* @__PURE__ */
|
|
11131
|
+
/* @__PURE__ */ jsx7(Box8, {
|
|
10569
11132
|
marginBottom: 1,
|
|
10570
|
-
children: /* @__PURE__ */
|
|
11133
|
+
children: /* @__PURE__ */ jsx7(Text10, {
|
|
10571
11134
|
children: "What would you like to do?"
|
|
10572
|
-
}
|
|
10573
|
-
}
|
|
10574
|
-
/* @__PURE__ */
|
|
11135
|
+
})
|
|
11136
|
+
}),
|
|
11137
|
+
/* @__PURE__ */ jsx7(SelectInput_default, {
|
|
10575
11138
|
items: mainMenuItems,
|
|
10576
11139
|
onSelect: handleMainSelect
|
|
10577
|
-
}
|
|
10578
|
-
/* @__PURE__ */
|
|
11140
|
+
}),
|
|
11141
|
+
/* @__PURE__ */ jsx7(Box8, {
|
|
10579
11142
|
marginTop: 1,
|
|
10580
|
-
children: /* @__PURE__ */
|
|
11143
|
+
children: /* @__PURE__ */ jsx7(Text10, {
|
|
10581
11144
|
dimColor: true,
|
|
10582
11145
|
children: "Press q to quit"
|
|
10583
|
-
}
|
|
10584
|
-
}
|
|
11146
|
+
})
|
|
11147
|
+
})
|
|
10585
11148
|
]
|
|
10586
|
-
}
|
|
10587
|
-
view === "browse" && !category && /* @__PURE__ */
|
|
11149
|
+
}),
|
|
11150
|
+
view === "browse" && !category && /* @__PURE__ */ jsx7(CategorySelect, {
|
|
10588
11151
|
onSelect: (cat) => {
|
|
10589
11152
|
setCategory(cat);
|
|
10590
11153
|
setView("hooks");
|
|
10591
11154
|
},
|
|
10592
11155
|
onBack: () => setView("main")
|
|
10593
|
-
}
|
|
10594
|
-
view === "hooks" && category && /* @__PURE__ */
|
|
11156
|
+
}),
|
|
11157
|
+
view === "hooks" && category && /* @__PURE__ */ jsx7(HookSelect, {
|
|
10595
11158
|
hooks: getHooksByCategory(category),
|
|
10596
11159
|
selected,
|
|
10597
11160
|
onToggle: handleToggle,
|
|
@@ -10600,57 +11163,57 @@ function App({ initialHooks, overwrite = false }) {
|
|
|
10600
11163
|
setCategory(null);
|
|
10601
11164
|
setView("browse");
|
|
10602
11165
|
}
|
|
10603
|
-
}
|
|
10604
|
-
view === "search" && /* @__PURE__ */
|
|
11166
|
+
}),
|
|
11167
|
+
view === "search" && /* @__PURE__ */ jsx7(SearchView, {
|
|
10605
11168
|
selected,
|
|
10606
11169
|
onToggle: handleToggle,
|
|
10607
11170
|
onConfirm: handleConfirm,
|
|
10608
11171
|
onBack: () => setView("main")
|
|
10609
|
-
}
|
|
10610
|
-
view === "installing" && /* @__PURE__ */
|
|
11172
|
+
}),
|
|
11173
|
+
view === "installing" && /* @__PURE__ */ jsx7(InstallProgress, {
|
|
10611
11174
|
hooks: Array.from(selected),
|
|
10612
11175
|
overwrite,
|
|
10613
11176
|
onComplete: handleComplete
|
|
10614
|
-
}
|
|
10615
|
-
view === "done" && /* @__PURE__ */
|
|
11177
|
+
}),
|
|
11178
|
+
view === "done" && /* @__PURE__ */ jsxs6(Box8, {
|
|
10616
11179
|
flexDirection: "column",
|
|
10617
11180
|
children: [
|
|
10618
|
-
/* @__PURE__ */
|
|
11181
|
+
/* @__PURE__ */ jsx7(Box8, {
|
|
10619
11182
|
marginBottom: 1,
|
|
10620
|
-
children: /* @__PURE__ */
|
|
11183
|
+
children: /* @__PURE__ */ jsx7(Text10, {
|
|
10621
11184
|
bold: true,
|
|
10622
11185
|
color: "green",
|
|
10623
11186
|
children: "Installation complete!"
|
|
10624
|
-
}
|
|
10625
|
-
}
|
|
10626
|
-
results.filter((r) => r.success).length > 0 && /* @__PURE__ */
|
|
11187
|
+
})
|
|
11188
|
+
}),
|
|
11189
|
+
results.filter((r) => r.success).length > 0 && /* @__PURE__ */ jsxs6(Box8, {
|
|
10627
11190
|
flexDirection: "column",
|
|
10628
11191
|
marginBottom: 1,
|
|
10629
11192
|
children: [
|
|
10630
|
-
/* @__PURE__ */
|
|
11193
|
+
/* @__PURE__ */ jsx7(Text10, {
|
|
10631
11194
|
bold: true,
|
|
10632
11195
|
children: "Installed:"
|
|
10633
|
-
}
|
|
10634
|
-
results.filter((r) => r.success).map((r) => /* @__PURE__ */
|
|
11196
|
+
}),
|
|
11197
|
+
results.filter((r) => r.success).map((r) => /* @__PURE__ */ jsxs6(Text10, {
|
|
10635
11198
|
color: "green",
|
|
10636
11199
|
children: [
|
|
10637
11200
|
"\u2713",
|
|
10638
11201
|
" ",
|
|
10639
11202
|
r.hook
|
|
10640
11203
|
]
|
|
10641
|
-
}, r.hook
|
|
11204
|
+
}, r.hook))
|
|
10642
11205
|
]
|
|
10643
|
-
}
|
|
10644
|
-
results.filter((r) => !r.success).length > 0 && /* @__PURE__ */
|
|
11206
|
+
}),
|
|
11207
|
+
results.filter((r) => !r.success).length > 0 && /* @__PURE__ */ jsxs6(Box8, {
|
|
10645
11208
|
flexDirection: "column",
|
|
10646
11209
|
marginBottom: 1,
|
|
10647
11210
|
children: [
|
|
10648
|
-
/* @__PURE__ */
|
|
11211
|
+
/* @__PURE__ */ jsx7(Text10, {
|
|
10649
11212
|
bold: true,
|
|
10650
11213
|
color: "red",
|
|
10651
11214
|
children: "Failed:"
|
|
10652
|
-
}
|
|
10653
|
-
results.filter((r) => !r.success).map((r) => /* @__PURE__ */
|
|
11215
|
+
}),
|
|
11216
|
+
results.filter((r) => !r.success).map((r) => /* @__PURE__ */ jsxs6(Text10, {
|
|
10654
11217
|
color: "red",
|
|
10655
11218
|
children: [
|
|
10656
11219
|
"\u2717",
|
|
@@ -10659,61 +11222,61 @@ function App({ initialHooks, overwrite = false }) {
|
|
|
10659
11222
|
": ",
|
|
10660
11223
|
r.error
|
|
10661
11224
|
]
|
|
10662
|
-
}, r.hook
|
|
11225
|
+
}, r.hook))
|
|
10663
11226
|
]
|
|
10664
|
-
}
|
|
10665
|
-
/* @__PURE__ */
|
|
11227
|
+
}),
|
|
11228
|
+
/* @__PURE__ */ jsxs6(Box8, {
|
|
10666
11229
|
marginTop: 1,
|
|
10667
11230
|
flexDirection: "column",
|
|
10668
11231
|
children: [
|
|
10669
|
-
/* @__PURE__ */
|
|
11232
|
+
/* @__PURE__ */ jsx7(Text10, {
|
|
10670
11233
|
bold: true,
|
|
10671
11234
|
children: "What happened:"
|
|
10672
|
-
}
|
|
10673
|
-
/* @__PURE__ */
|
|
11235
|
+
}),
|
|
11236
|
+
/* @__PURE__ */ jsx7(Text10, {
|
|
10674
11237
|
children: "1. Hook source copied to .hooks/"
|
|
10675
|
-
}
|
|
10676
|
-
/* @__PURE__ */
|
|
11238
|
+
}),
|
|
11239
|
+
/* @__PURE__ */ jsx7(Text10, {
|
|
10677
11240
|
children: "2. Hook registered in ~/.claude/settings.json"
|
|
10678
|
-
}
|
|
10679
|
-
/* @__PURE__ */
|
|
11241
|
+
}),
|
|
11242
|
+
/* @__PURE__ */ jsx7(Text10, {
|
|
10680
11243
|
children: "3. Ready to use in Claude Code sessions"
|
|
10681
|
-
}
|
|
11244
|
+
})
|
|
10682
11245
|
]
|
|
10683
|
-
}
|
|
10684
|
-
/* @__PURE__ */
|
|
11246
|
+
}),
|
|
11247
|
+
/* @__PURE__ */ jsxs6(Box8, {
|
|
10685
11248
|
marginTop: 1,
|
|
10686
11249
|
flexDirection: "column",
|
|
10687
11250
|
children: [
|
|
10688
|
-
/* @__PURE__ */
|
|
11251
|
+
/* @__PURE__ */ jsx7(Text10, {
|
|
10689
11252
|
bold: true,
|
|
10690
11253
|
children: "Next steps:"
|
|
10691
|
-
}
|
|
10692
|
-
/* @__PURE__ */
|
|
11254
|
+
}),
|
|
11255
|
+
/* @__PURE__ */ jsx7(Text10, {
|
|
10693
11256
|
dimColor: true,
|
|
10694
11257
|
children: " hooks list --registered Check active hooks"
|
|
10695
|
-
}
|
|
10696
|
-
/* @__PURE__ */
|
|
11258
|
+
}),
|
|
11259
|
+
/* @__PURE__ */ jsxs6(Text10, {
|
|
10697
11260
|
dimColor: true,
|
|
10698
11261
|
children: [
|
|
10699
11262
|
" hooks info ",
|
|
10700
11263
|
"<name>",
|
|
10701
11264
|
" View hook details"
|
|
10702
11265
|
]
|
|
10703
|
-
}
|
|
11266
|
+
})
|
|
10704
11267
|
]
|
|
10705
|
-
}
|
|
10706
|
-
/* @__PURE__ */
|
|
11268
|
+
}),
|
|
11269
|
+
/* @__PURE__ */ jsx7(Box8, {
|
|
10707
11270
|
marginTop: 1,
|
|
10708
|
-
children: /* @__PURE__ */
|
|
11271
|
+
children: /* @__PURE__ */ jsx7(Text10, {
|
|
10709
11272
|
dimColor: true,
|
|
10710
11273
|
children: "Press q to exit"
|
|
10711
|
-
}
|
|
10712
|
-
}
|
|
11274
|
+
})
|
|
11275
|
+
})
|
|
10713
11276
|
]
|
|
10714
|
-
}
|
|
11277
|
+
})
|
|
10715
11278
|
]
|
|
10716
|
-
}
|
|
11279
|
+
});
|
|
10717
11280
|
}
|
|
10718
11281
|
|
|
10719
11282
|
// src/cli/index.tsx
|
|
@@ -10725,10 +11288,10 @@ init_manifest();
|
|
|
10725
11288
|
init_resolve();
|
|
10726
11289
|
init_store();
|
|
10727
11290
|
init_semver();
|
|
10728
|
-
import {
|
|
10729
|
-
var __dirname4 =
|
|
10730
|
-
var pkgPath =
|
|
10731
|
-
var pkg2 = JSON.parse(
|
|
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"));
|
|
10732
11295
|
var program2 = new Command;
|
|
10733
11296
|
function resolveScope(options) {
|
|
10734
11297
|
if (options.project)
|
|
@@ -10762,7 +11325,7 @@ function projectLogRows2(rows) {
|
|
|
10762
11325
|
function readToken(tokenFile) {
|
|
10763
11326
|
if (tokenFile) {
|
|
10764
11327
|
try {
|
|
10765
|
-
return
|
|
11328
|
+
return readFileSync12(tokenFile, "utf-8").trim();
|
|
10766
11329
|
} catch {
|
|
10767
11330
|
return;
|
|
10768
11331
|
}
|
|
@@ -10823,7 +11386,7 @@ function suggestHooks(name, max = 3) {
|
|
|
10823
11386
|
}
|
|
10824
11387
|
program2.name("hooks").description("Install hooks for AI coding agents").version(pkg2.version);
|
|
10825
11388
|
program2.command("interactive", { isDefault: true }).alias("i").description("Interactive hook browser").action(() => {
|
|
10826
|
-
render(/* @__PURE__ */
|
|
11389
|
+
render(/* @__PURE__ */ jsx8(App, {}));
|
|
10827
11390
|
});
|
|
10828
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) => {
|
|
10829
11392
|
if (options.cloudflare) {
|
|
@@ -10895,11 +11458,11 @@ program2.command("run").argument("<hook>", "Hook to run").option("--profile <id>
|
|
|
10895
11458
|
process.exit(1);
|
|
10896
11459
|
}
|
|
10897
11460
|
const hookScript = resolveScriptPath2(hook);
|
|
10898
|
-
if (!hookScript || !
|
|
11461
|
+
if (!hookScript || !existsSync13(hookScript)) {
|
|
10899
11462
|
console.error(JSON.stringify({ error: `Hook script not found: ${hookScript ?? hook}` }));
|
|
10900
11463
|
process.exit(1);
|
|
10901
11464
|
}
|
|
10902
|
-
const content =
|
|
11465
|
+
const content = readFileSync12(hookScript);
|
|
10903
11466
|
const sha = sha256Of2(content);
|
|
10904
11467
|
const check = checkScriptHash2(hook, sha);
|
|
10905
11468
|
if (!check.ok) {
|
|
@@ -11005,7 +11568,7 @@ program2.command("run").argument("<hook>", "Hook to run").option("--profile <id>
|
|
|
11005
11568
|
process.stderr.write(stderr);
|
|
11006
11569
|
process.exit(exitCode);
|
|
11007
11570
|
});
|
|
11008
|
-
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
|
|
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) => {
|
|
11009
11572
|
const scope = resolveScope(options);
|
|
11010
11573
|
const target = resolveTarget(options);
|
|
11011
11574
|
let toInstall = hooks;
|
|
@@ -11026,7 +11589,7 @@ program2.command("install").alias("add").argument("[hooks...]", "Hooks to instal
|
|
|
11026
11589
|
toInstall = getHooksByCategory(category).map((h) => h.name);
|
|
11027
11590
|
}
|
|
11028
11591
|
if (toInstall.length === 0) {
|
|
11029
|
-
render(/* @__PURE__ */
|
|
11592
|
+
render(/* @__PURE__ */ jsx8(App, {}));
|
|
11030
11593
|
return;
|
|
11031
11594
|
}
|
|
11032
11595
|
if (options.applyCodewith && (target === "codewith" || target === "all") && !options.codewithConfig) {
|
|
@@ -11193,7 +11756,7 @@ Dry run \u2014 would install (${scope}, ${target}):
|
|
|
11193
11756
|
return;
|
|
11194
11757
|
}
|
|
11195
11758
|
const successCount = results.filter((r) => r.success).length;
|
|
11196
|
-
const settingsFile = target === "codewith" ? options.applyCodewith ? options.codewithConfig : "TOML fragment only (
|
|
11759
|
+
const settingsFile = target === "codewith" ? options.applyCodewith ? options.codewithConfig : "TOML fragment only (configs should apply)" : scope === "project" ? ".claude/settings.json" : "~/.claude/settings.json";
|
|
11197
11760
|
console.log(chalk2.bold(`
|
|
11198
11761
|
Installing hooks (${scope}, ${target})...
|
|
11199
11762
|
`));
|
|
@@ -11467,7 +12030,7 @@ program2.command("doctor").option("-g, --global", "Check global settings", false
|
|
|
11467
12030
|
const settingsPath = getSettingsPath(scope);
|
|
11468
12031
|
const issues = [];
|
|
11469
12032
|
const healthy = [];
|
|
11470
|
-
const settingsExist =
|
|
12033
|
+
const settingsExist = existsSync13(settingsPath);
|
|
11471
12034
|
if (!settingsExist) {
|
|
11472
12035
|
issues.push({ hook: "(settings)", issue: `${settingsPath} not found`, severity: "warning" });
|
|
11473
12036
|
}
|
|
@@ -11475,7 +12038,7 @@ program2.command("doctor").option("-g, --global", "Check global settings", false
|
|
|
11475
12038
|
let wiringCount = 0;
|
|
11476
12039
|
if (settingsExist) {
|
|
11477
12040
|
try {
|
|
11478
|
-
const settings = JSON.parse(
|
|
12041
|
+
const settings = JSON.parse(readFileSync12(settingsPath, "utf-8"));
|
|
11479
12042
|
wiringCount = countSettingsWiring(settings);
|
|
11480
12043
|
} catch {}
|
|
11481
12044
|
}
|
|
@@ -11484,7 +12047,7 @@ program2.command("doctor").option("-g, --global", "Check global settings", false
|
|
|
11484
12047
|
const meta = custom ? resolveHookMeta(name) : getHook(name);
|
|
11485
12048
|
let hookHealthy = true;
|
|
11486
12049
|
if (custom) {
|
|
11487
|
-
if (!
|
|
12050
|
+
if (!existsSync13(custom.scriptPath)) {
|
|
11488
12051
|
issues.push({ hook: name, issue: `Custom hook script missing: ${custom.scriptPath}`, severity: "error" });
|
|
11489
12052
|
hookHealthy = false;
|
|
11490
12053
|
}
|
|
@@ -11495,15 +12058,15 @@ program2.command("doctor").option("-g, --global", "Check global settings", false
|
|
|
11495
12058
|
continue;
|
|
11496
12059
|
}
|
|
11497
12060
|
const hookDir = getHookPath(name);
|
|
11498
|
-
const hookScript =
|
|
11499
|
-
if (!
|
|
12061
|
+
const hookScript = join14(hookDir, "src", "hook.ts");
|
|
12062
|
+
if (!existsSync13(hookScript)) {
|
|
11500
12063
|
issues.push({ hook: name, issue: "Missing src/hook.ts in package", severity: "error" });
|
|
11501
12064
|
hookHealthy = false;
|
|
11502
12065
|
}
|
|
11503
12066
|
}
|
|
11504
12067
|
if (meta && settingsExist) {
|
|
11505
12068
|
try {
|
|
11506
|
-
const settings = JSON.parse(
|
|
12069
|
+
const settings = JSON.parse(readFileSync12(settingsPath, "utf-8"));
|
|
11507
12070
|
if (!hookRegisteredInSettings(settings, name, meta.event, meta.matcher)) {
|
|
11508
12071
|
const eventName = meta.event.split(":")[0];
|
|
11509
12072
|
issues.push({ hook: name, issue: `Not registered under correct event (${eventName})`, severity: "error" });
|
|
@@ -11670,10 +12233,10 @@ program2.command("docs").argument("[hook]", "Hook name (shows general docs if om
|
|
|
11670
12233
|
return;
|
|
11671
12234
|
}
|
|
11672
12235
|
const hookPath = getHookPath(hook);
|
|
11673
|
-
const readmePath =
|
|
12236
|
+
const readmePath = join14(hookPath, "README.md");
|
|
11674
12237
|
let readme = "";
|
|
11675
|
-
if (
|
|
11676
|
-
readme =
|
|
12238
|
+
if (existsSync13(readmePath)) {
|
|
12239
|
+
readme = readFileSync12(readmePath, "utf-8");
|
|
11677
12240
|
}
|
|
11678
12241
|
if (options.json) {
|
|
11679
12242
|
console.log(JSON.stringify({ ...meta, readme }));
|
|
@@ -11727,7 +12290,7 @@ ${meta.displayName} v${meta.version}
|
|
|
11727
12290
|
installation: {
|
|
11728
12291
|
global: "hooks install gitguard",
|
|
11729
12292
|
project: "hooks install gitguard --project",
|
|
11730
|
-
codewith: "hooks install session-start --target codewith # emits TOML for
|
|
12293
|
+
codewith: "hooks install session-start --target codewith # emits TOML for configs to apply",
|
|
11731
12294
|
category: 'hooks install --category "Git Safety"',
|
|
11732
12295
|
all: "hooks install --all"
|
|
11733
12296
|
},
|
|
@@ -11870,9 +12433,9 @@ program2.command("profile-import").argument("<file>", "JSON file to import profi
|
|
|
11870
12433
|
if (file === "-") {
|
|
11871
12434
|
raw = await new Response(Bun.stdin.stream()).text();
|
|
11872
12435
|
} else {
|
|
11873
|
-
const { readFileSync:
|
|
12436
|
+
const { readFileSync: readFileSync13 } = await import("fs");
|
|
11874
12437
|
try {
|
|
11875
|
-
raw =
|
|
12438
|
+
raw = readFileSync13(file, "utf-8");
|
|
11876
12439
|
} catch {
|
|
11877
12440
|
if (options.json) {
|
|
11878
12441
|
console.log(JSON.stringify({ error: `Cannot read file: ${file}` }));
|