claude-brain 0.15.2 → 0.16.0
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 +29 -11
- package/bunfig.toml +8 -8
- package/package.json +82 -82
- package/packs/backend/node.json +173 -173
- package/packs/core/javascript.json +176 -176
- package/packs/core/typescript.json +222 -222
- package/packs/frontend/react.json +254 -254
- package/packs/meta/testing.json +172 -172
- package/scripts/postinstall.mjs +341 -341
- 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 +209 -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/refresh.ts +323 -0
- package/src/cli/commands/serve.ts +167 -173
- package/src/cli/commands/start.ts +42 -42
- package/src/cli/commands/uninstall-mcp.ts +41 -41
- package/src/cli/commands/update.ts +124 -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 +128 -112
- package/src/hooks/capture.ts +168 -205
- 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 +194 -194
- package/src/hooks/passive-classifier.ts +404 -723
- package/src/hooks/queue.ts +129 -129
- package/src/hooks/session-tracker.ts +312 -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 +155 -155
- 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 +450 -436
- package/src/routing/response-filter.ts +261 -258
- package/src/routing/router.ts +1441 -1322
- package/src/routing/search-engine.ts +515 -475
- package/src/routing/types.ts +94 -94
- 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 -129
- 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
- package/src/cli/auto-update.ts +0 -157
package/src/vault/frontmatter.ts
CHANGED
|
@@ -1,264 +1,264 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Frontmatter Utilities
|
|
3
|
-
* Utilities for parsing and validating YAML frontmatter in markdown files
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Frontmatter, ProjectStatus, ProgressStatus } from './types'
|
|
7
|
-
|
|
8
|
-
export interface ValidationResult {
|
|
9
|
-
valid: boolean
|
|
10
|
-
missing: string[]
|
|
11
|
-
invalid: string[]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class FrontmatterUtils {
|
|
15
|
-
/**
|
|
16
|
-
* Validate required frontmatter fields
|
|
17
|
-
*/
|
|
18
|
-
static validateFrontmatter(
|
|
19
|
-
frontmatter: Frontmatter,
|
|
20
|
-
requiredFields: string[]
|
|
21
|
-
): ValidationResult {
|
|
22
|
-
const missing = requiredFields.filter(
|
|
23
|
-
field => !(field in frontmatter) || frontmatter[field] === undefined
|
|
24
|
-
)
|
|
25
|
-
return {
|
|
26
|
-
valid: missing.length === 0,
|
|
27
|
-
missing,
|
|
28
|
-
invalid: []
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Validate frontmatter against expected types
|
|
34
|
-
*/
|
|
35
|
-
static validateTypes(
|
|
36
|
-
frontmatter: Frontmatter,
|
|
37
|
-
schema: Record<string, 'string' | 'number' | 'boolean' | 'array' | 'date'>
|
|
38
|
-
): ValidationResult {
|
|
39
|
-
const invalid: string[] = []
|
|
40
|
-
|
|
41
|
-
for (const [field, expectedType] of Object.entries(schema)) {
|
|
42
|
-
const value = frontmatter[field]
|
|
43
|
-
if (value === undefined) continue
|
|
44
|
-
|
|
45
|
-
let isValid = true
|
|
46
|
-
switch (expectedType) {
|
|
47
|
-
case 'string':
|
|
48
|
-
isValid = typeof value === 'string'
|
|
49
|
-
break
|
|
50
|
-
case 'number':
|
|
51
|
-
isValid = typeof value === 'number'
|
|
52
|
-
break
|
|
53
|
-
case 'boolean':
|
|
54
|
-
isValid = typeof value === 'boolean'
|
|
55
|
-
break
|
|
56
|
-
case 'array':
|
|
57
|
-
isValid = Array.isArray(value)
|
|
58
|
-
break
|
|
59
|
-
case 'date':
|
|
60
|
-
isValid = typeof value === 'string' && !isNaN(Date.parse(value))
|
|
61
|
-
break
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!isValid) {
|
|
65
|
-
invalid.push(`${field}: expected ${expectedType}`)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
valid: invalid.length === 0,
|
|
71
|
-
missing: [],
|
|
72
|
-
invalid
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Extract project name from frontmatter
|
|
78
|
-
*/
|
|
79
|
-
static getProject(frontmatter: Frontmatter): string | undefined {
|
|
80
|
-
return frontmatter.project
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Extract status from frontmatter
|
|
85
|
-
*/
|
|
86
|
-
static getStatus(frontmatter: Frontmatter): string | undefined {
|
|
87
|
-
return frontmatter.status
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Check if status is a valid project status
|
|
92
|
-
*/
|
|
93
|
-
static isValidProjectStatus(status: string): status is ProjectStatus {
|
|
94
|
-
return ['active', 'planning', 'archived'].includes(status)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Check if status is a valid progress status
|
|
99
|
-
*/
|
|
100
|
-
static isValidProgressStatus(status: string): status is ProgressStatus {
|
|
101
|
-
return ['in-progress', 'blocked', 'completed'].includes(status)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Extract tags from frontmatter
|
|
106
|
-
*/
|
|
107
|
-
static getTags(frontmatter: Frontmatter): string[] {
|
|
108
|
-
const tags = frontmatter.tags
|
|
109
|
-
if (Array.isArray(tags)) {
|
|
110
|
-
return tags.filter(tag => typeof tag === 'string')
|
|
111
|
-
}
|
|
112
|
-
return []
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Extract tech stack from frontmatter
|
|
117
|
-
*/
|
|
118
|
-
static getTechStack(frontmatter: Frontmatter): string[] {
|
|
119
|
-
const techStack = frontmatter.tech_stack
|
|
120
|
-
if (Array.isArray(techStack)) {
|
|
121
|
-
return techStack.filter(tech => typeof tech === 'string')
|
|
122
|
-
}
|
|
123
|
-
return []
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Extract languages from frontmatter
|
|
128
|
-
*/
|
|
129
|
-
static getLanguages(frontmatter: Frontmatter): string[] {
|
|
130
|
-
const languages = frontmatter.languages
|
|
131
|
-
if (Array.isArray(languages)) {
|
|
132
|
-
return languages.filter(lang => typeof lang === 'string')
|
|
133
|
-
}
|
|
134
|
-
return []
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Extract frameworks from frontmatter
|
|
139
|
-
*/
|
|
140
|
-
static getFrameworks(frontmatter: Frontmatter): string[] {
|
|
141
|
-
const frameworks = frontmatter.frameworks
|
|
142
|
-
if (Array.isArray(frameworks)) {
|
|
143
|
-
return frameworks.filter(fw => typeof fw === 'string')
|
|
144
|
-
}
|
|
145
|
-
return []
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Extract date from frontmatter
|
|
150
|
-
*/
|
|
151
|
-
static getDate(
|
|
152
|
-
frontmatter: Frontmatter,
|
|
153
|
-
field: string = 'updated'
|
|
154
|
-
): Date | undefined {
|
|
155
|
-
const dateStr = frontmatter[field]
|
|
156
|
-
if (typeof dateStr === 'string') {
|
|
157
|
-
const date = new Date(dateStr)
|
|
158
|
-
return isNaN(date.getTime()) ? undefined : date
|
|
159
|
-
}
|
|
160
|
-
return undefined
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Get the document type from frontmatter
|
|
165
|
-
*/
|
|
166
|
-
static getType(frontmatter: Frontmatter): string | undefined {
|
|
167
|
-
return frontmatter.type
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Check if frontmatter indicates a specific document type
|
|
172
|
-
*/
|
|
173
|
-
static isType(frontmatter: Frontmatter, type: string): boolean {
|
|
174
|
-
return frontmatter.type === type
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Get completion percentage from progress frontmatter
|
|
179
|
-
*/
|
|
180
|
-
static getCompletionPercentage(frontmatter: Frontmatter): number {
|
|
181
|
-
const percentage = frontmatter.completion_percentage
|
|
182
|
-
if (typeof percentage === 'number' && percentage >= 0 && percentage <= 100) {
|
|
183
|
-
return percentage
|
|
184
|
-
}
|
|
185
|
-
return 0
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Get current phase from progress frontmatter
|
|
190
|
-
*/
|
|
191
|
-
static getCurrentPhase(frontmatter: Frontmatter): string | undefined {
|
|
192
|
-
return frontmatter.current_phase
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Get decision count from decisions frontmatter
|
|
197
|
-
*/
|
|
198
|
-
static getDecisionCount(frontmatter: Frontmatter): number {
|
|
199
|
-
const count = frontmatter.decision_count
|
|
200
|
-
return typeof count === 'number' && count >= 0 ? count : 0
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Create default frontmatter for a document type
|
|
205
|
-
*/
|
|
206
|
-
static createDefaultFrontmatter(
|
|
207
|
-
type: string,
|
|
208
|
-
projectName: string
|
|
209
|
-
): Frontmatter {
|
|
210
|
-
const today = new Date().toISOString().split('T')[0]
|
|
211
|
-
const base: Frontmatter = {
|
|
212
|
-
type,
|
|
213
|
-
project: projectName,
|
|
214
|
-
created: today,
|
|
215
|
-
updated: today
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
switch (type) {
|
|
219
|
-
case 'project-context':
|
|
220
|
-
return {
|
|
221
|
-
...base,
|
|
222
|
-
status: 'planning',
|
|
223
|
-
tech_stack: [],
|
|
224
|
-
tags: []
|
|
225
|
-
}
|
|
226
|
-
case 'decision-log':
|
|
227
|
-
return {
|
|
228
|
-
...base,
|
|
229
|
-
decision_count: 0,
|
|
230
|
-
last_updated: today
|
|
231
|
-
}
|
|
232
|
-
case 'progress-tracker':
|
|
233
|
-
return {
|
|
234
|
-
...base,
|
|
235
|
-
status: 'in-progress',
|
|
236
|
-
current_phase: 'setup',
|
|
237
|
-
completion_percentage: 0,
|
|
238
|
-
last_updated: today
|
|
239
|
-
}
|
|
240
|
-
case 'coding-standards':
|
|
241
|
-
return {
|
|
242
|
-
...base,
|
|
243
|
-
languages: [],
|
|
244
|
-
frameworks: [],
|
|
245
|
-
last_updated: today
|
|
246
|
-
}
|
|
247
|
-
default:
|
|
248
|
-
return base
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Merge frontmatter with updates, preserving existing fields
|
|
254
|
-
*/
|
|
255
|
-
static mergeFrontmatter(
|
|
256
|
-
existing: Frontmatter,
|
|
257
|
-
updates: Partial<Frontmatter>
|
|
258
|
-
): Frontmatter {
|
|
259
|
-
const merged = { ...existing, ...updates }
|
|
260
|
-
// Always update the 'updated' timestamp
|
|
261
|
-
merged.updated = new Date().toISOString().split('T')[0]
|
|
262
|
-
return merged
|
|
263
|
-
}
|
|
264
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Frontmatter Utilities
|
|
3
|
+
* Utilities for parsing and validating YAML frontmatter in markdown files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Frontmatter, ProjectStatus, ProgressStatus } from './types'
|
|
7
|
+
|
|
8
|
+
export interface ValidationResult {
|
|
9
|
+
valid: boolean
|
|
10
|
+
missing: string[]
|
|
11
|
+
invalid: string[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class FrontmatterUtils {
|
|
15
|
+
/**
|
|
16
|
+
* Validate required frontmatter fields
|
|
17
|
+
*/
|
|
18
|
+
static validateFrontmatter(
|
|
19
|
+
frontmatter: Frontmatter,
|
|
20
|
+
requiredFields: string[]
|
|
21
|
+
): ValidationResult {
|
|
22
|
+
const missing = requiredFields.filter(
|
|
23
|
+
field => !(field in frontmatter) || frontmatter[field] === undefined
|
|
24
|
+
)
|
|
25
|
+
return {
|
|
26
|
+
valid: missing.length === 0,
|
|
27
|
+
missing,
|
|
28
|
+
invalid: []
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Validate frontmatter against expected types
|
|
34
|
+
*/
|
|
35
|
+
static validateTypes(
|
|
36
|
+
frontmatter: Frontmatter,
|
|
37
|
+
schema: Record<string, 'string' | 'number' | 'boolean' | 'array' | 'date'>
|
|
38
|
+
): ValidationResult {
|
|
39
|
+
const invalid: string[] = []
|
|
40
|
+
|
|
41
|
+
for (const [field, expectedType] of Object.entries(schema)) {
|
|
42
|
+
const value = frontmatter[field]
|
|
43
|
+
if (value === undefined) continue
|
|
44
|
+
|
|
45
|
+
let isValid = true
|
|
46
|
+
switch (expectedType) {
|
|
47
|
+
case 'string':
|
|
48
|
+
isValid = typeof value === 'string'
|
|
49
|
+
break
|
|
50
|
+
case 'number':
|
|
51
|
+
isValid = typeof value === 'number'
|
|
52
|
+
break
|
|
53
|
+
case 'boolean':
|
|
54
|
+
isValid = typeof value === 'boolean'
|
|
55
|
+
break
|
|
56
|
+
case 'array':
|
|
57
|
+
isValid = Array.isArray(value)
|
|
58
|
+
break
|
|
59
|
+
case 'date':
|
|
60
|
+
isValid = typeof value === 'string' && !isNaN(Date.parse(value))
|
|
61
|
+
break
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!isValid) {
|
|
65
|
+
invalid.push(`${field}: expected ${expectedType}`)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
valid: invalid.length === 0,
|
|
71
|
+
missing: [],
|
|
72
|
+
invalid
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Extract project name from frontmatter
|
|
78
|
+
*/
|
|
79
|
+
static getProject(frontmatter: Frontmatter): string | undefined {
|
|
80
|
+
return frontmatter.project
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Extract status from frontmatter
|
|
85
|
+
*/
|
|
86
|
+
static getStatus(frontmatter: Frontmatter): string | undefined {
|
|
87
|
+
return frontmatter.status
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check if status is a valid project status
|
|
92
|
+
*/
|
|
93
|
+
static isValidProjectStatus(status: string): status is ProjectStatus {
|
|
94
|
+
return ['active', 'planning', 'archived'].includes(status)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if status is a valid progress status
|
|
99
|
+
*/
|
|
100
|
+
static isValidProgressStatus(status: string): status is ProgressStatus {
|
|
101
|
+
return ['in-progress', 'blocked', 'completed'].includes(status)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Extract tags from frontmatter
|
|
106
|
+
*/
|
|
107
|
+
static getTags(frontmatter: Frontmatter): string[] {
|
|
108
|
+
const tags = frontmatter.tags
|
|
109
|
+
if (Array.isArray(tags)) {
|
|
110
|
+
return tags.filter(tag => typeof tag === 'string')
|
|
111
|
+
}
|
|
112
|
+
return []
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Extract tech stack from frontmatter
|
|
117
|
+
*/
|
|
118
|
+
static getTechStack(frontmatter: Frontmatter): string[] {
|
|
119
|
+
const techStack = frontmatter.tech_stack
|
|
120
|
+
if (Array.isArray(techStack)) {
|
|
121
|
+
return techStack.filter(tech => typeof tech === 'string')
|
|
122
|
+
}
|
|
123
|
+
return []
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Extract languages from frontmatter
|
|
128
|
+
*/
|
|
129
|
+
static getLanguages(frontmatter: Frontmatter): string[] {
|
|
130
|
+
const languages = frontmatter.languages
|
|
131
|
+
if (Array.isArray(languages)) {
|
|
132
|
+
return languages.filter(lang => typeof lang === 'string')
|
|
133
|
+
}
|
|
134
|
+
return []
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Extract frameworks from frontmatter
|
|
139
|
+
*/
|
|
140
|
+
static getFrameworks(frontmatter: Frontmatter): string[] {
|
|
141
|
+
const frameworks = frontmatter.frameworks
|
|
142
|
+
if (Array.isArray(frameworks)) {
|
|
143
|
+
return frameworks.filter(fw => typeof fw === 'string')
|
|
144
|
+
}
|
|
145
|
+
return []
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Extract date from frontmatter
|
|
150
|
+
*/
|
|
151
|
+
static getDate(
|
|
152
|
+
frontmatter: Frontmatter,
|
|
153
|
+
field: string = 'updated'
|
|
154
|
+
): Date | undefined {
|
|
155
|
+
const dateStr = frontmatter[field]
|
|
156
|
+
if (typeof dateStr === 'string') {
|
|
157
|
+
const date = new Date(dateStr)
|
|
158
|
+
return isNaN(date.getTime()) ? undefined : date
|
|
159
|
+
}
|
|
160
|
+
return undefined
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get the document type from frontmatter
|
|
165
|
+
*/
|
|
166
|
+
static getType(frontmatter: Frontmatter): string | undefined {
|
|
167
|
+
return frontmatter.type
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check if frontmatter indicates a specific document type
|
|
172
|
+
*/
|
|
173
|
+
static isType(frontmatter: Frontmatter, type: string): boolean {
|
|
174
|
+
return frontmatter.type === type
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get completion percentage from progress frontmatter
|
|
179
|
+
*/
|
|
180
|
+
static getCompletionPercentage(frontmatter: Frontmatter): number {
|
|
181
|
+
const percentage = frontmatter.completion_percentage
|
|
182
|
+
if (typeof percentage === 'number' && percentage >= 0 && percentage <= 100) {
|
|
183
|
+
return percentage
|
|
184
|
+
}
|
|
185
|
+
return 0
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get current phase from progress frontmatter
|
|
190
|
+
*/
|
|
191
|
+
static getCurrentPhase(frontmatter: Frontmatter): string | undefined {
|
|
192
|
+
return frontmatter.current_phase
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get decision count from decisions frontmatter
|
|
197
|
+
*/
|
|
198
|
+
static getDecisionCount(frontmatter: Frontmatter): number {
|
|
199
|
+
const count = frontmatter.decision_count
|
|
200
|
+
return typeof count === 'number' && count >= 0 ? count : 0
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Create default frontmatter for a document type
|
|
205
|
+
*/
|
|
206
|
+
static createDefaultFrontmatter(
|
|
207
|
+
type: string,
|
|
208
|
+
projectName: string
|
|
209
|
+
): Frontmatter {
|
|
210
|
+
const today = new Date().toISOString().split('T')[0]
|
|
211
|
+
const base: Frontmatter = {
|
|
212
|
+
type,
|
|
213
|
+
project: projectName,
|
|
214
|
+
created: today,
|
|
215
|
+
updated: today
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
switch (type) {
|
|
219
|
+
case 'project-context':
|
|
220
|
+
return {
|
|
221
|
+
...base,
|
|
222
|
+
status: 'planning',
|
|
223
|
+
tech_stack: [],
|
|
224
|
+
tags: []
|
|
225
|
+
}
|
|
226
|
+
case 'decision-log':
|
|
227
|
+
return {
|
|
228
|
+
...base,
|
|
229
|
+
decision_count: 0,
|
|
230
|
+
last_updated: today
|
|
231
|
+
}
|
|
232
|
+
case 'progress-tracker':
|
|
233
|
+
return {
|
|
234
|
+
...base,
|
|
235
|
+
status: 'in-progress',
|
|
236
|
+
current_phase: 'setup',
|
|
237
|
+
completion_percentage: 0,
|
|
238
|
+
last_updated: today
|
|
239
|
+
}
|
|
240
|
+
case 'coding-standards':
|
|
241
|
+
return {
|
|
242
|
+
...base,
|
|
243
|
+
languages: [],
|
|
244
|
+
frameworks: [],
|
|
245
|
+
last_updated: today
|
|
246
|
+
}
|
|
247
|
+
default:
|
|
248
|
+
return base
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Merge frontmatter with updates, preserving existing fields
|
|
254
|
+
*/
|
|
255
|
+
static mergeFrontmatter(
|
|
256
|
+
existing: Frontmatter,
|
|
257
|
+
updates: Partial<Frontmatter>
|
|
258
|
+
): Frontmatter {
|
|
259
|
+
const merged = { ...existing, ...updates }
|
|
260
|
+
// Always update the 'updated' timestamp
|
|
261
|
+
merged.updated = new Date().toISOString().split('T')[0]
|
|
262
|
+
return merged
|
|
263
|
+
}
|
|
264
|
+
}
|