@hasna/hooks 0.0.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.
Files changed (110) hide show
  1. package/.npmrc.example +2 -0
  2. package/AGENTS.md +54 -0
  3. package/CLAUDE.md +70 -0
  4. package/CONTRIBUTING.md +45 -0
  5. package/README.md +232 -0
  6. package/bin/index.js +5171 -0
  7. package/hooks/hook-agentmessages/CLAUDE.md +79 -0
  8. package/hooks/hook-agentmessages/LICENSE +21 -0
  9. package/hooks/hook-agentmessages/README.md +107 -0
  10. package/hooks/hook-agentmessages/package.json +31 -0
  11. package/hooks/hook-agentmessages/src/check-messages.ts +151 -0
  12. package/hooks/hook-agentmessages/src/install.ts +126 -0
  13. package/hooks/hook-agentmessages/src/session-start.ts +255 -0
  14. package/hooks/hook-agentmessages/src/uninstall.ts +89 -0
  15. package/hooks/hook-branchprotect/CLAUDE.md +23 -0
  16. package/hooks/hook-branchprotect/README.md +25 -0
  17. package/hooks/hook-branchprotect/package.json +42 -0
  18. package/hooks/hook-branchprotect/src/cli.ts +126 -0
  19. package/hooks/hook-branchprotect/src/hook.ts +88 -0
  20. package/hooks/hook-branchprotect/tsconfig.json +25 -0
  21. package/hooks/hook-checkbugs/LICENSE +21 -0
  22. package/hooks/hook-checkbugs/README.md +140 -0
  23. package/hooks/hook-checkbugs/package.json +58 -0
  24. package/hooks/hook-checkbugs/src/cli.ts +628 -0
  25. package/hooks/hook-checkbugs/src/hook.ts +335 -0
  26. package/hooks/hook-checkbugs/tsconfig.json +15 -0
  27. package/hooks/hook-checkdocs/README.md +137 -0
  28. package/hooks/hook-checkdocs/package.json +57 -0
  29. package/hooks/hook-checkdocs/src/cli.ts +628 -0
  30. package/hooks/hook-checkdocs/src/hook.ts +310 -0
  31. package/hooks/hook-checkdocs/tsconfig.json +15 -0
  32. package/hooks/hook-checkfiles/LICENSE +21 -0
  33. package/hooks/hook-checkfiles/README.md +141 -0
  34. package/hooks/hook-checkfiles/package.json +56 -0
  35. package/hooks/hook-checkfiles/src/cli.ts +545 -0
  36. package/hooks/hook-checkfiles/src/hook.ts +321 -0
  37. package/hooks/hook-checkfiles/tsconfig.json +15 -0
  38. package/hooks/hook-checklint/LICENSE +21 -0
  39. package/hooks/hook-checklint/README.md +147 -0
  40. package/hooks/hook-checklint/package.json +57 -0
  41. package/hooks/hook-checklint/src/cli-patch.ts +32 -0
  42. package/hooks/hook-checklint/src/cli.ts +667 -0
  43. package/hooks/hook-checklint/src/hook.ts +473 -0
  44. package/hooks/hook-checklint/tsconfig.json +15 -0
  45. package/hooks/hook-checkpoint/CLAUDE.md +23 -0
  46. package/hooks/hook-checkpoint/README.md +37 -0
  47. package/hooks/hook-checkpoint/package.json +58 -0
  48. package/hooks/hook-checkpoint/src/cli.ts +191 -0
  49. package/hooks/hook-checkpoint/src/hook.ts +207 -0
  50. package/hooks/hook-checkpoint/tsconfig.json +25 -0
  51. package/hooks/hook-checksecurity/LICENSE +21 -0
  52. package/hooks/hook-checksecurity/README.md +158 -0
  53. package/hooks/hook-checksecurity/package.json +57 -0
  54. package/hooks/hook-checksecurity/src/cli.ts +601 -0
  55. package/hooks/hook-checksecurity/src/hook.ts +334 -0
  56. package/hooks/hook-checksecurity/tsconfig.json +15 -0
  57. package/hooks/hook-checktasks/README.md +144 -0
  58. package/hooks/hook-checktasks/package.json +55 -0
  59. package/hooks/hook-checktasks/src/cli.ts +578 -0
  60. package/hooks/hook-checktasks/src/hook.ts +308 -0
  61. package/hooks/hook-checktasks/tsconfig.json +20 -0
  62. package/hooks/hook-checktests/LICENSE +21 -0
  63. package/hooks/hook-checktests/README.md +137 -0
  64. package/hooks/hook-checktests/package.json +57 -0
  65. package/hooks/hook-checktests/src/cli.ts +627 -0
  66. package/hooks/hook-checktests/src/hook.ts +334 -0
  67. package/hooks/hook-checktests/tsconfig.json +15 -0
  68. package/hooks/hook-contextrefresh/CLAUDE.md +23 -0
  69. package/hooks/hook-contextrefresh/README.md +42 -0
  70. package/hooks/hook-contextrefresh/package.json +42 -0
  71. package/hooks/hook-contextrefresh/src/cli.ts +152 -0
  72. package/hooks/hook-contextrefresh/src/hook.ts +148 -0
  73. package/hooks/hook-contextrefresh/tsconfig.json +25 -0
  74. package/hooks/hook-gitguard/CLAUDE.md +22 -0
  75. package/hooks/hook-gitguard/README.md +30 -0
  76. package/hooks/hook-gitguard/package.json +57 -0
  77. package/hooks/hook-gitguard/src/cli.ts +159 -0
  78. package/hooks/hook-gitguard/src/hook.ts +129 -0
  79. package/hooks/hook-gitguard/tsconfig.json +25 -0
  80. package/hooks/hook-packageage/CLAUDE.md +23 -0
  81. package/hooks/hook-packageage/README.md +33 -0
  82. package/hooks/hook-packageage/package.json +42 -0
  83. package/hooks/hook-packageage/src/cli.ts +165 -0
  84. package/hooks/hook-packageage/src/hook.ts +177 -0
  85. package/hooks/hook-packageage/tsconfig.json +25 -0
  86. package/hooks/hook-phonenotify/CLAUDE.md +25 -0
  87. package/hooks/hook-phonenotify/README.md +44 -0
  88. package/hooks/hook-phonenotify/package.json +42 -0
  89. package/hooks/hook-phonenotify/src/cli.ts +196 -0
  90. package/hooks/hook-phonenotify/src/hook.ts +139 -0
  91. package/hooks/hook-phonenotify/tsconfig.json +25 -0
  92. package/hooks/hook-precompact/CLAUDE.md +23 -0
  93. package/hooks/hook-precompact/README.md +36 -0
  94. package/hooks/hook-precompact/package.json +42 -0
  95. package/hooks/hook-precompact/src/cli.ts +168 -0
  96. package/hooks/hook-precompact/src/hook.ts +122 -0
  97. package/hooks/hook-precompact/tsconfig.json +25 -0
  98. package/package.json +61 -0
  99. package/src/cli/components/App.tsx +191 -0
  100. package/src/cli/components/CategorySelect.tsx +37 -0
  101. package/src/cli/components/DataTable.tsx +133 -0
  102. package/src/cli/components/Header.tsx +18 -0
  103. package/src/cli/components/HookSelect.tsx +29 -0
  104. package/src/cli/components/InstallProgress.tsx +105 -0
  105. package/src/cli/components/SearchView.tsx +86 -0
  106. package/src/cli/index.tsx +218 -0
  107. package/src/index.ts +31 -0
  108. package/src/lib/installer.ts +288 -0
  109. package/src/lib/registry.ts +205 -0
  110. package/tsconfig.json +17 -0
