@hasna/hooks 0.0.1 → 0.0.2

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 (62) hide show
  1. package/dist/index.js +366 -0
  2. package/hooks/hook-agentmessages/bin/cli.ts +125 -0
  3. package/package.json +2 -2
  4. package/hooks/hook-agentmessages/src/check-messages.ts +0 -151
  5. package/hooks/hook-agentmessages/src/install.ts +0 -126
  6. package/hooks/hook-agentmessages/src/session-start.ts +0 -255
  7. package/hooks/hook-agentmessages/src/uninstall.ts +0 -89
  8. package/hooks/hook-branchprotect/src/cli.ts +0 -126
  9. package/hooks/hook-branchprotect/src/hook.ts +0 -88
  10. package/hooks/hook-branchprotect/tsconfig.json +0 -25
  11. package/hooks/hook-checkbugs/src/cli.ts +0 -628
  12. package/hooks/hook-checkbugs/src/hook.ts +0 -335
  13. package/hooks/hook-checkbugs/tsconfig.json +0 -15
  14. package/hooks/hook-checkdocs/src/cli.ts +0 -628
  15. package/hooks/hook-checkdocs/src/hook.ts +0 -310
  16. package/hooks/hook-checkdocs/tsconfig.json +0 -15
  17. package/hooks/hook-checkfiles/src/cli.ts +0 -545
  18. package/hooks/hook-checkfiles/src/hook.ts +0 -321
  19. package/hooks/hook-checkfiles/tsconfig.json +0 -15
  20. package/hooks/hook-checklint/src/cli-patch.ts +0 -32
  21. package/hooks/hook-checklint/src/cli.ts +0 -667
  22. package/hooks/hook-checklint/src/hook.ts +0 -473
  23. package/hooks/hook-checklint/tsconfig.json +0 -15
  24. package/hooks/hook-checkpoint/src/cli.ts +0 -191
  25. package/hooks/hook-checkpoint/src/hook.ts +0 -207
  26. package/hooks/hook-checkpoint/tsconfig.json +0 -25
  27. package/hooks/hook-checksecurity/src/cli.ts +0 -601
  28. package/hooks/hook-checksecurity/src/hook.ts +0 -334
  29. package/hooks/hook-checksecurity/tsconfig.json +0 -15
  30. package/hooks/hook-checktasks/src/cli.ts +0 -578
  31. package/hooks/hook-checktasks/src/hook.ts +0 -308
  32. package/hooks/hook-checktasks/tsconfig.json +0 -20
  33. package/hooks/hook-checktests/src/cli.ts +0 -627
  34. package/hooks/hook-checktests/src/hook.ts +0 -334
  35. package/hooks/hook-checktests/tsconfig.json +0 -15
  36. package/hooks/hook-contextrefresh/src/cli.ts +0 -152
  37. package/hooks/hook-contextrefresh/src/hook.ts +0 -148
  38. package/hooks/hook-contextrefresh/tsconfig.json +0 -25
  39. package/hooks/hook-gitguard/src/cli.ts +0 -159
  40. package/hooks/hook-gitguard/src/hook.ts +0 -129
  41. package/hooks/hook-gitguard/tsconfig.json +0 -25
  42. package/hooks/hook-packageage/src/cli.ts +0 -165
  43. package/hooks/hook-packageage/src/hook.ts +0 -177
  44. package/hooks/hook-packageage/tsconfig.json +0 -25
  45. package/hooks/hook-phonenotify/src/cli.ts +0 -196
  46. package/hooks/hook-phonenotify/src/hook.ts +0 -139
  47. package/hooks/hook-phonenotify/tsconfig.json +0 -25
  48. package/hooks/hook-precompact/src/cli.ts +0 -168
  49. package/hooks/hook-precompact/src/hook.ts +0 -122
  50. package/hooks/hook-precompact/tsconfig.json +0 -25
  51. package/src/cli/components/App.tsx +0 -191
  52. package/src/cli/components/CategorySelect.tsx +0 -37
  53. package/src/cli/components/DataTable.tsx +0 -133
  54. package/src/cli/components/Header.tsx +0 -18
  55. package/src/cli/components/HookSelect.tsx +0 -29
  56. package/src/cli/components/InstallProgress.tsx +0 -105
  57. package/src/cli/components/SearchView.tsx +0 -86
  58. package/src/cli/index.tsx +0 -218
  59. package/src/index.ts +0 -31
  60. package/src/lib/installer.ts +0 -288
  61. package/src/lib/registry.ts +0 -205
  62. package/tsconfig.json +0 -17
