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,284 +1,284 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TaskParser - Parse tasks from Obsidian markdown files
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { Frontmatter, MarkdownFile } from '@/vault/types'
|
|
6
|
-
|
|
7
|
-
export type TaskStatus = 'todo' | 'in-progress' | 'done' | 'blocked'
|
|
8
|
-
export type TaskPriority = 'low' | 'medium' | 'high'
|
|
9
|
-
|
|
10
|
-
export interface Task {
|
|
11
|
-
id: string
|
|
12
|
-
title: string
|
|
13
|
-
description?: string
|
|
14
|
-
status: TaskStatus
|
|
15
|
-
priority?: TaskPriority
|
|
16
|
-
dependencies?: string[]
|
|
17
|
-
assignee?: string
|
|
18
|
-
dueDate?: Date
|
|
19
|
-
project: string
|
|
20
|
-
createdAt: Date
|
|
21
|
-
updatedAt: Date
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class TaskParser {
|
|
25
|
-
/**
|
|
26
|
-
* Extract tasks from markdown file
|
|
27
|
-
*/
|
|
28
|
-
static extractFromFile(file: MarkdownFile): Task[] {
|
|
29
|
-
const tasks: Task[] = []
|
|
30
|
-
const project = (file.frontmatter.project as string) || 'unknown'
|
|
31
|
-
|
|
32
|
-
// Try to extract from frontmatter first
|
|
33
|
-
if (file.frontmatter.tasks) {
|
|
34
|
-
const frontmatterTasks = this.extractFromFrontmatter(
|
|
35
|
-
file.frontmatter,
|
|
36
|
-
project
|
|
37
|
-
)
|
|
38
|
-
tasks.push(...frontmatterTasks)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Extract from content
|
|
42
|
-
const contentTasks = this.extractFromContent(file.content, project)
|
|
43
|
-
tasks.push(...contentTasks)
|
|
44
|
-
|
|
45
|
-
return tasks
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Extract tasks from frontmatter
|
|
50
|
-
*/
|
|
51
|
-
private static extractFromFrontmatter(
|
|
52
|
-
frontmatter: Frontmatter,
|
|
53
|
-
project: string
|
|
54
|
-
): Task[] {
|
|
55
|
-
const tasksData = frontmatter.tasks
|
|
56
|
-
if (!Array.isArray(tasksData)) {
|
|
57
|
-
return []
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return tasksData.map((task: Record<string, unknown>) => ({
|
|
61
|
-
id: (task.id as string) || this.generateTaskId(task.title as string),
|
|
62
|
-
title: task.title as string,
|
|
63
|
-
description: task.description as string | undefined,
|
|
64
|
-
status: this.normalizeStatus((task.status as string) || 'todo'),
|
|
65
|
-
priority: task.priority as TaskPriority | undefined,
|
|
66
|
-
dependencies: (task.dependencies as string[]) || [],
|
|
67
|
-
assignee: task.assignee as string | undefined,
|
|
68
|
-
dueDate: task.dueDate ? new Date(task.dueDate as string) : undefined,
|
|
69
|
-
project,
|
|
70
|
-
createdAt: task.createdAt ? new Date(task.createdAt as string) : new Date(),
|
|
71
|
-
updatedAt: task.updatedAt ? new Date(task.updatedAt as string) : new Date()
|
|
72
|
-
}))
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Extract tasks from markdown content
|
|
77
|
-
*/
|
|
78
|
-
private static extractFromContent(content: string, project: string): Task[] {
|
|
79
|
-
const tasks: Task[] = []
|
|
80
|
-
|
|
81
|
-
// Match task patterns:
|
|
82
|
-
// - [ ] Task title
|
|
83
|
-
// - [x] Completed task
|
|
84
|
-
// - [>] In progress task
|
|
85
|
-
// - [!] Blocked task
|
|
86
|
-
const taskRegex = /^[-*]\s+\[([ xX>!])\]\s+(.+)$/gm
|
|
87
|
-
|
|
88
|
-
let match
|
|
89
|
-
while ((match = taskRegex.exec(content)) !== null) {
|
|
90
|
-
const statusChar = match[1] ?? ' '
|
|
91
|
-
const fullLine = match[2]?.trim() ?? ''
|
|
92
|
-
if (!fullLine) continue
|
|
93
|
-
|
|
94
|
-
// Determine status from checkbox
|
|
95
|
-
let status: TaskStatus
|
|
96
|
-
if (statusChar === 'x' || statusChar === 'X') {
|
|
97
|
-
status = 'done'
|
|
98
|
-
} else if (statusChar === '>') {
|
|
99
|
-
status = 'in-progress'
|
|
100
|
-
} else if (statusChar === '!') {
|
|
101
|
-
status = 'blocked'
|
|
102
|
-
} else {
|
|
103
|
-
status = 'todo'
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Parse the line for additional metadata
|
|
107
|
-
const { title, priority, dueDate } = this.parseTaskLine(fullLine)
|
|
108
|
-
|
|
109
|
-
tasks.push({
|
|
110
|
-
id: this.generateTaskId(title),
|
|
111
|
-
title,
|
|
112
|
-
status,
|
|
113
|
-
priority,
|
|
114
|
-
dueDate,
|
|
115
|
-
project,
|
|
116
|
-
createdAt: new Date(),
|
|
117
|
-
updatedAt: new Date()
|
|
118
|
-
})
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return tasks
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Parse a task line for title and metadata
|
|
126
|
-
*/
|
|
127
|
-
private static parseTaskLine(line: string): {
|
|
128
|
-
title: string
|
|
129
|
-
priority?: TaskPriority
|
|
130
|
-
dueDate?: Date
|
|
131
|
-
} {
|
|
132
|
-
let title = line
|
|
133
|
-
|
|
134
|
-
// Extract priority from tags like #high, #medium, #low
|
|
135
|
-
const priorityMatch = /#(high|medium|low)/i.exec(line)
|
|
136
|
-
const priority = priorityMatch?.[1]
|
|
137
|
-
? (priorityMatch[1].toLowerCase() as TaskPriority)
|
|
138
|
-
: undefined
|
|
139
|
-
|
|
140
|
-
// Remove priority tag from title
|
|
141
|
-
if (priorityMatch) {
|
|
142
|
-
title = title.replace(priorityMatch[0], '').trim()
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Extract due date from patterns like [due: 2024-01-15] or @due(2024-01-15)
|
|
146
|
-
const dueDateMatch = /\[due:\s*(\d{4}-\d{2}-\d{2})\]|@due\((\d{4}-\d{2}-\d{2})\)/i.exec(line)
|
|
147
|
-
const dateStr = dueDateMatch?.[1] ?? dueDateMatch?.[2]
|
|
148
|
-
const dueDate = dateStr ? new Date(dateStr) : undefined
|
|
149
|
-
|
|
150
|
-
// Remove due date from title
|
|
151
|
-
if (dueDateMatch) {
|
|
152
|
-
title = title.replace(dueDateMatch[0], '').trim()
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return { title, priority, dueDate }
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Normalize status string to valid status
|
|
160
|
-
*/
|
|
161
|
-
private static normalizeStatus(status: string): TaskStatus {
|
|
162
|
-
const normalized = status.toLowerCase()
|
|
163
|
-
|
|
164
|
-
if (normalized.includes('done') || normalized.includes('complete')) {
|
|
165
|
-
return 'done'
|
|
166
|
-
} else if (normalized.includes('progress') || normalized.includes('doing')) {
|
|
167
|
-
return 'in-progress'
|
|
168
|
-
} else if (normalized.includes('block')) {
|
|
169
|
-
return 'blocked'
|
|
170
|
-
} else {
|
|
171
|
-
return 'todo'
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Generate task ID from title
|
|
177
|
-
*/
|
|
178
|
-
private static generateTaskId(title: string): string {
|
|
179
|
-
return title
|
|
180
|
-
.toLowerCase()
|
|
181
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
182
|
-
.replace(/^-+|-+$/g, '')
|
|
183
|
-
.slice(0, 50)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Validate task object
|
|
188
|
-
*/
|
|
189
|
-
static validateTask(task: unknown): task is Task {
|
|
190
|
-
if (!task || typeof task !== 'object') {
|
|
191
|
-
return false
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const t = task as Record<string, unknown>
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
typeof t.id === 'string' &&
|
|
198
|
-
typeof t.title === 'string' &&
|
|
199
|
-
['todo', 'in-progress', 'done', 'blocked'].includes(t.status as string) &&
|
|
200
|
-
typeof t.project === 'string'
|
|
201
|
-
)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Format task as markdown checkbox
|
|
206
|
-
*/
|
|
207
|
-
static formatAsMarkdown(task: Task): string {
|
|
208
|
-
let checkbox: string
|
|
209
|
-
switch (task.status) {
|
|
210
|
-
case 'done':
|
|
211
|
-
checkbox = '[x]'
|
|
212
|
-
break
|
|
213
|
-
case 'in-progress':
|
|
214
|
-
checkbox = '[>]'
|
|
215
|
-
break
|
|
216
|
-
case 'blocked':
|
|
217
|
-
checkbox = '[!]'
|
|
218
|
-
break
|
|
219
|
-
default:
|
|
220
|
-
checkbox = '[ ]'
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
let line = `- ${checkbox} ${task.title}`
|
|
224
|
-
|
|
225
|
-
if (task.priority) {
|
|
226
|
-
line += ` #${task.priority}`
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (task.dueDate) {
|
|
230
|
-
line += ` [due: ${task.dueDate.toISOString().split('T')[0]}]`
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return line
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Get tasks by status
|
|
238
|
-
*/
|
|
239
|
-
static filterByStatus(tasks: Task[], status: TaskStatus): Task[] {
|
|
240
|
-
return tasks.filter(t => t.status === status)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Get overdue tasks
|
|
245
|
-
*/
|
|
246
|
-
static getOverdueTasks(tasks: Task[]): Task[] {
|
|
247
|
-
const now = new Date()
|
|
248
|
-
return tasks.filter(
|
|
249
|
-
t => t.dueDate && t.dueDate < now && t.status !== 'done'
|
|
250
|
-
)
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Sort tasks by priority then due date
|
|
255
|
-
*/
|
|
256
|
-
static sortByPriority(tasks: Task[]): Task[] {
|
|
257
|
-
const priorityOrder: Record<TaskPriority, number> = {
|
|
258
|
-
high: 0,
|
|
259
|
-
medium: 1,
|
|
260
|
-
low: 2
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return [...tasks].sort((a, b) => {
|
|
264
|
-
// Sort by priority first
|
|
265
|
-
const aPriority = a.priority ? priorityOrder[a.priority] : 3
|
|
266
|
-
const bPriority = b.priority ? priorityOrder[b.priority] : 3
|
|
267
|
-
|
|
268
|
-
if (aPriority !== bPriority) {
|
|
269
|
-
return aPriority - bPriority
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Then by due date
|
|
273
|
-
if (a.dueDate && b.dueDate) {
|
|
274
|
-
return a.dueDate.getTime() - b.dueDate.getTime()
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Tasks with due dates come first
|
|
278
|
-
if (a.dueDate) return -1
|
|
279
|
-
if (b.dueDate) return 1
|
|
280
|
-
|
|
281
|
-
return 0
|
|
282
|
-
})
|
|
283
|
-
}
|
|
284
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* TaskParser - Parse tasks from Obsidian markdown files
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Frontmatter, MarkdownFile } from '@/vault/types'
|
|
6
|
+
|
|
7
|
+
export type TaskStatus = 'todo' | 'in-progress' | 'done' | 'blocked'
|
|
8
|
+
export type TaskPriority = 'low' | 'medium' | 'high'
|
|
9
|
+
|
|
10
|
+
export interface Task {
|
|
11
|
+
id: string
|
|
12
|
+
title: string
|
|
13
|
+
description?: string
|
|
14
|
+
status: TaskStatus
|
|
15
|
+
priority?: TaskPriority
|
|
16
|
+
dependencies?: string[]
|
|
17
|
+
assignee?: string
|
|
18
|
+
dueDate?: Date
|
|
19
|
+
project: string
|
|
20
|
+
createdAt: Date
|
|
21
|
+
updatedAt: Date
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class TaskParser {
|
|
25
|
+
/**
|
|
26
|
+
* Extract tasks from markdown file
|
|
27
|
+
*/
|
|
28
|
+
static extractFromFile(file: MarkdownFile): Task[] {
|
|
29
|
+
const tasks: Task[] = []
|
|
30
|
+
const project = (file.frontmatter.project as string) || 'unknown'
|
|
31
|
+
|
|
32
|
+
// Try to extract from frontmatter first
|
|
33
|
+
if (file.frontmatter.tasks) {
|
|
34
|
+
const frontmatterTasks = this.extractFromFrontmatter(
|
|
35
|
+
file.frontmatter,
|
|
36
|
+
project
|
|
37
|
+
)
|
|
38
|
+
tasks.push(...frontmatterTasks)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Extract from content
|
|
42
|
+
const contentTasks = this.extractFromContent(file.content, project)
|
|
43
|
+
tasks.push(...contentTasks)
|
|
44
|
+
|
|
45
|
+
return tasks
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Extract tasks from frontmatter
|
|
50
|
+
*/
|
|
51
|
+
private static extractFromFrontmatter(
|
|
52
|
+
frontmatter: Frontmatter,
|
|
53
|
+
project: string
|
|
54
|
+
): Task[] {
|
|
55
|
+
const tasksData = frontmatter.tasks
|
|
56
|
+
if (!Array.isArray(tasksData)) {
|
|
57
|
+
return []
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return tasksData.map((task: Record<string, unknown>) => ({
|
|
61
|
+
id: (task.id as string) || this.generateTaskId(task.title as string),
|
|
62
|
+
title: task.title as string,
|
|
63
|
+
description: task.description as string | undefined,
|
|
64
|
+
status: this.normalizeStatus((task.status as string) || 'todo'),
|
|
65
|
+
priority: task.priority as TaskPriority | undefined,
|
|
66
|
+
dependencies: (task.dependencies as string[]) || [],
|
|
67
|
+
assignee: task.assignee as string | undefined,
|
|
68
|
+
dueDate: task.dueDate ? new Date(task.dueDate as string) : undefined,
|
|
69
|
+
project,
|
|
70
|
+
createdAt: task.createdAt ? new Date(task.createdAt as string) : new Date(),
|
|
71
|
+
updatedAt: task.updatedAt ? new Date(task.updatedAt as string) : new Date()
|
|
72
|
+
}))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Extract tasks from markdown content
|
|
77
|
+
*/
|
|
78
|
+
private static extractFromContent(content: string, project: string): Task[] {
|
|
79
|
+
const tasks: Task[] = []
|
|
80
|
+
|
|
81
|
+
// Match task patterns:
|
|
82
|
+
// - [ ] Task title
|
|
83
|
+
// - [x] Completed task
|
|
84
|
+
// - [>] In progress task
|
|
85
|
+
// - [!] Blocked task
|
|
86
|
+
const taskRegex = /^[-*]\s+\[([ xX>!])\]\s+(.+)$/gm
|
|
87
|
+
|
|
88
|
+
let match
|
|
89
|
+
while ((match = taskRegex.exec(content)) !== null) {
|
|
90
|
+
const statusChar = match[1] ?? ' '
|
|
91
|
+
const fullLine = match[2]?.trim() ?? ''
|
|
92
|
+
if (!fullLine) continue
|
|
93
|
+
|
|
94
|
+
// Determine status from checkbox
|
|
95
|
+
let status: TaskStatus
|
|
96
|
+
if (statusChar === 'x' || statusChar === 'X') {
|
|
97
|
+
status = 'done'
|
|
98
|
+
} else if (statusChar === '>') {
|
|
99
|
+
status = 'in-progress'
|
|
100
|
+
} else if (statusChar === '!') {
|
|
101
|
+
status = 'blocked'
|
|
102
|
+
} else {
|
|
103
|
+
status = 'todo'
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Parse the line for additional metadata
|
|
107
|
+
const { title, priority, dueDate } = this.parseTaskLine(fullLine)
|
|
108
|
+
|
|
109
|
+
tasks.push({
|
|
110
|
+
id: this.generateTaskId(title),
|
|
111
|
+
title,
|
|
112
|
+
status,
|
|
113
|
+
priority,
|
|
114
|
+
dueDate,
|
|
115
|
+
project,
|
|
116
|
+
createdAt: new Date(),
|
|
117
|
+
updatedAt: new Date()
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return tasks
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Parse a task line for title and metadata
|
|
126
|
+
*/
|
|
127
|
+
private static parseTaskLine(line: string): {
|
|
128
|
+
title: string
|
|
129
|
+
priority?: TaskPriority
|
|
130
|
+
dueDate?: Date
|
|
131
|
+
} {
|
|
132
|
+
let title = line
|
|
133
|
+
|
|
134
|
+
// Extract priority from tags like #high, #medium, #low
|
|
135
|
+
const priorityMatch = /#(high|medium|low)/i.exec(line)
|
|
136
|
+
const priority = priorityMatch?.[1]
|
|
137
|
+
? (priorityMatch[1].toLowerCase() as TaskPriority)
|
|
138
|
+
: undefined
|
|
139
|
+
|
|
140
|
+
// Remove priority tag from title
|
|
141
|
+
if (priorityMatch) {
|
|
142
|
+
title = title.replace(priorityMatch[0], '').trim()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Extract due date from patterns like [due: 2024-01-15] or @due(2024-01-15)
|
|
146
|
+
const dueDateMatch = /\[due:\s*(\d{4}-\d{2}-\d{2})\]|@due\((\d{4}-\d{2}-\d{2})\)/i.exec(line)
|
|
147
|
+
const dateStr = dueDateMatch?.[1] ?? dueDateMatch?.[2]
|
|
148
|
+
const dueDate = dateStr ? new Date(dateStr) : undefined
|
|
149
|
+
|
|
150
|
+
// Remove due date from title
|
|
151
|
+
if (dueDateMatch) {
|
|
152
|
+
title = title.replace(dueDateMatch[0], '').trim()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { title, priority, dueDate }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Normalize status string to valid status
|
|
160
|
+
*/
|
|
161
|
+
private static normalizeStatus(status: string): TaskStatus {
|
|
162
|
+
const normalized = status.toLowerCase()
|
|
163
|
+
|
|
164
|
+
if (normalized.includes('done') || normalized.includes('complete')) {
|
|
165
|
+
return 'done'
|
|
166
|
+
} else if (normalized.includes('progress') || normalized.includes('doing')) {
|
|
167
|
+
return 'in-progress'
|
|
168
|
+
} else if (normalized.includes('block')) {
|
|
169
|
+
return 'blocked'
|
|
170
|
+
} else {
|
|
171
|
+
return 'todo'
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Generate task ID from title
|
|
177
|
+
*/
|
|
178
|
+
private static generateTaskId(title: string): string {
|
|
179
|
+
return title
|
|
180
|
+
.toLowerCase()
|
|
181
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
182
|
+
.replace(/^-+|-+$/g, '')
|
|
183
|
+
.slice(0, 50)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Validate task object
|
|
188
|
+
*/
|
|
189
|
+
static validateTask(task: unknown): task is Task {
|
|
190
|
+
if (!task || typeof task !== 'object') {
|
|
191
|
+
return false
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const t = task as Record<string, unknown>
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
typeof t.id === 'string' &&
|
|
198
|
+
typeof t.title === 'string' &&
|
|
199
|
+
['todo', 'in-progress', 'done', 'blocked'].includes(t.status as string) &&
|
|
200
|
+
typeof t.project === 'string'
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Format task as markdown checkbox
|
|
206
|
+
*/
|
|
207
|
+
static formatAsMarkdown(task: Task): string {
|
|
208
|
+
let checkbox: string
|
|
209
|
+
switch (task.status) {
|
|
210
|
+
case 'done':
|
|
211
|
+
checkbox = '[x]'
|
|
212
|
+
break
|
|
213
|
+
case 'in-progress':
|
|
214
|
+
checkbox = '[>]'
|
|
215
|
+
break
|
|
216
|
+
case 'blocked':
|
|
217
|
+
checkbox = '[!]'
|
|
218
|
+
break
|
|
219
|
+
default:
|
|
220
|
+
checkbox = '[ ]'
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let line = `- ${checkbox} ${task.title}`
|
|
224
|
+
|
|
225
|
+
if (task.priority) {
|
|
226
|
+
line += ` #${task.priority}`
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (task.dueDate) {
|
|
230
|
+
line += ` [due: ${task.dueDate.toISOString().split('T')[0]}]`
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return line
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get tasks by status
|
|
238
|
+
*/
|
|
239
|
+
static filterByStatus(tasks: Task[], status: TaskStatus): Task[] {
|
|
240
|
+
return tasks.filter(t => t.status === status)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get overdue tasks
|
|
245
|
+
*/
|
|
246
|
+
static getOverdueTasks(tasks: Task[]): Task[] {
|
|
247
|
+
const now = new Date()
|
|
248
|
+
return tasks.filter(
|
|
249
|
+
t => t.dueDate && t.dueDate < now && t.status !== 'done'
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Sort tasks by priority then due date
|
|
255
|
+
*/
|
|
256
|
+
static sortByPriority(tasks: Task[]): Task[] {
|
|
257
|
+
const priorityOrder: Record<TaskPriority, number> = {
|
|
258
|
+
high: 0,
|
|
259
|
+
medium: 1,
|
|
260
|
+
low: 2
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return [...tasks].sort((a, b) => {
|
|
264
|
+
// Sort by priority first
|
|
265
|
+
const aPriority = a.priority ? priorityOrder[a.priority] : 3
|
|
266
|
+
const bPriority = b.priority ? priorityOrder[b.priority] : 3
|
|
267
|
+
|
|
268
|
+
if (aPriority !== bPriority) {
|
|
269
|
+
return aPriority - bPriority
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Then by due date
|
|
273
|
+
if (a.dueDate && b.dueDate) {
|
|
274
|
+
return a.dueDate.getTime() - b.dueDate.getTime()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Tasks with due dates come first
|
|
278
|
+
if (a.dueDate) return -1
|
|
279
|
+
if (b.dueDate) return 1
|
|
280
|
+
|
|
281
|
+
return 0
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
}
|