@@ -0,0 +1,79 @@
1
+ # CLAUDE.md
2
+
3
+ Instructions for working with hook-agentmessages codebase.
4
+
5
+ ## Project Overview
6
+
7
+ **hook-agentmessages** is a Claude Code hook package that integrates with service-message for automatic agent registration and message notifications.
8
+
9
+ - **Package:** `@hasnaxyz/hook-agentmessages`
10
+ - **CLI command:** `hook-agentmessages`
11
+ - **Hooks location:** `~/.claude/settings.json`
12
+
13
+ ## What it Does
14
+
15
+ ### SessionStart Hook (`src/session-start.ts`)
16
+
17
+ Runs when Claude Code session starts:
18
+ 1. Auto-registers agent with ID format: `{type}-{shortid}` (claude-abc123, codex-xyz789)
19
+ 2. Registers project from `$CLAUDE_PROJECT_DIR`
20
+ 3. Creates session linked to Claude session ID
21
+ 4. Exports env vars: `SMSG_AGENT_ID`, `SMSG_SESSION_ID`, `SMSG_PROJECT_ID`
22
+
23
+ ### Stop Hook (`src/check-messages.ts`)
24
+
25
+ Runs after Claude finishes each response:
26
+ 1. Checks for unread messages (efficient, no polling)
27
+ 2. Returns notification if unread messages exist
28
+ 3. 5s timeout to avoid blocking
29
+
30
+ ## Development
31
+
32
+ ### Key Files
33
+
34
+ ```
35
+ bin/cli.ts # CLI entry point
36
+ src/
37
+ ├── session-start.ts # SessionStart hook
38
+ ├── check-messages.ts # Stop hook
39
+ ├── install.ts # Install hooks to Claude Code
40
+ └── uninstall.ts # Remove hooks
41
+ ```
42
+
43
+ ### Hook Input Format
44
+
45
+ Hooks receive JSON via stdin:
46
+ ```json
47
+ {
48
+ "session_id": "abc123",
49
+ "cwd": "/path/to/project",
50
+ "model": "claude-opus-4-5-20251101",
51
+ "hook_event_name": "SessionStart"
52
+ }
53
+ ```
54
+
55
+ ### Hook Output Format
56
+
57
+ ```json
58
+ {
59
+ "decision": "continue",
60
+ "message": "Optional message shown to Claude"
61
+ }
62
+ ```
63
+
64
+ ## Testing
65
+
66
+ ```bash
67
+ # Test session-start hook
68
+ echo '{"session_id":"test","cwd":"/tmp"}' | bun run src/session-start.ts
69
+
70
+ # Test check-messages hook
71
+ SMSG_AGENT_ID=claude-1 bun run src/check-messages.ts
72
+
73
+ # Install/uninstall
74
+ bun run install-hook
75
+ bun run uninstall-hook
76
+
77
+ # Check status
78
+ hook-agentmessages status
79
+ ```
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hasna
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,107 @@
1
+ # hook-agentmessages
2
+
3
+ Claude Code hook for service-message integration.
4
+
5
+ Automatically registers projects and sessions when Claude Code starts, and checks for unread messages after each response.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # Install globally
11
+ bun install -g @hasnaxyz/hook-agentmessages
12
+
13
+ # Install hooks into Claude Code
14
+ hook-agentmessages install
15
+ ```
16
+
17
+ ## Prerequisites
18
+
19
+ Before using this hook, you must initialize an agent with service-message:
20
+
21
+ ```bash
22
+ service-message init
23
+ ```
24
+
25
+ This hook does NOT auto-generate agents. It only works if an agent is already configured.
26
+
27
+ ## What it does
28
+
29
+ ### SessionStart Hook
30
+
31
+ When a Claude Code session starts:
32
+
33
+ 1. **Reads existing agent** from service-message config
34
+ 2. **Registers the project** from `$CLAUDE_PROJECT_DIR` (folder name becomes project ID)
35
+ 3. **Starts a session** linked to the Claude session ID
36
+ 4. **Exports environment variables** for the session:
37
+ - `SMSG_AGENT_ID`
38
+ - `SMSG_SESSION_ID`
39
+ - `SMSG_PROJECT_ID`
40
+
41
+ ### Stop Hook
42
+
43
+ After Claude finishes each response:
44
+
45
+ 1. Checks for unread messages (efficient, no polling)
46
+ 2. Notifies Claude if there are pending messages
47
+ 3. Runs with 5s timeout to avoid blocking
48
+
49
+ ## CLI Commands
50
+
51
+ ```bash
52
+ hook-agentmessages install # Install hooks
53
+ hook-agentmessages uninstall # Remove hooks
54
+ hook-agentmessages status # Check installation status
55
+ ```
56
+
57
+ ## Configuration
58
+
59
+ Hooks are stored in `~/.claude/settings.json`:
60
+
61
+ ```json
62
+ {
63
+ "hooks": {
64
+ "SessionStart": [
65
+ {
66
+ "hooks": [
67
+ {
68
+ "type": "command",
69
+ "command": "bun /path/to/hook-agentmessages/src/session-start.ts",
70
+ "timeout": 10
71
+ }
72
+ ]
73
+ }
74
+ ],
75
+ "Stop": [
76
+ {
77
+ "hooks": [
78
+ {
79
+ "type": "command",
80
+ "command": "bun /path/to/hook-agentmessages/src/check-messages.ts",
81
+ "timeout": 5
82
+ }
83
+ ]
84
+ }
85
+ ]
86
+ }
87
+ }
88
+ ```
89
+
90
+ ## Efficiency
91
+
92
+ The hook is designed to be lightweight:
93
+
94
+ - **SessionStart**: Runs once per session (10s timeout)
95
+ - **Stop**: Only checks for messages after Claude responds (5s timeout)
96
+ - **No polling**: Doesn't continuously monitor, only checks on events
97
+ - **Fast file reads**: Uses Bun's native file APIs
98
+
99
+ ## Requirements
100
+
101
+ - Bun runtime
102
+ - service-message (`@hasnaxyz/service-message`) installed and initialized
103
+ - Claude Code with hooks support
104
+
105
+ ## License
106
+
107
+ MIT
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@hasnaxyz/hook-agentmessages",
3
+ "version": "0.2.1",
4
+ "type": "module",
5
+ "description": "Claude Code hook for service-message integration - auto-registers agents, sessions, and checks messages",
6
+ "bin": {
7
+ "hook-agentmessages": "./bin/cli.ts"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/hasnaxyz/hook-agentmessages.git"
12
+ },
13
+ "homepage": "https://github.com/hasnaxyz/hook-agentmessages#readme",
14
+ "publishConfig": {
15
+ "access": "restricted",
16
+ "registry": "https://registry.npmjs.org/"
17
+ },
18
+ "scripts": {
19
+ "session-start": "bun run src/session-start.ts",
20
+ "check-messages": "bun run src/check-messages.ts",
21
+ "install-hook": "bun run src/install.ts",
22
+ "uninstall-hook": "bun run src/uninstall.ts"
23
+ },
24
+ "dependencies": {},
25
+ "devDependencies": {
26
+ "@types/bun": "^1.3.8",
27
+ "typescript": "^5.7.2"
28
+ },
29
+ "author": "Hasna",
30
+ "license": "MIT"
31
+ }
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Stop hook for service-message
4
+ *
5
+ * Runs when Claude finishes a response. Checks for unread messages
6
+ * and notifies Claude if there are any pending messages.
7
+ *
8
+ * This is efficient because it only runs after Claude completes a turn,
9
+ * not continuously polling.
10
+ */
11
+
12
+ import { homedir } from 'os';
13
+ import { join } from 'path';
14
+ import { readdir } from 'fs/promises';
15
+
16
+ interface HookInput {
17
+ session_id: string;
18
+ cwd: string;
19
+ hook_event_name: string;
20
+ stop_hook_active?: boolean;
21
+ }
22
+
23
+ interface Message {
24
+ id: string;
25
+ timestamp: number;
26
+ from: string;
27
+ to: string;
28
+ project: string;
29
+ subject: string;
30
+ body: string;
31
+ read?: boolean;
32
+ }
33
+
34
+ const SERVICE_DIR = join(homedir(), '.service', 'service-message');
35
+
36
+ /**
37
+ * Sanitize ID to prevent path traversal attacks
38
+ */
39
+ function sanitizeId(id: string): string | null {
40
+ if (!id || typeof id !== 'string') return null;
41
+ // Only allow alphanumeric, dash, underscore
42
+ if (!/^[a-zA-Z0-9_-]+$/.test(id)) return null;
43
+ // Reject path traversal attempts
44
+ if (id.includes('..') || id.includes('/') || id.includes('\\')) return null;
45
+ return id;
46
+ }
47
+
48
+ async function readJson<T>(path: string): Promise<T | null> {
49
+ try {
50
+ const file = Bun.file(path);
51
+ if (await file.exists()) {
52
+ return await file.json();
53
+ }
54
+ } catch {}
55
+ return null;
56
+ }
57
+
58
+ async function getUnreadMessages(agentId: string, projectId?: string): Promise<Message[]> {
59
+ const messages: Message[] = [];
60
+ const messagesDir = join(SERVICE_DIR, 'messages');
61
+
62
+ try {
63
+ const rawProjects = projectId ? [projectId] : await readdir(messagesDir);
64
+ // Sanitize all project names to prevent path traversal
65
+ const projects = rawProjects.map(p => sanitizeId(p)).filter((p): p is string => p !== null);
66
+
67
+ for (const proj of projects) {
68
+ // Check inbox
69
+ const inboxDir = join(messagesDir, proj, 'inbox', agentId);
70
+ try {
71
+ const files = await readdir(inboxDir);
72
+ for (const file of files) {
73
+ if (!file.endsWith('.json')) continue;
74
+ // Sanitize filename to prevent path traversal
75
+ const safeFile = sanitizeId(file.replace('.json', ''));
76
+ if (!safeFile) continue;
77
+ const msg = await readJson<Message>(join(inboxDir, `${safeFile}.json`));
78
+ if (msg && !msg.read) {
79
+ messages.push(msg);
80
+ }
81
+ }
82
+ } catch {}
83
+
84
+ // Check broadcast
85
+ const broadcastDir = join(messagesDir, proj, 'broadcast');
86
+ try {
87
+ const files = await readdir(broadcastDir);
88
+ for (const file of files) {
89
+ if (!file.endsWith('.json')) continue;
90
+ // Sanitize filename to prevent path traversal
91
+ const safeFile = sanitizeId(file.replace('.json', ''));
92
+ if (!safeFile) continue;
93
+ const msg = await readJson<Message>(join(broadcastDir, `${safeFile}.json`));
94
+ if (msg && !msg.read && msg.from !== agentId) {
95
+ messages.push(msg);
96
+ }
97
+ }
98
+ } catch {}
99
+ }
100
+ } catch {}
101
+
102
+ return messages.sort((a, b) => b.timestamp - a.timestamp);
103
+ }
104
+
105
+ async function main() {
106
+ // Get agent ID from environment (set by session-start hook)
107
+ const rawAgentId = process.env.SMSG_AGENT_ID;
108
+ const rawProjectId = process.env.SMSG_PROJECT_ID;
109
+
110
+ // Sanitize IDs to prevent path traversal
111
+ const agentId = rawAgentId ? sanitizeId(rawAgentId) : null;
112
+ const projectId = rawProjectId ? sanitizeId(rawProjectId) : undefined;
113
+
114
+ if (!agentId) {
115
+ // Agent not configured or invalid, skip silently
116
+ console.log(JSON.stringify({ continue: true }));
117
+ return;
118
+ }
119
+
120
+ // Check for unread messages
121
+ const unreadMessages = await getUnreadMessages(agentId, projectId);
122
+
123
+ if (unreadMessages.length === 0) {
124
+ // No messages, continue normally
125
+ console.log(JSON.stringify({ continue: true }));
126
+ return;
127
+ }
128
+
129
+ // Format message summary in a friendly way
130
+ const msgList = unreadMessages.slice(0, 3).map(msg => {
131
+ const time = new Date(msg.timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
132
+ const preview = msg.body.slice(0, 60).replace(/\n/g, ' ');
133
+ return `📨 **${msg.subject}** from \`${msg.from}\` (${time})\n ${preview}${msg.body.length > 60 ? '...' : ''}`;
134
+ }).join('\n\n');
135
+
136
+ const moreNote = unreadMessages.length > 3
137
+ ? `\n\n_...and ${unreadMessages.length - 3} more message(s)_`
138
+ : '';
139
+
140
+ // Inject message in a friendly, readable format
141
+ console.log(JSON.stringify({
142
+ continue: true,
143
+ stopReason: `📬 **You have ${unreadMessages.length} unread message(s):**\n\n${msgList}${moreNote}\n\n💡 Use \`service-message inbox\` to see all messages or \`service-message read <id>\` to read one.`
144
+ }));
145
+ }
146
+
147
+ main().catch(() => {
148
+ // Don't block on errors
149
+ console.log(JSON.stringify({ continue: true }));
150
+ process.exit(0);
151
+ });
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Install hook-agentmessages into Claude Code settings
4
+ *
5
+ * Adds hooks to ~/.claude/settings.json:
6
+ * - SessionStart: Auto-register agent, project, session
7
+ * - Stop: Check for unread messages
8
+ */
9
+
10
+ import { homedir } from 'os';
11
+ import { join } from 'path';
12
+
13
+ const CLAUDE_SETTINGS_DIR = join(homedir(), '.claude');
14
+ const CLAUDE_SETTINGS_FILE = join(CLAUDE_SETTINGS_DIR, 'settings.json');
15
+ const HOOK_DIR = import.meta.dir.replace('/src', '');
16
+
17
+ interface HookConfig {
18
+ type: 'command';
19
+ command: string;
20
+ timeout?: number;
21
+ }
22
+
23
+ interface HookMatcher {
24
+ matcher?: string;
25
+ hooks: HookConfig[];
26
+ }
27
+
28
+ interface Settings {
29
+ hooks?: {
30
+ SessionStart?: HookMatcher[];
31
+ Stop?: HookMatcher[];
32
+ [key: string]: HookMatcher[] | undefined;
33
+ };
34
+ [key: string]: unknown;
35
+ }
36
+
37
+ async function readSettings(): Promise<Settings> {
38
+ try {
39
+ const file = Bun.file(CLAUDE_SETTINGS_FILE);
40
+ if (await file.exists()) {
41
+ return await file.json();
42
+ }
43
+ } catch {}
44
+ return {};
45
+ }
46
+
47
+ async function writeSettings(settings: Settings): Promise<void> {
48
+ // Ensure .claude directory exists
49
+ await Bun.write(join(CLAUDE_SETTINGS_DIR, '.gitkeep'), '');
50
+ await Bun.write(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2));
51
+ }
52
+
53
+ function findHookIndex(hooks: HookMatcher[], command: string): number {
54
+ return hooks.findIndex(h =>
55
+ h.hooks.some(hook => hook.command.includes('hook-agentmessages'))
56
+ );
57
+ }
58
+
59
+ async function main() {
60
+ console.log('Installing hook-agentmessages into Claude Code...\n');
61
+
62
+ const settings = await readSettings();
63
+
64
+ // Initialize hooks object if not exists
65
+ if (!settings.hooks) {
66
+ settings.hooks = {};
67
+ }
68
+
69
+ // SessionStart hook
70
+ const sessionStartHook: HookMatcher = {
71
+ hooks: [
72
+ {
73
+ type: 'command',
74
+ command: `bun ${join(HOOK_DIR, 'src/session-start.ts')}`,
75
+ timeout: 10,
76
+ },
77
+ ],
78
+ };
79
+
80
+ if (!settings.hooks.SessionStart) {
81
+ settings.hooks.SessionStart = [];
82
+ }
83
+
84
+ // Remove existing hook-agentmessages hooks
85
+ const existingSessionStartIdx = findHookIndex(settings.hooks.SessionStart, 'hook-agentmessages');
86
+ if (existingSessionStartIdx >= 0) {
87
+ settings.hooks.SessionStart.splice(existingSessionStartIdx, 1);
88
+ }
89
+ settings.hooks.SessionStart.push(sessionStartHook);
90
+
91
+ // Stop hook (check messages after each response)
92
+ const stopHook: HookMatcher = {
93
+ hooks: [
94
+ {
95
+ type: 'command',
96
+ command: `bun ${join(HOOK_DIR, 'src/check-messages.ts')}`,
97
+ timeout: 5,
98
+ },
99
+ ],
100
+ };
101
+
102
+ if (!settings.hooks.Stop) {
103
+ settings.hooks.Stop = [];
104
+ }
105
+
106
+ const existingStopIdx = findHookIndex(settings.hooks.Stop, 'hook-agentmessages');
107
+ if (existingStopIdx >= 0) {
108
+ settings.hooks.Stop.splice(existingStopIdx, 1);
109
+ }
110
+ settings.hooks.Stop.push(stopHook);
111
+
112
+ // Write updated settings
113
+ await writeSettings(settings);
114
+
115
+ console.log('Hooks installed successfully!\n');
116
+ console.log('Installed hooks:');
117
+ console.log(' - SessionStart: Auto-registers agent, project, and session');
118
+ console.log(' - Stop: Checks for unread messages after each response\n');
119
+ console.log(`Settings file: ${CLAUDE_SETTINGS_FILE}`);
120
+ console.log('\nRestart Claude Code for hooks to take effect.');
121
+ }
122
+
123
+ main().catch((err) => {
124
+ console.error('Installation failed:', err.message);
125
+ process.exit(1);
126
+ });