@rlabs-inc/memory 0.3.3 → 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.
- package/dist/index.js +2520 -1404
- package/dist/index.mjs +2513 -1397
- package/dist/server/index.mjs +35910 -2628
- package/package.json +1 -1
- package/src/cli/commands/install.ts +267 -29
- package/src/cli/index.ts +8 -9
- package/src/cli/commands/install-gemini.ts +0 -146
- /package/hooks/{curation.ts → claude/curation.ts} +0 -0
- /package/hooks/{session-start.ts → claude/session-start.ts} +0 -0
- /package/hooks/{user-prompt.ts → claude/user-prompt.ts} +0 -0
- /package/{gemini-hooks → hooks/gemini}/curation.ts +0 -0
- /package/{gemini-hooks → hooks/gemini}/session-start.ts +0 -0
- /package/{gemini-hooks → hooks/gemini}/user-prompt.ts +0 -0
package/package.json
CHANGED
|
@@ -1,20 +1,41 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
|
-
// INSTALL COMMAND - Set up Claude Code
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
119
|
-
|
|
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(
|
|
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(
|
|
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}
|
|
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
|
|
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('#
|
|
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')
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// INSTALL GEMINI COMMAND - Set up Gemini CLI hooks
|
|
3
|
-
// ============================================================================
|
|
4
|
-
|
|
5
|
-
import { homedir } from 'os'
|
|
6
|
-
import { join } from 'path'
|
|
7
|
-
import { existsSync, mkdirSync } from 'fs'
|
|
8
|
-
import { c, symbols, fmt } from '../colors.ts'
|
|
9
|
-
|
|
10
|
-
interface InstallOptions {
|
|
11
|
-
verbose?: boolean
|
|
12
|
-
force?: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function installGemini(options: InstallOptions) {
|
|
16
|
-
console.log()
|
|
17
|
-
console.log(c.header(`${symbols.brain} Memory - Install Gemini Hooks`))
|
|
18
|
-
console.log()
|
|
19
|
-
|
|
20
|
-
const geminiDir = join(homedir(), '.gemini')
|
|
21
|
-
const settingsPath = join(geminiDir, 'settings.json')
|
|
22
|
-
|
|
23
|
-
// Find the hooks directory (relative to this CLI)
|
|
24
|
-
const cliPath = import.meta.dir
|
|
25
|
-
const packageRoot = join(cliPath, '..', '..', '..')
|
|
26
|
-
const hooksDir = join(packageRoot, 'gemini-hooks')
|
|
27
|
-
|
|
28
|
-
console.log(` ${fmt.kv('Gemini config', geminiDir)}`)
|
|
29
|
-
console.log(` ${fmt.kv('Hooks source', hooksDir)}`)
|
|
30
|
-
console.log()
|
|
31
|
-
|
|
32
|
-
// Check if hooks directory exists
|
|
33
|
-
if (!existsSync(hooksDir)) {
|
|
34
|
-
console.log(c.error(` ${symbols.cross} Hooks directory not found at ${hooksDir}`))
|
|
35
|
-
process.exit(1)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Ensure .gemini directory exists
|
|
39
|
-
if (!existsSync(geminiDir)) {
|
|
40
|
-
try {
|
|
41
|
-
mkdirSync(geminiDir, { recursive: true })
|
|
42
|
-
console.log(` ${c.success(symbols.tick)} Created ${geminiDir}`)
|
|
43
|
-
} catch {
|
|
44
|
-
console.log(` ${c.warn(symbols.warning)} Could not create ${geminiDir} (sandbox restriction?)`)
|
|
45
|
-
console.log(` ${c.muted('Skipping config write, printing manual instructions instead.')}`)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Read existing settings or create new
|
|
50
|
-
let settings: any = {}
|
|
51
|
-
if (existsSync(settingsPath)) {
|
|
52
|
-
try {
|
|
53
|
-
const content = await Bun.file(settingsPath).text()
|
|
54
|
-
settings = JSON.parse(content)
|
|
55
|
-
console.log(` ${c.success(symbols.tick)} Found existing settings.json`)
|
|
56
|
-
} catch {
|
|
57
|
-
console.log(` ${c.warn(symbols.warning)} Could not parse settings.json`)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Build hooks configuration
|
|
62
|
-
const sessionStartHook = join(hooksDir, 'session-start.ts')
|
|
63
|
-
const userPromptHook = join(hooksDir, 'user-prompt.ts')
|
|
64
|
-
const curationHook = join(hooksDir, 'curation.ts')
|
|
65
|
-
|
|
66
|
-
// Based on Gemini CLI documentation
|
|
67
|
-
const hooksConfig = {
|
|
68
|
-
SessionStart: [
|
|
69
|
-
{
|
|
70
|
-
matcher: 'startup|resume',
|
|
71
|
-
hooks: [
|
|
72
|
-
{
|
|
73
|
-
type: 'command',
|
|
74
|
-
command: `bun "${sessionStartHook}"`,
|
|
75
|
-
timeout: 10000
|
|
76
|
-
}
|
|
77
|
-
]
|
|
78
|
-
}
|
|
79
|
-
],
|
|
80
|
-
BeforeAgent: [
|
|
81
|
-
{
|
|
82
|
-
hooks: [
|
|
83
|
-
{
|
|
84
|
-
type: 'command',
|
|
85
|
-
command: `bun "${userPromptHook}"`,
|
|
86
|
-
timeout: 10000
|
|
87
|
-
}
|
|
88
|
-
]
|
|
89
|
-
}
|
|
90
|
-
],
|
|
91
|
-
PreCompress: [
|
|
92
|
-
{
|
|
93
|
-
matcher: 'auto|manual',
|
|
94
|
-
hooks: [
|
|
95
|
-
{
|
|
96
|
-
type: 'command',
|
|
97
|
-
command: `bun "${curationHook}"`,
|
|
98
|
-
timeout: 120000
|
|
99
|
-
}
|
|
100
|
-
]
|
|
101
|
-
}
|
|
102
|
-
],
|
|
103
|
-
SessionEnd: [
|
|
104
|
-
{
|
|
105
|
-
hooks: [
|
|
106
|
-
{
|
|
107
|
-
type: 'command',
|
|
108
|
-
command: `bun "${curationHook}"`,
|
|
109
|
-
timeout: 120000
|
|
110
|
-
}
|
|
111
|
-
]
|
|
112
|
-
}
|
|
113
|
-
]
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Merge hooks
|
|
117
|
-
if (!settings.hooks) {
|
|
118
|
-
settings.hooks = {}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
settings.hooks = {
|
|
122
|
-
...settings.hooks,
|
|
123
|
-
...hooksConfig
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Write settings
|
|
127
|
-
try {
|
|
128
|
-
if (existsSync(geminiDir)) {
|
|
129
|
-
await Bun.write(settingsPath, JSON.stringify(settings, null, 2))
|
|
130
|
-
console.log(` ${c.success(symbols.tick)} Updated ${settingsPath}`)
|
|
131
|
-
} else {
|
|
132
|
-
throw new Error("Gemini directory does not exist")
|
|
133
|
-
}
|
|
134
|
-
} catch (error: any) {
|
|
135
|
-
console.log(c.error(` ${symbols.cross} Failed to write settings: ${error.message}`))
|
|
136
|
-
console.log()
|
|
137
|
-
console.log(c.bold('Manual Installation Instructions:'))
|
|
138
|
-
console.log('Add the following to your ~/.gemini/settings.json:')
|
|
139
|
-
console.log()
|
|
140
|
-
console.log(JSON.stringify({ hooks: hooksConfig }, null, 2))
|
|
141
|
-
console.log()
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
console.log()
|
|
145
|
-
console.log(c.success(`${symbols.sparkles} Gemini hooks configured!`))
|
|
146
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|