claude-brain 0.30.2 → 0.30.3
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/README.md +241 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +29 -29
- package/package.json +7 -3
- package/packs/backend/node.json +173 -173
- package/packs/core/javascript.json +176 -176
- package/packs/core/typescript.json +222 -222
- package/packs/frontend/react.json +254 -254
- package/packs/meta/testing.json +172 -172
- package/scripts/postinstall.mjs +531 -531
- package/src/automation/decision-detector.ts +452 -452
- package/src/automation/phase12-manager.ts +456 -456
- package/src/automation/proactive-recall.ts +373 -373
- package/src/automation/project-detector.ts +310 -310
- package/src/automation/repo-scanner.ts +210 -205
- package/src/cli/auto-setup.ts +75 -75
- package/src/cli/auto-start.ts +266 -266
- package/src/cli/bin.ts +264 -264
- package/src/cli/commands/autostart.ts +90 -90
- package/src/cli/commands/chroma.ts +578 -577
- package/src/cli/commands/export-training.ts +70 -70
- package/src/cli/commands/export.ts +130 -130
- package/src/cli/commands/git-hook.ts +183 -183
- package/src/cli/commands/hooks.ts +217 -217
- package/src/cli/commands/init.ts +123 -123
- package/src/cli/commands/install-mcp.ts +122 -111
- package/src/cli/commands/models.ts +979 -979
- package/src/cli/commands/pack.ts +200 -200
- package/src/cli/commands/refresh.ts +344 -339
- package/src/cli/commands/reindex.ts +120 -120
- package/src/cli/commands/serve.ts +466 -463
- package/src/cli/commands/start.ts +44 -44
- package/src/cli/commands/status.ts +220 -203
- package/src/cli/commands/uninstall-mcp.ts +45 -41
- package/src/cli/commands/update.ts +130 -124
- package/src/cli/migrate-chroma.ts +106 -106
- package/src/cli/ui/animations.ts +80 -80
- package/src/cli/ui/components.ts +82 -82
- package/src/cli/ui/index.ts +4 -4
- package/src/cli/ui/logo.ts +36 -36
- package/src/cli/ui/theme.ts +55 -55
- package/src/code-intelligence/indexer.ts +352 -352
- package/src/code-intelligence/linker.ts +178 -178
- package/src/code-intelligence/parser.ts +484 -484
- package/src/code-intelligence/query.ts +291 -291
- package/src/code-intelligence/schema.ts +83 -83
- package/src/code-intelligence/types.ts +95 -95
- package/src/config/defaults.ts +52 -52
- package/src/config/home.ts +56 -56
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +192 -192
- package/src/config/schema.ts +446 -415
- package/src/config/validator.ts +182 -182
- package/src/context/assembler.ts +407 -400
- package/src/context/index.ts +79 -79
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +122 -121
- package/src/health/index.ts +233 -232
- package/src/hooks/brain-hook.ts +134 -131
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/claude-code-mastery.md +112 -112
- package/src/hooks/context-hook.ts +260 -245
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +211 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +306 -288
- package/src/hooks/interceptor-hook.ts +204 -201
- package/src/hooks/passive-classifier.ts +397 -397
- package/src/hooks/queue.ts +160 -129
- package/src/hooks/session-tracker.ts +312 -312
- package/src/hooks/types.ts +52 -52
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +7 -7
- package/src/intelligence/hf-downloader.ts +222 -222
- package/src/intelligence/hf-manifest.json +78 -78
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/inference-router.ts +762 -762
- package/src/intelligence/model-manager.ts +263 -245
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +213 -207
- package/src/intelligence/prediction/index.ts +7 -7
- package/src/intelligence/prediction/recommender.ts +276 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +243 -247
- package/src/intelligence/reasoning/index.ts +7 -7
- package/src/intelligence/temporal/evolution.ts +193 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +272 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/intelligence/tokenizer.ts +118 -118
- package/src/knowledge/entity-extractor.ts +447 -443
- package/src/knowledge/graph/builder.ts +185 -185
- package/src/knowledge/graph/linker.ts +201 -201
- package/src/knowledge/graph/memory-graph.ts +359 -359
- package/src/knowledge/graph/schema.ts +99 -99
- package/src/knowledge/graph/search.ts +166 -166
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +211 -192
- package/src/memory/chroma/collection-manager.ts +92 -92
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +177 -175
- package/src/memory/chroma/index.ts +82 -82
- package/src/memory/chroma/migration.ts +270 -270
- package/src/memory/chroma/schemas.ts +69 -69
- package/src/memory/chroma/search.ts +319 -315
- package/src/memory/chroma/store.ts +755 -747
- package/src/memory/compression.ts +121 -121
- package/src/memory/consolidation/archiver.ts +162 -165
- package/src/memory/consolidation/merger.ts +182 -186
- package/src/memory/consolidation/scorer.ts +136 -136
- package/src/memory/database.ts +9 -0
- package/src/memory/dual-write.ts +145 -0
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +347 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/fts5-search.ts +692 -633
- package/src/memory/index.ts +943 -1060
- package/src/memory/migrations/add-fts5.ts +118 -108
- package/src/memory/patterns.ts +438 -438
- package/src/memory/pruning.ts +60 -60
- package/src/memory/schema.ts +88 -88
- package/src/memory/store.ts +911 -787
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- package/src/packs/index.ts +9 -9
- package/src/packs/loader.ts +134 -134
- package/src/packs/manager.ts +204 -204
- package/src/packs/ranker.ts +78 -78
- package/src/packs/types.ts +81 -81
- package/src/phase12/index.ts +5 -5
- package/src/retrieval/bm25/index.ts +300 -297
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +221 -221
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +221 -221
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +165 -165
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +203 -203
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +252 -252
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +189 -188
- package/src/retrieval/reranker/model.ts +99 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +454 -454
- package/src/routing/handlers/exploration-handler.ts +369 -0
- package/src/routing/handlers/index.ts +19 -0
- package/src/routing/handlers/memory-handler.ts +273 -0
- package/src/routing/handlers/mutation-handler.ts +241 -0
- package/src/routing/handlers/recall-handler.ts +642 -0
- package/src/routing/handlers/shared.ts +515 -0
- package/src/routing/handlers/types.ts +48 -0
- package/src/routing/intent-classifier.ts +552 -552
- package/src/routing/response-filter.ts +399 -391
- package/src/routing/router.ts +245 -2193
- package/src/routing/search-engine.ts +521 -514
- package/src/routing/types.ts +104 -94
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/auto-updater.ts +283 -276
- package/src/server/handlers/call-tool.ts +159 -159
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/auto-remember.ts +165 -165
- package/src/server/handlers/tools/brain.ts +86 -86
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/get-code-standards.ts +123 -123
- package/src/server/handlers/tools/get-corrections.ts +152 -152
- package/src/server/handlers/tools/get-patterns.ts +156 -156
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/index.ts +30 -30
- package/src/server/handlers/tools/init-project.ts +756 -756
- package/src/server/handlers/tools/list-projects.ts +126 -126
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +132 -132
- package/src/server/handlers/tools/record-correction.ts +131 -131
- package/src/server/handlers/tools/remember-decision.ts +168 -168
- package/src/server/handlers/tools/schemas.ts +179 -179
- package/src/server/handlers/tools/search-code.ts +122 -122
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/http-api.ts +215 -1229
- package/src/server/mcp-proxy.ts +85 -84
- package/src/server/mcp-server.ts +285 -284
- package/src/server/middleware/auth.ts +39 -0
- package/src/server/middleware/error-handler.ts +37 -0
- package/src/server/middleware/rate-limit.ts +53 -0
- package/src/server/middleware/validate.ts +42 -0
- package/src/server/pid-manager.ts +137 -136
- package/src/server/providers/resources.ts +581 -581
- package/src/server/routes/code.ts +228 -0
- package/src/server/routes/context.ts +26 -0
- package/src/server/routes/health.ts +19 -0
- package/src/server/routes/helpers.ts +100 -0
- package/src/server/routes/hooks.ts +197 -0
- package/src/server/routes/mcp.ts +47 -0
- package/src/server/routes/memory.ts +397 -0
- package/src/server/routes/models.ts +96 -0
- package/src/server/routes/projects.ts +89 -0
- package/src/server/routes/types.ts +21 -0
- package/src/server/schemas/api-schemas.ts +202 -0
- package/src/server/services.ts +720 -720
- package/src/server/utils/memory-indicator.ts +84 -84
- package/src/server/utils/response-formatter.ts +129 -129
- package/src/server/web-viewer.ts +1145 -1115
- package/src/setup/index.ts +38 -38
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.ts +666 -666
- package/src/tools/types.ts +412 -412
- package/src/training/data-store.ts +320 -298
- package/src/training/retrain-pipeline.ts +399 -394
- package/src/utils/error-handler.ts +136 -136
- package/src/utils/index.ts +58 -58
- package/src/utils/kill-port.ts +55 -53
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/safe-path.ts +43 -0
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/index.ts +4 -3
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +4 -1
- package/src/vault/reader.ts +44 -1
- package/src/vault/watcher.ts +24 -1
- package/src/vault/writer.ts +487 -413
- package/skills/persistent-memory/SKILL.md +0 -148
- package/skills/persistent-memory/references/tool-reference.md +0 -90
package/src/cli/auto-start.ts
CHANGED
|
@@ -1,266 +1,266 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auto-start integration for claude-brain server.
|
|
3
|
-
* Installs/uninstalls a shell profile snippet so the server
|
|
4
|
-
* auto-starts on every new terminal session.
|
|
5
|
-
*
|
|
6
|
-
* Pure Node.js — no Bun-specific APIs.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
10
|
-
import { join, dirname } from 'node:path'
|
|
11
|
-
import { homedir, platform } from 'node:os'
|
|
12
|
-
|
|
13
|
-
const START_MARKER = '# >>> claude-brain auto-start >>>'
|
|
14
|
-
const END_MARKER = '# <<< claude-brain auto-start <<<'
|
|
15
|
-
|
|
16
|
-
const PREFIX = '[claude-brain]'
|
|
17
|
-
|
|
18
|
-
function log(msg: string) {
|
|
19
|
-
console.error(`${PREFIX} ${msg}`)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface AutoStartResult {
|
|
23
|
-
success: boolean
|
|
24
|
-
message: string
|
|
25
|
-
profilePath: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Build the Unix/macOS/Linux shell snippet.
|
|
30
|
-
* Uses `lsof` to check if the port is already in use before starting.
|
|
31
|
-
*/
|
|
32
|
-
function buildUnixSnippet(port: number): string {
|
|
33
|
-
return [
|
|
34
|
-
START_MARKER,
|
|
35
|
-
'# Auto-start claude-brain HTTP server if not already running',
|
|
36
|
-
`(command -v claude-brain >/dev/null 2>&1 && ! lsof -ti :${port} >/dev/null 2>&1) && {`,
|
|
37
|
-
' nohup claude-brain serve --http-only >/dev/null 2>&1 &',
|
|
38
|
-
' disown 2>/dev/null',
|
|
39
|
-
'}',
|
|
40
|
-
END_MARKER,
|
|
41
|
-
].join('\n')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Build the Fish shell snippet.
|
|
46
|
-
* Fish uses different syntax than bash/zsh.
|
|
47
|
-
*/
|
|
48
|
-
function buildFishSnippet(port: number): string {
|
|
49
|
-
return [
|
|
50
|
-
START_MARKER,
|
|
51
|
-
'# Auto-start claude-brain HTTP server if not already running',
|
|
52
|
-
`if command -v claude-brain >/dev/null 2>&1; and not lsof -ti :${port} >/dev/null 2>&1`,
|
|
53
|
-
' nohup claude-brain serve --http-only >/dev/null 2>&1 &',
|
|
54
|
-
'end',
|
|
55
|
-
END_MARKER,
|
|
56
|
-
].join('\n')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Build the PowerShell snippet for Windows.
|
|
61
|
-
*/
|
|
62
|
-
function buildPowerShellSnippet(port: number): string {
|
|
63
|
-
return [
|
|
64
|
-
START_MARKER,
|
|
65
|
-
'if (Get-Command claude-brain -ErrorAction SilentlyContinue) {',
|
|
66
|
-
` $port = ${port}`,
|
|
67
|
-
' $listening = Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue',
|
|
68
|
-
' if (-not $listening) {',
|
|
69
|
-
' Start-Process -NoNewWindow -FilePath "claude-brain" -ArgumentList "serve","--http-only" -WindowStyle Hidden',
|
|
70
|
-
' }',
|
|
71
|
-
'}',
|
|
72
|
-
END_MARKER,
|
|
73
|
-
].join('\n')
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Detect available shell profile files based on platform.
|
|
78
|
-
* Returns paths that exist or are the primary target for the detected shell.
|
|
79
|
-
*/
|
|
80
|
-
export function getShellProfiles(): string[] {
|
|
81
|
-
const home = homedir()
|
|
82
|
-
const os = platform()
|
|
83
|
-
const profiles: string[] = []
|
|
84
|
-
|
|
85
|
-
if (os === 'win32') {
|
|
86
|
-
// PowerShell profile
|
|
87
|
-
const psProfile = process.env.USERPROFILE
|
|
88
|
-
? join(
|
|
89
|
-
process.env.USERPROFILE,
|
|
90
|
-
'Documents',
|
|
91
|
-
'PowerShell',
|
|
92
|
-
'Microsoft.PowerShell_profile.ps1'
|
|
93
|
-
)
|
|
94
|
-
: null
|
|
95
|
-
// Also check WindowsPowerShell variant
|
|
96
|
-
const psProfileLegacy = process.env.USERPROFILE
|
|
97
|
-
? join(
|
|
98
|
-
process.env.USERPROFILE,
|
|
99
|
-
'Documents',
|
|
100
|
-
'WindowsPowerShell',
|
|
101
|
-
'Microsoft.PowerShell_profile.ps1'
|
|
102
|
-
)
|
|
103
|
-
: null
|
|
104
|
-
|
|
105
|
-
if (psProfile) profiles.push(psProfile)
|
|
106
|
-
if (psProfileLegacy && psProfileLegacy !== psProfile) profiles.push(psProfileLegacy)
|
|
107
|
-
return profiles
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Unix/macOS/Linux
|
|
111
|
-
if (os === 'darwin') {
|
|
112
|
-
// macOS defaults to zsh
|
|
113
|
-
profiles.push(join(home, '.zshrc'))
|
|
114
|
-
profiles.push(join(home, '.zprofile'))
|
|
115
|
-
} else {
|
|
116
|
-
// Linux defaults to bash
|
|
117
|
-
profiles.push(join(home, '.bashrc'))
|
|
118
|
-
profiles.push(join(home, '.bash_profile'))
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Fish (cross-platform Unix)
|
|
122
|
-
const fishConfig = join(home, '.config', 'fish', 'config.fish')
|
|
123
|
-
if (existsSync(fishConfig)) {
|
|
124
|
-
profiles.push(fishConfig)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return profiles
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Check if the auto-start snippet is already in any profile file.
|
|
132
|
-
*/
|
|
133
|
-
function profileContainsSnippet(profilePath: string): boolean {
|
|
134
|
-
if (!existsSync(profilePath)) return false
|
|
135
|
-
const content = readFileSync(profilePath, 'utf-8')
|
|
136
|
-
return content.includes(START_MARKER)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Get the appropriate snippet for a given profile path.
|
|
141
|
-
*/
|
|
142
|
-
function getSnippetForProfile(profilePath: string, port: number): string {
|
|
143
|
-
if (profilePath.endsWith('.ps1')) {
|
|
144
|
-
return buildPowerShellSnippet(port)
|
|
145
|
-
}
|
|
146
|
-
if (profilePath.includes('fish')) {
|
|
147
|
-
return buildFishSnippet(port)
|
|
148
|
-
}
|
|
149
|
-
return buildUnixSnippet(port)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Install auto-start snippet into the primary shell profile.
|
|
154
|
-
* Idempotent — skips if already installed.
|
|
155
|
-
*/
|
|
156
|
-
export function installAutoStart(port: number = 3000): AutoStartResult {
|
|
157
|
-
const profiles = getShellProfiles()
|
|
158
|
-
|
|
159
|
-
if (profiles.length === 0) {
|
|
160
|
-
return {
|
|
161
|
-
success: false,
|
|
162
|
-
message: 'No supported shell profile found',
|
|
163
|
-
profilePath: '',
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Use the first (primary) profile
|
|
168
|
-
const profilePath = profiles[0]
|
|
169
|
-
|
|
170
|
-
// Already installed?
|
|
171
|
-
if (profileContainsSnippet(profilePath)) {
|
|
172
|
-
log(`Auto-start already installed in ${profilePath}`)
|
|
173
|
-
return {
|
|
174
|
-
success: true,
|
|
175
|
-
message: 'Auto-start already installed',
|
|
176
|
-
profilePath,
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Ensure parent directory exists
|
|
181
|
-
const dir = dirname(profilePath)
|
|
182
|
-
if (!existsSync(dir)) {
|
|
183
|
-
mkdirSync(dir, { recursive: true })
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Read existing content or start fresh
|
|
187
|
-
const existing = existsSync(profilePath) ? readFileSync(profilePath, 'utf-8') : ''
|
|
188
|
-
|
|
189
|
-
// Append snippet with a blank line separator
|
|
190
|
-
const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n\n' : '\n'
|
|
191
|
-
const snippet = getSnippetForProfile(profilePath, port)
|
|
192
|
-
writeFileSync(profilePath, existing + separator + snippet + '\n', 'utf-8')
|
|
193
|
-
|
|
194
|
-
log(`Auto-start installed in ${profilePath}`)
|
|
195
|
-
return {
|
|
196
|
-
success: true,
|
|
197
|
-
message: `Auto-start installed in ${profilePath}`,
|
|
198
|
-
profilePath,
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Remove auto-start snippet from all known shell profiles.
|
|
204
|
-
* Removes everything between and including the marker comments.
|
|
205
|
-
*/
|
|
206
|
-
export function uninstallAutoStart(): AutoStartResult {
|
|
207
|
-
const profiles = getShellProfiles()
|
|
208
|
-
let removedFrom = ''
|
|
209
|
-
|
|
210
|
-
for (const profilePath of profiles) {
|
|
211
|
-
if (!existsSync(profilePath)) continue
|
|
212
|
-
|
|
213
|
-
const content = readFileSync(profilePath, 'utf-8')
|
|
214
|
-
if (!content.includes(START_MARKER)) continue
|
|
215
|
-
|
|
216
|
-
// Remove the snippet block (markers + everything between)
|
|
217
|
-
const startIdx = content.indexOf(START_MARKER)
|
|
218
|
-
const endIdx = content.indexOf(END_MARKER)
|
|
219
|
-
|
|
220
|
-
if (startIdx === -1 || endIdx === -1) continue
|
|
221
|
-
|
|
222
|
-
const endOfMarker = endIdx + END_MARKER.length
|
|
223
|
-
// Also remove trailing newline after end marker
|
|
224
|
-
const endWithNewline =
|
|
225
|
-
endOfMarker < content.length && content[endOfMarker] === '\n'
|
|
226
|
-
? endOfMarker + 1
|
|
227
|
-
: endOfMarker
|
|
228
|
-
|
|
229
|
-
// Remove leading blank line if the snippet was appended with separator
|
|
230
|
-
let start = startIdx
|
|
231
|
-
if (start > 0 && content[start - 1] === '\n') {
|
|
232
|
-
start--
|
|
233
|
-
if (start > 0 && content[start - 1] === '\n') {
|
|
234
|
-
start--
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const cleaned = content.slice(0, start) + content.slice(endWithNewline)
|
|
239
|
-
writeFileSync(profilePath, cleaned, 'utf-8')
|
|
240
|
-
removedFrom = profilePath
|
|
241
|
-
log(`Auto-start removed from ${profilePath}`)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (!removedFrom) {
|
|
245
|
-
log('Auto-start was not installed in any profile')
|
|
246
|
-
return {
|
|
247
|
-
success: true,
|
|
248
|
-
message: 'Auto-start was not installed in any profile',
|
|
249
|
-
profilePath: '',
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return {
|
|
254
|
-
success: true,
|
|
255
|
-
message: `Auto-start removed from ${removedFrom}`,
|
|
256
|
-
profilePath: removedFrom,
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Check if auto-start is installed in any known shell profile.
|
|
262
|
-
*/
|
|
263
|
-
export function isAutoStartInstalled(): boolean {
|
|
264
|
-
const profiles = getShellProfiles()
|
|
265
|
-
return profiles.some((p) => profileContainsSnippet(p))
|
|
266
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Auto-start integration for claude-brain server.
|
|
3
|
+
* Installs/uninstalls a shell profile snippet so the server
|
|
4
|
+
* auto-starts on every new terminal session.
|
|
5
|
+
*
|
|
6
|
+
* Pure Node.js — no Bun-specific APIs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
10
|
+
import { join, dirname } from 'node:path'
|
|
11
|
+
import { homedir, platform } from 'node:os'
|
|
12
|
+
|
|
13
|
+
const START_MARKER = '# >>> claude-brain auto-start >>>'
|
|
14
|
+
const END_MARKER = '# <<< claude-brain auto-start <<<'
|
|
15
|
+
|
|
16
|
+
const PREFIX = '[claude-brain]'
|
|
17
|
+
|
|
18
|
+
function log(msg: string) {
|
|
19
|
+
console.error(`${PREFIX} ${msg}`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface AutoStartResult {
|
|
23
|
+
success: boolean
|
|
24
|
+
message: string
|
|
25
|
+
profilePath: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build the Unix/macOS/Linux shell snippet.
|
|
30
|
+
* Uses `lsof` to check if the port is already in use before starting.
|
|
31
|
+
*/
|
|
32
|
+
function buildUnixSnippet(port: number): string {
|
|
33
|
+
return [
|
|
34
|
+
START_MARKER,
|
|
35
|
+
'# Auto-start claude-brain HTTP server if not already running',
|
|
36
|
+
`(command -v claude-brain >/dev/null 2>&1 && ! lsof -ti :${port} >/dev/null 2>&1) && {`,
|
|
37
|
+
' nohup claude-brain serve --http-only >/dev/null 2>&1 &',
|
|
38
|
+
' disown 2>/dev/null',
|
|
39
|
+
'}',
|
|
40
|
+
END_MARKER,
|
|
41
|
+
].join('\n')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Build the Fish shell snippet.
|
|
46
|
+
* Fish uses different syntax than bash/zsh.
|
|
47
|
+
*/
|
|
48
|
+
function buildFishSnippet(port: number): string {
|
|
49
|
+
return [
|
|
50
|
+
START_MARKER,
|
|
51
|
+
'# Auto-start claude-brain HTTP server if not already running',
|
|
52
|
+
`if command -v claude-brain >/dev/null 2>&1; and not lsof -ti :${port} >/dev/null 2>&1`,
|
|
53
|
+
' nohup claude-brain serve --http-only >/dev/null 2>&1 &',
|
|
54
|
+
'end',
|
|
55
|
+
END_MARKER,
|
|
56
|
+
].join('\n')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Build the PowerShell snippet for Windows.
|
|
61
|
+
*/
|
|
62
|
+
function buildPowerShellSnippet(port: number): string {
|
|
63
|
+
return [
|
|
64
|
+
START_MARKER,
|
|
65
|
+
'if (Get-Command claude-brain -ErrorAction SilentlyContinue) {',
|
|
66
|
+
` $port = ${port}`,
|
|
67
|
+
' $listening = Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue',
|
|
68
|
+
' if (-not $listening) {',
|
|
69
|
+
' Start-Process -NoNewWindow -FilePath "claude-brain" -ArgumentList "serve","--http-only" -WindowStyle Hidden',
|
|
70
|
+
' }',
|
|
71
|
+
'}',
|
|
72
|
+
END_MARKER,
|
|
73
|
+
].join('\n')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Detect available shell profile files based on platform.
|
|
78
|
+
* Returns paths that exist or are the primary target for the detected shell.
|
|
79
|
+
*/
|
|
80
|
+
export function getShellProfiles(): string[] {
|
|
81
|
+
const home = homedir()
|
|
82
|
+
const os = platform()
|
|
83
|
+
const profiles: string[] = []
|
|
84
|
+
|
|
85
|
+
if (os === 'win32') {
|
|
86
|
+
// PowerShell profile
|
|
87
|
+
const psProfile = process.env.USERPROFILE
|
|
88
|
+
? join(
|
|
89
|
+
process.env.USERPROFILE,
|
|
90
|
+
'Documents',
|
|
91
|
+
'PowerShell',
|
|
92
|
+
'Microsoft.PowerShell_profile.ps1'
|
|
93
|
+
)
|
|
94
|
+
: null
|
|
95
|
+
// Also check WindowsPowerShell variant
|
|
96
|
+
const psProfileLegacy = process.env.USERPROFILE
|
|
97
|
+
? join(
|
|
98
|
+
process.env.USERPROFILE,
|
|
99
|
+
'Documents',
|
|
100
|
+
'WindowsPowerShell',
|
|
101
|
+
'Microsoft.PowerShell_profile.ps1'
|
|
102
|
+
)
|
|
103
|
+
: null
|
|
104
|
+
|
|
105
|
+
if (psProfile) profiles.push(psProfile)
|
|
106
|
+
if (psProfileLegacy && psProfileLegacy !== psProfile) profiles.push(psProfileLegacy)
|
|
107
|
+
return profiles
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Unix/macOS/Linux
|
|
111
|
+
if (os === 'darwin') {
|
|
112
|
+
// macOS defaults to zsh
|
|
113
|
+
profiles.push(join(home, '.zshrc'))
|
|
114
|
+
profiles.push(join(home, '.zprofile'))
|
|
115
|
+
} else {
|
|
116
|
+
// Linux defaults to bash
|
|
117
|
+
profiles.push(join(home, '.bashrc'))
|
|
118
|
+
profiles.push(join(home, '.bash_profile'))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Fish (cross-platform Unix)
|
|
122
|
+
const fishConfig = join(home, '.config', 'fish', 'config.fish')
|
|
123
|
+
if (existsSync(fishConfig)) {
|
|
124
|
+
profiles.push(fishConfig)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return profiles
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if the auto-start snippet is already in any profile file.
|
|
132
|
+
*/
|
|
133
|
+
function profileContainsSnippet(profilePath: string): boolean {
|
|
134
|
+
if (!existsSync(profilePath)) return false
|
|
135
|
+
const content = readFileSync(profilePath, 'utf-8')
|
|
136
|
+
return content.includes(START_MARKER)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get the appropriate snippet for a given profile path.
|
|
141
|
+
*/
|
|
142
|
+
function getSnippetForProfile(profilePath: string, port: number): string {
|
|
143
|
+
if (profilePath.endsWith('.ps1')) {
|
|
144
|
+
return buildPowerShellSnippet(port)
|
|
145
|
+
}
|
|
146
|
+
if (profilePath.includes('fish')) {
|
|
147
|
+
return buildFishSnippet(port)
|
|
148
|
+
}
|
|
149
|
+
return buildUnixSnippet(port)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Install auto-start snippet into the primary shell profile.
|
|
154
|
+
* Idempotent — skips if already installed.
|
|
155
|
+
*/
|
|
156
|
+
export function installAutoStart(port: number = 3000): AutoStartResult {
|
|
157
|
+
const profiles = getShellProfiles()
|
|
158
|
+
|
|
159
|
+
if (profiles.length === 0) {
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
message: 'No supported shell profile found',
|
|
163
|
+
profilePath: '',
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Use the first (primary) profile
|
|
168
|
+
const profilePath = profiles[0]
|
|
169
|
+
|
|
170
|
+
// Already installed?
|
|
171
|
+
if (profileContainsSnippet(profilePath)) {
|
|
172
|
+
log(`Auto-start already installed in ${profilePath}`)
|
|
173
|
+
return {
|
|
174
|
+
success: true,
|
|
175
|
+
message: 'Auto-start already installed',
|
|
176
|
+
profilePath,
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Ensure parent directory exists
|
|
181
|
+
const dir = dirname(profilePath)
|
|
182
|
+
if (!existsSync(dir)) {
|
|
183
|
+
mkdirSync(dir, { recursive: true })
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Read existing content or start fresh
|
|
187
|
+
const existing = existsSync(profilePath) ? readFileSync(profilePath, 'utf-8') : ''
|
|
188
|
+
|
|
189
|
+
// Append snippet with a blank line separator
|
|
190
|
+
const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n\n' : '\n'
|
|
191
|
+
const snippet = getSnippetForProfile(profilePath, port)
|
|
192
|
+
writeFileSync(profilePath, existing + separator + snippet + '\n', 'utf-8')
|
|
193
|
+
|
|
194
|
+
log(`Auto-start installed in ${profilePath}`)
|
|
195
|
+
return {
|
|
196
|
+
success: true,
|
|
197
|
+
message: `Auto-start installed in ${profilePath}`,
|
|
198
|
+
profilePath,
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Remove auto-start snippet from all known shell profiles.
|
|
204
|
+
* Removes everything between and including the marker comments.
|
|
205
|
+
*/
|
|
206
|
+
export function uninstallAutoStart(): AutoStartResult {
|
|
207
|
+
const profiles = getShellProfiles()
|
|
208
|
+
let removedFrom = ''
|
|
209
|
+
|
|
210
|
+
for (const profilePath of profiles) {
|
|
211
|
+
if (!existsSync(profilePath)) continue
|
|
212
|
+
|
|
213
|
+
const content = readFileSync(profilePath, 'utf-8')
|
|
214
|
+
if (!content.includes(START_MARKER)) continue
|
|
215
|
+
|
|
216
|
+
// Remove the snippet block (markers + everything between)
|
|
217
|
+
const startIdx = content.indexOf(START_MARKER)
|
|
218
|
+
const endIdx = content.indexOf(END_MARKER)
|
|
219
|
+
|
|
220
|
+
if (startIdx === -1 || endIdx === -1) continue
|
|
221
|
+
|
|
222
|
+
const endOfMarker = endIdx + END_MARKER.length
|
|
223
|
+
// Also remove trailing newline after end marker
|
|
224
|
+
const endWithNewline =
|
|
225
|
+
endOfMarker < content.length && content[endOfMarker] === '\n'
|
|
226
|
+
? endOfMarker + 1
|
|
227
|
+
: endOfMarker
|
|
228
|
+
|
|
229
|
+
// Remove leading blank line if the snippet was appended with separator
|
|
230
|
+
let start = startIdx
|
|
231
|
+
if (start > 0 && content[start - 1] === '\n') {
|
|
232
|
+
start--
|
|
233
|
+
if (start > 0 && content[start - 1] === '\n') {
|
|
234
|
+
start--
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const cleaned = content.slice(0, start) + content.slice(endWithNewline)
|
|
239
|
+
writeFileSync(profilePath, cleaned, 'utf-8')
|
|
240
|
+
removedFrom = profilePath
|
|
241
|
+
log(`Auto-start removed from ${profilePath}`)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!removedFrom) {
|
|
245
|
+
log('Auto-start was not installed in any profile')
|
|
246
|
+
return {
|
|
247
|
+
success: true,
|
|
248
|
+
message: 'Auto-start was not installed in any profile',
|
|
249
|
+
profilePath: '',
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
success: true,
|
|
255
|
+
message: `Auto-start removed from ${removedFrom}`,
|
|
256
|
+
profilePath: removedFrom,
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Check if auto-start is installed in any known shell profile.
|
|
262
|
+
*/
|
|
263
|
+
export function isAutoStartInstalled(): boolean {
|
|
264
|
+
const profiles = getShellProfiles()
|
|
265
|
+
return profiles.some((p) => profileContainsSnippet(p))
|
|
266
|
+
}
|