@@ -1,310 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- /**
4
- * Claude Code Hook: check-docs
5
- *
6
- * Runs a headless Claude Code agent to check for missing documentation.
7
- * Uses service-implementation CLI to dispatch tasks.
8
- *
9
- * This hook runs ASYNC (non-blocking) on PostToolUse.
10
- * Only runs for repos matching [prefix]-[name] pattern.
11
- *
12
- * Configuration:
13
- * - taskListId: task list for dispatching doc tasks
14
- * - editThreshold: run check after this many edits (default: 3, range: 3-7)
15
- * - keywords: keywords that trigger the check (default: ["dev"])
16
- * - enabled: enable/disable the hook
17
- */
18
-
19
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
20
- import { join } from "path";
21
- import { homedir } from "os";
22
- import { spawn } from "child_process";
23
-
24
- interface CheckDocsConfig {
25
- taskListId?: string;
26
- editThreshold?: number;
27
- keywords?: string[];
28
- enabled?: boolean;
29
- }
30
-
31
- interface HookInput {
32
- session_id: string;
33
- transcript_path: string;
34
- cwd: string;
35
- tool_name: string;
36
- tool_input: Record<string, unknown>;
37
- tool_output?: string;
38
- }
39
-
40
- interface SessionState {
41
- editCount: number;
42
- editedFiles: string[];
43
- lastCheckRun: number;
44
- checkInProgress: boolean;
45
- }
46
-
47
- const CONFIG_KEY = "checkDocsConfig";
48
- const STATE_DIR = join(homedir(), ".claude", "hook-state");
49
- const EDIT_TOOLS = ["Edit", "Write", "NotebookEdit"];
50
-
51
- const DOCS_PROMPT = `You are a documentation reviewer. Review the following files that were recently edited and identify missing or outdated documentation:
52
-
53
- FILES TO REVIEW:
54
- {files}
55
-
56
- For each documentation issue found, create a task using the service-implementation CLI:
57
- service-implementation task dispatch "{taskListId}" -s "DOCS: [brief description]" -d "[detailed description of what docs need to be added/updated]"
58
-
59
- Focus on:
60
- - Missing function/method documentation
61
- - Outdated README sections
62
- - Missing API documentation
63
- - Missing inline comments for complex logic
64
- - Missing type definitions documentation
65
- - Missing usage examples
66
-
67
- If no documentation issues are found, do not create any tasks.
68
- Only create tasks for meaningful documentation gaps, not trivial ones.
69
- Limit to max 5 most important documentation tasks.`;
70
-
71
- function isValidRepoPattern(cwd: string): boolean {
72
- const dirName = cwd.split("/").filter(Boolean).pop() || "";
73
- // Match: hook-checklint, skill-installhook, iapp-mail, etc.
74
- return /^[a-z]+-[a-z0-9-]+$/i.test(dirName);
75
- }
76
-
77
- function readStdinJson(): HookInput | null {
78
- try {
79
- const stdin = readFileSync(0, "utf-8");
80
- return JSON.parse(stdin);
81
- } catch {
82
- return null;
83
- }
84
- }
85
-
86
- function readSettings(path: string): Record<string, unknown> {
87
- if (!existsSync(path)) return {};
88
- try {
89
- return JSON.parse(readFileSync(path, "utf-8"));
90
- } catch {
91
- return {};
92
- }
93
- }
94
-
95
- function getConfig(cwd: string): CheckDocsConfig {
96
- // Try project settings first
97
- const projectSettings = readSettings(join(cwd, ".claude", "settings.json"));
98
- if (projectSettings[CONFIG_KEY]) {
99
- return projectSettings[CONFIG_KEY] as CheckDocsConfig;
100
- }
101
-
102
- // Fall back to global settings
103
- const globalSettings = readSettings(join(homedir(), ".claude", "settings.json"));
104
- if (globalSettings[CONFIG_KEY]) {
105
- return globalSettings[CONFIG_KEY] as CheckDocsConfig;
106
- }
107
-
108
- // Default config
109
- return {
110
- editThreshold: 3,
111
- keywords: ["dev"],
112
- enabled: true,
113
- };
114
- }
115
-
116
- function getStateFile(sessionId: string): string {
117
- mkdirSync(STATE_DIR, { recursive: true });
118
- return join(STATE_DIR, `checkdocs-${sessionId}.json`);
119
- }
120
-
121
- function getSessionState(sessionId: string): SessionState {
122
- const stateFile = getStateFile(sessionId);
123
- if (existsSync(stateFile)) {
124
- try {
125
- return JSON.parse(readFileSync(stateFile, "utf-8"));
126
- } catch {
127
- // Corrupted state, reset
128
- }
129
- }
130
- return { editCount: 0, editedFiles: [], lastCheckRun: 0, checkInProgress: false };
131
- }
132
-
133
- function saveSessionState(sessionId: string, state: SessionState): void {
134
- const stateFile = getStateFile(sessionId);
135
- writeFileSync(stateFile, JSON.stringify(state, null, 2));
136
- }
137
-
138
- function getSessionName(transcriptPath: string): string | null {
139
- if (!existsSync(transcriptPath)) return null;
140
-
141
- try {
142
- const content = readFileSync(transcriptPath, "utf-8");
143
- let lastTitle: string | null = null;
144
- let searchStart = 0;
145
-
146
- while (true) {
147
- const titleIndex = content.indexOf('"custom-title"', searchStart);
148
- if (titleIndex === -1) break;
149
-
150
- const lineStart = content.lastIndexOf("\n", titleIndex) + 1;
151
- const lineEnd = content.indexOf("\n", titleIndex);
152
- const line = content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd);
153
-
154
- try {
155
- const entry = JSON.parse(line);
156
- if (entry.type === "custom-title" && entry.customTitle) {
157
- lastTitle = entry.customTitle;
158
- }
159
- } catch {
160
- // Skip malformed lines
161
- }
162
-
163
- searchStart = titleIndex + 1;
164
- }
165
-
166
- return lastTitle;
167
- } catch {
168
- return null;
169
- }
170
- }
171
-
172
- function getProjectTaskListId(cwd: string): string | null {
173
- const dirName = cwd.split("/").filter(Boolean).pop() || "";
174
- return `${dirName}-dev`;
175
- }
176
-
177
- function runHeadlessDocsCheck(
178
- cwd: string,
179
- files: string[],
180
- taskListId: string
181
- ): void {
182
- const filesFormatted = files.map((f) => `- ${f}`).join("\n");
183
-
184
- const prompt = DOCS_PROMPT
185
- .replace("{files}", filesFormatted)
186
- .replace("{taskListId}", taskListId);
187
-
188
- // Spawn headless Claude Code agent in background
189
- const child = spawn(
190
- "claude",
191
- [
192
- "-p",
193
- prompt,
194
- "--permission-mode",
195
- "acceptEdits",
196
- "--allowedTools",
197
- "Bash,Read",
198
- "--no-session-persistence",
199
- ],
200
- {
201
- cwd,
202
- detached: true,
203
- stdio: "ignore",
204
- }
205
- );
206
-
207
- // Detach from parent process
208
- child.unref();
209
-
210
- console.error(`[hook-checkdocs] Started docs check for ${files.length} files`);
211
- }
212
-
213
- function approve() {
214
- console.log(JSON.stringify({ decision: "approve" }));
215
- process.exit(0);
216
- }
217
-
218
- export function run() {
219
- const hookInput = readStdinJson();
220
- if (!hookInput) {
221
- approve();
222
- return;
223
- }
224
-
225
- const { session_id, cwd, tool_name, tool_input, transcript_path } = hookInput;
226
-
227
- // Only process edit tools
228
- if (!EDIT_TOOLS.includes(tool_name)) {
229
- approve();
230
- return;
231
- }
232
-
233
- // Check repo pattern - only run for [prefix]-[name] folders
234
- if (!isValidRepoPattern(cwd)) {
235
- approve();
236
- return;
237
- }
238
-
239
- const config = getConfig(cwd);
240
-
241
- // Check if hook is disabled
242
- if (config.enabled === false) {
243
- approve();
244
- return;
245
- }
246
-
247
- // Check keywords match
248
- const sessionName = transcript_path ? getSessionName(transcript_path) : null;
249
- const nameToCheck = sessionName || config.taskListId || "";
250
- const keywords = config.keywords || ["dev"];
251
-
252
- const matchesKeyword = keywords.some((keyword) =>
253
- nameToCheck.toLowerCase().includes(keyword.toLowerCase())
254
- );
255
-
256
- if (!matchesKeyword && keywords.length > 0 && nameToCheck) {
257
- approve();
258
- return;
259
- }
260
-
261
- // Get edited file path
262
- const filePath = (tool_input.file_path || tool_input.notebook_path) as string | undefined;
263
- if (!filePath) {
264
- approve();
265
- return;
266
- }
267
-
268
- // Update session state
269
- const state = getSessionState(session_id);
270
-
271
- // Skip if check already in progress
272
- if (state.checkInProgress) {
273
- approve();
274
- return;
275
- }
276
-
277
- state.editCount++;
278
-
279
- if (!state.editedFiles.includes(filePath)) {
280
- state.editedFiles.push(filePath);
281
- }
282
-
283
- const threshold = Math.min(7, Math.max(3, config.editThreshold || 3));
284
-
285
- // Check if we should run docs check
286
- if (state.editCount >= threshold) {
287
- const taskListId = config.taskListId || getProjectTaskListId(cwd) || "default-dev";
288
-
289
- // Mark check in progress
290
- state.checkInProgress = true;
291
- saveSessionState(session_id, state);
292
-
293
- // Run headless docs check (async, non-blocking)
294
- runHeadlessDocsCheck(cwd, state.editedFiles, taskListId);
295
-
296
- // Reset counter after starting check
297
- state.editCount = 0;
298
- state.editedFiles = [];
299
- state.lastCheckRun = Date.now();
300
- state.checkInProgress = false;
301
- }
302
-
303
- saveSessionState(session_id, state);
304
- approve();
305
- }
306
-
307
- // Allow direct execution
308
- if (import.meta.main) {
309
- run();
310
- }
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "outDir": "./dist",
10
- "declaration": true,
11
- "declarationMap": true
12
- },
13
- "include": ["src/**/*"],
14
- "exclude": ["node_modules", "dist"]
15
- }