@hasna/hooks 0.7.9 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -6
- package/bin/index.js +1573 -695
- package/bin/serve.js +713 -335
- package/dist/config.d.ts +25 -19
- package/dist/db/index.d.ts +3 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1162 -473
- package/dist/lib/app-home.d.ts +74 -0
- package/dist/lib/installer.d.ts +0 -9
- package/dist/lib/local-opt-in.d.ts +80 -0
- package/dist/lib/profiles.d.ts +14 -1
- package/dist/lib/resolver-types.d.ts +45 -0
- package/dist/lib/sync.d.ts +13 -8
- package/dist/lib/transport.d.ts +87 -0
- package/dist/openapi.d.ts +1 -1
- package/dist/serve.d.ts +7 -0
- package/dist/storage.js +141 -27
- package/hooks/hook-affected-tests/package.json +1 -1
- package/hooks/hook-agent-rules-version-check/package.json +1 -1
- package/hooks/hook-agentmessages/package.json +1 -1
- package/hooks/hook-announce-start/package.json +1 -1
- package/hooks/hook-announce-stop/package.json +1 -1
- package/hooks/hook-autoformat/package.json +1 -1
- package/hooks/hook-branchprotect/package.json +1 -1
- package/hooks/hook-checkbugs/package.json +1 -1
- package/hooks/hook-checkdocs/package.json +1 -1
- package/hooks/hook-checkfiles/package.json +1 -1
- package/hooks/hook-checklint/package.json +1 -1
- package/hooks/hook-checkpoint/package.json +1 -1
- package/hooks/hook-checksecurity/package.json +1 -1
- package/hooks/hook-checktasks/package.json +1 -1
- package/hooks/hook-checktests/package.json +1 -1
- package/hooks/hook-conflict-detect/package.json +1 -1
- package/hooks/hook-contextrefresh/package.json +1 -1
- package/hooks/hook-desktopnotify/package.json +1 -1
- package/hooks/hook-dm-inject/package.json +1 -1
- package/hooks/hook-envsetup/package.json +1 -1
- package/hooks/hook-failure-to-task/package.json +1 -1
- package/hooks/hook-filelock/package.json +1 -1
- package/hooks/hook-fleet-blockers-gate/package.json +1 -1
- package/hooks/hook-fleet-catchup/package.json +1 -1
- package/hooks/hook-gitguard/package.json +1 -1
- package/hooks/hook-packageage/package.json +1 -1
- package/hooks/hook-permissionguard/package.json +1 -1
- package/hooks/hook-phonenotify/package.json +1 -1
- package/hooks/hook-precompact/package.json +1 -1
- package/hooks/hook-protectfiles/package.json +1 -1
- package/hooks/hook-stylescheck/package.json +1 -1
- package/hooks/hook-typecheck-gate/package.json +1 -1
- package/package.json +6 -9
- package/scripts/artifact-scan.ts +49 -0
- package/scripts/ensure-profiles-dir.mjs +99 -0
- package/scripts/validate-package.ts +280 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/hooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
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": {
|
|
@@ -27,10 +27,7 @@
|
|
|
27
27
|
"typecheck": "tsc --noEmit",
|
|
28
28
|
"validate:package": "bun run build && bun scripts/validate-package.ts",
|
|
29
29
|
"prepublishOnly": "bun run validate:package",
|
|
30
|
-
"
|
|
31
|
-
"dashboard:build": "cd dashboard && bun run build",
|
|
32
|
-
"dashboard:preview": "cd dashboard && bun run preview",
|
|
33
|
-
"postinstall": "mkdir -p $HOME/.hasna/hooks/profiles 2>/dev/null || true",
|
|
30
|
+
"postinstall": "node scripts/ensure-profiles-dir.mjs",
|
|
34
31
|
"scan:artifact": "bun scripts/artifact-scan.ts",
|
|
35
32
|
"prepack": "bun run build && bun run scan:artifact"
|
|
36
33
|
},
|
|
@@ -47,12 +44,12 @@
|
|
|
47
44
|
"author": "Hasna",
|
|
48
45
|
"license": "Apache-2.0",
|
|
49
46
|
"devDependencies": {
|
|
50
|
-
"@types/bun": "
|
|
47
|
+
"@types/bun": "1.3.14",
|
|
51
48
|
"@types/pg": "^8.15.6",
|
|
52
49
|
"@types/react": "^18.2.0",
|
|
53
50
|
"typescript": "^5",
|
|
54
51
|
"bun-types": "1.3.14",
|
|
55
|
-
"@hasna/contracts": "0.
|
|
52
|
+
"@hasna/contracts": "1.0.2"
|
|
56
53
|
},
|
|
57
54
|
"dependencies": {
|
|
58
55
|
"@hasna/events": "^0.1.16",
|
|
@@ -67,8 +64,7 @@
|
|
|
67
64
|
"ink-text-input": "^6.0.0",
|
|
68
65
|
"pg": "^8.16.3",
|
|
69
66
|
"react": "^18.2.0",
|
|
70
|
-
"zod": "3.25.76"
|
|
71
|
-
"@hasna/contracts": "0.14.0"
|
|
67
|
+
"zod": "3.25.76"
|
|
72
68
|
},
|
|
73
69
|
"engines": {
|
|
74
70
|
"bun": ">=1.0.0"
|
|
@@ -82,6 +78,7 @@
|
|
|
82
78
|
"dist/",
|
|
83
79
|
"hooks/",
|
|
84
80
|
"!hooks/*/dist/",
|
|
81
|
+
"scripts/",
|
|
85
82
|
"README.md",
|
|
86
83
|
"LICENSE"
|
|
87
84
|
],
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Pack this repo the way `npm publish` would, then scan what actually shipped.
|
|
2
|
+
//
|
|
3
|
+
// `contracts artifact-scan` takes a PACKED .tgz — running it against `src/` is
|
|
4
|
+
// explicitly wrong, and running it with no target at all just exits 1 and takes
|
|
5
|
+
// `npm pack` / `bun publish` down with it. So this script produces the tarball
|
|
6
|
+
// first and hands the real artifact to the scanner.
|
|
7
|
+
//
|
|
8
|
+
// Packing uses `--ignore-scripts`: without it, packing from inside `prepack`
|
|
9
|
+
// would re-enter `prepack` forever.
|
|
10
|
+
//
|
|
11
|
+
// The scanner comes from the pinned `@hasna/contracts` devDependency
|
|
12
|
+
// (node_modules/.bin), never `bunx` — an unpinned package runner makes the
|
|
13
|
+
// release gate depend on whatever the registry serves that day.
|
|
14
|
+
|
|
15
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { isAbsolute, join } from "node:path";
|
|
18
|
+
|
|
19
|
+
function run(command: string[], cwd: string): string {
|
|
20
|
+
const result = Bun.spawnSync(command, { cwd, stdout: "pipe", stderr: "pipe" });
|
|
21
|
+
const stdout = new TextDecoder().decode(result.stdout).trim();
|
|
22
|
+
if (result.exitCode !== 0) {
|
|
23
|
+
const stderr = new TextDecoder().decode(result.stderr).trim();
|
|
24
|
+
throw new Error(`${command.join(" ")} exited ${result.exitCode}\n${stdout}\n${stderr}`);
|
|
25
|
+
}
|
|
26
|
+
return stdout;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const repoRoot = join(import.meta.dir, "..");
|
|
30
|
+
const scanner = join(repoRoot, "node_modules", "@hasna", "contracts", "dist", "cli", "index.js");
|
|
31
|
+
const workspace = mkdtempSync(join(tmpdir(), "hooks-artifact-scan-"));
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const packed = run(["bun", "pm", "pack", "--destination", workspace, "--ignore-scripts", "--quiet"], repoRoot);
|
|
35
|
+
const archive = isAbsolute(packed) ? packed : join(workspace, packed);
|
|
36
|
+
// The manifest is where a reviewed waiver would be declared, so the scanner
|
|
37
|
+
// has to read it; a waiver the enforcement never loads is a dead end.
|
|
38
|
+
const scan = Bun.spawnSync([scanner, "artifact-scan", archive, "--manifest", join(repoRoot, "hasna.contract.json")], {
|
|
39
|
+
cwd: repoRoot,
|
|
40
|
+
stdout: "inherit",
|
|
41
|
+
stderr: "inherit",
|
|
42
|
+
});
|
|
43
|
+
if (scan.exitCode !== 0) {
|
|
44
|
+
console.error("\nA published artifact must not carry a bulk asset inventory.");
|
|
45
|
+
process.exit(scan.exitCode ?? 1);
|
|
46
|
+
}
|
|
47
|
+
} finally {
|
|
48
|
+
rmSync(workspace, { recursive: true, force: true });
|
|
49
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Ensure the hooks agent-profile directory exists at the effective data root.
|
|
2
|
+
// Runs at install time (postinstall). Mirrors src/lib/app-home.ts resolution:
|
|
3
|
+
// granular data-dir override (HASNA_HOOKS_DATA_DIR/HOOKS_DATA_DIR), then
|
|
4
|
+
// exact-app override (HASNA_HOOKS_HOME/HOOKS_HOME), then the @hasna/paths
|
|
5
|
+
// resolver data root once adopted (HASNA_DATA_HOME set or hooks.db already
|
|
6
|
+
// present there), then the legacy ~/.hasna/hooks default. Best-effort — the
|
|
7
|
+
// package must never fail to install because this script cannot run.
|
|
8
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
9
|
+
// --- Local path resolver -------------------------------------------------
|
|
10
|
+
// @hasna/paths was deleted (hasna/apps#1535, 2026-09-03); this in-package
|
|
11
|
+
// implementation preserves the resolver contract (XDG / macOS home layout
|
|
12
|
+
// honoring HASNA_{CONFIG,DATA,STATE,CACHE}_HOME, with the same env-override
|
|
13
|
+
// and home-override semantics the deleted package had).
|
|
14
|
+
import { homedir as pathsResolverHomedir } from "node:os";
|
|
15
|
+
import { join as pathsResolverJoin } from "node:path";
|
|
16
|
+
|
|
17
|
+
const PATHS_RESOLVER_KIND_ENV = {
|
|
18
|
+
config: "HASNA_CONFIG_HOME",
|
|
19
|
+
data: "HASNA_DATA_HOME",
|
|
20
|
+
state: "HASNA_STATE_HOME",
|
|
21
|
+
cache: "HASNA_CACHE_HOME",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function pathsResolverBaseDir(kind, options) {
|
|
25
|
+
const env = options.env ?? process.env;
|
|
26
|
+
const override = env[PATHS_RESOLVER_KIND_ENV[kind]];
|
|
27
|
+
if (typeof override === "string" && override.length > 0) return override;
|
|
28
|
+
const home = options.home ?? pathsResolverHomedir();
|
|
29
|
+
const platform = options.platform ?? process.platform;
|
|
30
|
+
if (platform === "darwin") {
|
|
31
|
+
switch (kind) {
|
|
32
|
+
case "config":
|
|
33
|
+
case "data":
|
|
34
|
+
return pathsResolverJoin(home, "Library", "Application Support", "Hasna");
|
|
35
|
+
case "cache":
|
|
36
|
+
return pathsResolverJoin(home, "Library", "Caches", "Hasna");
|
|
37
|
+
case "state":
|
|
38
|
+
return pathsResolverJoin(home, "Library", "Logs", "Hasna");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
switch (kind) {
|
|
42
|
+
case "config":
|
|
43
|
+
return pathsResolverJoin(home, ".config", "hasna");
|
|
44
|
+
case "data":
|
|
45
|
+
return pathsResolverJoin(home, ".local", "share", "hasna");
|
|
46
|
+
case "state":
|
|
47
|
+
return pathsResolverJoin(home, ".local", "state", "hasna");
|
|
48
|
+
case "cache":
|
|
49
|
+
return pathsResolverJoin(home, ".cache", "hasna");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function pathsResolverResolve(kind, options) {
|
|
54
|
+
const appSegment = options.internal === true ? pathsResolverJoin("internal", options.app) : options.app;
|
|
55
|
+
return pathsResolverJoin(pathsResolverBaseDir(kind, options), appSegment);
|
|
56
|
+
}
|
|
57
|
+
function dataDir(options) {
|
|
58
|
+
return pathsResolverResolve("data", options);
|
|
59
|
+
}
|
|
60
|
+
import { homedir } from "node:os";
|
|
61
|
+
import { join, resolve } from "node:path";
|
|
62
|
+
|
|
63
|
+
const HOME = process.env.HOME || process.env.USERPROFILE || homedir();
|
|
64
|
+
|
|
65
|
+
function legacyDataRoot() {
|
|
66
|
+
return join(HOME, ".hasna", "hooks");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function adoptResolverRoot(resolved) {
|
|
70
|
+
const override = process.env.HASNA_DATA_HOME;
|
|
71
|
+
if (typeof override === "string" && override.trim().length > 0) return true;
|
|
72
|
+
return existsSync(join(resolved, "hooks.db"));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function effectiveDataRoot() {
|
|
76
|
+
const explicit = process.env.HASNA_HOOKS_DATA_DIR ?? process.env.HOOKS_DATA_DIR;
|
|
77
|
+
if (typeof explicit === "string" && explicit.trim().length > 0) return explicit.trim();
|
|
78
|
+
|
|
79
|
+
const exact = process.env.HASNA_HOOKS_HOME?.trim() || process.env.HOOKS_HOME?.trim();
|
|
80
|
+
if (exact) return resolve(exact);
|
|
81
|
+
|
|
82
|
+
let resolved = null;
|
|
83
|
+
try {
|
|
84
|
+
// (local resolver — @hasna/paths deleted, hasna/apps#1535)
|
|
85
|
+
resolved = dataDir({ app: "hooks", home: HOME });
|
|
86
|
+
} catch {
|
|
87
|
+
// @hasna/paths unavailable — fall back to the legacy root.
|
|
88
|
+
}
|
|
89
|
+
if (resolved && adoptResolverRoot(resolved)) return resolve(resolved);
|
|
90
|
+
return legacyDataRoot();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Best-effort: a profile-directory creation failure must never block install.
|
|
94
|
+
try {
|
|
95
|
+
const root = await effectiveDataRoot();
|
|
96
|
+
mkdirSync(join(root, "profiles"), { recursive: true });
|
|
97
|
+
} catch {
|
|
98
|
+
// ignore
|
|
99
|
+
}
|