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/packs/manager.ts
CHANGED
|
@@ -1,204 +1,204 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 18: Pack Manager
|
|
3
|
-
* Handles discovery, loading, and manifest tracking for knowledge packs
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from 'fs/promises'
|
|
7
|
-
import path from 'path'
|
|
8
|
-
import type { Logger } from 'pino'
|
|
9
|
-
import type { PacksConfig } from '@/config/schema'
|
|
10
|
-
import { KnowledgePackSchema, type KnowledgePack, type PackManifest } from './types'
|
|
11
|
-
|
|
12
|
-
/** Maps tech stack names to relevant pack IDs */
|
|
13
|
-
const STACK_MAP: Record<string, string[]> = {
|
|
14
|
-
// Languages
|
|
15
|
-
typescript: ['core/typescript'],
|
|
16
|
-
javascript: ['core/javascript'],
|
|
17
|
-
|
|
18
|
-
// Frontend frameworks
|
|
19
|
-
react: ['frontend/react'],
|
|
20
|
-
vue: ['frontend/react'], // reuse performance patterns; will add vue pack later
|
|
21
|
-
angular: ['frontend/react'],
|
|
22
|
-
svelte: ['frontend/react'],
|
|
23
|
-
'next.js': ['frontend/react'],
|
|
24
|
-
nuxt: ['frontend/react'],
|
|
25
|
-
remix: ['frontend/react'],
|
|
26
|
-
gatsby: ['frontend/react'],
|
|
27
|
-
|
|
28
|
-
// Backend
|
|
29
|
-
node: ['backend/node'],
|
|
30
|
-
express: ['backend/node'],
|
|
31
|
-
fastify: ['backend/node'],
|
|
32
|
-
hono: ['backend/node'],
|
|
33
|
-
elysia: ['backend/node'],
|
|
34
|
-
nestjs: ['backend/node'],
|
|
35
|
-
bun: ['backend/node'],
|
|
36
|
-
|
|
37
|
-
// Meta
|
|
38
|
-
jest: ['meta/testing'],
|
|
39
|
-
vitest: ['meta/testing'],
|
|
40
|
-
mocha: ['meta/testing'],
|
|
41
|
-
'bun:test': ['meta/testing'],
|
|
42
|
-
testing: ['meta/testing']
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export class PackManager {
|
|
46
|
-
private logger: Logger
|
|
47
|
-
private config: PacksConfig
|
|
48
|
-
private packageRoot: string
|
|
49
|
-
private dataDir: string
|
|
50
|
-
|
|
51
|
-
constructor(logger: Logger, config: PacksConfig, packageRoot: string, dataDir: string) {
|
|
52
|
-
this.logger = logger.child({ component: 'pack-manager' })
|
|
53
|
-
this.config = config
|
|
54
|
-
this.packageRoot = packageRoot
|
|
55
|
-
this.dataDir = dataDir
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Find pack IDs relevant to a given tech stack */
|
|
59
|
-
findRelevantPacks(techStack: string[]): string[] {
|
|
60
|
-
const packIds = new Set<string>()
|
|
61
|
-
|
|
62
|
-
// Always include core and meta packs when configured
|
|
63
|
-
if (this.config.alwaysLoadCore) {
|
|
64
|
-
packIds.add('core/typescript')
|
|
65
|
-
packIds.add('core/javascript')
|
|
66
|
-
}
|
|
67
|
-
if (this.config.alwaysLoadMeta) {
|
|
68
|
-
packIds.add('meta/testing')
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Add tech-stack specific packs
|
|
72
|
-
for (const tech of techStack) {
|
|
73
|
-
const normalizedTech = tech.toLowerCase()
|
|
74
|
-
const mapped = STACK_MAP[normalizedTech]
|
|
75
|
-
if (mapped) {
|
|
76
|
-
for (const packId of mapped) {
|
|
77
|
-
packIds.add(packId)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return Array.from(packIds)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Load and validate a pack from the packs directory */
|
|
86
|
-
async loadPack(packId: string): Promise<KnowledgePack> {
|
|
87
|
-
const packPath = path.join(this.packageRoot, this.config.packsDir, `${packId}.json`)
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
const content = await fs.readFile(packPath, 'utf-8')
|
|
91
|
-
const raw = JSON.parse(content)
|
|
92
|
-
const pack = KnowledgePackSchema.parse(raw)
|
|
93
|
-
this.logger.debug({ packId, entries: pack.entries.length }, 'Pack loaded and validated')
|
|
94
|
-
return pack
|
|
95
|
-
} catch (error) {
|
|
96
|
-
this.logger.error({ error, packId, packPath }, 'Failed to load pack')
|
|
97
|
-
throw new Error(`Failed to load pack "${packId}": ${error instanceof Error ? error.message : String(error)}`)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Get the manifest for a project (tracks which packs are loaded) */
|
|
102
|
-
async getManifest(project: string): Promise<PackManifest> {
|
|
103
|
-
const manifestPath = this.getManifestPath(project)
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
const content = await fs.readFile(manifestPath, 'utf-8')
|
|
107
|
-
return JSON.parse(content) as PackManifest
|
|
108
|
-
} catch {
|
|
109
|
-
// No manifest yet — return empty
|
|
110
|
-
return {
|
|
111
|
-
project,
|
|
112
|
-
packs: [],
|
|
113
|
-
lastUpdated: new Date().toISOString()
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** Save manifest after loading packs */
|
|
119
|
-
async saveManifest(manifest: PackManifest): Promise<void> {
|
|
120
|
-
const manifestPath = this.getManifestPath(manifest.project)
|
|
121
|
-
const manifestDir = path.dirname(manifestPath)
|
|
122
|
-
|
|
123
|
-
await fs.mkdir(manifestDir, { recursive: true })
|
|
124
|
-
manifest.lastUpdated = new Date().toISOString()
|
|
125
|
-
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8')
|
|
126
|
-
|
|
127
|
-
this.logger.debug({ project: manifest.project, packs: manifest.packs.length }, 'Manifest saved')
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Delete manifest for a project (used by reload) */
|
|
131
|
-
async deleteManifest(project: string): Promise<void> {
|
|
132
|
-
const manifestPath = this.getManifestPath(project)
|
|
133
|
-
try {
|
|
134
|
-
await fs.unlink(manifestPath)
|
|
135
|
-
} catch {
|
|
136
|
-
// File didn't exist, that's fine
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** Check if a pack version is already loaded */
|
|
141
|
-
isPackLoaded(manifest: PackManifest, packId: string, version: string): boolean {
|
|
142
|
-
return manifest.packs.some(p => p.packId === packId && p.version === version)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** List all available packs in the packs directory */
|
|
146
|
-
async listAvailablePacks(): Promise<Array<{ id: string; name: string; description: string; entries: number; version: string }>> {
|
|
147
|
-
const packsDir = path.join(this.packageRoot, this.config.packsDir)
|
|
148
|
-
const packs: Array<{ id: string; name: string; description: string; entries: number; version: string }> = []
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
await this.scanPacksDir(packsDir, '', packs)
|
|
152
|
-
} catch (error) {
|
|
153
|
-
this.logger.warn({ error, packsDir }, 'Failed to scan packs directory')
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return packs
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
private async scanPacksDir(
|
|
160
|
-
dir: string,
|
|
161
|
-
prefix: string,
|
|
162
|
-
result: Array<{ id: string; name: string; description: string; entries: number; version: string }>
|
|
163
|
-
): Promise<void> {
|
|
164
|
-
let entries: import('fs').Dirent[]
|
|
165
|
-
try {
|
|
166
|
-
entries = await fs.readdir(dir, { withFileTypes: true })
|
|
167
|
-
} catch {
|
|
168
|
-
return
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
for (const entry of entries) {
|
|
172
|
-
if (entry.isDirectory()) {
|
|
173
|
-
await this.scanPacksDir(
|
|
174
|
-
path.join(dir, entry.name),
|
|
175
|
-
prefix ? `${prefix}/${entry.name}` : entry.name,
|
|
176
|
-
result
|
|
177
|
-
)
|
|
178
|
-
} else if (entry.name.endsWith('.json')) {
|
|
179
|
-
const packId = prefix
|
|
180
|
-
? `${prefix}/${entry.name.replace('.json', '')}`
|
|
181
|
-
: entry.name.replace('.json', '')
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const content = await fs.readFile(path.join(dir, entry.name), 'utf-8')
|
|
185
|
-
const raw = JSON.parse(content)
|
|
186
|
-
const pack = KnowledgePackSchema.parse(raw)
|
|
187
|
-
result.push({
|
|
188
|
-
id: packId,
|
|
189
|
-
name: pack.name,
|
|
190
|
-
description: pack.description,
|
|
191
|
-
entries: pack.entries.length,
|
|
192
|
-
version: pack.version
|
|
193
|
-
})
|
|
194
|
-
} catch (error) {
|
|
195
|
-
this.logger.warn({ error, packId }, 'Skipping invalid pack file')
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
private getManifestPath(project: string): string {
|
|
202
|
-
return path.join(this.dataDir, 'pack-manifests', `${project}.json`)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Phase 18: Pack Manager
|
|
3
|
+
* Handles discovery, loading, and manifest tracking for knowledge packs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs/promises'
|
|
7
|
+
import path from 'path'
|
|
8
|
+
import type { Logger } from 'pino'
|
|
9
|
+
import type { PacksConfig } from '@/config/schema'
|
|
10
|
+
import { KnowledgePackSchema, type KnowledgePack, type PackManifest } from './types'
|
|
11
|
+
|
|
12
|
+
/** Maps tech stack names to relevant pack IDs */
|
|
13
|
+
const STACK_MAP: Record<string, string[]> = {
|
|
14
|
+
// Languages
|
|
15
|
+
typescript: ['core/typescript'],
|
|
16
|
+
javascript: ['core/javascript'],
|
|
17
|
+
|
|
18
|
+
// Frontend frameworks
|
|
19
|
+
react: ['frontend/react'],
|
|
20
|
+
vue: ['frontend/react'], // reuse performance patterns; will add vue pack later
|
|
21
|
+
angular: ['frontend/react'],
|
|
22
|
+
svelte: ['frontend/react'],
|
|
23
|
+
'next.js': ['frontend/react'],
|
|
24
|
+
nuxt: ['frontend/react'],
|
|
25
|
+
remix: ['frontend/react'],
|
|
26
|
+
gatsby: ['frontend/react'],
|
|
27
|
+
|
|
28
|
+
// Backend
|
|
29
|
+
node: ['backend/node'],
|
|
30
|
+
express: ['backend/node'],
|
|
31
|
+
fastify: ['backend/node'],
|
|
32
|
+
hono: ['backend/node'],
|
|
33
|
+
elysia: ['backend/node'],
|
|
34
|
+
nestjs: ['backend/node'],
|
|
35
|
+
bun: ['backend/node'],
|
|
36
|
+
|
|
37
|
+
// Meta
|
|
38
|
+
jest: ['meta/testing'],
|
|
39
|
+
vitest: ['meta/testing'],
|
|
40
|
+
mocha: ['meta/testing'],
|
|
41
|
+
'bun:test': ['meta/testing'],
|
|
42
|
+
testing: ['meta/testing']
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class PackManager {
|
|
46
|
+
private logger: Logger
|
|
47
|
+
private config: PacksConfig
|
|
48
|
+
private packageRoot: string
|
|
49
|
+
private dataDir: string
|
|
50
|
+
|
|
51
|
+
constructor(logger: Logger, config: PacksConfig, packageRoot: string, dataDir: string) {
|
|
52
|
+
this.logger = logger.child({ component: 'pack-manager' })
|
|
53
|
+
this.config = config
|
|
54
|
+
this.packageRoot = packageRoot
|
|
55
|
+
this.dataDir = dataDir
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Find pack IDs relevant to a given tech stack */
|
|
59
|
+
findRelevantPacks(techStack: string[]): string[] {
|
|
60
|
+
const packIds = new Set<string>()
|
|
61
|
+
|
|
62
|
+
// Always include core and meta packs when configured
|
|
63
|
+
if (this.config.alwaysLoadCore) {
|
|
64
|
+
packIds.add('core/typescript')
|
|
65
|
+
packIds.add('core/javascript')
|
|
66
|
+
}
|
|
67
|
+
if (this.config.alwaysLoadMeta) {
|
|
68
|
+
packIds.add('meta/testing')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Add tech-stack specific packs
|
|
72
|
+
for (const tech of techStack) {
|
|
73
|
+
const normalizedTech = tech.toLowerCase()
|
|
74
|
+
const mapped = STACK_MAP[normalizedTech]
|
|
75
|
+
if (mapped) {
|
|
76
|
+
for (const packId of mapped) {
|
|
77
|
+
packIds.add(packId)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return Array.from(packIds)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Load and validate a pack from the packs directory */
|
|
86
|
+
async loadPack(packId: string): Promise<KnowledgePack> {
|
|
87
|
+
const packPath = path.join(this.packageRoot, this.config.packsDir, `${packId}.json`)
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const content = await fs.readFile(packPath, 'utf-8')
|
|
91
|
+
const raw = JSON.parse(content)
|
|
92
|
+
const pack = KnowledgePackSchema.parse(raw)
|
|
93
|
+
this.logger.debug({ packId, entries: pack.entries.length }, 'Pack loaded and validated')
|
|
94
|
+
return pack
|
|
95
|
+
} catch (error) {
|
|
96
|
+
this.logger.error({ error, packId, packPath }, 'Failed to load pack')
|
|
97
|
+
throw new Error(`Failed to load pack "${packId}": ${error instanceof Error ? error.message : String(error)}`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Get the manifest for a project (tracks which packs are loaded) */
|
|
102
|
+
async getManifest(project: string): Promise<PackManifest> {
|
|
103
|
+
const manifestPath = this.getManifestPath(project)
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const content = await fs.readFile(manifestPath, 'utf-8')
|
|
107
|
+
return JSON.parse(content) as PackManifest
|
|
108
|
+
} catch {
|
|
109
|
+
// No manifest yet — return empty
|
|
110
|
+
return {
|
|
111
|
+
project,
|
|
112
|
+
packs: [],
|
|
113
|
+
lastUpdated: new Date().toISOString()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Save manifest after loading packs */
|
|
119
|
+
async saveManifest(manifest: PackManifest): Promise<void> {
|
|
120
|
+
const manifestPath = this.getManifestPath(manifest.project)
|
|
121
|
+
const manifestDir = path.dirname(manifestPath)
|
|
122
|
+
|
|
123
|
+
await fs.mkdir(manifestDir, { recursive: true })
|
|
124
|
+
manifest.lastUpdated = new Date().toISOString()
|
|
125
|
+
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8')
|
|
126
|
+
|
|
127
|
+
this.logger.debug({ project: manifest.project, packs: manifest.packs.length }, 'Manifest saved')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Delete manifest for a project (used by reload) */
|
|
131
|
+
async deleteManifest(project: string): Promise<void> {
|
|
132
|
+
const manifestPath = this.getManifestPath(project)
|
|
133
|
+
try {
|
|
134
|
+
await fs.unlink(manifestPath)
|
|
135
|
+
} catch {
|
|
136
|
+
// File didn't exist, that's fine
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Check if a pack version is already loaded */
|
|
141
|
+
isPackLoaded(manifest: PackManifest, packId: string, version: string): boolean {
|
|
142
|
+
return manifest.packs.some(p => p.packId === packId && p.version === version)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** List all available packs in the packs directory */
|
|
146
|
+
async listAvailablePacks(): Promise<Array<{ id: string; name: string; description: string; entries: number; version: string }>> {
|
|
147
|
+
const packsDir = path.join(this.packageRoot, this.config.packsDir)
|
|
148
|
+
const packs: Array<{ id: string; name: string; description: string; entries: number; version: string }> = []
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
await this.scanPacksDir(packsDir, '', packs)
|
|
152
|
+
} catch (error) {
|
|
153
|
+
this.logger.warn({ error, packsDir }, 'Failed to scan packs directory')
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return packs
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private async scanPacksDir(
|
|
160
|
+
dir: string,
|
|
161
|
+
prefix: string,
|
|
162
|
+
result: Array<{ id: string; name: string; description: string; entries: number; version: string }>
|
|
163
|
+
): Promise<void> {
|
|
164
|
+
let entries: import('fs').Dirent[]
|
|
165
|
+
try {
|
|
166
|
+
entries = await fs.readdir(dir, { withFileTypes: true })
|
|
167
|
+
} catch {
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
for (const entry of entries) {
|
|
172
|
+
if (entry.isDirectory()) {
|
|
173
|
+
await this.scanPacksDir(
|
|
174
|
+
path.join(dir, entry.name),
|
|
175
|
+
prefix ? `${prefix}/${entry.name}` : entry.name,
|
|
176
|
+
result
|
|
177
|
+
)
|
|
178
|
+
} else if (entry.name.endsWith('.json')) {
|
|
179
|
+
const packId = prefix
|
|
180
|
+
? `${prefix}/${entry.name.replace('.json', '')}`
|
|
181
|
+
: entry.name.replace('.json', '')
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const content = await fs.readFile(path.join(dir, entry.name), 'utf-8')
|
|
185
|
+
const raw = JSON.parse(content)
|
|
186
|
+
const pack = KnowledgePackSchema.parse(raw)
|
|
187
|
+
result.push({
|
|
188
|
+
id: packId,
|
|
189
|
+
name: pack.name,
|
|
190
|
+
description: pack.description,
|
|
191
|
+
entries: pack.entries.length,
|
|
192
|
+
version: pack.version
|
|
193
|
+
})
|
|
194
|
+
} catch (error) {
|
|
195
|
+
this.logger.warn({ error, packId }, 'Skipping invalid pack file')
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private getManifestPath(project: string): string {
|
|
202
|
+
return path.join(this.dataDir, 'pack-manifests', `${project}.json`)
|
|
203
|
+
}
|
|
204
|
+
}
|
package/src/packs/ranker.ts
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 18: Knowledge Ranker
|
|
3
|
-
* Post-processing layer for search results that boosts personal entries
|
|
4
|
-
* over community (pack) entries
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { PacksConfig } from '@/config/schema'
|
|
8
|
-
|
|
9
|
-
export interface RankedResult {
|
|
10
|
-
id: string
|
|
11
|
-
content: string
|
|
12
|
-
metadata: Record<string, any>
|
|
13
|
-
similarity: number
|
|
14
|
-
adjustedScore: number
|
|
15
|
-
badge: 'personal' | 'community'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class KnowledgeRanker {
|
|
19
|
-
private personalBoost: number
|
|
20
|
-
private projectBoost: number
|
|
21
|
-
|
|
22
|
-
constructor(config: PacksConfig) {
|
|
23
|
-
this.personalBoost = config.personalBoost
|
|
24
|
-
this.projectBoost = config.projectBoost
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Adjust similarity scores based on entry source */
|
|
28
|
-
rank(
|
|
29
|
-
results: Array<{ id: string; content: string; metadata: Record<string, any>; similarity: number }>,
|
|
30
|
-
currentProject?: string
|
|
31
|
-
): RankedResult[] {
|
|
32
|
-
const ranked = results.map(result => {
|
|
33
|
-
const isCommunity = this.isCommunityEntry(result.metadata)
|
|
34
|
-
let adjustedScore = result.similarity
|
|
35
|
-
|
|
36
|
-
if (!isCommunity) {
|
|
37
|
-
// Personal entries get a boost
|
|
38
|
-
adjustedScore *= this.personalBoost
|
|
39
|
-
|
|
40
|
-
// Project-specific entries get an additional boost
|
|
41
|
-
if (currentProject && result.metadata.project === currentProject) {
|
|
42
|
-
adjustedScore *= this.projectBoost
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
// Community entries: no boost (effectively ranked lower)
|
|
46
|
-
|
|
47
|
-
// Cap at 1.0
|
|
48
|
-
adjustedScore = Math.min(adjustedScore, 1.0)
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
id: result.id,
|
|
52
|
-
content: result.content,
|
|
53
|
-
metadata: result.metadata,
|
|
54
|
-
similarity: result.similarity,
|
|
55
|
-
adjustedScore,
|
|
56
|
-
badge: isCommunity ? 'community' as const : 'personal' as const
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
// Sort by adjusted score descending
|
|
61
|
-
return ranked.sort((a, b) => b.adjustedScore - a.adjustedScore)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Detect if a search result is from a community pack */
|
|
65
|
-
isCommunityEntry(metadata: Record<string, any>): boolean {
|
|
66
|
-
// Check source field
|
|
67
|
-
if (typeof metadata.source === 'string' && metadata.source.startsWith('pack:')) {
|
|
68
|
-
return true
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Check context field for community marker
|
|
72
|
-
if (typeof metadata.context === 'string' && metadata.context.includes('[community:pack:')) {
|
|
73
|
-
return true
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return false
|
|
77
|
-
}
|
|
78
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Phase 18: Knowledge Ranker
|
|
3
|
+
* Post-processing layer for search results that boosts personal entries
|
|
4
|
+
* over community (pack) entries
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PacksConfig } from '@/config/schema'
|
|
8
|
+
|
|
9
|
+
export interface RankedResult {
|
|
10
|
+
id: string
|
|
11
|
+
content: string
|
|
12
|
+
metadata: Record<string, any>
|
|
13
|
+
similarity: number
|
|
14
|
+
adjustedScore: number
|
|
15
|
+
badge: 'personal' | 'community'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class KnowledgeRanker {
|
|
19
|
+
private personalBoost: number
|
|
20
|
+
private projectBoost: number
|
|
21
|
+
|
|
22
|
+
constructor(config: PacksConfig) {
|
|
23
|
+
this.personalBoost = config.personalBoost
|
|
24
|
+
this.projectBoost = config.projectBoost
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Adjust similarity scores based on entry source */
|
|
28
|
+
rank(
|
|
29
|
+
results: Array<{ id: string; content: string; metadata: Record<string, any>; similarity: number }>,
|
|
30
|
+
currentProject?: string
|
|
31
|
+
): RankedResult[] {
|
|
32
|
+
const ranked = results.map(result => {
|
|
33
|
+
const isCommunity = this.isCommunityEntry(result.metadata)
|
|
34
|
+
let adjustedScore = result.similarity
|
|
35
|
+
|
|
36
|
+
if (!isCommunity) {
|
|
37
|
+
// Personal entries get a boost
|
|
38
|
+
adjustedScore *= this.personalBoost
|
|
39
|
+
|
|
40
|
+
// Project-specific entries get an additional boost
|
|
41
|
+
if (currentProject && result.metadata.project === currentProject) {
|
|
42
|
+
adjustedScore *= this.projectBoost
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Community entries: no boost (effectively ranked lower)
|
|
46
|
+
|
|
47
|
+
// Cap at 1.0
|
|
48
|
+
adjustedScore = Math.min(adjustedScore, 1.0)
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
id: result.id,
|
|
52
|
+
content: result.content,
|
|
53
|
+
metadata: result.metadata,
|
|
54
|
+
similarity: result.similarity,
|
|
55
|
+
adjustedScore,
|
|
56
|
+
badge: isCommunity ? 'community' as const : 'personal' as const
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Sort by adjusted score descending
|
|
61
|
+
return ranked.sort((a, b) => b.adjustedScore - a.adjustedScore)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Detect if a search result is from a community pack */
|
|
65
|
+
isCommunityEntry(metadata: Record<string, any>): boolean {
|
|
66
|
+
// Check source field
|
|
67
|
+
if (typeof metadata.source === 'string' && metadata.source.startsWith('pack:')) {
|
|
68
|
+
return true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Check context field for community marker
|
|
72
|
+
if (typeof metadata.context === 'string' && metadata.context.includes('[community:pack:')) {
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
}
|