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,135 +1,135 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create Project Handler
|
|
3
|
-
* Creates a new project in the vault with all necessary files
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Logger } from 'pino'
|
|
7
|
-
import type { ToolResponse } from '@/tools/types'
|
|
8
|
-
import { getVaultService, isServicesInitialized } from '@/server/services'
|
|
9
|
-
import { ToolValidator } from '@/server/utils/validators'
|
|
10
|
-
import { ResponseFormatter } from '@/server/utils/response-formatter'
|
|
11
|
-
import { ErrorHandler } from '@/server/utils/error-handler'
|
|
12
|
-
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
|
13
|
-
import { CreateProjectSchema } from './schemas'
|
|
14
|
-
|
|
15
|
-
export async function handleCreateProject(
|
|
16
|
-
args: unknown,
|
|
17
|
-
logger: Logger
|
|
18
|
-
): Promise<ToolResponse> {
|
|
19
|
-
try {
|
|
20
|
-
const input = ToolValidator.validate(args, CreateProjectSchema)
|
|
21
|
-
const {
|
|
22
|
-
project_name,
|
|
23
|
-
description,
|
|
24
|
-
tech_stack,
|
|
25
|
-
status,
|
|
26
|
-
tags
|
|
27
|
-
} = input
|
|
28
|
-
|
|
29
|
-
// project_name is already validated by schema to be lowercase with hyphens
|
|
30
|
-
const normalizedName = project_name
|
|
31
|
-
|
|
32
|
-
logger.debug({
|
|
33
|
-
project_name: normalizedName,
|
|
34
|
-
description,
|
|
35
|
-
tech_stack,
|
|
36
|
-
status,
|
|
37
|
-
tags
|
|
38
|
-
}, 'Creating project')
|
|
39
|
-
|
|
40
|
-
if (!isServicesInitialized()) {
|
|
41
|
-
throw new McpError(
|
|
42
|
-
ErrorCode.InternalError,
|
|
43
|
-
'Services not initialized'
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const vault = getVaultService()
|
|
48
|
-
|
|
49
|
-
// Check if project already exists
|
|
50
|
-
const existingProjects = await vault.listProjects()
|
|
51
|
-
if (existingProjects.includes(normalizedName)) {
|
|
52
|
-
throw new McpError(
|
|
53
|
-
ErrorCode.InvalidParams,
|
|
54
|
-
`Project "${normalizedName}" already exists. Use get_project_context to view it.`
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Create the project structure
|
|
59
|
-
await vault.createProject(normalizedName)
|
|
60
|
-
|
|
61
|
-
// Update context.md with provided metadata
|
|
62
|
-
const projectPaths = vault.getProjectPaths(normalizedName)
|
|
63
|
-
|
|
64
|
-
if (description || tech_stack.length > 0 || tags.length > 0 || status !== 'planning') {
|
|
65
|
-
await vault.writer.updateFrontmatter(projectPaths.context, {
|
|
66
|
-
description: description || '',
|
|
67
|
-
tech_stack: tech_stack,
|
|
68
|
-
tags: tags,
|
|
69
|
-
status: status
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
// Also update the content with description
|
|
73
|
-
if (description) {
|
|
74
|
-
const contextFile = await vault.reader.readMarkdownFile(projectPaths.context)
|
|
75
|
-
const updatedContent = contextFile.content.replace(
|
|
76
|
-
'<!-- Project description and goals -->',
|
|
77
|
-
description
|
|
78
|
-
)
|
|
79
|
-
await vault.writer.writeMarkdownFile(
|
|
80
|
-
projectPaths.context,
|
|
81
|
-
{ ...contextFile.frontmatter, description, tech_stack, tags, status },
|
|
82
|
-
updatedContent,
|
|
83
|
-
{ createBackup: false }
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Build response
|
|
89
|
-
const parts: string[] = []
|
|
90
|
-
parts.push(`## ✅ Project Created: ${normalizedName}\n`)
|
|
91
|
-
parts.push(`Your project has been set up in the Obsidian vault.\n`)
|
|
92
|
-
parts.push(`### Files Created:\n`)
|
|
93
|
-
parts.push(`- \`${normalizedName}/context.md\` - Project overview and architecture`)
|
|
94
|
-
parts.push(`- \`${normalizedName}/progress.md\` - Progress tracking`)
|
|
95
|
-
parts.push(`- \`${normalizedName}/decisions.md\` - Decision log`)
|
|
96
|
-
parts.push(`- \`${normalizedName}/standards.md\` - Coding standards\n`)
|
|
97
|
-
|
|
98
|
-
if (description) {
|
|
99
|
-
parts.push(`### Description:\n${description}\n`)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (tech_stack.length > 0) {
|
|
103
|
-
parts.push(`### Tech Stack:\n${tech_stack.join(', ')}\n`)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (tags.length > 0) {
|
|
107
|
-
parts.push(`### Tags:\n${tags.join(', ')}\n`)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
parts.push(`### Next Steps:\n`)
|
|
111
|
-
parts.push(`1. Use \`get_project_context("${normalizedName}")\` to view the project`)
|
|
112
|
-
parts.push(`2. Edit the markdown files in your Obsidian vault to add details`)
|
|
113
|
-
parts.push(`3. Use \`remember_decision\` to store architectural decisions`)
|
|
114
|
-
parts.push(`4. Use \`update_progress\` to track your work`)
|
|
115
|
-
|
|
116
|
-
logger.info({
|
|
117
|
-
project_name: normalizedName,
|
|
118
|
-
status
|
|
119
|
-
}, 'Project created successfully')
|
|
120
|
-
|
|
121
|
-
return ResponseFormatter.text(parts.join('\n'))
|
|
122
|
-
|
|
123
|
-
} catch (error) {
|
|
124
|
-
ErrorHandler.logError(logger, error, { tool: 'create_project', args })
|
|
125
|
-
|
|
126
|
-
if (error instanceof McpError) {
|
|
127
|
-
throw error
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
throw new McpError(
|
|
131
|
-
ErrorCode.InternalError,
|
|
132
|
-
`Failed to create project: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
133
|
-
)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Create Project Handler
|
|
3
|
+
* Creates a new project in the vault with all necessary files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
import type { ToolResponse } from '@/tools/types'
|
|
8
|
+
import { getVaultService, isServicesInitialized } from '@/server/services'
|
|
9
|
+
import { ToolValidator } from '@/server/utils/validators'
|
|
10
|
+
import { ResponseFormatter } from '@/server/utils/response-formatter'
|
|
11
|
+
import { ErrorHandler } from '@/server/utils/error-handler'
|
|
12
|
+
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
|
13
|
+
import { CreateProjectSchema } from './schemas'
|
|
14
|
+
|
|
15
|
+
export async function handleCreateProject(
|
|
16
|
+
args: unknown,
|
|
17
|
+
logger: Logger
|
|
18
|
+
): Promise<ToolResponse> {
|
|
19
|
+
try {
|
|
20
|
+
const input = ToolValidator.validate(args, CreateProjectSchema)
|
|
21
|
+
const {
|
|
22
|
+
project_name,
|
|
23
|
+
description,
|
|
24
|
+
tech_stack,
|
|
25
|
+
status,
|
|
26
|
+
tags
|
|
27
|
+
} = input
|
|
28
|
+
|
|
29
|
+
// project_name is already validated by schema to be lowercase with hyphens
|
|
30
|
+
const normalizedName = project_name
|
|
31
|
+
|
|
32
|
+
logger.debug({
|
|
33
|
+
project_name: normalizedName,
|
|
34
|
+
description,
|
|
35
|
+
tech_stack,
|
|
36
|
+
status,
|
|
37
|
+
tags
|
|
38
|
+
}, 'Creating project')
|
|
39
|
+
|
|
40
|
+
if (!isServicesInitialized()) {
|
|
41
|
+
throw new McpError(
|
|
42
|
+
ErrorCode.InternalError,
|
|
43
|
+
'Services not initialized'
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const vault = getVaultService()
|
|
48
|
+
|
|
49
|
+
// Check if project already exists
|
|
50
|
+
const existingProjects = await vault.listProjects()
|
|
51
|
+
if (existingProjects.includes(normalizedName)) {
|
|
52
|
+
throw new McpError(
|
|
53
|
+
ErrorCode.InvalidParams,
|
|
54
|
+
`Project "${normalizedName}" already exists. Use get_project_context to view it.`
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Create the project structure
|
|
59
|
+
await vault.createProject(normalizedName)
|
|
60
|
+
|
|
61
|
+
// Update context.md with provided metadata
|
|
62
|
+
const projectPaths = vault.getProjectPaths(normalizedName)
|
|
63
|
+
|
|
64
|
+
if (description || tech_stack.length > 0 || tags.length > 0 || status !== 'planning') {
|
|
65
|
+
await vault.writer.updateFrontmatter(projectPaths.context, {
|
|
66
|
+
description: description || '',
|
|
67
|
+
tech_stack: tech_stack,
|
|
68
|
+
tags: tags,
|
|
69
|
+
status: status
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Also update the content with description
|
|
73
|
+
if (description) {
|
|
74
|
+
const contextFile = await vault.reader.readMarkdownFile(projectPaths.context)
|
|
75
|
+
const updatedContent = contextFile.content.replace(
|
|
76
|
+
'<!-- Project description and goals -->',
|
|
77
|
+
description
|
|
78
|
+
)
|
|
79
|
+
await vault.writer.writeMarkdownFile(
|
|
80
|
+
projectPaths.context,
|
|
81
|
+
{ ...contextFile.frontmatter, description, tech_stack, tags, status },
|
|
82
|
+
updatedContent,
|
|
83
|
+
{ createBackup: false }
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Build response
|
|
89
|
+
const parts: string[] = []
|
|
90
|
+
parts.push(`## ✅ Project Created: ${normalizedName}\n`)
|
|
91
|
+
parts.push(`Your project has been set up in the Obsidian vault.\n`)
|
|
92
|
+
parts.push(`### Files Created:\n`)
|
|
93
|
+
parts.push(`- \`${normalizedName}/context.md\` - Project overview and architecture`)
|
|
94
|
+
parts.push(`- \`${normalizedName}/progress.md\` - Progress tracking`)
|
|
95
|
+
parts.push(`- \`${normalizedName}/decisions.md\` - Decision log`)
|
|
96
|
+
parts.push(`- \`${normalizedName}/standards.md\` - Coding standards\n`)
|
|
97
|
+
|
|
98
|
+
if (description) {
|
|
99
|
+
parts.push(`### Description:\n${description}\n`)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (tech_stack.length > 0) {
|
|
103
|
+
parts.push(`### Tech Stack:\n${tech_stack.join(', ')}\n`)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (tags.length > 0) {
|
|
107
|
+
parts.push(`### Tags:\n${tags.join(', ')}\n`)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
parts.push(`### Next Steps:\n`)
|
|
111
|
+
parts.push(`1. Use \`get_project_context("${normalizedName}")\` to view the project`)
|
|
112
|
+
parts.push(`2. Edit the markdown files in your Obsidian vault to add details`)
|
|
113
|
+
parts.push(`3. Use \`remember_decision\` to store architectural decisions`)
|
|
114
|
+
parts.push(`4. Use \`update_progress\` to track your work`)
|
|
115
|
+
|
|
116
|
+
logger.info({
|
|
117
|
+
project_name: normalizedName,
|
|
118
|
+
status
|
|
119
|
+
}, 'Project created successfully')
|
|
120
|
+
|
|
121
|
+
return ResponseFormatter.text(parts.join('\n'))
|
|
122
|
+
|
|
123
|
+
} catch (error) {
|
|
124
|
+
ErrorHandler.logError(logger, error, { tool: 'create_project', args })
|
|
125
|
+
|
|
126
|
+
if (error instanceof McpError) {
|
|
127
|
+
throw error
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
throw new McpError(
|
|
131
|
+
ErrorCode.InternalError,
|
|
132
|
+
`Failed to create project: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -1,144 +1,144 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detect Trends Handler
|
|
3
|
-
* Detects technology, pattern, and concern trends across decisions
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Logger } from 'pino'
|
|
7
|
-
import type { ToolResponse } from '@/tools/types'
|
|
8
|
-
import { getMemoryService } from '@/server/services'
|
|
9
|
-
import { ToolValidator } from '@/server/utils/validators'
|
|
10
|
-
import { ResponseFormatter } from '@/server/utils/response-formatter'
|
|
11
|
-
import { withMemoryIndicator, formatMemoryStats } from '@/server/utils/memory-indicator'
|
|
12
|
-
import { ErrorHandler } from '@/server/utils/error-handler'
|
|
13
|
-
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
|
14
|
-
import { DetectTrendsSchema } from './schemas'
|
|
15
|
-
import { TrendDetector } from '@/intelligence/temporal/trends'
|
|
16
|
-
|
|
17
|
-
export async function handleDetectTrends(
|
|
18
|
-
args: unknown,
|
|
19
|
-
logger: Logger
|
|
20
|
-
): Promise<ToolResponse> {
|
|
21
|
-
try {
|
|
22
|
-
const input = ToolValidator.validate(args, DetectTrendsSchema)
|
|
23
|
-
const { project_name, period_days, min_occurrences, limit } = input
|
|
24
|
-
|
|
25
|
-
const memory = getMemoryService()
|
|
26
|
-
|
|
27
|
-
if (!memory.isChromaDBEnabled()) {
|
|
28
|
-
// SQLite fallback: frequency-based trend analysis
|
|
29
|
-
const decisions = await memory.fetchAllDecisions(project_name)
|
|
30
|
-
|
|
31
|
-
if (decisions.length === 0) {
|
|
32
|
-
return ResponseFormatter.text('No decisions found for trend analysis.')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Filter by period
|
|
36
|
-
const cutoffDate = new Date()
|
|
37
|
-
cutoffDate.setDate(cutoffDate.getDate() - (period_days || 90))
|
|
38
|
-
const filtered = decisions.filter(d => new Date(d.date) >= cutoffDate)
|
|
39
|
-
|
|
40
|
-
if (filtered.length === 0) {
|
|
41
|
-
return ResponseFormatter.text(`No decisions found in the last ${period_days || 90} days.`)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Extract term frequencies from decision content
|
|
45
|
-
const stopWords = new Set(['the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'may', 'might', 'can', 'shall', 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'between', 'out', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'and', 'but', 'or', 'nor', 'not', 'so', 'yet', 'both', 'either', 'neither', 'each', 'every', 'all', 'any', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'only', 'own', 'same', 'than', 'too', 'very', 'just', 'because', 'if', 'when', 'where', 'how', 'what', 'which', 'who', 'whom', 'this', 'that', 'these', 'those', 'i', 'me', 'my', 'we', 'our', 'you', 'your', 'he', 'him', 'his', 'she', 'her', 'it', 'its', 'they', 'them', 'their', 'use', 'using', 'used', 'decision', 'context', 'reasoning'])
|
|
46
|
-
const termCounts: Record<string, { count: number; projects: Set<string> }> = {}
|
|
47
|
-
|
|
48
|
-
for (const d of filtered) {
|
|
49
|
-
const text = `${d.decision || ''} ${d.context || ''} ${d.reasoning || ''}`.toLowerCase()
|
|
50
|
-
const words = text.match(/\b[a-z][a-z-]{2,}\b/g) || []
|
|
51
|
-
const seen = new Set<string>()
|
|
52
|
-
for (const word of words) {
|
|
53
|
-
if (stopWords.has(word) || seen.has(word)) continue
|
|
54
|
-
seen.add(word)
|
|
55
|
-
if (!termCounts[word]) termCounts[word] = { count: 0, projects: new Set() }
|
|
56
|
-
termCounts[word].count++
|
|
57
|
-
if (d.project) termCounts[word].projects.add(d.project)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const minOcc = min_occurrences || 2
|
|
62
|
-
const trends = Object.entries(termCounts)
|
|
63
|
-
.filter(([, v]) => v.count >= minOcc)
|
|
64
|
-
.map(([term, v]) => ({ term, occurrences: v.count, projects: Array.from(v.projects) }))
|
|
65
|
-
.sort((a, b) => b.occurrences - a.occurrences)
|
|
66
|
-
.slice(0, limit || 20)
|
|
67
|
-
|
|
68
|
-
const parts: string[] = []
|
|
69
|
-
parts.push(`## Trend Analysis (SQLite)`)
|
|
70
|
-
if (project_name) parts.push(`**Project:** ${project_name}`)
|
|
71
|
-
parts.push(`**Period:** Last ${period_days || 90} days`)
|
|
72
|
-
parts.push(`**Decisions analyzed:** ${filtered.length}`)
|
|
73
|
-
parts.push('')
|
|
74
|
-
|
|
75
|
-
if (trends.length > 0) {
|
|
76
|
-
parts.push('### Top Terms by Frequency')
|
|
77
|
-
for (const t of trends) {
|
|
78
|
-
parts.push(`- **${t.term}** (${t.occurrences}x, projects: ${t.projects.join(', ')})`)
|
|
79
|
-
}
|
|
80
|
-
} else {
|
|
81
|
-
parts.push('No significant trends found with current filters.')
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
parts.push('\n*Note: SQLite mode provides frequency-based analysis. Enable ChromaDB for momentum and rising/declining classification.*')
|
|
85
|
-
|
|
86
|
-
const statsLine = formatMemoryStats({ recalled: filtered.length })
|
|
87
|
-
return ResponseFormatter.text(withMemoryIndicator(parts.join('\n'), filtered.length) + '\n\n' + statsLine)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const detector = new TrendDetector(logger, memory.chroma.collections)
|
|
91
|
-
|
|
92
|
-
const analysis = await detector.detectTrends({
|
|
93
|
-
project: project_name,
|
|
94
|
-
periodDays: period_days,
|
|
95
|
-
minOccurrences: min_occurrences,
|
|
96
|
-
limit
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
if (analysis.totalDecisionsAnalyzed === 0) {
|
|
100
|
-
return ResponseFormatter.text('No decisions found for trend analysis.')
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const parts: string[] = []
|
|
104
|
-
parts.push(`## Trend Analysis`)
|
|
105
|
-
if (project_name) parts.push(`**Project:** ${project_name}`)
|
|
106
|
-
parts.push(`**Period:** ${analysis.period}`)
|
|
107
|
-
parts.push(`**Decisions analyzed:** ${analysis.totalDecisionsAnalyzed}`)
|
|
108
|
-
parts.push('')
|
|
109
|
-
|
|
110
|
-
if (analysis.emergingTopics.length > 0) {
|
|
111
|
-
parts.push('### 📈 Emerging Topics')
|
|
112
|
-
for (const trend of analysis.emergingTopics.slice(0, 10)) {
|
|
113
|
-
const momentum = Math.round(trend.momentum * 100)
|
|
114
|
-
parts.push(`- **${trend.term}** (${trend.occurrences}x, +${momentum}% momentum, projects: ${trend.projects.join(', ')})`)
|
|
115
|
-
}
|
|
116
|
-
parts.push('')
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (analysis.decliningTopics.length > 0) {
|
|
120
|
-
parts.push('### 📉 Declining Topics')
|
|
121
|
-
for (const trend of analysis.decliningTopics.slice(0, 10)) {
|
|
122
|
-
const momentum = Math.round(trend.momentum * 100)
|
|
123
|
-
parts.push(`- **${trend.term}** (${trend.occurrences}x, ${momentum}% momentum)`)
|
|
124
|
-
}
|
|
125
|
-
parts.push('')
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (analysis.topTrends.length > 0) {
|
|
129
|
-
parts.push('### 🔥 Top Trends')
|
|
130
|
-
for (const trend of analysis.topTrends.slice(0, 10)) {
|
|
131
|
-
const icon = trend.trend === 'rising' ? '📈' : trend.trend === 'declining' ? '📉' : '➡️'
|
|
132
|
-
parts.push(`- ${icon} **${trend.term}** (${trend.occurrences}x, ${trend.trend})`)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const statsLine = formatMemoryStats({ recalled: analysis.totalDecisionsAnalyzed })
|
|
137
|
-
return ResponseFormatter.text(withMemoryIndicator(parts.join('\n'), analysis.totalDecisionsAnalyzed) + '\n\n' + statsLine)
|
|
138
|
-
|
|
139
|
-
} catch (error) {
|
|
140
|
-
ErrorHandler.logError(logger, error, { tool: 'detect_trends' })
|
|
141
|
-
if (error instanceof McpError) throw error
|
|
142
|
-
throw new McpError(ErrorCode.InternalError, `Trend detection failed: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Detect Trends Handler
|
|
3
|
+
* Detects technology, pattern, and concern trends across decisions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
import type { ToolResponse } from '@/tools/types'
|
|
8
|
+
import { getMemoryService } from '@/server/services'
|
|
9
|
+
import { ToolValidator } from '@/server/utils/validators'
|
|
10
|
+
import { ResponseFormatter } from '@/server/utils/response-formatter'
|
|
11
|
+
import { withMemoryIndicator, formatMemoryStats } from '@/server/utils/memory-indicator'
|
|
12
|
+
import { ErrorHandler } from '@/server/utils/error-handler'
|
|
13
|
+
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
|
14
|
+
import { DetectTrendsSchema } from './schemas'
|
|
15
|
+
import { TrendDetector } from '@/intelligence/temporal/trends'
|
|
16
|
+
|
|
17
|
+
export async function handleDetectTrends(
|
|
18
|
+
args: unknown,
|
|
19
|
+
logger: Logger
|
|
20
|
+
): Promise<ToolResponse> {
|
|
21
|
+
try {
|
|
22
|
+
const input = ToolValidator.validate(args, DetectTrendsSchema)
|
|
23
|
+
const { project_name, period_days, min_occurrences, limit } = input
|
|
24
|
+
|
|
25
|
+
const memory = getMemoryService()
|
|
26
|
+
|
|
27
|
+
if (!memory.isChromaDBEnabled()) {
|
|
28
|
+
// SQLite fallback: frequency-based trend analysis
|
|
29
|
+
const decisions = await memory.fetchAllDecisions(project_name)
|
|
30
|
+
|
|
31
|
+
if (decisions.length === 0) {
|
|
32
|
+
return ResponseFormatter.text('No decisions found for trend analysis.')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Filter by period
|
|
36
|
+
const cutoffDate = new Date()
|
|
37
|
+
cutoffDate.setDate(cutoffDate.getDate() - (period_days || 90))
|
|
38
|
+
const filtered = decisions.filter(d => new Date(d.date) >= cutoffDate)
|
|
39
|
+
|
|
40
|
+
if (filtered.length === 0) {
|
|
41
|
+
return ResponseFormatter.text(`No decisions found in the last ${period_days || 90} days.`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Extract term frequencies from decision content
|
|
45
|
+
const stopWords = new Set(['the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'may', 'might', 'can', 'shall', 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'between', 'out', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'and', 'but', 'or', 'nor', 'not', 'so', 'yet', 'both', 'either', 'neither', 'each', 'every', 'all', 'any', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'only', 'own', 'same', 'than', 'too', 'very', 'just', 'because', 'if', 'when', 'where', 'how', 'what', 'which', 'who', 'whom', 'this', 'that', 'these', 'those', 'i', 'me', 'my', 'we', 'our', 'you', 'your', 'he', 'him', 'his', 'she', 'her', 'it', 'its', 'they', 'them', 'their', 'use', 'using', 'used', 'decision', 'context', 'reasoning'])
|
|
46
|
+
const termCounts: Record<string, { count: number; projects: Set<string> }> = {}
|
|
47
|
+
|
|
48
|
+
for (const d of filtered) {
|
|
49
|
+
const text = `${d.decision || ''} ${d.context || ''} ${d.reasoning || ''}`.toLowerCase()
|
|
50
|
+
const words = text.match(/\b[a-z][a-z-]{2,}\b/g) || []
|
|
51
|
+
const seen = new Set<string>()
|
|
52
|
+
for (const word of words) {
|
|
53
|
+
if (stopWords.has(word) || seen.has(word)) continue
|
|
54
|
+
seen.add(word)
|
|
55
|
+
if (!termCounts[word]) termCounts[word] = { count: 0, projects: new Set() }
|
|
56
|
+
termCounts[word].count++
|
|
57
|
+
if (d.project) termCounts[word].projects.add(d.project)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const minOcc = min_occurrences || 2
|
|
62
|
+
const trends = Object.entries(termCounts)
|
|
63
|
+
.filter(([, v]) => v.count >= minOcc)
|
|
64
|
+
.map(([term, v]) => ({ term, occurrences: v.count, projects: Array.from(v.projects) }))
|
|
65
|
+
.sort((a, b) => b.occurrences - a.occurrences)
|
|
66
|
+
.slice(0, limit || 20)
|
|
67
|
+
|
|
68
|
+
const parts: string[] = []
|
|
69
|
+
parts.push(`## Trend Analysis (SQLite)`)
|
|
70
|
+
if (project_name) parts.push(`**Project:** ${project_name}`)
|
|
71
|
+
parts.push(`**Period:** Last ${period_days || 90} days`)
|
|
72
|
+
parts.push(`**Decisions analyzed:** ${filtered.length}`)
|
|
73
|
+
parts.push('')
|
|
74
|
+
|
|
75
|
+
if (trends.length > 0) {
|
|
76
|
+
parts.push('### Top Terms by Frequency')
|
|
77
|
+
for (const t of trends) {
|
|
78
|
+
parts.push(`- **${t.term}** (${t.occurrences}x, projects: ${t.projects.join(', ')})`)
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
parts.push('No significant trends found with current filters.')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
parts.push('\n*Note: SQLite mode provides frequency-based analysis. Enable ChromaDB for momentum and rising/declining classification.*')
|
|
85
|
+
|
|
86
|
+
const statsLine = formatMemoryStats({ recalled: filtered.length })
|
|
87
|
+
return ResponseFormatter.text(withMemoryIndicator(parts.join('\n'), filtered.length) + '\n\n' + statsLine)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const detector = new TrendDetector(logger, memory.chroma.collections)
|
|
91
|
+
|
|
92
|
+
const analysis = await detector.detectTrends({
|
|
93
|
+
project: project_name,
|
|
94
|
+
periodDays: period_days,
|
|
95
|
+
minOccurrences: min_occurrences,
|
|
96
|
+
limit
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
if (analysis.totalDecisionsAnalyzed === 0) {
|
|
100
|
+
return ResponseFormatter.text('No decisions found for trend analysis.')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const parts: string[] = []
|
|
104
|
+
parts.push(`## Trend Analysis`)
|
|
105
|
+
if (project_name) parts.push(`**Project:** ${project_name}`)
|
|
106
|
+
parts.push(`**Period:** ${analysis.period}`)
|
|
107
|
+
parts.push(`**Decisions analyzed:** ${analysis.totalDecisionsAnalyzed}`)
|
|
108
|
+
parts.push('')
|
|
109
|
+
|
|
110
|
+
if (analysis.emergingTopics.length > 0) {
|
|
111
|
+
parts.push('### 📈 Emerging Topics')
|
|
112
|
+
for (const trend of analysis.emergingTopics.slice(0, 10)) {
|
|
113
|
+
const momentum = Math.round(trend.momentum * 100)
|
|
114
|
+
parts.push(`- **${trend.term}** (${trend.occurrences}x, +${momentum}% momentum, projects: ${trend.projects.join(', ')})`)
|
|
115
|
+
}
|
|
116
|
+
parts.push('')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (analysis.decliningTopics.length > 0) {
|
|
120
|
+
parts.push('### 📉 Declining Topics')
|
|
121
|
+
for (const trend of analysis.decliningTopics.slice(0, 10)) {
|
|
122
|
+
const momentum = Math.round(trend.momentum * 100)
|
|
123
|
+
parts.push(`- **${trend.term}** (${trend.occurrences}x, ${momentum}% momentum)`)
|
|
124
|
+
}
|
|
125
|
+
parts.push('')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (analysis.topTrends.length > 0) {
|
|
129
|
+
parts.push('### 🔥 Top Trends')
|
|
130
|
+
for (const trend of analysis.topTrends.slice(0, 10)) {
|
|
131
|
+
const icon = trend.trend === 'rising' ? '📈' : trend.trend === 'declining' ? '📉' : '➡️'
|
|
132
|
+
parts.push(`- ${icon} **${trend.term}** (${trend.occurrences}x, ${trend.trend})`)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const statsLine = formatMemoryStats({ recalled: analysis.totalDecisionsAnalyzed })
|
|
137
|
+
return ResponseFormatter.text(withMemoryIndicator(parts.join('\n'), analysis.totalDecisionsAnalyzed) + '\n\n' + statsLine)
|
|
138
|
+
|
|
139
|
+
} catch (error) {
|
|
140
|
+
ErrorHandler.logError(logger, error, { tool: 'detect_trends' })
|
|
141
|
+
if (error instanceof McpError) throw error
|
|
142
|
+
throw new McpError(ErrorCode.InternalError, `Trend detection failed: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
143
|
+
}
|
|
144
|
+
}
|