@hasna/hooks 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/bin/index.js +645 -43
  2. package/dist/index.js +89 -5
  3. package/hooks/hook-affected-tests/LICENSE +191 -0
  4. package/hooks/hook-affected-tests/README.md +37 -0
  5. package/hooks/hook-affected-tests/package.json +50 -0
  6. package/hooks/hook-affected-tests/src/hook.ts +148 -0
  7. package/hooks/hook-affected-tests/tsconfig.json +25 -0
  8. package/hooks/hook-agentmessages/bin/cli.ts +125 -0
  9. package/hooks/hook-announce-start/LICENSE +191 -0
  10. package/hooks/hook-announce-start/README.md +35 -0
  11. package/hooks/hook-announce-start/package.json +51 -0
  12. package/hooks/hook-announce-start/src/hook.ts +137 -0
  13. package/hooks/hook-announce-start/tsconfig.json +25 -0
  14. package/hooks/hook-announce-stop/LICENSE +191 -0
  15. package/hooks/hook-announce-stop/README.md +35 -0
  16. package/hooks/hook-announce-stop/package.json +51 -0
  17. package/hooks/hook-announce-stop/src/hook.ts +160 -0
  18. package/hooks/hook-announce-stop/tsconfig.json +25 -0
  19. package/hooks/hook-checkdocs/bun.lock +25 -0
  20. package/hooks/hook-commandlog/src/hook.ts +15 -38
  21. package/hooks/hook-conflict-detect/LICENSE +191 -0
  22. package/hooks/hook-conflict-detect/README.md +38 -0
  23. package/hooks/hook-conflict-detect/package.json +50 -0
  24. package/hooks/hook-conflict-detect/src/hook.ts +116 -0
  25. package/hooks/hook-conflict-detect/tsconfig.json +25 -0
  26. package/hooks/hook-costwatch/src/hook.ts +39 -42
  27. package/hooks/hook-dm-inject/LICENSE +191 -0
  28. package/hooks/hook-dm-inject/README.md +42 -0
  29. package/hooks/hook-dm-inject/package.json +51 -0
  30. package/hooks/hook-dm-inject/src/hook.ts +115 -0
  31. package/hooks/hook-dm-inject/tsconfig.json +25 -0
  32. package/hooks/hook-errornotify/src/hook.ts +20 -65
  33. package/hooks/hook-failure-to-task/LICENSE +191 -0
  34. package/hooks/hook-failure-to-task/README.md +40 -0
  35. package/hooks/hook-failure-to-task/package.json +51 -0
  36. package/hooks/hook-failure-to-task/src/hook.ts +171 -0
  37. package/hooks/hook-failure-to-task/tsconfig.json +25 -0
  38. package/hooks/hook-filelock/LICENSE +191 -0
  39. package/hooks/hook-filelock/README.md +44 -0
  40. package/hooks/hook-filelock/package.json +50 -0
  41. package/hooks/hook-filelock/src/hook.ts +147 -0
  42. package/hooks/hook-filelock/tsconfig.json +25 -0
  43. package/hooks/hook-sessionlog/src/hook.ts +11 -52
  44. package/hooks/hook-typecheck-gate/LICENSE +191 -0
  45. package/hooks/hook-typecheck-gate/README.md +46 -0
  46. package/hooks/hook-typecheck-gate/package.json +50 -0
  47. package/hooks/hook-typecheck-gate/src/hook.ts +152 -0
  48. package/hooks/hook-typecheck-gate/tsconfig.json +25 -0
  49. package/package.json +1 -1
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * Claude Code Hook: typecheck-gate
5
+ *
6
+ * Stop hook that runs TypeScript type checking before Claude finishes.
7
+ * Blocks the session stop if type errors are found, forcing Claude to fix them first.
8
+ *
9
+ * Configure via ~/.claude/settings.json:
10
+ * {
11
+ * "typecheckGateConfig": {
12
+ * "enabled": true,
13
+ * "command": "bun run typecheck" // override auto-detection
14
+ * }
15
+ * }
16
+ */
17
+
18
+ import { readFileSync, existsSync } from "fs";
19
+ import { join } from "path";
20
+ import { homedir } from "os";
21
+ import { execSync } from "child_process";
22
+
23
+ interface HookInput {
24
+ session_id: string;
25
+ cwd: string;
26
+ hook_event_name: string;
27
+ transcript_path?: string;
28
+ }
29
+
30
+ interface HookOutput {
31
+ decision?: "block";
32
+ reason?: string;
33
+ continue?: boolean;
34
+ }
35
+
36
+ interface TypecheckGateConfig {
37
+ enabled?: boolean;
38
+ command?: string;
39
+ }
40
+
41
+ const CONFIG_KEY = "typecheckGateConfig";
42
+
43
+ function readStdinJson(): HookInput | null {
44
+ try {
45
+ const input = readFileSync(0, "utf-8").trim();
46
+ if (!input) return null;
47
+ return JSON.parse(input);
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ function respond(output: HookOutput): void {
54
+ console.log(JSON.stringify(output));
55
+ }
56
+
57
+ function getConfig(cwd: string): TypecheckGateConfig {
58
+ for (const settingsPath of [
59
+ join(cwd, ".claude", "settings.json"),
60
+ join(homedir(), ".claude", "settings.json"),
61
+ ]) {
62
+ try {
63
+ if (existsSync(settingsPath)) {
64
+ const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
65
+ if (settings[CONFIG_KEY]) return settings[CONFIG_KEY] as TypecheckGateConfig;
66
+ }
67
+ } catch {}
68
+ }
69
+ return { enabled: true };
70
+ }
71
+
72
+ function detectTypecheckCommand(cwd: string): string | null {
73
+ const pkgPath = join(cwd, "package.json");
74
+ if (existsSync(pkgPath)) {
75
+ try {
76
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
77
+ const scripts = pkg.scripts || {};
78
+ if (scripts.typecheck) return "bun run typecheck";
79
+ if (scripts["type-check"]) return "bun run type-check";
80
+ if (scripts.tsc) return "bun run tsc";
81
+ if (scripts["build:types"]) return "bun run build:types";
82
+ } catch {}
83
+ }
84
+ if (existsSync(join(cwd, "tsconfig.json"))) {
85
+ return "bunx tsc --noEmit";
86
+ }
87
+ return null;
88
+ }
89
+
90
+ function runTypecheck(cwd: string, command: string): { success: boolean; output: string } {
91
+ try {
92
+ execSync(command, {
93
+ cwd,
94
+ encoding: "utf-8",
95
+ stdio: ["pipe", "pipe", "pipe"],
96
+ timeout: 120_000,
97
+ });
98
+ return { success: true, output: "" };
99
+ } catch (error: unknown) {
100
+ const execError = error as { stdout?: string; stderr?: string };
101
+ const output = (execError.stdout || "") + (execError.stderr || "");
102
+ return { success: false, output };
103
+ }
104
+ }
105
+
106
+ export function run(): void {
107
+ const input = readStdinJson();
108
+
109
+ if (!input) {
110
+ respond({ continue: true });
111
+ return;
112
+ }
113
+
114
+ const config = getConfig(input.cwd);
115
+
116
+ if (config.enabled === false) {
117
+ respond({ continue: true });
118
+ return;
119
+ }
120
+
121
+ const command = config.command || detectTypecheckCommand(input.cwd);
122
+
123
+ if (!command) {
124
+ console.error("[hook-typecheck-gate] No TypeScript project detected, skipping.");
125
+ respond({ continue: true });
126
+ return;
127
+ }
128
+
129
+ console.error(`[hook-typecheck-gate] Running: ${command}`);
130
+
131
+ const { success, output } = runTypecheck(input.cwd, command);
132
+
133
+ if (success) {
134
+ console.error("[hook-typecheck-gate] TypeScript OK — no errors.");
135
+ respond({ continue: true });
136
+ return;
137
+ }
138
+
139
+ const lines = output.split("\n").filter((l) => l.trim());
140
+ const errorLines = lines.filter((l) => /error TS\d+/.test(l));
141
+ const errorCount = errorLines.length || lines.filter((l) => /error/i.test(l)).length;
142
+ const summary = errorLines.slice(0, 5).join("\n") || lines.slice(0, 5).join("\n");
143
+ const more = errorCount > 5 ? `\n... and ${errorCount - 5} more error(s)` : "";
144
+
145
+ const reason = `TypeScript type check failed with ${errorCount} error(s).\n\nFix these before finishing:\n${summary}${more}`;
146
+ console.error(`[hook-typecheck-gate] ${errorCount} TypeScript error(s) found — blocking stop.`);
147
+ respond({ decision: "block", reason });
148
+ }
149
+
150
+ if (import.meta.main) {
151
+ run();
152
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "lib": ["ESNext"],
6
+ "moduleResolution": "bundler",
7
+ "allowImportingTsExtensions": true,
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "declaration": true,
18
+ "declarationMap": true,
19
+ "outDir": "./dist",
20
+ "rootDir": "./src",
21
+ "types": ["bun-types"]
22
+ },
23
+ "include": ["src/**/*"],
24
+ "exclude": ["node_modules", "dist"]
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/hooks",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
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": {