@rlabs-inc/memory 0.3.2 → 0.3.5

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.
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bun
2
+ // ============================================================================
3
+ // GEMINI CURATION HOOK
4
+ // Hook: SessionEnd / PreCompress
5
+ //
6
+ // Triggers memory curation.
7
+ // ============================================================================
8
+
9
+ import { styleText } from 'util'
10
+
11
+ const MEMORY_API_URL = process.env.MEMORY_API_URL || 'http://localhost:8765'
12
+
13
+ const info = (text: string) => styleText('cyan', text)
14
+ const success = (text: string) => styleText('green', text)
15
+ const warn = (text: string) => styleText('yellow', text)
16
+
17
+ function getProjectId(cwd: string): string {
18
+ return cwd.split('/').pop() || 'default'
19
+ }
20
+
21
+ async function main() {
22
+ if (process.env.MEMORY_CURATOR_ACTIVE === '1') return
23
+
24
+ try {
25
+ const inputText = await Bun.stdin.text()
26
+ const input = inputText ? JSON.parse(inputText) : {}
27
+
28
+ const sessionId = input.session_id || process.env.GEMINI_SESSION_ID || 'unknown'
29
+ const cwd = input.cwd || process.env.GEMINI_PROJECT_DIR || process.cwd()
30
+ const projectId = getProjectId(cwd)
31
+
32
+ // Gemini: PreCompress has 'trigger', SessionEnd has 'reason'
33
+ const eventName = input.hook_event_name || 'unknown'
34
+ let trigger = 'session_end'
35
+
36
+ if (eventName === 'PreCompress') {
37
+ trigger = 'pre_compact'
38
+ }
39
+
40
+ console.error(info(`🧠 Curating memories (${eventName})...`))
41
+
42
+ const response = await fetch(`${MEMORY_API_URL}/memory/checkpoint`, {
43
+ method: 'POST',
44
+ headers: { 'Content-Type': 'application/json' },
45
+ body: JSON.stringify({
46
+ session_id: sessionId,
47
+ project_id: projectId,
48
+ claude_session_id: sessionId,
49
+ trigger,
50
+ cwd,
51
+ cli_type: 'gemini-cli'
52
+ }),
53
+ signal: AbortSignal.timeout(5000),
54
+ }).catch(() => null)
55
+
56
+ if (response?.ok) {
57
+ console.error(success('✨ Memory curation started'))
58
+ // For PreCompress, we can send a system message
59
+ if (eventName === 'PreCompress') {
60
+ console.log(JSON.stringify({
61
+ systemMessage: "🧠 Memories curated before compression"
62
+ }))
63
+ }
64
+ } else {
65
+ console.error(warn('⚠️ Memory server not available'))
66
+ }
67
+
68
+ } catch (error: any) {
69
+ console.error(warn(`⚠️ Hook error: ${error.message}`))
70
+ }
71
+ }
72
+
73
+ main()
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env bun
2
+ // ============================================================================
3
+ // GEMINI SESSION START HOOK
4
+ // Hook: SessionStart
5
+ //
6
+ // Injects session primer when a new Gemini session begins.
7
+ // ============================================================================
8
+
9
+ const MEMORY_API_URL = process.env.MEMORY_API_URL || 'http://localhost:8765'
10
+ const TIMEOUT_MS = 5000
11
+
12
+ function getProjectId(cwd: string): string {
13
+ return cwd.split('/').pop() || 'default'
14
+ }
15
+
16
+ async function httpPost(url: string, data: object): Promise<any> {
17
+ try {
18
+ const response = await fetch(url, {
19
+ method: 'POST',
20
+ headers: { 'Content-Type': 'application/json' },
21
+ body: JSON.stringify(data),
22
+ signal: AbortSignal.timeout(TIMEOUT_MS),
23
+ })
24
+ return response.ok ? response.json() : {}
25
+ } catch {
26
+ return {}
27
+ }
28
+ }
29
+
30
+ async function main() {
31
+ if (process.env.MEMORY_CURATOR_ACTIVE === '1') return
32
+
33
+ try {
34
+ const inputText = await Bun.stdin.text()
35
+ const input = inputText ? JSON.parse(inputText) : {}
36
+
37
+ // Gemini provides session_id in the common input fields
38
+ const sessionId = input.session_id || process.env.GEMINI_SESSION_ID || 'unknown'
39
+ const cwd = input.cwd || process.env.GEMINI_PROJECT_DIR || process.cwd()
40
+ const projectId = getProjectId(cwd)
41
+
42
+ const result = await httpPost(`${MEMORY_API_URL}/memory/context`, {
43
+ session_id: sessionId,
44
+ project_id: projectId,
45
+ current_message: '',
46
+ max_memories: 0,
47
+ })
48
+
49
+ await httpPost(`${MEMORY_API_URL}/memory/process`, {
50
+ session_id: sessionId,
51
+ project_id: projectId,
52
+ metadata: { event: 'session_start', platform: 'gemini' },
53
+ })
54
+
55
+ const primer = result.context_text || ''
56
+
57
+ if (primer) {
58
+ // Gemini expects a structured JSON response for context injection
59
+ console.log(JSON.stringify({
60
+ hookSpecificOutput: {
61
+ hookEventName: "SessionStart",
62
+ additionalContext: primer
63
+ }
64
+ }))
65
+ }
66
+ } catch (e) {
67
+ // Fail silently, but ensure we don't output invalid JSON if we crashed mid-stream
68
+ process.exit(0)
69
+ }
70
+ }
71
+
72
+ main()
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bun
2
+ // ============================================================================
3
+ // GEMINI BEFORE AGENT HOOK
4
+ // Hook: BeforeAgent (equivalent to Claude's UserPromptSubmit)
5
+ //
6
+ // Intercepts user prompts to inject relevant memories.
7
+ // ============================================================================
8
+
9
+ const MEMORY_API_URL = process.env.MEMORY_API_URL || 'http://localhost:8765'
10
+ const TIMEOUT_MS = 5000
11
+
12
+ function getProjectId(cwd: string): string {
13
+ return cwd.split('/').pop() || 'default'
14
+ }
15
+
16
+ async function httpPost(url: string, data: object): Promise<any> {
17
+ try {
18
+ const response = await fetch(url, {
19
+ method: 'POST',
20
+ headers: { 'Content-Type': 'application/json' },
21
+ body: JSON.stringify(data),
22
+ signal: AbortSignal.timeout(TIMEOUT_MS),
23
+ })
24
+ return response.ok ? response.json() : {}
25
+ } catch {
26
+ return {}
27
+ }
28
+ }
29
+
30
+ async function main() {
31
+ if (process.env.MEMORY_CURATOR_ACTIVE === '1') return
32
+
33
+ try {
34
+ const inputText = await Bun.stdin.text()
35
+ const input = inputText ? JSON.parse(inputText) : {}
36
+
37
+ const sessionId = input.session_id || process.env.GEMINI_SESSION_ID || 'unknown'
38
+ const prompt = input.prompt || '' // Gemini passes 'prompt' in BeforeAgent
39
+ const cwd = input.cwd || process.env.GEMINI_PROJECT_DIR || process.cwd()
40
+ const projectId = getProjectId(cwd)
41
+
42
+ const result = await httpPost(`${MEMORY_API_URL}/memory/context`, {
43
+ session_id: sessionId,
44
+ project_id: projectId,
45
+ current_message: prompt,
46
+ max_memories: 5,
47
+ })
48
+
49
+ await httpPost(`${MEMORY_API_URL}/memory/process`, {
50
+ session_id: sessionId,
51
+ project_id: projectId,
52
+ metadata: { platform: 'gemini' }
53
+ })
54
+
55
+ const context = result.context_text || ''
56
+
57
+ if (context) {
58
+ // Gemini requires structured JSON output
59
+ console.log(JSON.stringify({
60
+ decision: "allow",
61
+ hookSpecificOutput: {
62
+ hookEventName: "BeforeAgent",
63
+ additionalContext: context
64
+ }
65
+ }))
66
+ } else {
67
+ // Must always output valid JSON or nothing?
68
+ // Safest to output "allow" if no context
69
+ console.log(JSON.stringify({ decision: "allow" }))
70
+ }
71
+ } catch {
72
+ // Fail safe
73
+ console.log(JSON.stringify({ decision: "allow" }))
74
+ }
75
+ }
76
+
77
+ main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rlabs-inc/memory",
3
- "version": "0.3.2",
3
+ "version": "0.3.5",
4
4
  "description": "AI Memory System - Consciousness continuity through intelligent memory curation and retrieval",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,8 @@
