claude-brain 0.14.2 → 0.14.4
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 +191 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +11 -11
- package/bunfig.toml +8 -8
- package/package.json +80 -80
- 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/src/automation/auto-context.ts +240 -240
- package/src/automation/decision-detector.ts +452 -452
- package/src/automation/index.ts +11 -11
- 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 +205 -205
- package/src/cli/auto-setup.ts +82 -82
- package/src/cli/bin.ts +202 -202
- package/src/cli/commands/chroma.ts +573 -573
- package/src/cli/commands/git-hook.ts +189 -189
- package/src/cli/commands/hooks.ts +213 -213
- package/src/cli/commands/init.ts +122 -122
- package/src/cli/commands/install-mcp.ts +92 -92
- package/src/cli/commands/pack.ts +197 -197
- package/src/cli/commands/serve.ts +167 -167
- package/src/cli/commands/start.ts +42 -42
- package/src/cli/commands/uninstall-mcp.ts +41 -41
- package/src/cli/commands/update.ts +121 -121
- package/src/cli/diagnose.ts +4 -4
- package/src/cli/health-check.ts +4 -4
- package/src/cli/migrate-chroma.ts +106 -106
- package/src/cli/setup.ts +4 -4
- 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/config/defaults.ts +50 -50
- package/src/config/home.ts +55 -55
- package/src/config/index.ts +7 -7
- package/src/config/loader.ts +166 -166
- package/src/config/migration.ts +76 -76
- package/src/config/schema.ts +360 -360
- package/src/config/validator.ts +184 -184
- package/src/config/watcher.ts +86 -86
- package/src/context/assembler.ts +398 -398
- package/src/context/cache-manager.ts +101 -101
- package/src/context/formatter.ts +84 -84
- package/src/context/hierarchy.ts +85 -85
- package/src/context/index.ts +83 -83
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/types.ts +252 -252
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +123 -123
- package/src/health/index.ts +229 -229
- package/src/hooks/brain-hook.ts +112 -112
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +207 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +191 -194
- package/src/hooks/passive-classifier.ts +366 -366
- package/src/hooks/queue.ts +129 -129
- package/src/hooks/session-tracker.ts +275 -275
- package/src/hooks/types.ts +47 -47
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/affinity.ts +162 -162
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +13 -13
- package/src/intelligence/cross-project/transfer.ts +201 -201
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +207 -207
- package/src/intelligence/prediction/context-anticipator.ts +198 -198
- package/src/intelligence/prediction/decision-predictor.ts +184 -184
- package/src/intelligence/prediction/index.ts +13 -13
- package/src/intelligence/prediction/recommender.ts +268 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +247 -247
- package/src/intelligence/reasoning/counterfactual.ts +248 -248
- package/src/intelligence/reasoning/index.ts +13 -13
- package/src/intelligence/reasoning/synthesizer.ts +169 -169
- package/src/intelligence/temporal/evolution.ts +197 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +259 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/knowledge/entity-extractor.ts +416 -416
- 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 +168 -168
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +174 -174
- package/src/memory/chroma/collection-manager.ts +94 -94
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +153 -153
- 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 +315 -315
- package/src/memory/chroma/store.ts +741 -741
- package/src/memory/consolidation/archiver.ts +164 -164
- package/src/memory/consolidation/merger.ts +186 -186
- package/src/memory/consolidation/scorer.ts +138 -138
- package/src/memory/context-builder.ts +236 -236
- package/src/memory/database.ts +169 -169
- package/src/memory/embedding-utils.ts +156 -156
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +351 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/index.ts +582 -582
- package/src/memory/knowledge-extractor.ts +455 -455
- package/src/memory/learning.ts +378 -378
- package/src/memory/patterns.ts +396 -396
- package/src/memory/schema.ts +88 -88
- package/src/memory/search.ts +309 -309
- package/src/memory/store.ts +787 -787
- package/src/memory/types.ts +121 -121
- package/src/orchestrator/coordinator.ts +272 -272
- package/src/orchestrator/decision-logger.ts +228 -228
- package/src/orchestrator/event-emitter.ts +198 -198
- package/src/orchestrator/event-queue.ts +184 -184
- package/src/orchestrator/handlers/base-handler.ts +70 -70
- package/src/orchestrator/handlers/context-handler.ts +73 -73
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- package/src/orchestrator/handlers/index.ts +10 -10
- package/src/orchestrator/handlers/status-handler.ts +131 -131
- package/src/orchestrator/handlers/task-handler.ts +171 -171
- package/src/orchestrator/index.ts +275 -275
- package/src/orchestrator/task-parser.ts +284 -284
- package/src/orchestrator/types.ts +98 -98
- 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 -300
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +223 -223
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +223 -223
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +163 -163
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +198 -198
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +236 -236
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +188 -188
- package/src/retrieval/reranker/model.ts +95 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +428 -428
- package/src/routing/intent-classifier.ts +436 -436
- package/src/routing/response-filter.ts +258 -254
- package/src/routing/router.ts +1322 -1314
- package/src/routing/search-engine.ts +475 -475
- package/src/routing/types.ts +94 -84
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/handlers/call-tool.ts +156 -156
- package/src/server/handlers/index.ts +9 -9
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/analyze-decision-evolution.ts +151 -151
- package/src/server/handlers/tools/auto-remember.ts +200 -200
- package/src/server/handlers/tools/brain.ts +85 -85
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/detect-trends.ts +144 -144
- package/src/server/handlers/tools/find-cross-project-patterns.ts +168 -168
- package/src/server/handlers/tools/get-activity-log.ts +194 -194
- package/src/server/handlers/tools/get-code-standards.ts +124 -124
- package/src/server/handlers/tools/get-corrections.ts +154 -154
- package/src/server/handlers/tools/get-decision-timeline.ts +172 -172
- package/src/server/handlers/tools/get-episode.ts +103 -103
- package/src/server/handlers/tools/get-patterns.ts +158 -158
- package/src/server/handlers/tools/get-phase12-status.ts +63 -63
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/get-recommendations.ts +145 -145
- package/src/server/handlers/tools/index.ts +31 -31
- package/src/server/handlers/tools/init-project.ts +757 -757
- package/src/server/handlers/tools/list-episodes.ts +90 -90
- package/src/server/handlers/tools/list-projects.ts +125 -125
- package/src/server/handlers/tools/rate-memory.ts +101 -101
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +126 -126
- package/src/server/handlers/tools/record-correction.ts +125 -125
- package/src/server/handlers/tools/remember-decision.ts +153 -153
- package/src/server/handlers/tools/schemas.ts +253 -253
- package/src/server/handlers/tools/search-knowledge-graph.ts +102 -102
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/handlers/tools/what-if-analysis.ts +135 -135
- package/src/server/http-api.ts +693 -693
- package/src/server/index.ts +40 -40
- package/src/server/mcp-server.ts +283 -283
- package/src/server/providers/index.ts +7 -7
- package/src/server/providers/prompts.ts +327 -327
- package/src/server/providers/resources.ts +622 -622
- package/src/server/services.ts +468 -468
- package/src/server/types.ts +39 -39
- package/src/server/utils/error-handler.ts +155 -155
- package/src/server/utils/index.ts +13 -13
- package/src/server/utils/memory-indicator.ts +83 -83
- package/src/server/utils/request-context.ts +122 -122
- package/src/server/utils/response-formatter.ts +129 -124
- package/src/server/utils/validators.ts +210 -210
- package/src/setup/index.ts +48 -48
- package/src/setup/wizard.ts +461 -461
- package/src/tools/index.ts +24 -24
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.test.ts +30 -30
- package/src/tools/schemas.ts +617 -617
- package/src/tools/types.ts +412 -412
- package/src/utils/circuit-breaker.ts +130 -130
- package/src/utils/cleanup.ts +34 -34
- package/src/utils/error-handler.ts +132 -132
- package/src/utils/error-messages.ts +60 -60
- package/src/utils/fallback.ts +45 -45
- package/src/utils/index.ts +54 -54
- package/src/utils/logger-utils.ts +80 -80
- package/src/utils/logger.ts +88 -88
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/retry.ts +94 -94
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/frontmatter.ts +264 -264
- package/src/vault/index.ts +318 -318
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +422 -422
- package/src/vault/reader.ts +264 -264
- package/src/vault/templates.ts +186 -186
- package/src/vault/types.ts +73 -73
- package/src/vault/watcher.ts +277 -277
- package/src/vault/writer.ts +413 -413
- package/tsconfig.json +30 -30
|
@@ -1,622 +1,622 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resource Provider
|
|
3
|
-
* Phase 6.5: Expose project context as resources
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Logger } from 'pino'
|
|
7
|
-
import path from 'path'
|
|
8
|
-
import {
|
|
9
|
-
getVaultService,
|
|
10
|
-
getContextService,
|
|
11
|
-
getMemoryService,
|
|
12
|
-
getEpisodeService,
|
|
13
|
-
getSessionTracker,
|
|
14
|
-
isServicesInitialized
|
|
15
|
-
} from '@/server/services'
|
|
16
|
-
import { isPhase12Available, getPhase12Instance } from '@/utils/phase12-helper'
|
|
17
|
-
import type { ContextFormatter } from '@/context'
|
|
18
|
-
|
|
19
|
-
interface DetectedProject {
|
|
20
|
-
name: string
|
|
21
|
-
path: string
|
|
22
|
-
confidence: number
|
|
23
|
-
indicators: string[]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface ResourceItem {
|
|
27
|
-
uri: string
|
|
28
|
-
name: string
|
|
29
|
-
description: string
|
|
30
|
-
mimeType: string
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export class ResourceProvider {
|
|
34
|
-
private logger: Logger
|
|
35
|
-
|
|
36
|
-
constructor(logger: Logger) {
|
|
37
|
-
this.logger = logger.child({ component: 'resource-provider' })
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* List all available resources
|
|
42
|
-
*/
|
|
43
|
-
async listResources() {
|
|
44
|
-
const detectedProject = await this.detectCurrentProject()
|
|
45
|
-
|
|
46
|
-
const resources: ResourceItem[] = [
|
|
47
|
-
{
|
|
48
|
-
uri: 'brain://context/auto',
|
|
49
|
-
name: 'Auto Context',
|
|
50
|
-
description: 'Session context auto-loaded by brain',
|
|
51
|
-
mimeType: 'text/markdown'
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
uri: 'project://list',
|
|
55
|
-
name: 'All Projects',
|
|
56
|
-
description: 'List of all projects in vault',
|
|
57
|
-
mimeType: 'text/plain'
|
|
58
|
-
}
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
if (detectedProject) {
|
|
62
|
-
resources.push(
|
|
63
|
-
{
|
|
64
|
-
uri: 'project://current/context',
|
|
65
|
-
name: 'Current Project Context',
|
|
66
|
-
description: `Context for ${detectedProject.name}`,
|
|
67
|
-
mimeType: 'text/markdown'
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
uri: 'project://current/decisions',
|
|
71
|
-
name: 'Recent Decisions',
|
|
72
|
-
description: `Recent decisions for ${detectedProject.name}`,
|
|
73
|
-
mimeType: 'text/markdown'
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
uri: 'project://current/standards',
|
|
77
|
-
name: 'Coding Standards',
|
|
78
|
-
description: `Standards for ${detectedProject.name}`,
|
|
79
|
-
mimeType: 'text/markdown'
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
uri: 'project://current/progress',
|
|
83
|
-
name: 'Current Progress',
|
|
84
|
-
description: `Progress tracker for ${detectedProject.name}`,
|
|
85
|
-
mimeType: 'text/markdown'
|
|
86
|
-
}
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return { resources }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Read a specific resource
|
|
95
|
-
*/
|
|
96
|
-
async readResource(uri: string) {
|
|
97
|
-
this.logger.info({ uri }, 'Reading resource')
|
|
98
|
-
|
|
99
|
-
if (uri.startsWith('brain://')) {
|
|
100
|
-
return await this.getBrainResource(uri)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (uri === 'project://list') {
|
|
104
|
-
return await this.getProjectListResource()
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (uri.startsWith('project://current/')) {
|
|
108
|
-
const resourceType = uri.split('/').pop()
|
|
109
|
-
return await this.getCurrentProjectResource(resourceType!)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
throw new Error(`Unknown resource URI: ${uri}`)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Get project list resource
|
|
117
|
-
*/
|
|
118
|
-
private async getProjectListResource() {
|
|
119
|
-
if (!isServicesInitialized()) {
|
|
120
|
-
return {
|
|
121
|
-
contents: [
|
|
122
|
-
{
|
|
123
|
-
uri: 'project://list',
|
|
124
|
-
mimeType: 'text/plain',
|
|
125
|
-
text: 'Services not initialized'
|
|
126
|
-
}
|
|
127
|
-
]
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const vault = getVaultService()
|
|
132
|
-
const projects = await vault.reader.getProjectDirectories(vault.paths.projects)
|
|
133
|
-
|
|
134
|
-
let content = '# Available Projects\n\n'
|
|
135
|
-
|
|
136
|
-
for (const project of projects) {
|
|
137
|
-
try {
|
|
138
|
-
const projectPaths = vault.getProjectPaths(project)
|
|
139
|
-
const context = await vault.reader.readMarkdownFile(projectPaths.context)
|
|
140
|
-
|
|
141
|
-
content += `## ${project}\n`
|
|
142
|
-
content += `**Status:** ${context.frontmatter.status || 'unknown'}\n`
|
|
143
|
-
|
|
144
|
-
if (context.frontmatter.tech_stack) {
|
|
145
|
-
const techStack = Array.isArray(context.frontmatter.tech_stack)
|
|
146
|
-
? context.frontmatter.tech_stack.join(', ')
|
|
147
|
-
: context.frontmatter.tech_stack
|
|
148
|
-
content += `**Tech Stack:** ${techStack}\n`
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (context.frontmatter.description) {
|
|
152
|
-
content += `**Description:** ${context.frontmatter.description}\n`
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
content += '\n'
|
|
156
|
-
|
|
157
|
-
} catch (error) {
|
|
158
|
-
content += `## ${project}\n*Context not available*\n\n`
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (projects.length === 0) {
|
|
163
|
-
content += '*No projects found in vault*\n'
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
contents: [
|
|
168
|
-
{
|
|
169
|
-
uri: 'project://list',
|
|
170
|
-
mimeType: 'text/markdown',
|
|
171
|
-
text: content
|
|
172
|
-
}
|
|
173
|
-
]
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Get current project resource
|
|
179
|
-
*/
|
|
180
|
-
private async getCurrentProjectResource(resourceType: string) {
|
|
181
|
-
const detectedProject = await this.detectCurrentProject()
|
|
182
|
-
|
|
183
|
-
if (!detectedProject) {
|
|
184
|
-
return {
|
|
185
|
-
contents: [
|
|
186
|
-
{
|
|
187
|
-
uri: `project://current/${resourceType}`,
|
|
188
|
-
mimeType: 'text/plain',
|
|
189
|
-
text: 'No project detected in current working directory'
|
|
190
|
-
}
|
|
191
|
-
]
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
let content: string
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
switch (resourceType) {
|
|
199
|
-
case 'context':
|
|
200
|
-
content = await this.getContextContent(detectedProject.name)
|
|
201
|
-
break
|
|
202
|
-
case 'decisions':
|
|
203
|
-
content = await this.getDecisionsContent(detectedProject.name)
|
|
204
|
-
break
|
|
205
|
-
case 'standards':
|
|
206
|
-
content = await this.getStandardsContent(detectedProject.name)
|
|
207
|
-
break
|
|
208
|
-
case 'progress':
|
|
209
|
-
content = await this.getProgressContent(detectedProject.name)
|
|
210
|
-
break
|
|
211
|
-
default:
|
|
212
|
-
throw new Error(`Unknown resource type: ${resourceType}`)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
} catch (error) {
|
|
216
|
-
this.logger.error({ error, project: detectedProject.name, resourceType }, 'Failed to read resource')
|
|
217
|
-
content = `Error loading ${resourceType} for ${detectedProject.name}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
contents: [
|
|
222
|
-
{
|
|
223
|
-
uri: `project://current/${resourceType}`,
|
|
224
|
-
mimeType: 'text/markdown',
|
|
225
|
-
text: content
|
|
226
|
-
}
|
|
227
|
-
]
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Get context content
|
|
233
|
-
*/
|
|
234
|
-
private async getContextContent(projectName: string): Promise<string> {
|
|
235
|
-
if (!isServicesInitialized()) {
|
|
236
|
-
return `# ${projectName}\n\nServices not initialized`
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const contextService = getContextService()
|
|
240
|
-
|
|
241
|
-
const context = await contextService.getContext(projectName, {
|
|
242
|
-
includeMemories: true,
|
|
243
|
-
includeProgress: true,
|
|
244
|
-
includeStandards: true,
|
|
245
|
-
maxTokens: 3000
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
return contextService.formatter.format(context)
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Get recent decisions
|
|
253
|
-
*/
|
|
254
|
-
private async getDecisionsContent(projectName: string): Promise<string> {
|
|
255
|
-
if (!isServicesInitialized()) {
|
|
256
|
-
return `# Recent Decisions: ${projectName}\n\nServices not initialized`
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const memory = getMemoryService()
|
|
260
|
-
|
|
261
|
-
// Search for decisions related to this project
|
|
262
|
-
const decisions = await memory.searchRaw(projectName, {
|
|
263
|
-
project: projectName,
|
|
264
|
-
limit: 5,
|
|
265
|
-
minSimilarity: 0.1
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
if (decisions.length === 0) {
|
|
269
|
-
return `# Recent Decisions: ${projectName}\n\nNo decisions recorded yet.\n\n*Use \`remember_decision\` to store important decisions.*`
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
let content = `# Recent Decisions: ${projectName}\n\n`
|
|
273
|
-
|
|
274
|
-
for (const [i, decision] of decisions.entries()) {
|
|
275
|
-
content += `## ${i + 1}. ${decision.decision || 'Untitled'}\n\n`
|
|
276
|
-
|
|
277
|
-
if (decision.context) {
|
|
278
|
-
content += `**Context:** ${decision.context}\n\n`
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (decision.reasoning) {
|
|
282
|
-
content += `**Reasoning:** ${decision.reasoning}\n\n`
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (decision.alternatives) {
|
|
286
|
-
content += `**Alternatives:** ${decision.alternatives}\n\n`
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (decision.timestamp) {
|
|
290
|
-
content += `*Recorded: ${new Date(decision.timestamp).toLocaleDateString()}*\n\n`
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
content += '---\n\n'
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return content
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Get coding standards
|
|
301
|
-
*/
|
|
302
|
-
private async getStandardsContent(projectName: string): Promise<string> {
|
|
303
|
-
if (!isServicesInitialized()) {
|
|
304
|
-
return `# Coding Standards: ${projectName}\n\nServices not initialized`
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const contextService = getContextService()
|
|
308
|
-
|
|
309
|
-
try {
|
|
310
|
-
const standards = await contextService.standards.getProjectStandards(projectName)
|
|
311
|
-
|
|
312
|
-
let content = `# Coding Standards: ${projectName}\n\n`
|
|
313
|
-
|
|
314
|
-
if (standards.languages && standards.languages.length > 0) {
|
|
315
|
-
for (const lang of standards.languages) {
|
|
316
|
-
content += `## ${lang.language}\n\n`
|
|
317
|
-
|
|
318
|
-
if (lang.version) {
|
|
319
|
-
content += `**Version:** ${lang.version}\n`
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (lang.style) {
|
|
323
|
-
content += `**Style:** ${lang.style}\n\n`
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (lang.conventions && lang.conventions.length > 0) {
|
|
327
|
-
content += '**Conventions:**\n'
|
|
328
|
-
for (const conv of lang.conventions) {
|
|
329
|
-
content += `- ${conv}\n`
|
|
330
|
-
}
|
|
331
|
-
content += '\n'
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
} else {
|
|
335
|
-
content += '*No specific language standards defined.*\n\n'
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return content
|
|
339
|
-
|
|
340
|
-
} catch (error) {
|
|
341
|
-
return `# Coding Standards: ${projectName}\n\n*Standards not available. Check if standards.md exists.*`
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Get progress content
|
|
347
|
-
*/
|
|
348
|
-
private async getProgressContent(projectName: string): Promise<string> {
|
|
349
|
-
if (!isServicesInitialized()) {
|
|
350
|
-
return `# Progress: ${projectName}\n\nServices not initialized`
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const vault = getVaultService()
|
|
354
|
-
const projectPaths = vault.getProjectPaths(projectName)
|
|
355
|
-
|
|
356
|
-
try {
|
|
357
|
-
const progress = await vault.reader.readMarkdownFile(projectPaths.progress)
|
|
358
|
-
|
|
359
|
-
let content = `# Progress: ${projectName}\n\n`
|
|
360
|
-
|
|
361
|
-
// Add frontmatter info
|
|
362
|
-
if (progress.frontmatter) {
|
|
363
|
-
if (progress.frontmatter.current_phase) {
|
|
364
|
-
content += `**Current Phase:** ${progress.frontmatter.current_phase}\n`
|
|
365
|
-
}
|
|
366
|
-
if (progress.frontmatter.completion_percentage !== undefined) {
|
|
367
|
-
content += `**Completion:** ${progress.frontmatter.completion_percentage}%\n`
|
|
368
|
-
}
|
|
369
|
-
if (progress.frontmatter.status) {
|
|
370
|
-
content += `**Status:** ${progress.frontmatter.status}\n`
|
|
371
|
-
}
|
|
372
|
-
content += '\n'
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Add content
|
|
376
|
-
if (progress.content) {
|
|
377
|
-
content += progress.content
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
return content
|
|
381
|
-
|
|
382
|
-
} catch (error) {
|
|
383
|
-
return `# Progress: ${projectName}\n\n*Progress file not found.*`
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Handle brain:// resource URIs
|
|
389
|
-
*/
|
|
390
|
-
private async getBrainResource(uri: string) {
|
|
391
|
-
if (uri === 'brain://context/auto') {
|
|
392
|
-
const content = await this.getAutoContextContent()
|
|
393
|
-
return {
|
|
394
|
-
contents: [
|
|
395
|
-
{
|
|
396
|
-
uri,
|
|
397
|
-
mimeType: 'text/markdown',
|
|
398
|
-
text: content
|
|
399
|
-
}
|
|
400
|
-
]
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
throw new Error(`Unknown brain resource URI: ${uri}`)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Phase 21: Build aggregated auto-context payload
|
|
409
|
-
* Combines last session summary, recent decisions, corrections,
|
|
410
|
-
* cross-project knowledge, and current session info.
|
|
411
|
-
*/
|
|
412
|
-
private async getAutoContextContent(): Promise<string> {
|
|
413
|
-
if (!isServicesInitialized()) {
|
|
414
|
-
return '# Auto Context\n\nServices not initialized.'
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const sections: string[] = ['# Auto Context\n']
|
|
418
|
-
const detectedProject = await this.detectCurrentProject()
|
|
419
|
-
const projectName = detectedProject?.name
|
|
420
|
-
|
|
421
|
-
// 1. Last session summary
|
|
422
|
-
try {
|
|
423
|
-
const episodeService = getEpisodeService()
|
|
424
|
-
if (episodeService) {
|
|
425
|
-
const recentEpisodes = await episodeService.getRecentEpisodes(projectName, 1)
|
|
426
|
-
if (recentEpisodes.length > 0 && recentEpisodes[0].summary?.brief) {
|
|
427
|
-
const ep = recentEpisodes[0]
|
|
428
|
-
sections.push('## Last Session\n')
|
|
429
|
-
sections.push(ep.summary!.brief)
|
|
430
|
-
if (ep.summary!.key_topics.length > 0) {
|
|
431
|
-
sections.push(`\n**Topics:** ${ep.summary!.key_topics.join(', ')}`)
|
|
432
|
-
}
|
|
433
|
-
sections.push('')
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
} catch (err) {
|
|
437
|
-
this.logger.debug({ err }, 'Failed to get last session for auto-context')
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// 2. Current session info
|
|
441
|
-
try {
|
|
442
|
-
const tracker = getSessionTracker()
|
|
443
|
-
if (tracker) {
|
|
444
|
-
const stats = tracker.getStats()
|
|
445
|
-
if (stats.activeSessions > 0) {
|
|
446
|
-
sections.push('## Current Session\n')
|
|
447
|
-
sections.push(`Active sessions: ${stats.activeSessions}, items tracked: ${stats.totalItems}`)
|
|
448
|
-
sections.push('')
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
} catch (err) {
|
|
452
|
-
this.logger.debug({ err }, 'Failed to get current session for auto-context')
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// 3. Recent decisions
|
|
456
|
-
try {
|
|
457
|
-
const memory = getMemoryService()
|
|
458
|
-
const searchProject = projectName || 'general'
|
|
459
|
-
const decisions = await memory.searchRaw(searchProject, {
|
|
460
|
-
project: searchProject,
|
|
461
|
-
limit: 5,
|
|
462
|
-
minSimilarity: 0.1
|
|
463
|
-
})
|
|
464
|
-
|
|
465
|
-
if (decisions.length > 0) {
|
|
466
|
-
sections.push('## Recent Decisions\n')
|
|
467
|
-
for (const d of decisions) {
|
|
468
|
-
const title = d.decision || d.content || 'Untitled'
|
|
469
|
-
const truncated = title.length > 120 ? title.slice(0, 120) + '...' : title
|
|
470
|
-
sections.push(`- ${truncated}`)
|
|
471
|
-
}
|
|
472
|
-
sections.push('')
|
|
473
|
-
}
|
|
474
|
-
} catch (err) {
|
|
475
|
-
this.logger.debug({ err }, 'Failed to get decisions for auto-context')
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// 4. Corrections (watch out for)
|
|
479
|
-
try {
|
|
480
|
-
const memory = getMemoryService()
|
|
481
|
-
if (typeof memory.getCorrections === 'function') {
|
|
482
|
-
const corrections = await memory.getCorrections(projectName || 'general', { limit: 3 })
|
|
483
|
-
if (corrections && corrections.length > 0) {
|
|
484
|
-
sections.push('## Watch Out For\n')
|
|
485
|
-
for (const c of corrections) {
|
|
486
|
-
const text = typeof c === 'string' ? c : (c.content || c.correction || JSON.stringify(c))
|
|
487
|
-
const truncated = text.length > 120 ? text.slice(0, 120) + '...' : text
|
|
488
|
-
sections.push(`- ${truncated}`)
|
|
489
|
-
}
|
|
490
|
-
sections.push('')
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
} catch (err) {
|
|
494
|
-
this.logger.debug({ err }, 'Failed to get corrections for auto-context')
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// 5. Cross-project knowledge
|
|
498
|
-
if (projectName) {
|
|
499
|
-
try {
|
|
500
|
-
const vault = getVaultService()
|
|
501
|
-
const allProjects = await vault.reader.getProjectDirectories(vault.paths.projects)
|
|
502
|
-
const otherProjects = allProjects.filter(p => p !== projectName)
|
|
503
|
-
|
|
504
|
-
if (otherProjects.length > 0) {
|
|
505
|
-
const memory = getMemoryService()
|
|
506
|
-
if (memory.isChromaDBEnabled()) {
|
|
507
|
-
const { KnowledgeTransfer } = await import('@/intelligence/cross-project/transfer')
|
|
508
|
-
const transfer = new KnowledgeTransfer(
|
|
509
|
-
this.logger,
|
|
510
|
-
memory.chroma.collections,
|
|
511
|
-
memory.chroma.embeddings
|
|
512
|
-
)
|
|
513
|
-
|
|
514
|
-
const transferItems: string[] = []
|
|
515
|
-
// Check up to 3 other projects for transferable knowledge
|
|
516
|
-
for (const other of otherProjects.slice(0, 3)) {
|
|
517
|
-
try {
|
|
518
|
-
const result = await transfer.findTransferable(other, projectName, { limit: 2, minRelevance: 0.5 })
|
|
519
|
-
for (const item of result.items) {
|
|
520
|
-
const truncated = item.content.length > 100 ? item.content.slice(0, 100) + '...' : item.content
|
|
521
|
-
transferItems.push(`- [${item.type} from ${other}] ${truncated}`)
|
|
522
|
-
}
|
|
523
|
-
} catch {
|
|
524
|
-
// Skip failed transfers
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (transferItems.length > 0) {
|
|
529
|
-
sections.push('## From Other Projects\n')
|
|
530
|
-
sections.push(...transferItems)
|
|
531
|
-
sections.push('')
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
} catch (err) {
|
|
536
|
-
this.logger.debug({ err }, 'Failed to get cross-project knowledge for auto-context')
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// If no sections beyond the header, try auto-scan for first-session value
|
|
541
|
-
if (sections.length <= 1) {
|
|
542
|
-
try {
|
|
543
|
-
const cwd = process.cwd()
|
|
544
|
-
const { scanRepo } = await import('@/automation/repo-scanner')
|
|
545
|
-
const repoContext = await scanRepo(cwd)
|
|
546
|
-
if (repoContext.name) {
|
|
547
|
-
sections.push('## Project Detected\n')
|
|
548
|
-
sections.push(`**Name:** ${repoContext.name}`)
|
|
549
|
-
if (repoContext.techStack.length > 0) {
|
|
550
|
-
sections.push(`**Tech stack:** ${repoContext.techStack.join(', ')}`)
|
|
551
|
-
}
|
|
552
|
-
if (repoContext.description) {
|
|
553
|
-
const desc = repoContext.description.split('\n')[0]?.slice(0, 200) || ''
|
|
554
|
-
sections.push(`**Description:** ${desc}`)
|
|
555
|
-
}
|
|
556
|
-
sections.push('')
|
|
557
|
-
}
|
|
558
|
-
} catch {
|
|
559
|
-
// Scan failed, continue with empty context
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
if (sections.length <= 1) {
|
|
564
|
-
return '# Auto Context\n\nNo context available yet. Use brain to store decisions, patterns, and corrections.'
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
return sections.join('\n')
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
/**
|
|
571
|
-
* Detect current project from working directory
|
|
572
|
-
*/
|
|
573
|
-
private async detectCurrentProject(): Promise<DetectedProject | null> {
|
|
574
|
-
try {
|
|
575
|
-
const cwd = process.cwd()
|
|
576
|
-
|
|
577
|
-
// Check if Phase 12 is available with better detection
|
|
578
|
-
if (isPhase12Available()) {
|
|
579
|
-
const phase12 = getPhase12Instance()
|
|
580
|
-
if (phase12?.projectDetector?.detectProject) {
|
|
581
|
-
return await phase12.projectDetector.detectProject(cwd)
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Fallback detection
|
|
586
|
-
if (!isServicesInitialized()) {
|
|
587
|
-
return null
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
const vault = getVaultService()
|
|
591
|
-
const projects = await vault.reader.getProjectDirectories(vault.paths.projects)
|
|
592
|
-
const dirName = path.basename(cwd)
|
|
593
|
-
|
|
594
|
-
if (projects.includes(dirName)) {
|
|
595
|
-
return {
|
|
596
|
-
name: dirName,
|
|
597
|
-
path: cwd,
|
|
598
|
-
confidence: 1.0,
|
|
599
|
-
indicators: ['directory-match']
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
// Check if cwd contains a project folder name
|
|
604
|
-
for (const project of projects) {
|
|
605
|
-
if (cwd.toLowerCase().includes(project.toLowerCase())) {
|
|
606
|
-
return {
|
|
607
|
-
name: project,
|
|
608
|
-
path: cwd,
|
|
609
|
-
confidence: 0.7,
|
|
610
|
-
indicators: ['path-contains-project']
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
return null
|
|
616
|
-
|
|
617
|
-
} catch (error) {
|
|
618
|
-
this.logger.debug({ error }, 'Project detection failed')
|
|
619
|
-
return null
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Resource Provider
|
|
3
|
+
* Phase 6.5: Expose project context as resources
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
import path from 'path'
|
|
8
|
+
import {
|
|
9
|
+
getVaultService,
|
|
10
|
+
getContextService,
|
|
11
|
+
getMemoryService,
|
|
12
|
+
getEpisodeService,
|
|
13
|
+
getSessionTracker,
|
|
14
|
+
isServicesInitialized
|
|
15
|
+
} from '@/server/services'
|
|
16
|
+
import { isPhase12Available, getPhase12Instance } from '@/utils/phase12-helper'
|
|
17
|
+
import type { ContextFormatter } from '@/context'
|
|
18
|
+
|
|
19
|
+
interface DetectedProject {
|
|
20
|
+
name: string
|
|
21
|
+
path: string
|
|
22
|
+
confidence: number
|
|
23
|
+
indicators: string[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ResourceItem {
|
|
27
|
+
uri: string
|
|
28
|
+
name: string
|
|
29
|
+
description: string
|
|
30
|
+
mimeType: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class ResourceProvider {
|
|
34
|
+
private logger: Logger
|
|
35
|
+
|
|
36
|
+
constructor(logger: Logger) {
|
|
37
|
+
this.logger = logger.child({ component: 'resource-provider' })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* List all available resources
|
|
42
|
+
*/
|
|
43
|
+
async listResources() {
|
|
44
|
+
const detectedProject = await this.detectCurrentProject()
|
|
45
|
+
|
|
46
|
+
const resources: ResourceItem[] = [
|
|
47
|
+
{
|
|
48
|
+
uri: 'brain://context/auto',
|
|
49
|
+
name: 'Auto Context',
|
|
50
|
+
description: 'Session context auto-loaded by brain',
|
|
51
|
+
mimeType: 'text/markdown'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
uri: 'project://list',
|
|
55
|
+
name: 'All Projects',
|
|
56
|
+
description: 'List of all projects in vault',
|
|
57
|
+
mimeType: 'text/plain'
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
if (detectedProject) {
|
|
62
|
+
resources.push(
|
|
63
|
+
{
|
|
64
|
+
uri: 'project://current/context',
|
|
65
|
+
name: 'Current Project Context',
|
|
66
|
+
description: `Context for ${detectedProject.name}`,
|
|
67
|
+
mimeType: 'text/markdown'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
uri: 'project://current/decisions',
|
|
71
|
+
name: 'Recent Decisions',
|
|
72
|
+
description: `Recent decisions for ${detectedProject.name}`,
|
|
73
|
+
mimeType: 'text/markdown'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
uri: 'project://current/standards',
|
|
77
|
+
name: 'Coding Standards',
|
|
78
|
+
description: `Standards for ${detectedProject.name}`,
|
|
79
|
+
mimeType: 'text/markdown'
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
uri: 'project://current/progress',
|
|
83
|
+
name: 'Current Progress',
|
|
84
|
+
description: `Progress tracker for ${detectedProject.name}`,
|
|
85
|
+
mimeType: 'text/markdown'
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { resources }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Read a specific resource
|
|
95
|
+
*/
|
|
96
|
+
async readResource(uri: string) {
|
|
97
|
+
this.logger.info({ uri }, 'Reading resource')
|
|
98
|
+
|
|
99
|
+
if (uri.startsWith('brain://')) {
|
|
100
|
+
return await this.getBrainResource(uri)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (uri === 'project://list') {
|
|
104
|
+
return await this.getProjectListResource()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (uri.startsWith('project://current/')) {
|
|
108
|
+
const resourceType = uri.split('/').pop()
|
|
109
|
+
return await this.getCurrentProjectResource(resourceType!)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
throw new Error(`Unknown resource URI: ${uri}`)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get project list resource
|
|
117
|
+
*/
|
|
118
|
+
private async getProjectListResource() {
|
|
119
|
+
if (!isServicesInitialized()) {
|
|
120
|
+
return {
|
|
121
|
+
contents: [
|
|
122
|
+
{
|
|
123
|
+
uri: 'project://list',
|
|
124
|
+
mimeType: 'text/plain',
|
|
125
|
+
text: 'Services not initialized'
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const vault = getVaultService()
|
|
132
|
+
const projects = await vault.reader.getProjectDirectories(vault.paths.projects)
|
|
133
|
+
|
|
134
|
+
let content = '# Available Projects\n\n'
|
|
135
|
+
|
|
136
|
+
for (const project of projects) {
|
|
137
|
+
try {
|
|
138
|
+
const projectPaths = vault.getProjectPaths(project)
|
|
139
|
+
const context = await vault.reader.readMarkdownFile(projectPaths.context)
|
|
140
|
+
|
|
141
|
+
content += `## ${project}\n`
|
|
142
|
+
content += `**Status:** ${context.frontmatter.status || 'unknown'}\n`
|
|
143
|
+
|
|
144
|
+
if (context.frontmatter.tech_stack) {
|
|
145
|
+
const techStack = Array.isArray(context.frontmatter.tech_stack)
|
|
146
|
+
? context.frontmatter.tech_stack.join(', ')
|
|
147
|
+
: context.frontmatter.tech_stack
|
|
148
|
+
content += `**Tech Stack:** ${techStack}\n`
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (context.frontmatter.description) {
|
|
152
|
+
content += `**Description:** ${context.frontmatter.description}\n`
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
content += '\n'
|
|
156
|
+
|
|
157
|
+
} catch (error) {
|
|
158
|
+
content += `## ${project}\n*Context not available*\n\n`
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (projects.length === 0) {
|
|
163
|
+
content += '*No projects found in vault*\n'
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
contents: [
|
|
168
|
+
{
|
|
169
|
+
uri: 'project://list',
|
|
170
|
+
mimeType: 'text/markdown',
|
|
171
|
+
text: content
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get current project resource
|
|
179
|
+
*/
|
|
180
|
+
private async getCurrentProjectResource(resourceType: string) {
|
|
181
|
+
const detectedProject = await this.detectCurrentProject()
|
|
182
|
+
|
|
183
|
+
if (!detectedProject) {
|
|
184
|
+
return {
|
|
185
|
+
contents: [
|
|
186
|
+
{
|
|
187
|
+
uri: `project://current/${resourceType}`,
|
|
188
|
+
mimeType: 'text/plain',
|
|
189
|
+
text: 'No project detected in current working directory'
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let content: string
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
switch (resourceType) {
|
|
199
|
+
case 'context':
|
|
200
|
+
content = await this.getContextContent(detectedProject.name)
|
|
201
|
+
break
|
|
202
|
+
case 'decisions':
|
|
203
|
+
content = await this.getDecisionsContent(detectedProject.name)
|
|
204
|
+
break
|
|
205
|
+
case 'standards':
|
|
206
|
+
content = await this.getStandardsContent(detectedProject.name)
|
|
207
|
+
break
|
|
208
|
+
case 'progress':
|
|
209
|
+
content = await this.getProgressContent(detectedProject.name)
|
|
210
|
+
break
|
|
211
|
+
default:
|
|
212
|
+
throw new Error(`Unknown resource type: ${resourceType}`)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
} catch (error) {
|
|
216
|
+
this.logger.error({ error, project: detectedProject.name, resourceType }, 'Failed to read resource')
|
|
217
|
+
content = `Error loading ${resourceType} for ${detectedProject.name}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
contents: [
|
|
222
|
+
{
|
|
223
|
+
uri: `project://current/${resourceType}`,
|
|
224
|
+
mimeType: 'text/markdown',
|
|
225
|
+
text: content
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get context content
|
|
233
|
+
*/
|
|
234
|
+
private async getContextContent(projectName: string): Promise<string> {
|
|
235
|
+
if (!isServicesInitialized()) {
|
|
236
|
+
return `# ${projectName}\n\nServices not initialized`
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const contextService = getContextService()
|
|
240
|
+
|
|
241
|
+
const context = await contextService.getContext(projectName, {
|
|
242
|
+
includeMemories: true,
|
|
243
|
+
includeProgress: true,
|
|
244
|
+
includeStandards: true,
|
|
245
|
+
maxTokens: 3000
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
return contextService.formatter.format(context)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get recent decisions
|
|
253
|
+
*/
|
|
254
|
+
private async getDecisionsContent(projectName: string): Promise<string> {
|
|
255
|
+
if (!isServicesInitialized()) {
|
|
256
|
+
return `# Recent Decisions: ${projectName}\n\nServices not initialized`
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const memory = getMemoryService()
|
|
260
|
+
|
|
261
|
+
// Search for decisions related to this project
|
|
262
|
+
const decisions = await memory.searchRaw(projectName, {
|
|
263
|
+
project: projectName,
|
|
264
|
+
limit: 5,
|
|
265
|
+
minSimilarity: 0.1
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
if (decisions.length === 0) {
|
|
269
|
+
return `# Recent Decisions: ${projectName}\n\nNo decisions recorded yet.\n\n*Use \`remember_decision\` to store important decisions.*`
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
let content = `# Recent Decisions: ${projectName}\n\n`
|
|
273
|
+
|
|
274
|
+
for (const [i, decision] of decisions.entries()) {
|
|
275
|
+
content += `## ${i + 1}. ${decision.decision || 'Untitled'}\n\n`
|
|
276
|
+
|
|
277
|
+
if (decision.context) {
|
|
278
|
+
content += `**Context:** ${decision.context}\n\n`
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (decision.reasoning) {
|
|
282
|
+
content += `**Reasoning:** ${decision.reasoning}\n\n`
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (decision.alternatives) {
|
|
286
|
+
content += `**Alternatives:** ${decision.alternatives}\n\n`
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (decision.timestamp) {
|
|
290
|
+
content += `*Recorded: ${new Date(decision.timestamp).toLocaleDateString()}*\n\n`
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
content += '---\n\n'
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return content
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get coding standards
|
|
301
|
+
*/
|
|
302
|
+
private async getStandardsContent(projectName: string): Promise<string> {
|
|
303
|
+
if (!isServicesInitialized()) {
|
|
304
|
+
return `# Coding Standards: ${projectName}\n\nServices not initialized`
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const contextService = getContextService()
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
const standards = await contextService.standards.getProjectStandards(projectName)
|
|
311
|
+
|
|
312
|
+
let content = `# Coding Standards: ${projectName}\n\n`
|
|
313
|
+
|
|
314
|
+
if (standards.languages && standards.languages.length > 0) {
|
|
315
|
+
for (const lang of standards.languages) {
|
|
316
|
+
content += `## ${lang.language}\n\n`
|
|
317
|
+
|
|
318
|
+
if (lang.version) {
|
|
319
|
+
content += `**Version:** ${lang.version}\n`
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (lang.style) {
|
|
323
|
+
content += `**Style:** ${lang.style}\n\n`
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (lang.conventions && lang.conventions.length > 0) {
|
|
327
|
+
content += '**Conventions:**\n'
|
|
328
|
+
for (const conv of lang.conventions) {
|
|
329
|
+
content += `- ${conv}\n`
|
|
330
|
+
}
|
|
331
|
+
content += '\n'
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
content += '*No specific language standards defined.*\n\n'
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return content
|
|
339
|
+
|
|
340
|
+
} catch (error) {
|
|
341
|
+
return `# Coding Standards: ${projectName}\n\n*Standards not available. Check if standards.md exists.*`
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Get progress content
|
|
347
|
+
*/
|
|
348
|
+
private async getProgressContent(projectName: string): Promise<string> {
|
|
349
|
+
if (!isServicesInitialized()) {
|
|
350
|
+
return `# Progress: ${projectName}\n\nServices not initialized`
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const vault = getVaultService()
|
|
354
|
+
const projectPaths = vault.getProjectPaths(projectName)
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
const progress = await vault.reader.readMarkdownFile(projectPaths.progress)
|
|
358
|
+
|
|
359
|
+
let content = `# Progress: ${projectName}\n\n`
|
|
360
|
+
|
|
361
|
+
// Add frontmatter info
|
|
362
|
+
if (progress.frontmatter) {
|
|
363
|
+
if (progress.frontmatter.current_phase) {
|
|
364
|
+
content += `**Current Phase:** ${progress.frontmatter.current_phase}\n`
|
|
365
|
+
}
|
|
366
|
+
if (progress.frontmatter.completion_percentage !== undefined) {
|
|
367
|
+
content += `**Completion:** ${progress.frontmatter.completion_percentage}%\n`
|
|
368
|
+
}
|
|
369
|
+
if (progress.frontmatter.status) {
|
|
370
|
+
content += `**Status:** ${progress.frontmatter.status}\n`
|
|
371
|
+
}
|
|
372
|
+
content += '\n'
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Add content
|
|
376
|
+
if (progress.content) {
|
|
377
|
+
content += progress.content
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return content
|
|
381
|
+
|
|
382
|
+
} catch (error) {
|
|
383
|
+
return `# Progress: ${projectName}\n\n*Progress file not found.*`
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Handle brain:// resource URIs
|
|
389
|
+
*/
|
|
390
|
+
private async getBrainResource(uri: string) {
|
|
391
|
+
if (uri === 'brain://context/auto') {
|
|
392
|
+
const content = await this.getAutoContextContent()
|
|
393
|
+
return {
|
|
394
|
+
contents: [
|
|
395
|
+
{
|
|
396
|
+
uri,
|
|
397
|
+
mimeType: 'text/markdown',
|
|
398
|
+
text: content
|
|
399
|
+
}
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
throw new Error(`Unknown brain resource URI: ${uri}`)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Phase 21: Build aggregated auto-context payload
|
|
409
|
+
* Combines last session summary, recent decisions, corrections,
|
|
410
|
+
* cross-project knowledge, and current session info.
|
|
411
|
+
*/
|
|
412
|
+
private async getAutoContextContent(): Promise<string> {
|
|
413
|
+
if (!isServicesInitialized()) {
|
|
414
|
+
return '# Auto Context\n\nServices not initialized.'
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const sections: string[] = ['# Auto Context\n']
|
|
418
|
+
const detectedProject = await this.detectCurrentProject()
|
|
419
|
+
const projectName = detectedProject?.name
|
|
420
|
+
|
|
421
|
+
// 1. Last session summary
|
|
422
|
+
try {
|
|
423
|
+
const episodeService = getEpisodeService()
|
|
424
|
+
if (episodeService) {
|
|
425
|
+
const recentEpisodes = await episodeService.getRecentEpisodes(projectName, 1)
|
|
426
|
+
if (recentEpisodes.length > 0 && recentEpisodes[0].summary?.brief) {
|
|
427
|
+
const ep = recentEpisodes[0]
|
|
428
|
+
sections.push('## Last Session\n')
|
|
429
|
+
sections.push(ep.summary!.brief)
|
|
430
|
+
if (ep.summary!.key_topics.length > 0) {
|
|
431
|
+
sections.push(`\n**Topics:** ${ep.summary!.key_topics.join(', ')}`)
|
|
432
|
+
}
|
|
433
|
+
sections.push('')
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
} catch (err) {
|
|
437
|
+
this.logger.debug({ err }, 'Failed to get last session for auto-context')
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// 2. Current session info
|
|
441
|
+
try {
|
|
442
|
+
const tracker = getSessionTracker()
|
|
443
|
+
if (tracker) {
|
|
444
|
+
const stats = tracker.getStats()
|
|
445
|
+
if (stats.activeSessions > 0) {
|
|
446
|
+
sections.push('## Current Session\n')
|
|
447
|
+
sections.push(`Active sessions: ${stats.activeSessions}, items tracked: ${stats.totalItems}`)
|
|
448
|
+
sections.push('')
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
} catch (err) {
|
|
452
|
+
this.logger.debug({ err }, 'Failed to get current session for auto-context')
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// 3. Recent decisions
|
|
456
|
+
try {
|
|
457
|
+
const memory = getMemoryService()
|
|
458
|
+
const searchProject = projectName || 'general'
|
|
459
|
+
const decisions = await memory.searchRaw(searchProject, {
|
|
460
|
+
project: searchProject,
|
|
461
|
+
limit: 5,
|
|
462
|
+
minSimilarity: 0.1
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
if (decisions.length > 0) {
|
|
466
|
+
sections.push('## Recent Decisions\n')
|
|
467
|
+
for (const d of decisions) {
|
|
468
|
+
const title = d.decision || d.content || 'Untitled'
|
|
469
|
+
const truncated = title.length > 120 ? title.slice(0, 120) + '...' : title
|
|
470
|
+
sections.push(`- ${truncated}`)
|
|
471
|
+
}
|
|
472
|
+
sections.push('')
|
|
473
|
+
}
|
|
474
|
+
} catch (err) {
|
|
475
|
+
this.logger.debug({ err }, 'Failed to get decisions for auto-context')
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// 4. Corrections (watch out for)
|
|
479
|
+
try {
|
|
480
|
+
const memory = getMemoryService()
|
|
481
|
+
if (typeof memory.getCorrections === 'function') {
|
|
482
|
+
const corrections = await memory.getCorrections(projectName || 'general', { limit: 3 })
|
|
483
|
+
if (corrections && corrections.length > 0) {
|
|
484
|
+
sections.push('## Watch Out For\n')
|
|
485
|
+
for (const c of corrections) {
|
|
486
|
+
const text = typeof c === 'string' ? c : (c.content || c.correction || JSON.stringify(c))
|
|
487
|
+
const truncated = text.length > 120 ? text.slice(0, 120) + '...' : text
|
|
488
|
+
sections.push(`- ${truncated}`)
|
|
489
|
+
}
|
|
490
|
+
sections.push('')
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
} catch (err) {
|
|
494
|
+
this.logger.debug({ err }, 'Failed to get corrections for auto-context')
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// 5. Cross-project knowledge
|
|
498
|
+
if (projectName) {
|
|
499
|
+
try {
|
|
500
|
+
const vault = getVaultService()
|
|
501
|
+
const allProjects = await vault.reader.getProjectDirectories(vault.paths.projects)
|
|
502
|
+
const otherProjects = allProjects.filter(p => p !== projectName)
|
|
503
|
+
|
|
504
|
+
if (otherProjects.length > 0) {
|
|
505
|
+
const memory = getMemoryService()
|
|
506
|
+
if (memory.isChromaDBEnabled()) {
|
|
507
|
+
const { KnowledgeTransfer } = await import('@/intelligence/cross-project/transfer')
|
|
508
|
+
const transfer = new KnowledgeTransfer(
|
|
509
|
+
this.logger,
|
|
510
|
+
memory.chroma.collections,
|
|
511
|
+
memory.chroma.embeddings
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
const transferItems: string[] = []
|
|
515
|
+
// Check up to 3 other projects for transferable knowledge
|
|
516
|
+
for (const other of otherProjects.slice(0, 3)) {
|
|
517
|
+
try {
|
|
518
|
+
const result = await transfer.findTransferable(other, projectName, { limit: 2, minRelevance: 0.5 })
|
|
519
|
+
for (const item of result.items) {
|
|
520
|
+
const truncated = item.content.length > 100 ? item.content.slice(0, 100) + '...' : item.content
|
|
521
|
+
transferItems.push(`- [${item.type} from ${other}] ${truncated}`)
|
|
522
|
+
}
|
|
523
|
+
} catch {
|
|
524
|
+
// Skip failed transfers
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (transferItems.length > 0) {
|
|
529
|
+
sections.push('## From Other Projects\n')
|
|
530
|
+
sections.push(...transferItems)
|
|
531
|
+
sections.push('')
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} catch (err) {
|
|
536
|
+
this.logger.debug({ err }, 'Failed to get cross-project knowledge for auto-context')
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// If no sections beyond the header, try auto-scan for first-session value
|
|
541
|
+
if (sections.length <= 1) {
|
|
542
|
+
try {
|
|
543
|
+
const cwd = process.cwd()
|
|
544
|
+
const { scanRepo } = await import('@/automation/repo-scanner')
|
|
545
|
+
const repoContext = await scanRepo(cwd)
|
|
546
|
+
if (repoContext.name) {
|
|
547
|
+
sections.push('## Project Detected\n')
|
|
548
|
+
sections.push(`**Name:** ${repoContext.name}`)
|
|
549
|
+
if (repoContext.techStack.length > 0) {
|
|
550
|
+
sections.push(`**Tech stack:** ${repoContext.techStack.join(', ')}`)
|
|
551
|
+
}
|
|
552
|
+
if (repoContext.description) {
|
|
553
|
+
const desc = repoContext.description.split('\n')[0]?.slice(0, 200) || ''
|
|
554
|
+
sections.push(`**Description:** ${desc}`)
|
|
555
|
+
}
|
|
556
|
+
sections.push('')
|
|
557
|
+
}
|
|
558
|
+
} catch {
|
|
559
|
+
// Scan failed, continue with empty context
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (sections.length <= 1) {
|
|
564
|
+
return '# Auto Context\n\nNo context available yet. Use brain to store decisions, patterns, and corrections.'
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return sections.join('\n')
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Detect current project from working directory
|
|
572
|
+
*/
|
|
573
|
+
private async detectCurrentProject(): Promise<DetectedProject | null> {
|
|
574
|
+
try {
|
|
575
|
+
const cwd = process.cwd()
|
|
576
|
+
|
|
577
|
+
// Check if Phase 12 is available with better detection
|
|
578
|
+
if (isPhase12Available()) {
|
|
579
|
+
const phase12 = getPhase12Instance()
|
|
580
|
+
if (phase12?.projectDetector?.detectProject) {
|
|
581
|
+
return await phase12.projectDetector.detectProject(cwd)
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Fallback detection
|
|
586
|
+
if (!isServicesInitialized()) {
|
|
587
|
+
return null
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const vault = getVaultService()
|
|
591
|
+
const projects = await vault.reader.getProjectDirectories(vault.paths.projects)
|
|
592
|
+
const dirName = path.basename(cwd)
|
|
593
|
+
|
|
594
|
+
if (projects.includes(dirName)) {
|
|
595
|
+
return {
|
|
596
|
+
name: dirName,
|
|
597
|
+
path: cwd,
|
|
598
|
+
confidence: 1.0,
|
|
599
|
+
indicators: ['directory-match']
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Check if cwd contains a project folder name
|
|
604
|
+
for (const project of projects) {
|
|
605
|
+
if (cwd.toLowerCase().includes(project.toLowerCase())) {
|
|
606
|
+
return {
|
|
607
|
+
name: project,
|
|
608
|
+
path: cwd,
|
|
609
|
+
confidence: 0.7,
|
|
610
|
+
indicators: ['path-contains-project']
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return null
|
|
616
|
+
|
|
617
|
+
} catch (error) {
|
|
618
|
+
this.logger.debug({ error }, 'Project detection failed')
|
|
619
|
+
return null
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|