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
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code intelligence routes
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Hono } from 'hono'
|
|
6
|
+
import type { Context } from 'hono'
|
|
7
|
+
import { validateBody, validateQuery } from '@/server/middleware/validate'
|
|
8
|
+
import {
|
|
9
|
+
CodeIndexSchema,
|
|
10
|
+
CodeReindexSchema,
|
|
11
|
+
CodeSearchSchema,
|
|
12
|
+
ProjectQuerySchema,
|
|
13
|
+
CodeDepsQuerySchema,
|
|
14
|
+
} from '@/server/schemas/api-schemas'
|
|
15
|
+
import { normalizeProject } from './helpers'
|
|
16
|
+
import type { RouteDeps } from './types'
|
|
17
|
+
|
|
18
|
+
export function registerCodeRoutes(app: Hono, deps: RouteDeps): void {
|
|
19
|
+
app.post('/api/code/index', (c) => handleCodeIndex(c, deps))
|
|
20
|
+
app.post('/api/code/reindex', (c) => handleCodeReindex(c, deps))
|
|
21
|
+
app.get('/api/code/search', (c) => handleCodeSearch(c, deps))
|
|
22
|
+
app.get('/api/code/file-map', (c) => handleCodeFileMap(c, deps))
|
|
23
|
+
app.get('/api/code/deps', (c) => handleCodeDeps(c, deps))
|
|
24
|
+
app.get('/api/code/status', (c) => handleCodeStatus(c, deps))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function handleCodeIndex(c: Context, deps: RouteDeps): Promise<Response> {
|
|
28
|
+
try {
|
|
29
|
+
const codeIndexer = deps.getCodeIndexer()
|
|
30
|
+
if (!codeIndexer) {
|
|
31
|
+
return Response.json(
|
|
32
|
+
{ success: false, error: 'Code intelligence not initialized' },
|
|
33
|
+
{ status: 503 }
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const data = await validateBody(c, CodeIndexSchema)
|
|
38
|
+
if (!data) return Response.json({ success: false, error: 'Validation failed' }, { status: 400 })
|
|
39
|
+
|
|
40
|
+
const { projectPath, projectName, force } = data
|
|
41
|
+
|
|
42
|
+
// BUG-004: Clear existing index data when force flag is set
|
|
43
|
+
const codeQuery = deps.getCodeQuery()
|
|
44
|
+
if (force && codeQuery) {
|
|
45
|
+
try {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accessing private db property for force reindex
|
|
47
|
+
const db = (codeQuery as Record<string, unknown>).db as { run: (sql: string, ...params: unknown[]) => void } | undefined
|
|
48
|
+
if (db) {
|
|
49
|
+
db.run('DELETE FROM code_files WHERE project = ?', projectName)
|
|
50
|
+
db.run('DELETE FROM code_symbols WHERE project = ?', projectName)
|
|
51
|
+
deps.logger.info({ projectName }, 'Force reindex: cleared existing data')
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
deps.logger.warn({ error, projectName }, 'Failed to clear index data for force reindex')
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const result = await codeIndexer.indexProject(projectPath, projectName)
|
|
59
|
+
return Response.json({ success: true, data: result })
|
|
60
|
+
} catch (error) {
|
|
61
|
+
deps.logger.error({ error }, 'Failed to index project')
|
|
62
|
+
return Response.json(
|
|
63
|
+
{ success: false, error: 'Failed to index project' },
|
|
64
|
+
{ status: 500 }
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function handleCodeReindex(c: Context, deps: RouteDeps): Promise<Response> {
|
|
70
|
+
try {
|
|
71
|
+
const codeIndexer = deps.getCodeIndexer()
|
|
72
|
+
if (!codeIndexer) {
|
|
73
|
+
return Response.json(
|
|
74
|
+
{ success: false, error: 'Code intelligence not initialized' },
|
|
75
|
+
{ status: 503 }
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const data = await validateBody(c, CodeReindexSchema)
|
|
80
|
+
if (!data) return Response.json({ success: false, error: 'Validation failed' }, { status: 400 })
|
|
81
|
+
|
|
82
|
+
const { filePath, project } = data
|
|
83
|
+
|
|
84
|
+
await codeIndexer.indexFile(filePath, project)
|
|
85
|
+
return Response.json({ success: true })
|
|
86
|
+
} catch (error) {
|
|
87
|
+
deps.logger.error({ error }, 'Failed to reindex file')
|
|
88
|
+
return Response.json(
|
|
89
|
+
{ success: false, error: 'Failed to reindex file' },
|
|
90
|
+
{ status: 500 }
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function handleCodeSearch(c: Context, deps: RouteDeps): Response {
|
|
96
|
+
try {
|
|
97
|
+
const codeQuery = deps.getCodeQuery()
|
|
98
|
+
if (!codeQuery) {
|
|
99
|
+
return Response.json(
|
|
100
|
+
{ success: false, error: 'Code intelligence not initialized' },
|
|
101
|
+
{ status: 503 }
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const params = validateQuery(c, CodeSearchSchema)
|
|
106
|
+
if (!params) {
|
|
107
|
+
return Response.json(
|
|
108
|
+
{ success: false, error: 'Validation failed: query and project are required' },
|
|
109
|
+
{ status: 400 }
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const query = params.query
|
|
114
|
+
const project = normalizeProject(params.project)
|
|
115
|
+
const limit = params.limit
|
|
116
|
+
|
|
117
|
+
if (!query) {
|
|
118
|
+
return Response.json(
|
|
119
|
+
{ success: false, error: 'query is required' },
|
|
120
|
+
{ status: 400 }
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const results = codeQuery.findSymbols(query, project, limit)
|
|
125
|
+
return Response.json({ success: true, data: results })
|
|
126
|
+
} catch (error) {
|
|
127
|
+
deps.logger.error({ error }, 'Failed to search code')
|
|
128
|
+
return Response.json(
|
|
129
|
+
{ success: false, error: 'Failed to search code' },
|
|
130
|
+
{ status: 500 }
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function handleCodeFileMap(c: Context, deps: RouteDeps): Response {
|
|
136
|
+
try {
|
|
137
|
+
const codeQuery = deps.getCodeQuery()
|
|
138
|
+
if (!codeQuery) {
|
|
139
|
+
return Response.json(
|
|
140
|
+
{ success: false, error: 'Code intelligence not initialized' },
|
|
141
|
+
{ status: 503 }
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const params = validateQuery(c, ProjectQuerySchema)
|
|
146
|
+
if (!params) {
|
|
147
|
+
return Response.json(
|
|
148
|
+
{ success: false, error: 'Validation failed: project query parameter is required (e.g., ?project=my-app)' },
|
|
149
|
+
{ status: 400 }
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const project = normalizeProject(params.project)
|
|
154
|
+
|
|
155
|
+
const entries = codeQuery.getFileMap(project)
|
|
156
|
+
const map = codeQuery.formatFileMap(entries)
|
|
157
|
+
return Response.json({ success: true, data: { map } })
|
|
158
|
+
} catch (error) {
|
|
159
|
+
deps.logger.error({ error }, 'Failed to get file map')
|
|
160
|
+
return Response.json(
|
|
161
|
+
{ success: false, error: 'Failed to get file map' },
|
|
162
|
+
{ status: 500 }
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function handleCodeDeps(c: Context, deps: RouteDeps): Response {
|
|
168
|
+
try {
|
|
169
|
+
const codeQuery = deps.getCodeQuery()
|
|
170
|
+
if (!codeQuery) {
|
|
171
|
+
return Response.json(
|
|
172
|
+
{ success: false, error: 'Code intelligence not initialized' },
|
|
173
|
+
{ status: 503 }
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const params = validateQuery(c, CodeDepsQuerySchema)
|
|
178
|
+
if (!params) {
|
|
179
|
+
return Response.json(
|
|
180
|
+
{ success: false, error: 'Validation failed: file and project query parameters are required (e.g., ?file=src/index.ts&project=my-app)' },
|
|
181
|
+
{ status: 400 }
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const file = params.file
|
|
186
|
+
const project = normalizeProject(params.project)
|
|
187
|
+
|
|
188
|
+
const result = codeQuery.getDependencies(file, project)
|
|
189
|
+
return Response.json({ success: true, data: result })
|
|
190
|
+
} catch (error) {
|
|
191
|
+
deps.logger.error({ error }, 'Failed to get dependencies')
|
|
192
|
+
return Response.json(
|
|
193
|
+
{ success: false, error: 'Failed to get dependencies' },
|
|
194
|
+
{ status: 500 }
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function handleCodeStatus(c: Context, deps: RouteDeps): Response {
|
|
200
|
+
try {
|
|
201
|
+
const codeQuery = deps.getCodeQuery()
|
|
202
|
+
if (!codeQuery) {
|
|
203
|
+
return Response.json(
|
|
204
|
+
{ success: false, error: 'Code intelligence not initialized' },
|
|
205
|
+
{ status: 503 }
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const params = validateQuery(c, ProjectQuerySchema)
|
|
210
|
+
if (!params) {
|
|
211
|
+
return Response.json(
|
|
212
|
+
{ success: false, error: 'Validation failed: project query parameter is required (e.g., ?project=my-app)' },
|
|
213
|
+
{ status: 400 }
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const project = normalizeProject(params.project)
|
|
218
|
+
|
|
219
|
+
const stats = codeQuery.getStats(project)
|
|
220
|
+
return Response.json({ success: true, data: stats })
|
|
221
|
+
} catch (error) {
|
|
222
|
+
deps.logger.error({ error }, 'Failed to get code status')
|
|
223
|
+
return Response.json(
|
|
224
|
+
{ success: false, error: 'Failed to get code status' },
|
|
225
|
+
{ status: 500 }
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context routes
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Hono } from 'hono'
|
|
6
|
+
import { ResourceProvider } from '@/server/providers/resources'
|
|
7
|
+
import type { RouteDeps } from './types'
|
|
8
|
+
|
|
9
|
+
export function registerContextRoutes(app: Hono, deps: RouteDeps): void {
|
|
10
|
+
app.get('/api/context/auto', () => handleContextAuto(deps))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function handleContextAuto(deps: RouteDeps): Promise<Response> {
|
|
14
|
+
try {
|
|
15
|
+
const resourceProvider = new ResourceProvider(deps.logger)
|
|
16
|
+
const result = await resourceProvider.readResource('brain://context/auto')
|
|
17
|
+
const text = result?.contents?.[0]?.text || ''
|
|
18
|
+
return Response.json({ success: true, data: { content: text } })
|
|
19
|
+
} catch (error) {
|
|
20
|
+
deps.logger.error({ error }, 'Failed to get auto context')
|
|
21
|
+
return Response.json(
|
|
22
|
+
{ success: false, error: 'Failed to get auto context' },
|
|
23
|
+
{ status: 500 }
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health check route
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Hono } from 'hono'
|
|
6
|
+
import { isServicesInitialized } from '@/server/services'
|
|
7
|
+
import type { RouteDeps } from './types'
|
|
8
|
+
|
|
9
|
+
export function registerHealthRoutes(app: Hono, _deps: RouteDeps): void {
|
|
10
|
+
app.get('/api/health', (c) => {
|
|
11
|
+
const initialized = isServicesInitialized()
|
|
12
|
+
return c.json({
|
|
13
|
+
success: true,
|
|
14
|
+
status: initialized ? 'ok' : 'initializing',
|
|
15
|
+
initialized,
|
|
16
|
+
timestamp: new Date().toISOString(),
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helper functions for route modules
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { MemoryManager } from '@/memory'
|
|
6
|
+
import type { CapturedKnowledge } from '@/hooks/types'
|
|
7
|
+
import { getVaultService } from '@/server/services'
|
|
8
|
+
|
|
9
|
+
/** Format a date as a relative age string (e.g., "2 days ago") */
|
|
10
|
+
export function formatAge(isoDate: string): string {
|
|
11
|
+
try {
|
|
12
|
+
const diff = Date.now() - new Date(isoDate).getTime()
|
|
13
|
+
const minutes = Math.floor(diff / 60000)
|
|
14
|
+
if (minutes < 1) return 'just now'
|
|
15
|
+
if (minutes < 60) return `${minutes}m ago`
|
|
16
|
+
const hours = Math.floor(minutes / 60)
|
|
17
|
+
if (hours < 24) return `${hours}h ago`
|
|
18
|
+
const days = Math.floor(hours / 24)
|
|
19
|
+
if (days === 1) return 'yesterday'
|
|
20
|
+
if (days < 30) return `${days} days ago`
|
|
21
|
+
return `${Math.floor(days / 30)} months ago`
|
|
22
|
+
} catch {
|
|
23
|
+
return ''
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Normalize project param — extract basename if full path provided */
|
|
28
|
+
export function normalizeProject(raw: string): string {
|
|
29
|
+
if (!raw) return raw
|
|
30
|
+
// If it looks like a path (contains /), extract the last segment
|
|
31
|
+
if (raw.includes('/')) {
|
|
32
|
+
return raw.split('/').filter(Boolean).pop() || raw
|
|
33
|
+
}
|
|
34
|
+
return raw
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Route captured knowledge to the appropriate store method */
|
|
38
|
+
export async function storeKnowledge(
|
|
39
|
+
knowledge: CapturedKnowledge,
|
|
40
|
+
memoryService: MemoryManager
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
const project = knowledge.project || 'unknown'
|
|
43
|
+
|
|
44
|
+
switch (knowledge.type) {
|
|
45
|
+
case 'decision':
|
|
46
|
+
await memoryService.rememberDecision(
|
|
47
|
+
project,
|
|
48
|
+
`Captured via hook: ${knowledge.metadata.action || 'tool-use'}`,
|
|
49
|
+
knowledge.content,
|
|
50
|
+
`Auto-captured from ${knowledge.source}`,
|
|
51
|
+
{ tags: knowledge.technologies }
|
|
52
|
+
)
|
|
53
|
+
break
|
|
54
|
+
|
|
55
|
+
case 'pattern':
|
|
56
|
+
await memoryService.storePattern({
|
|
57
|
+
project,
|
|
58
|
+
pattern_type: 'solution',
|
|
59
|
+
description: knowledge.content,
|
|
60
|
+
context: `Captured via hook: ${knowledge.metadata.action || 'tool-use'}`,
|
|
61
|
+
confidence: knowledge.confidence,
|
|
62
|
+
})
|
|
63
|
+
break
|
|
64
|
+
|
|
65
|
+
case 'correction':
|
|
66
|
+
await memoryService.storeCorrection({
|
|
67
|
+
project,
|
|
68
|
+
original: knowledge.metadata.command || 'Unknown',
|
|
69
|
+
correction: knowledge.content,
|
|
70
|
+
reasoning: 'Auto-captured from hook passive learning',
|
|
71
|
+
context: `Captured via hook: ${knowledge.metadata.action || 'tool-use'}`,
|
|
72
|
+
confidence: knowledge.confidence,
|
|
73
|
+
})
|
|
74
|
+
break
|
|
75
|
+
|
|
76
|
+
case 'progress': {
|
|
77
|
+
// Store in ChromaDB so it's searchable via brain tool
|
|
78
|
+
await memoryService.rememberDecision(
|
|
79
|
+
project,
|
|
80
|
+
`Captured via hook: ${knowledge.metadata.action || knowledge.metadata.source || 'progress'}`,
|
|
81
|
+
knowledge.content,
|
|
82
|
+
`Auto-captured from ${knowledge.source}`,
|
|
83
|
+
{ tags: knowledge.technologies }
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Also update vault progress if project exists
|
|
87
|
+
const vaultService = getVaultService()
|
|
88
|
+
if (vaultService) {
|
|
89
|
+
try {
|
|
90
|
+
await vaultService.updateProgress(project, {
|
|
91
|
+
status: `Hook: ${knowledge.content.slice(0, 200)}`,
|
|
92
|
+
})
|
|
93
|
+
} catch {
|
|
94
|
+
// Vault might not have this project
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
break
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook routes — passive learning endpoints
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Hono } from 'hono'
|
|
6
|
+
import type { Context } from 'hono'
|
|
7
|
+
import { getMemoryService } from '@/server/services'
|
|
8
|
+
import { validateBody, validateQuery } from '@/server/middleware/validate'
|
|
9
|
+
import {
|
|
10
|
+
IngestKnowledgeSchema,
|
|
11
|
+
SessionEndSchema,
|
|
12
|
+
ContextQuerySchema,
|
|
13
|
+
} from '@/server/schemas/api-schemas'
|
|
14
|
+
import type { CapturedKnowledge } from '@/hooks/types'
|
|
15
|
+
import { SmartDeduplicator } from '@/hooks/deduplicator'
|
|
16
|
+
import { storeKnowledge } from './helpers'
|
|
17
|
+
import type { RouteDeps } from './types'
|
|
18
|
+
|
|
19
|
+
export function registerHookRoutes(app: Hono, deps: RouteDeps): void {
|
|
20
|
+
app.post('/api/hooks/ingest', (c) => handleHookIngest(c, deps))
|
|
21
|
+
app.post('/api/hooks/session-end', (c) => handleHookSessionEnd(c, deps))
|
|
22
|
+
app.get('/api/hooks/status', () => handleHookStatus(deps))
|
|
23
|
+
app.get('/api/hooks/context-query', (c) => handleContextQuery(c, deps))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function handleHookIngest(c: Context, deps: RouteDeps): Promise<Response> {
|
|
27
|
+
try {
|
|
28
|
+
const data = await validateBody(c, IngestKnowledgeSchema)
|
|
29
|
+
if (!data) return Response.json({ success: false, error: 'Validation failed' }, { status: 400 })
|
|
30
|
+
|
|
31
|
+
const items: CapturedKnowledge[] = data.knowledge as CapturedKnowledge[]
|
|
32
|
+
const sessionId: string | undefined = data.sessionId
|
|
33
|
+
|
|
34
|
+
if (items.length === 0) {
|
|
35
|
+
return Response.json({ success: true, data: { stored: 0, skipped: 0, merged: 0 } })
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const memoryService = getMemoryService()
|
|
39
|
+
if (!memoryService || !memoryService.isInitialized()) {
|
|
40
|
+
return Response.json(
|
|
41
|
+
{ success: false, error: 'Memory service not initialized' },
|
|
42
|
+
{ status: 503 }
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const deduplicator = new SmartDeduplicator(deps.config.hooks?.deduplication)
|
|
47
|
+
let stored = 0, skipped = 0, merged = 0
|
|
48
|
+
|
|
49
|
+
for (const knowledge of items) {
|
|
50
|
+
const action = await deduplicator.beforeStore(knowledge, memoryService)
|
|
51
|
+
|
|
52
|
+
switch (action.action) {
|
|
53
|
+
case 'skip':
|
|
54
|
+
skipped++
|
|
55
|
+
deps.hookStats.totalSkipped++
|
|
56
|
+
break
|
|
57
|
+
|
|
58
|
+
case 'merge':
|
|
59
|
+
merged++
|
|
60
|
+
deps.hookStats.totalMerged++
|
|
61
|
+
// For merge, we store as update — treat as new store with merged content
|
|
62
|
+
await storeKnowledge({ ...knowledge, content: action.mergedContent }, memoryService)
|
|
63
|
+
stored++
|
|
64
|
+
break
|
|
65
|
+
|
|
66
|
+
case 'store_new':
|
|
67
|
+
await storeKnowledge(knowledge, memoryService)
|
|
68
|
+
stored++
|
|
69
|
+
deps.hookStats.totalCaptured++
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Track in session if sessionId provided
|
|
74
|
+
const sessionTracker = deps.getSessionTracker()
|
|
75
|
+
if (sessionId && sessionTracker) {
|
|
76
|
+
await sessionTracker.track(sessionId, knowledge)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
deps.hookStats.lastCaptureAt = new Date().toISOString()
|
|
81
|
+
|
|
82
|
+
deps.logger.debug(
|
|
83
|
+
{ stored, skipped, merged, sessionId },
|
|
84
|
+
'Hook ingest processed'
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return Response.json({ success: true, data: { stored, skipped, merged } })
|
|
88
|
+
} catch (error) {
|
|
89
|
+
deps.logger.error({ error }, 'Failed to process hook ingest')
|
|
90
|
+
return Response.json(
|
|
91
|
+
{ success: false, error: 'Failed to ingest hook data' },
|
|
92
|
+
{ status: 500 }
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function handleHookSessionEnd(c: Context, deps: RouteDeps): Promise<Response> {
|
|
98
|
+
try {
|
|
99
|
+
const data = await validateBody(c, SessionEndSchema)
|
|
100
|
+
if (!data) return Response.json({ success: false, error: 'Validation failed: sessionId required' }, { status: 400 })
|
|
101
|
+
|
|
102
|
+
const sessionId = data.sessionId
|
|
103
|
+
|
|
104
|
+
const sessionTracker = deps.getSessionTracker()
|
|
105
|
+
if (sessionTracker) {
|
|
106
|
+
await sessionTracker.endSession(sessionId)
|
|
107
|
+
deps.hookStats.sessionsTracked++
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return Response.json({ success: true, data: { message: 'Session ended' } })
|
|
111
|
+
} catch (error) {
|
|
112
|
+
deps.logger.error({ error }, 'Failed to end hook session')
|
|
113
|
+
return Response.json(
|
|
114
|
+
{ success: false, error: 'Failed to end session' },
|
|
115
|
+
{ status: 500 }
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function handleHookStatus(deps: RouteDeps): Response {
|
|
121
|
+
const sessionTracker = deps.getSessionTracker()
|
|
122
|
+
const sessionStats = sessionTracker?.getStats()
|
|
123
|
+
|
|
124
|
+
return Response.json({
|
|
125
|
+
success: true,
|
|
126
|
+
data: {
|
|
127
|
+
...deps.hookStats,
|
|
128
|
+
activeSessions: sessionStats?.activeSessions ?? 0,
|
|
129
|
+
sessionItems: sessionStats?.totalItems ?? 0,
|
|
130
|
+
hooksEnabled: deps.config.hooks?.enabled ?? true,
|
|
131
|
+
},
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function handleContextQuery(c: Context, deps: RouteDeps): Promise<Response> {
|
|
136
|
+
try {
|
|
137
|
+
const params = validateQuery(c, ContextQuerySchema)
|
|
138
|
+
const query = params?.query ?? ''
|
|
139
|
+
const type = params?.type ?? ''
|
|
140
|
+
const cwd = params?.cwd ?? ''
|
|
141
|
+
const limit = params?.limit ?? 5
|
|
142
|
+
|
|
143
|
+
const memoryService = getMemoryService()
|
|
144
|
+
if (!memoryService || !memoryService.isInitialized()) {
|
|
145
|
+
return Response.json({ success: true, context: '' })
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Extract project name from cwd (last path segment)
|
|
149
|
+
const projectName = cwd ? cwd.split('/').filter(Boolean).pop() : undefined
|
|
150
|
+
|
|
151
|
+
let contextParts: string[] = []
|
|
152
|
+
|
|
153
|
+
if (type === 'session-start') {
|
|
154
|
+
// Broader context for session start: recent decisions + patterns
|
|
155
|
+
const [recallText, patterns] = await Promise.all([
|
|
156
|
+
memoryService.recallSimilar(projectName || 'recent work', {
|
|
157
|
+
project: projectName,
|
|
158
|
+
limit: 5,
|
|
159
|
+
minSimilarity: 0.65,
|
|
160
|
+
}),
|
|
161
|
+
memoryService.getPatterns(projectName, { limit: 5 }),
|
|
162
|
+
])
|
|
163
|
+
|
|
164
|
+
if (recallText && recallText.trim()) {
|
|
165
|
+
contextParts.push(recallText)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (patterns && patterns.length > 0) {
|
|
169
|
+
const patternLines = patterns
|
|
170
|
+
.slice(0, 3)
|
|
171
|
+
.map((p: Record<string, unknown>) => `- Pattern: ${p.description}`)
|
|
172
|
+
.join('\n')
|
|
173
|
+
if (patternLines) {
|
|
174
|
+
contextParts.push(patternLines)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} else if (query) {
|
|
178
|
+
// Query-specific recall
|
|
179
|
+
const recallText = await memoryService.recallSimilar(query, {
|
|
180
|
+
project: projectName,
|
|
181
|
+
limit,
|
|
182
|
+
minSimilarity: 0.3,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
if (recallText && recallText.trim()) {
|
|
186
|
+
contextParts.push(recallText)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const context = contextParts.join('\n')
|
|
191
|
+
|
|
192
|
+
return Response.json({ success: true, context })
|
|
193
|
+
} catch (error) {
|
|
194
|
+
deps.logger.error({ error }, 'Failed to get context for hook')
|
|
195
|
+
return Response.json({ success: true, context: '' })
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP proxy routes
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Hono } from 'hono'
|
|
6
|
+
import type { Context } from 'hono'
|
|
7
|
+
import { validateBody } from '@/server/middleware/validate'
|
|
8
|
+
import { McpCallToolSchema } from '@/server/schemas/api-schemas'
|
|
9
|
+
import type { RouteDeps } from './types'
|
|
10
|
+
|
|
11
|
+
export function registerMcpRoutes(app: Hono, deps: RouteDeps): void {
|
|
12
|
+
app.get('/api/mcp/list-tools', () => handleMcpListTools(deps))
|
|
13
|
+
app.post('/api/mcp/call-tool', (c) => handleMcpCallTool(c, deps))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function handleMcpListTools(deps: RouteDeps): Promise<Response> {
|
|
17
|
+
try {
|
|
18
|
+
const { handleListTools } = await import('@/server/handlers/list-tools')
|
|
19
|
+
const result = await handleListTools()
|
|
20
|
+
return Response.json({ success: true, data: result })
|
|
21
|
+
} catch (error) {
|
|
22
|
+
deps.logger.error({ error }, 'Failed to list MCP tools')
|
|
23
|
+
return Response.json(
|
|
24
|
+
{ success: false, error: 'Failed to list tools' },
|
|
25
|
+
{ status: 500 }
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function handleMcpCallTool(c: Context, deps: RouteDeps): Promise<Response> {
|
|
31
|
+
try {
|
|
32
|
+
const data = await validateBody(c, McpCallToolSchema)
|
|
33
|
+
if (!data) return Response.json({ success: false, error: 'Validation failed: name is required' }, { status: 400 })
|
|
34
|
+
|
|
35
|
+
const { name, args } = data
|
|
36
|
+
|
|
37
|
+
const { handleCallTool } = await import('@/server/handlers/call-tool')
|
|
38
|
+
const result = await handleCallTool(name, args || {}, deps.logger)
|
|
39
|
+
return Response.json({ success: true, data: result })
|
|
40
|
+
} catch (error: unknown) {
|
|
41
|
+
deps.logger.error({ error }, 'Failed to call MCP tool')
|
|
42
|
+
return Response.json(
|
|
43
|
+
{ success: false, error: error instanceof Error ? error.message : 'Failed to call tool' },
|
|
44
|
+
{ status: 500 }
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
}
|