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