@hasna/hooks 0.8.0 → 0.9.1
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 +33 -5
- package/bin/hooks-mcp.js +4862 -0
- package/bin/index.js +592 -170
- package/bin/serve.js +33 -10
- package/dist/db/index.d.ts +8 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2870 -2709
- package/dist/lib/db-writer.d.ts +30 -1
- package/dist/lib/installer.d.ts +40 -1
- package/dist/lib/local-opt-in.d.ts +26 -0
- package/dist/lib/registration.d.ts +75 -0
- package/dist/lib/registry.d.ts +14 -0
- package/dist/lib/sync.d.ts +17 -1
- package/dist/sdk/index.d.ts +103 -0
- package/dist/sdk/index.js +1050 -0
- package/dist/storage.js +15 -1
- package/hooks/hook-agent-rules-version-check/README.md +1 -1
- package/hooks/hook-trash-guard/README.md +147 -0
- package/hooks/hook-trash-guard/package.json +12 -0
- package/hooks/hook-trash-guard/src/hook.ts +1142 -0
- package/package.json +10 -3
- package/scripts/validate-package.ts +31 -4
- package/hooks/codewith-native-common.test.ts +0 -1935
- package/hooks/hook-affected-tests/tsconfig.json +0 -25
- package/hooks/hook-agent-rules-version-check/src/hook.test.ts +0 -104
- package/hooks/hook-agent-rules-version-check/tsconfig.json +0 -25
- package/hooks/hook-announce-start/tsconfig.json +0 -25
- package/hooks/hook-announce-stop/tsconfig.json +0 -25
- package/hooks/hook-autoformat/tsconfig.json +0 -25
- package/hooks/hook-branchprotect/tsconfig.json +0 -25
- package/hooks/hook-checkbugs/tsconfig.json +0 -15
- package/hooks/hook-checkdocs/tsconfig.json +0 -15
- package/hooks/hook-checkfiles/tsconfig.json +0 -15
- package/hooks/hook-checklint/tsconfig.json +0 -15
- package/hooks/hook-checkpoint/tsconfig.json +0 -25
- package/hooks/hook-checksecurity/tsconfig.json +0 -15
- package/hooks/hook-checktasks/tsconfig.json +0 -20
- package/hooks/hook-checktests/tsconfig.json +0 -15
- package/hooks/hook-conflict-detect/tsconfig.json +0 -25
- package/hooks/hook-contextrefresh/tsconfig.json +0 -25
- package/hooks/hook-desktopnotify/tsconfig.json +0 -25
- package/hooks/hook-dm-inject/tsconfig.json +0 -25
- package/hooks/hook-envsetup/tsconfig.json +0 -25
- package/hooks/hook-failure-to-task/tsconfig.json +0 -25
- package/hooks/hook-filelock/tsconfig.json +0 -25
- package/hooks/hook-fleet-blockers-gate/src/hook.test.ts +0 -302
- package/hooks/hook-fleet-blockers-gate/tsconfig.json +0 -25
- package/hooks/hook-fleet-catchup/src/hook.test.ts +0 -156
- package/hooks/hook-fleet-catchup/tsconfig.json +0 -25
- package/hooks/hook-gitguard/tsconfig.json +0 -25
- package/hooks/hook-knowledge-context/src/hook.test.ts +0 -379
- package/hooks/hook-packageage/tsconfig.json +0 -25
- package/hooks/hook-permissionguard/tsconfig.json +0 -25
- package/hooks/hook-phonenotify/tsconfig.json +0 -25
- package/hooks/hook-precompact/tsconfig.json +0 -25
- package/hooks/hook-protectfiles/tsconfig.json +0 -25
- package/hooks/hook-scanoutput/src/hook.test.ts +0 -217
- package/hooks/hook-spiral-detector/src/hook.test.ts +0 -72
- package/hooks/hook-stylescheck/tsconfig.json +0 -25
- package/hooks/hook-typecheck-gate/tsconfig.json +0 -25
- package/hooks/hook-workspace-repos-guard/src/hook.test.ts +0 -466
- package/hooks/hook-workspace-repos-guard/tsconfig.json +0 -21
- package/hooks/mention-context/src/hook.test.ts +0 -68
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/hooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Open source hooks library for AI coding agents - Install safety, quality, and automation hooks with a single command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"hooks": "bin/index.js",
|
|
8
|
+
"hooks-mcp": "bin/hooks-mcp.js",
|
|
8
9
|
"hooks-serve": "bin/serve.js"
|
|
9
10
|
},
|
|
10
11
|
"exports": {
|
|
@@ -12,6 +13,10 @@
|
|
|
12
13
|
"types": "./dist/index.d.ts",
|
|
13
14
|
"import": "./dist/index.js"
|
|
14
15
|
},
|
|
16
|
+
"./sdk": {
|
|
17
|
+
"types": "./dist/sdk/index.d.ts",
|
|
18
|
+
"import": "./dist/sdk/index.js"
|
|
19
|
+
},
|
|
15
20
|
"./storage": {
|
|
16
21
|
"types": "./dist/storage.d.ts",
|
|
17
22
|
"import": "./dist/storage.js"
|
|
@@ -20,7 +25,7 @@
|
|
|
20
25
|
"main": "./dist/index.js",
|
|
21
26
|
"types": "./dist/index.d.ts",
|
|
22
27
|
"scripts": {
|
|
23
|
-
"build": "rm -rf dist bin && bun build ./src/cli/index.tsx --outdir ./bin --target bun --external pg --external ink --external react --external chalk --external conf --external @modelcontextprotocol/sdk --external zod && bun build ./src/serve.ts --outdir ./bin --target bun --external pg && bun build ./src/index.ts ./src/storage.ts --outdir ./dist --target bun --external pg && bun run build:types",
|
|
28
|
+
"build": "rm -rf dist bin && bun build ./src/cli/index.tsx --outdir ./bin --target bun --external pg --external ink --external react --external chalk --external conf --external @modelcontextprotocol/sdk --external zod && bun build ./src/serve.ts --outdir ./bin --target bun --external pg && bun build ./src/mcp/hooks-mcp.ts --outdir ./bin --target bun --external pg --external chalk --external conf --external @modelcontextprotocol/sdk --external zod && bun build ./src/sdk/index.ts --outdir ./dist/sdk --target bun && bun build ./src/index.ts ./src/storage.ts --outdir ./dist --target bun --external pg && bun run build:types",
|
|
24
29
|
"build:types": "tsc -p tsconfig.build.json",
|
|
25
30
|
"dev": "bun run ./src/cli/index.tsx",
|
|
26
31
|
"test": "bun test",
|
|
@@ -80,7 +85,9 @@
|
|
|
80
85
|
"!hooks/*/dist/",
|
|
81
86
|
"scripts/",
|
|
82
87
|
"README.md",
|
|
83
|
-
"LICENSE"
|
|
88
|
+
"LICENSE",
|
|
89
|
+
"!hooks/**/*.test.ts",
|
|
90
|
+
"!hooks/**/tsconfig.json"
|
|
84
91
|
],
|
|
85
92
|
"homepage": "https://github.com/hasna/hooks#readme",
|
|
86
93
|
"bugs": {
|
|
@@ -173,15 +173,27 @@ async function validateExtractedSmoke(pkg: PackageJson): Promise<void> {
|
|
|
173
173
|
const dataDir = join(workspace, "data");
|
|
174
174
|
const binIndex = join(packageDir, "bin", "index.js");
|
|
175
175
|
const binServe = join(packageDir, "bin", "serve.js");
|
|
176
|
+
const binMcp = join(packageDir, "bin", "hooks-mcp.js");
|
|
177
|
+
const sdkBundle = join(packageDir, "dist", "sdk", "index.js");
|
|
176
178
|
if (!existsSync(binIndex)) throw new Error(`packed tarball is missing bin/index.js (CLI bin)`);
|
|
177
179
|
if (!existsSync(binServe)) throw new Error(`packed tarball is missing bin/serve.js (serve bin)`);
|
|
180
|
+
if (!existsSync(binMcp)) throw new Error(`packed tarball is missing bin/hooks-mcp.js (MCP bin)`);
|
|
181
|
+
if (!existsSync(sdkBundle)) throw new Error(`packed tarball is missing dist/sdk/index.js (./sdk export)`);
|
|
178
182
|
|
|
183
|
+
// Hermetic route: the smoke lanes below exercise the on-box store on
|
|
184
|
+
// purpose, so they opt in explicitly (hasna/apps#1720 — without the
|
|
185
|
+
// opt-in `hooks run` and `hooks-mcp` fail closed, which the fail-closed
|
|
186
|
+
// lane checks separately) and keep the station's Keychain out of it.
|
|
179
187
|
const env = {
|
|
180
188
|
...process.env,
|
|
189
|
+
HASNA_STATION: "no-such-station",
|
|
190
|
+
HASNA_HOOKS_LOCAL: "1",
|
|
181
191
|
HASNA_HOOKS_DATA_DIR: dataDir,
|
|
182
192
|
HASNA_HOOKS_DB_PATH: join(dataDir, "hooks.db"),
|
|
183
193
|
NO_COLOR: "1",
|
|
184
194
|
};
|
|
195
|
+
const failClosedEnv = { ...env };
|
|
196
|
+
delete (failClosedEnv as Record<string, string | undefined>).HASNA_HOOKS_LOCAL;
|
|
185
197
|
|
|
186
198
|
// 1. CLI help.
|
|
187
199
|
const help = spawnSync("bun", ["run", binIndex, "--help"], { cwd: smokeDir, env, encoding: "utf8" });
|
|
@@ -210,8 +222,22 @@ async function validateExtractedSmoke(pkg: PackageJson): Promise<void> {
|
|
|
210
222
|
await new Promise((r) => setTimeout(r, 100));
|
|
211
223
|
}
|
|
212
224
|
|
|
213
|
-
//
|
|
214
|
-
|
|
225
|
+
// 2b. Fail-closed lane (hasna/apps#1720): with nothing configured the
|
|
226
|
+
// packed CLI and the packed hooks-mcp bin exit non-zero, name the
|
|
227
|
+
// credential tiers + the opt-in on stderr, and create no local store.
|
|
228
|
+
const failClosedHome = join(workspace, "failclosed-home");
|
|
229
|
+
const fcEnv = { ...failClosedEnv, HOME: failClosedHome, HASNA_HOOKS_DATA_DIR: join(failClosedHome, "data"), HASNA_HOOKS_DB_PATH: join(failClosedHome, "data", "hooks.db") };
|
|
230
|
+
for (const [label, argv] of [["packed CLI `hooks categories`", [binIndex, "categories"]], ["packed hooks-mcp", [binMcp]]] as Array<[string, string[]]>) {
|
|
231
|
+
const fc = spawnSync("bun", ["run", ...argv], { cwd: smokeDir, env: fcEnv, encoding: "utf8", input: "", timeout: 20000 });
|
|
232
|
+
if (fc.status === 0) throw new Error(`${label} exited 0 with nothing configured (must fail closed)`);
|
|
233
|
+
if (!/HASNA_HOOKS_LOCAL=1/.test(fc.stderr) || !/hasna\.credentials\.hooks\.api-key/.test(fc.stderr)) {
|
|
234
|
+
throw new Error(`${label} refusal did not name the tiers + opt-in: ${fc.stderr.slice(0, 300)}`);
|
|
235
|
+
}
|
|
236
|
+
if (existsSync(join(failClosedHome, "data", "hooks.db"))) throw new Error(`${label} created hooks.db while failing closed`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 3. MCP stdio startup (explicit local opt-in): an initialize handshake must get a response.
|
|
240
|
+
const mcpProc = spawn("bun", ["run", binMcp], {
|
|
215
241
|
cwd: smokeDir,
|
|
216
242
|
env,
|
|
217
243
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -252,11 +278,12 @@ async function validateExtractedSmoke(pkg: PackageJson): Promise<void> {
|
|
|
252
278
|
// pg link is best-effort; the import below will fail loudly if needed.
|
|
253
279
|
}
|
|
254
280
|
const sdkSmoke = join(smokeDir, "sdk-smoke.ts");
|
|
255
|
-
await writeFile(sdkSmoke, `import { HOOKS, getStorageStatus } from "@hasna/hooks";\nimport { getStorageStatus as ss } from "@hasna/hooks/storage";\nconsole.log(JSON.stringify({ count: HOOKS.length, backend: getStorageStatus().backend, ss: ss().backend }));\n`);
|
|
281
|
+
await writeFile(sdkSmoke, `import { HOOKS, getStorageStatus } from "@hasna/hooks";\nimport { getStorageStatus as ss } from "@hasna/hooks/storage";\nimport { HooksClient, createHooksClient } from "@hasna/hooks/sdk";\nlet sdk = "threw";\ntry { createHooksClient({ env: { HASNA_STATION: "no-such-station" }, credentials: { keychain: { enabled: false } } }); sdk = "returned"; } catch (e) { sdk = /REMOTE_API_/.test(String(e)) ? "fail-closed" : "threw"; }\nconsole.log(JSON.stringify({ count: HOOKS.length, backend: getStorageStatus().backend, ss: ss().backend, sdk, hasClient: typeof HooksClient === "function" }));\n`);
|
|
256
282
|
const sdk = spawnSync("bun", ["run", sdkSmoke], { cwd: smokeDir, env, encoding: "utf8", timeout: 20000 });
|
|
257
283
|
if (sdk.status !== 0) throw new Error(`packed SDK import failed: ${sdk.stderr}`);
|
|
258
|
-
const sdkOut = JSON.parse(sdk.stdout.trim()) as { count: number; backend: string };
|
|
284
|
+
const sdkOut = JSON.parse(sdk.stdout.trim()) as { count: number; backend: string; sdk: string; hasClient: boolean };
|
|
259
285
|
if (typeof sdkOut.count !== "number" || sdkOut.count <= 0) throw new Error(`packed SDK import returned count ${sdkOut.count}`);
|
|
286
|
+
if (sdkOut.sdk !== "fail-closed" || !sdkOut.hasClient) throw new Error(`packed ./sdk export did not fail closed with nothing configured: ${JSON.stringify(sdkOut)}`);
|
|
260
287
|
|
|
261
288
|
// 5. One bundled-hook run from the packed artifact (isolated data dir;
|
|
262
289
|
// first run self-trusts, then executes).
|