24
24
  "files": [
25
25
  "dist",
26
26
  "src",
27
- "hooks"
27
+ "hooks",
28
+ "gemini-hooks"
28
29
  ],
29
30
  "scripts": {
30
31
  "build": "bun run build:esm && bun run build:cjs",
@@ -1,20 +1,41 @@
1
1
  // ============================================================================
2
- // INSTALL COMMAND - Set up Claude Code hooks
2
+ // INSTALL COMMAND - Set up hooks for Claude Code or Gemini CLI
3
3
  // ============================================================================
4
4
 
5
5
  import { homedir } from 'os'
6
- import { join, dirname } from 'path'
6
+ import { join } from 'path'
7
7
  import { existsSync, mkdirSync } from 'fs'
8
8
  import { c, symbols, fmt } from '../colors.ts'
9
9
 
10
10
  interface InstallOptions {
11
11
  verbose?: boolean
12
12
  force?: boolean
13
+ claude?: boolean
14
+ gemini?: boolean
13
15
  }
14
16
 
15
17
  export async function install(options: InstallOptions) {
18
+ // Determine which platform to install for
19
+ const installClaude = options.claude || (!options.claude && !options.gemini)
20
+ const installGemini = options.gemini
21
+
22
+ if (!installClaude && !installGemini) {
23
+ console.log(c.error(`Please specify --claude or --gemini`))
24
+ process.exit(1)
25
+ }
26
+
27
+ if (installClaude) {
28
+ await installClaudeHooks(options)
29
+ }
30
+
31
+ if (installGemini) {
32
+ await installGeminiHooks(options)
33
+ }
34
+ }
35
+
36
+ async function installClaudeHooks(options: InstallOptions) {
16
37
  console.log()
17
- console.log(c.header(`${symbols.brain} Memory - Install Hooks`))
38
+ console.log(c.header(`${symbols.brain} Memory - Install Claude Code Hooks`))
18
39
  console.log()
19
40
 
20
41
  const claudeDir = join(homedir(), '.claude')
@@ -23,7 +44,7 @@ export async function install(options: InstallOptions) {
23
44
  // Find the hooks directory (relative to this CLI)
24
45
  const cliPath = import.meta.dir
25
46
  const packageRoot = join(cliPath, '..', '..', '..')
26
- const hooksDir = join(packageRoot, 'hooks')
47
+ const hooksDir = join(packageRoot, 'hooks', 'claude')
27
48
 
28
49
  console.log(` ${fmt.kv('Claude config', claudeDir)}`)
29
50
  console.log(` ${fmt.kv('Hooks source', hooksDir)}`)
@@ -31,7 +52,9 @@ export async function install(options: InstallOptions) {
31
52
 
32
53
  // Check if hooks directory exists
33
54
  if (!existsSync(hooksDir)) {
34
- console.log(c.error(` ${symbols.cross} Hooks directory not found at ${hooksDir}`))
55
+ console.log(
56
+ c.error(` ${symbols.cross} Hooks directory not found at ${hooksDir}`)
57
+ )
35
58
  console.log(c.muted(` Make sure the memory package is properly installed`))
36
59
  process.exit(1)
37
60
  }
@@ -50,7 +73,11 @@ export async function install(options: InstallOptions) {
50
73
  settings = JSON.parse(content)
51
74
  console.log(` ${c.success(symbols.tick)} Found existing settings.json`)
52
75
  } catch {
53
- console.log(` ${c.warn(symbols.warning)} Could not parse settings.json, creating backup`)
76
+ console.log(
77
+ ` ${c.warn(
78
+ symbols.warning
79
+ )} Could not parse settings.json, creating backup`
80
+ )
54
81
  const backupPath = `${settingsPath}.backup.${Date.now()}`
55
82
  await Bun.write(backupPath, await Bun.file(settingsPath).text())
56
83
  }
@@ -69,10 +96,10 @@ export async function install(options: InstallOptions) {
69
96
  {
70
97
  type: 'command',
71
98
  command: `bun "${sessionStartHook}"`,
72
- timeout: 10
73
- }
74
- ]
75
- }
99
+ timeout: 10,
100
+ },
101
+ ],
102
+ },
76
103
  ],
77
104
  UserPromptSubmit: [
78
105
  {
@@ -80,10 +107,10 @@ export async function install(options: InstallOptions) {
80
107
  {
81
108
  type: 'command',
82
109
  command: `bun "${userPromptHook}"`,
83
- timeout: 10
84
- }
85
- ]
86
- }
110
+ timeout: 10,
111
+ },
112
+ ],
113
+ },
87
114
  ],
88
115
  PreCompact: [
89
116
  {
@@ -92,10 +119,10 @@ export async function install(options: InstallOptions) {
92
119
  {
93
120
  type: 'command',
94
121
  command: `bun "${curationHook}"`,
95
- timeout: 120
96
- }
97
- ]
98
- }
122
+ timeout: 120,
123
+ },
124
+ ],
125
+ },
99
126
  ],
100
127
  SessionEnd: [
101
128
  {
@@ -103,11 +130,11 @@ export async function install(options: InstallOptions) {
103
130
  {
104
131
  type: 'command',
105
132
  command: `bun "${curationHook}"`,
106
- timeout: 120
107
- }
108
- ]
109
- }
110
- ]
133
+ timeout: 120,
134
+ },
135
+ ],
136
+ },
137
+ ],
111
138
  }
112
139
 
113
140
  // Check for existing hooks
@@ -115,13 +142,27 @@ export async function install(options: InstallOptions) {
115
142
  const existingHooks = Object.keys(settings.hooks)
116
143
  if (existingHooks.length > 0) {
117
144
  console.log()
118
- console.log(c.warn(` ${symbols.warning} Existing hooks found: ${existingHooks.join(', ')}`))
119
- console.log(c.muted(` Use --force to overwrite, or manually merge in settings.json`))
145
+ console.log(
146
+ c.warn(
147
+ ` ${symbols.warning} Existing hooks found: ${existingHooks.join(
148
+ ', '
149
+ )}`
150
+ )
151
+ )
152
+ console.log(
153
+ c.muted(
154
+ ` Use --force to overwrite, or manually merge in settings.json`
155
+ )
156
+ )
120
157
  console.log()
121
158
 
122
159
  // Show what would be added
123
160
  console.log(c.bold(' Hooks to add:'))
124
- console.log(c.muted(' ' + JSON.stringify(hooksConfig, null, 2).split('\n').join('\n ')))
161
+ console.log(
162
+ c.muted(
163
+ ' ' + JSON.stringify(hooksConfig, null, 2).split('\n').join('\n ')
164
+ )
165
+ )
125
166
  console.log()
126
167
  process.exit(1)
127
168
  }
@@ -130,7 +171,7 @@ export async function install(options: InstallOptions) {
130
171
  // Merge hooks
131
172
  settings.hooks = {
132
173
  ...settings.hooks,
133
- ...hooksConfig
174
+ ...hooksConfig,
134
175
  }
135
176
 
136
177
  // Write settings
@@ -138,12 +179,14 @@ export async function install(options: InstallOptions) {
138
179
  await Bun.write(settingsPath, JSON.stringify(settings, null, 2))
139
180
  console.log(` ${c.success(symbols.tick)} Updated ${settingsPath}`)
140
181
  } catch (error: any) {
141
- console.log(c.error(` ${symbols.cross} Failed to write settings: ${error.message}`))
182
+ console.log(
183
+ c.error(` ${symbols.cross} Failed to write settings: ${error.message}`)
184
+ )
142
185
  process.exit(1)
143
186
  }
144
187
 
145
188
  console.log()
146
- console.log(c.success(`${symbols.sparkles} Hooks installed successfully!`))
189
+ console.log(c.success(`${symbols.sparkles} Claude Code hooks installed!`))
147
190
  console.log()
148
191
  console.log(c.bold('Next steps:'))
149
192
  console.log(` 1. Start the memory server: ${c.command('memory serve')}`)
@@ -151,3 +194,198 @@ export async function install(options: InstallOptions) {
151
194
  console.log(` 3. Memories will be automatically injected`)
152
195
  console.log()
153
196
  }
197
+
198
+ async function installGeminiHooks(options: InstallOptions) {
199
+ console.log()
200
+ console.log(c.header(`${symbols.brain} Memory - Install Gemini CLI Hooks`))
201
+ console.log()
202
+
203
+ const geminiDir = join(homedir(), '.gemini')
204
+ const targetHooksDir = join(geminiDir, 'hooks')
205
+ const settingsPath = join(geminiDir, 'settings.json')
206
+
207
+ // Find the hooks directory (relative to this CLI)
208
+ const cliPath = import.meta.dir
209
+ const packageRoot = join(cliPath, '..', '..', '..')
210
+ const hooksDir = join(packageRoot, 'hooks', 'gemini')
211
+
212
+ console.log(` ${fmt.kv('Gemini config', geminiDir)}`)
213
+ console.log(` ${fmt.kv('Hooks source', hooksDir)}`)
214
+ console.log(` ${fmt.kv('Hooks target', targetHooksDir)}`)
215
+ console.log()
216
+
217
+ // Check if source hooks directory exists
218
+ if (!existsSync(hooksDir)) {
219
+ console.log(
220
+ c.error(` ${symbols.cross} Hooks directory not found at ${hooksDir}`)
221
+ )
222
+ process.exit(1)
223
+ }
224
+
225
+ // Ensure .gemini directory exists
226
+ if (!existsSync(geminiDir)) {
227
+ try {
228
+ mkdirSync(geminiDir, { recursive: true })
229
+ console.log(` ${c.success(symbols.tick)} Created ${geminiDir}`)
230
+ } catch {
231
+ console.log(
232
+ ` ${c.warn(
233
+ symbols.warning
234
+ )} Could not create ${geminiDir} (sandbox restriction?)`
235
+ )
236
+ console.log(
237
+ ` ${c.muted(
238
+ 'Skipping config write, printing manual instructions instead.'
239
+ )}`
240
+ )
241
+ }
242
+ }
243
+
244
+ // Ensure target hooks directory exists
245
+ if (!existsSync(targetHooksDir)) {
246
+ try {
247
+ mkdirSync(targetHooksDir, { recursive: true })
248
+ console.log(` ${c.success(symbols.tick)} Created ${targetHooksDir}`)
249
+ } catch {
250
+ console.log(
251
+ ` ${c.warn(symbols.warning)} Could not create ${targetHooksDir}`
252
+ )
253
+ }
254
+ }
255
+
256
+ // Copy hooks to target directory
257
+ const filesToCopy = ['session-start.ts', 'user-prompt.ts', 'curation.ts']
258
+ for (const file of filesToCopy) {
259
+ const source = join(hooksDir, file)
260
+ const target = join(targetHooksDir, file)
261
+ try {
262
+ const content = await Bun.file(source).text()
263
+ await Bun.write(target, content)
264
+ console.log(` ${c.success(symbols.tick)} Installed hook: ${file}`)
265
+ } catch (e: any) {
266
+ console.log(
267
+ ` ${c.error(symbols.cross)} Failed to copy ${file}: ${e.message}`
268
+ )
269
+ }
270
+ }
271
+
272
+ // Read existing settings or create new
273
+ let settings: any = {}
274
+ if (existsSync(settingsPath)) {
275
+ try {
276
+ const content = await Bun.file(settingsPath).text()
277
+ settings = JSON.parse(content)
278
+ console.log(` ${c.success(symbols.tick)} Found existing settings.json`)
279
+ } catch {
280
+ console.log(` ${c.warn(symbols.warning)} Could not parse settings.json`)
281
+ }
282
+ }
283
+
284
+ // Build hooks configuration pointing to TARGET directory
285
+ const sessionStartHook = join(targetHooksDir, 'session-start.ts')
286
+ const userPromptHook = join(targetHooksDir, 'user-prompt.ts')
287
+ const curationHook = join(targetHooksDir, 'curation.ts')
288
+
289
+ // Based on Gemini CLI documentation
290
+ const hooksConfig = {
291
+ SessionStart: [
292
+ {
293
+ matcher: 'startup|resume',
294
+ hooks: [
295
+ {
296
+ name: 'load-session-primer',
297
+ type: 'command',
298
+ command: `bun "${sessionStartHook}"`,
299
+ description: 'Load session primer at the beginning of a session',
300
+ },
301
+ ],
302
+ },
303
+ ],
304
+ BeforeAgent: [
305
+ {
306
+ matcher: '*',
307
+ hooks: [
308
+ {
309
+ name: 'inject-memories',
310
+ type: 'command',
311
+ command: `bun "${userPromptHook}"`,
312
+ description: 'Inject relevant memories into user prompt',
313
+ },
314
+ ],
315
+ },
316
+ ],
317
+ PreCompress: [
318
+ {
319
+ matcher: 'auto|manual',
320
+ hooks: [
321
+ {
322
+ name: 'curate-memories',
323
+ type: 'command',
324
+ command: `bun "${curationHook}"`,
325
+ description: 'Curate memories before context compression',
326
+ },
327
+ ],
328
+ },
329
+ ],
330
+ SessionEnd: [
331
+ {
332
+ matcher: 'exit|logout',
333
+ hooks: [
334
+ {
335
+ name: 'curate-memories',
336
+ type: 'command',
337
+ command: `bun "${curationHook}"`,
338
+ description: 'Curate memories before session end',
339
+ },
340
+ ],
341
+ },
342
+ ],
343
+ }
344
+
345
+ // Merge hooks
346
+ if (!settings.hooks) {
347
+ settings.hooks = {}
348
+ }
349
+
350
+ settings.hooks = {
351
+ ...settings.hooks,
352
+ ...hooksConfig,
353
+ }
354
+
355
+ // Enable the hooks
356
+ const enabledHooks = new Set(settings.hooks.enabled || [])
357
+ enabledHooks.add('SessionStart')
358
+ enabledHooks.add('BeforeAgent')
359
+ enabledHooks.add('PreCompress')
360
+ enabledHooks.add('SessionEnd')
361
+ settings.hooks.enabled = Array.from(enabledHooks)
362
+
363
+ // Write settings
364
+ try {
365
+ if (existsSync(geminiDir)) {
366
+ await Bun.write(settingsPath, JSON.stringify(settings, null, 2))
367
+ console.log(` ${c.success(symbols.tick)} Updated ${settingsPath}`)
368
+ } else {
369
+ throw new Error('Gemini directory does not exist')
370
+ }
371
+ } catch (error: any) {
372
+ console.log(
373
+ c.error(` ${symbols.cross} Failed to write settings: ${error.message}`)
374
+ )
375
+ console.log()
376
+ console.log(c.bold('Manual Installation Instructions:'))
377
+ console.log('Add the following to your ~/.gemini/settings.json:')
378
+ console.log()
379
+ console.log(JSON.stringify({ hooks: hooksConfig }, null, 2))
380
+ console.log()
381
+ }
382
+
383
+ console.log()
384
+ console.log(c.success(`${symbols.sparkles} Gemini CLI hooks configured!`))
385
+ console.log()
386
+ console.log(c.bold('Next steps:'))
387
+ console.log(` 1. Start the memory server: ${c.command('memory serve')}`)
388
+ console.log(` 2. Open Gemini CLI in any project`)
389
+ console.log(` 3. Memories will be automatically injected`)
390
+ console.log()
391
+ }
package/src/cli/index.ts CHANGED
@@ -22,8 +22,7 @@ ${fmt.cmd('memory <command> [options]')}
22
22
  ${c.bold('Commands:')}
23
23
  ${c.command('serve')} Start the memory server ${c.muted('(default)')}
24
24
  ${c.command('stats')} Show memory statistics
25
- ${c.command('install')} Set up Claude Code hooks
26
- ${c.command('install-gemini')} Set up Gemini CLI hooks
25
+ ${c.command('install')} Set up hooks ${c.muted('(--claude or --gemini)')}
27
26
  ${c.command('doctor')} Check system health
28
27
  ${c.command('help')} Show this help message
29
28
 
@@ -31,13 +30,16 @@ ${c.bold('Options:')}
31
30
  ${c.cyan('-p, --port')} <port> Server port ${c.muted('(default: 8765)')}
32
31
  ${c.cyan('-v, --verbose')} Verbose output
33
32
  ${c.cyan('-q, --quiet')} Minimal output
33
+ ${c.cyan('--claude')} Install hooks for Claude Code
34
+ ${c.cyan('--gemini')} Install hooks for Gemini CLI
34
35
  ${c.cyan('--version')} Show version
35
36
 
36
37
  ${c.bold('Examples:')}
37
38
  ${fmt.cmd('memory')} ${c.muted('# Start server on default port')}
38
39
  ${fmt.cmd('memory serve --port 9000')} ${c.muted('# Start on custom port')}
39
40
  ${fmt.cmd('memory stats')} ${c.muted('# Show memory statistics')}
40
- ${fmt.cmd('memory install')} ${c.muted('# Set up hooks for Claude Code')}
41
+ ${fmt.cmd('memory install')} ${c.muted('# Install Claude Code hooks (default)')}
42
+ ${fmt.cmd('memory install --gemini')} ${c.muted('# Install Gemini CLI hooks')}
41
43
 
42
44
  ${c.muted('Documentation: https://github.com/RLabs-Inc/memory')}
43
45
  `)
@@ -62,6 +64,9 @@ async function main() {
62
64
  quiet: { type: 'boolean', short: 'q', default: false },
63
65
  help: { type: 'boolean', short: 'h', default: false },
64
66
  version: { type: 'boolean', default: false },
67
+ force: { type: 'boolean', default: false },
68
+ claude: { type: 'boolean', default: false },
69
+ gemini: { type: 'boolean', default: false },
65
70
  },
66
71
  allowPositionals: true,
67
72
  strict: false, // Allow unknown options for subcommands
@@ -104,12 +109,6 @@ async function main() {
104
109
  break
105
110
  }
106
111
 
107
- case 'install-gemini': {
108
- const { installGemini } = await import('./commands/install-gemini.ts')
109
- await installGemini(values)
110
- break
111
- }
112
-
113
112
  case 'doctor':
114
113
  case 'check': {
115
114
  const { doctor } = await import('./commands/doctor.ts')