mcp-adr-analysis-server 2.0.10 → 2.0.11

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.
Files changed (69) hide show
  1. package/README.md +1 -1
  2. package/dist/src/index.d.ts +1 -2
  3. package/dist/src/index.d.ts.map +1 -1
  4. package/dist/src/index.js +260 -464
  5. package/dist/src/index.js.map +1 -1
  6. package/dist/src/prompts/adr-suggestion-prompts.d.ts.map +1 -1
  7. package/dist/src/prompts/adr-suggestion-prompts.js +2 -0
  8. package/dist/src/prompts/adr-suggestion-prompts.js.map +1 -1
  9. package/dist/src/tools/adr-suggestion-tool.d.ts +16 -0
  10. package/dist/src/tools/adr-suggestion-tool.d.ts.map +1 -1
  11. package/dist/src/tools/adr-suggestion-tool.js +55 -2
  12. package/dist/src/tools/adr-suggestion-tool.js.map +1 -1
  13. package/dist/src/tools/smart-git-push-tool.d.ts +16 -0
  14. package/dist/src/tools/smart-git-push-tool.d.ts.map +1 -1
  15. package/dist/src/tools/smart-git-push-tool.js +111 -3
  16. package/dist/src/tools/smart-git-push-tool.js.map +1 -1
  17. package/dist/src/tools/smart-score-tool.d.ts +39 -4
  18. package/dist/src/tools/smart-score-tool.d.ts.map +1 -1
  19. package/dist/src/tools/smart-score-tool.js +94 -15
  20. package/dist/src/tools/smart-score-tool.js.map +1 -1
  21. package/dist/src/tools/todo-management-tool-v2.d.ts +329 -0
  22. package/dist/src/tools/todo-management-tool-v2.d.ts.map +1 -0
  23. package/dist/src/tools/todo-management-tool-v2.js +644 -0
  24. package/dist/src/tools/todo-management-tool-v2.js.map +1 -0
  25. package/dist/src/tools/tool-chain-orchestrator.d.ts +3 -41
  26. package/dist/src/tools/tool-chain-orchestrator.d.ts.map +1 -1
  27. package/dist/src/tools/tool-chain-orchestrator.js +10 -198
  28. package/dist/src/tools/tool-chain-orchestrator.js.map +1 -1
  29. package/dist/src/types/conversation-context.d.ts +6 -0
  30. package/dist/src/types/conversation-context.d.ts.map +1 -1
  31. package/dist/src/types/conversation-context.js +9 -1
  32. package/dist/src/types/conversation-context.js.map +1 -1
  33. package/dist/src/types/knowledge-graph-schemas.d.ts +298 -6
  34. package/dist/src/types/knowledge-graph-schemas.d.ts.map +1 -1
  35. package/dist/src/types/knowledge-graph-schemas.js +31 -3
  36. package/dist/src/types/knowledge-graph-schemas.js.map +1 -1
  37. package/dist/src/types/todo-json-schemas.d.ts +663 -0
  38. package/dist/src/types/todo-json-schemas.d.ts.map +1 -0
  39. package/dist/src/types/todo-json-schemas.js +113 -0
  40. package/dist/src/types/todo-json-schemas.js.map +1 -0
  41. package/dist/src/utils/chain-of-thought-template.d.ts.map +1 -1
  42. package/dist/src/utils/chain-of-thought-template.js.map +1 -1
  43. package/dist/src/utils/knowledge-graph-manager.d.ts +37 -0
  44. package/dist/src/utils/knowledge-graph-manager.d.ts.map +1 -1
  45. package/dist/src/utils/knowledge-graph-manager.js +169 -3
  46. package/dist/src/utils/knowledge-graph-manager.js.map +1 -1
  47. package/dist/src/utils/project-health-scoring.js +5 -5
  48. package/dist/src/utils/project-health-scoring.js.map +1 -1
  49. package/dist/src/utils/todo-json-manager.d.ts +71 -0
  50. package/dist/src/utils/todo-json-manager.d.ts.map +1 -0
  51. package/dist/src/utils/todo-json-manager.js +394 -0
  52. package/dist/src/utils/todo-json-manager.js.map +1 -0
  53. package/dist/src/utils/todo-markdown-converter.d.ts +16 -0
  54. package/dist/src/utils/todo-markdown-converter.d.ts.map +1 -0
  55. package/dist/src/utils/todo-markdown-converter.js +362 -0
  56. package/dist/src/utils/todo-markdown-converter.js.map +1 -0
  57. package/dist/src/utils/todo-migration.d.ts +47 -0
  58. package/dist/src/utils/todo-migration.d.ts.map +1 -0
  59. package/dist/src/utils/todo-migration.js +261 -0
  60. package/dist/src/utils/todo-migration.js.map +1 -0
  61. package/package.json +1 -1
  62. package/dist/src/tools/human-override-tool.d.ts +0 -42
  63. package/dist/src/tools/human-override-tool.d.ts.map +0 -1
  64. package/dist/src/tools/human-override-tool.js +0 -431
  65. package/dist/src/tools/human-override-tool.js.map +0 -1
  66. package/dist/src/tools/todo-management-tool.d.ts +0 -344
  67. package/dist/src/tools/todo-management-tool.d.ts.map +0 -1
  68. package/dist/src/tools/todo-management-tool.js +0 -1249
  69. package/dist/src/tools/todo-management-tool.js.map +0 -1
@@ -0,0 +1,644 @@
1
+ /**
2
+ * JSON-First TODO Management Tool
3
+ *
4
+ * Replaces markdown-based TODO management with structured JSON backend
5
+ * for consistent LLM interactions and automatic scoring integration.
6
+ *
7
+ * IMPORTANT FOR AI ASSISTANTS: This tool requires the .mcp-adr-cache infrastructure
8
+ * to be initialized first. Run `discover_existing_adrs` before using this tool.
9
+ *
10
+ * Cache Dependencies:
11
+ * - Requires: .mcp-adr-cache/todo-data.json (main TODO storage)
12
+ * - Requires: .mcp-adr-cache/knowledge-graph-snapshots.json (for intent tracking)
13
+ * - Updates: .mcp-adr-cache/project-health-scores.json (task completion metrics)
14
+ *
15
+ * Key Features:
16
+ * - JSON-first storage with automatic Markdown synchronization
17
+ * - Automatic project health scoring integration
18
+ * - Knowledge graph intent tracking
19
+ * - Bidirectional TODO.md compatibility
20
+ */
21
+ import { z } from 'zod';
22
+ import { McpAdrError } from '../types/index.js';
23
+ import { TodoJsonManager } from '../utils/todo-json-manager.js';
24
+ import { KnowledgeGraphManager } from '../utils/knowledge-graph-manager.js';
25
+ // Remove unused import
26
+ // Task operations schema
27
+ const CreateTaskSchema = z.object({
28
+ operation: z.literal('create_task'),
29
+ projectPath: z.string().describe('Project root path'),
30
+ title: z.string().describe('Task title'),
31
+ description: z.string().optional().describe('Task description'),
32
+ priority: z.enum(['low', 'medium', 'high', 'critical']).default('medium'),
33
+ assignee: z.string().optional().describe('Task assignee'),
34
+ dueDate: z.string().optional().describe('Due date (ISO string)'),
35
+ category: z.string().optional().describe('Task category'),
36
+ tags: z.array(z.string()).default([]).describe('Task tags'),
37
+ dependencies: z.array(z.string()).default([]).describe('Task dependencies (IDs)'),
38
+ intentId: z.string().optional().describe('Link to knowledge graph intent'),
39
+ linkedAdrs: z.array(z.string()).default([]).describe('Related ADR files'),
40
+ autoComplete: z.boolean().default(false).describe('Auto-complete when criteria met'),
41
+ completionCriteria: z.string().optional().describe('Auto-completion rules')
42
+ });
43
+ const UpdateTaskSchema = z.object({
44
+ operation: z.literal('update_task'),
45
+ projectPath: z.string().describe('Project root path'),
46
+ taskId: z.string().describe('Task ID to update'),
47
+ updates: z.object({
48
+ title: z.string().optional(),
49
+ description: z.string().optional(),
50
+ status: z.enum(['pending', 'in_progress', 'completed', 'blocked', 'cancelled']).optional(),
51
+ priority: z.enum(['low', 'medium', 'high', 'critical']).optional(),
52
+ assignee: z.string().optional(),
53
+ dueDate: z.string().optional(),
54
+ progressPercentage: z.number().min(0).max(100).optional(),
55
+ notes: z.string().optional(),
56
+ tags: z.array(z.string()).optional()
57
+ }).describe('Fields to update'),
58
+ reason: z.string().describe('Reason for update (for changelog)')
59
+ });
60
+ const BulkUpdateSchema = z.object({
61
+ operation: z.literal('bulk_update'),
62
+ projectPath: z.string().describe('Project root path'),
63
+ updates: z.array(z.object({
64
+ taskId: z.string(),
65
+ status: z.enum(['pending', 'in_progress', 'completed', 'blocked', 'cancelled']).optional(),
66
+ priority: z.enum(['low', 'medium', 'high', 'critical']).optional(),
67
+ assignee: z.string().optional(),
68
+ notes: z.string().optional()
69
+ })).describe('Bulk status updates'),
70
+ reason: z.string().describe('Reason for bulk update')
71
+ });
72
+ const GetTasksSchema = z.object({
73
+ operation: z.literal('get_tasks'),
74
+ projectPath: z.string().describe('Project root path'),
75
+ filters: z.object({
76
+ status: z.enum(['pending', 'in_progress', 'completed', 'blocked', 'cancelled']).optional(),
77
+ priority: z.enum(['low', 'medium', 'high', 'critical']).optional(),
78
+ assignee: z.string().optional(),
79
+ category: z.string().optional(),
80
+ hasDeadline: z.boolean().optional(),
81
+ overdue: z.boolean().optional(),
82
+ tags: z.array(z.string()).optional()
83
+ }).optional().describe('Filter criteria'),
84
+ sortBy: z.enum(['priority', 'dueDate', 'createdAt', 'updatedAt']).default('priority'),
85
+ sortOrder: z.enum(['asc', 'desc']).default('desc'),
86
+ limit: z.number().optional().describe('Maximum number of tasks to return')
87
+ });
88
+ const GetAnalyticsSchema = z.object({
89
+ operation: z.literal('get_analytics'),
90
+ projectPath: z.string().describe('Project root path'),
91
+ timeframe: z.enum(['day', 'week', 'month', 'all']).default('week').describe('Analysis timeframe'),
92
+ includeVelocity: z.boolean().default(true).describe('Include velocity metrics'),
93
+ includeScoring: z.boolean().default(true).describe('Include scoring metrics')
94
+ });
95
+ const ImportAdrTasksSchema = z.object({
96
+ operation: z.literal('import_adr_tasks'),
97
+ projectPath: z.string().describe('Project root path'),
98
+ adrDirectory: z.string().default('docs/adrs').describe('ADR directory path'),
99
+ intentId: z.string().optional().describe('Link imported tasks to intent'),
100
+ preserveExisting: z.boolean().default(true).describe('Keep existing tasks'),
101
+ autoLinkDependencies: z.boolean().default(true).describe('Auto-detect task dependencies')
102
+ });
103
+ const SyncWithKnowledgeGraphSchema = z.object({
104
+ operation: z.literal('sync_knowledge_graph'),
105
+ projectPath: z.string().describe('Project root path'),
106
+ direction: z.enum(['to_kg', 'from_kg', 'bidirectional']).default('bidirectional'),
107
+ intentId: z.string().optional().describe('Specific intent to sync with')
108
+ });
109
+ const SyncToMarkdownSchema = z.object({
110
+ operation: z.literal('sync_to_markdown'),
111
+ projectPath: z.string().describe('Project root path'),
112
+ force: z.boolean().default(false).describe('Force sync even if markdown is newer')
113
+ });
114
+ const ImportFromMarkdownSchema = z.object({
115
+ operation: z.literal('import_from_markdown'),
116
+ projectPath: z.string().describe('Project root path'),
117
+ mergeStrategy: z.enum(['overwrite', 'merge', 'preserve_json']).default('merge'),
118
+ backupExisting: z.boolean().default(true).describe('Create backup before import')
119
+ });
120
+ // Main operation schema
121
+ const TodoManagementV2Schema = z.union([
122
+ CreateTaskSchema,
123
+ UpdateTaskSchema,
124
+ BulkUpdateSchema,
125
+ GetTasksSchema,
126
+ GetAnalyticsSchema,
127
+ ImportAdrTasksSchema,
128
+ SyncWithKnowledgeGraphSchema,
129
+ SyncToMarkdownSchema,
130
+ ImportFromMarkdownSchema
131
+ ]);
132
+ /**
133
+ * Main TODO management function with JSON backend
134
+ */
135
+ export async function manageTodoV2(args) {
136
+ try {
137
+ const validatedArgs = TodoManagementV2Schema.parse(args);
138
+ const todoManager = new TodoJsonManager(validatedArgs.projectPath);
139
+ const kgManager = new KnowledgeGraphManager();
140
+ switch (validatedArgs.operation) {
141
+ case 'create_task': {
142
+ const taskId = await todoManager.createTask({
143
+ title: validatedArgs.title,
144
+ description: validatedArgs.description,
145
+ priority: validatedArgs.priority,
146
+ assignee: validatedArgs.assignee,
147
+ dueDate: validatedArgs.dueDate,
148
+ category: validatedArgs.category,
149
+ tags: validatedArgs.tags,
150
+ dependencies: validatedArgs.dependencies,
151
+ intentId: validatedArgs.intentId,
152
+ linkedAdrs: validatedArgs.linkedAdrs,
153
+ autoComplete: validatedArgs.autoComplete,
154
+ completionCriteria: validatedArgs.completionCriteria
155
+ });
156
+ return {
157
+ content: [{
158
+ type: 'text',
159
+ text: `✅ Task created successfully!\n\n**Task ID**: ${taskId}\n**Title**: ${validatedArgs.title}\n**Priority**: ${validatedArgs.priority}\n**Status**: pending\n\n*Task has been added to JSON backend and will sync to TODO.md automatically.*`
160
+ }]
161
+ };
162
+ }
163
+ case 'update_task': {
164
+ // Filter out undefined values
165
+ const cleanUpdates = {};
166
+ if (validatedArgs.updates.title !== undefined)
167
+ cleanUpdates.title = validatedArgs.updates.title;
168
+ if (validatedArgs.updates.description !== undefined)
169
+ cleanUpdates.description = validatedArgs.updates.description;
170
+ if (validatedArgs.updates.status !== undefined)
171
+ cleanUpdates.status = validatedArgs.updates.status;
172
+ if (validatedArgs.updates.priority !== undefined)
173
+ cleanUpdates.priority = validatedArgs.updates.priority;
174
+ if (validatedArgs.updates.assignee !== undefined)
175
+ cleanUpdates.assignee = validatedArgs.updates.assignee;
176
+ if (validatedArgs.updates.dueDate !== undefined)
177
+ cleanUpdates.dueDate = validatedArgs.updates.dueDate;
178
+ if (validatedArgs.updates.progressPercentage !== undefined)
179
+ cleanUpdates.progressPercentage = validatedArgs.updates.progressPercentage;
180
+ if (validatedArgs.updates.notes !== undefined)
181
+ cleanUpdates.notes = validatedArgs.updates.notes;
182
+ if (validatedArgs.updates.tags !== undefined)
183
+ cleanUpdates.tags = validatedArgs.updates.tags;
184
+ await todoManager.updateTask({
185
+ taskId: validatedArgs.taskId,
186
+ updates: cleanUpdates,
187
+ reason: validatedArgs.reason,
188
+ triggeredBy: 'tool'
189
+ });
190
+ return {
191
+ content: [{
192
+ type: 'text',
193
+ text: `✅ Task updated successfully!\n\n**Task ID**: ${validatedArgs.taskId}\n**Updates**: ${Object.keys(validatedArgs.updates).join(', ')}\n**Reason**: ${validatedArgs.reason}\n\n*Changes synced to JSON backend and TODO.md.*`
194
+ }]
195
+ };
196
+ }
197
+ case 'bulk_update': {
198
+ let updateCount = 0;
199
+ for (const update of validatedArgs.updates) {
200
+ // Filter out undefined values
201
+ const cleanUpdates = {};
202
+ if (update.status !== undefined)
203
+ cleanUpdates.status = update.status;
204
+ if (update.priority !== undefined)
205
+ cleanUpdates.priority = update.priority;
206
+ if (update.assignee !== undefined)
207
+ cleanUpdates.assignee = update.assignee;
208
+ if (update.notes !== undefined)
209
+ cleanUpdates.notes = update.notes;
210
+ await todoManager.updateTask({
211
+ taskId: update.taskId,
212
+ updates: cleanUpdates,
213
+ reason: validatedArgs.reason,
214
+ triggeredBy: 'tool'
215
+ });
216
+ updateCount++;
217
+ }
218
+ return {
219
+ content: [{
220
+ type: 'text',
221
+ text: `✅ Bulk update completed!\n\n**Tasks Updated**: ${updateCount}\n**Reason**: ${validatedArgs.reason}\n\n*All changes synced to JSON backend and TODO.md.*`
222
+ }]
223
+ };
224
+ }
225
+ case 'get_tasks': {
226
+ const data = await todoManager.loadTodoData();
227
+ let tasks = Object.values(data.tasks);
228
+ // Apply filters
229
+ if (validatedArgs.filters) {
230
+ const filters = validatedArgs.filters;
231
+ if (filters.status) {
232
+ tasks = tasks.filter(t => t.status === filters.status);
233
+ }
234
+ if (filters.priority) {
235
+ tasks = tasks.filter(t => t.priority === filters.priority);
236
+ }
237
+ if (filters.assignee) {
238
+ tasks = tasks.filter(t => t.assignee === filters.assignee);
239
+ }
240
+ if (filters.category) {
241
+ tasks = tasks.filter(t => t.category === filters.category);
242
+ }
243
+ if (filters.hasDeadline) {
244
+ tasks = tasks.filter(t => Boolean(t.dueDate));
245
+ }
246
+ if (filters.overdue) {
247
+ const now = new Date();
248
+ tasks = tasks.filter(t => t.dueDate && new Date(t.dueDate) < now && t.status !== 'completed');
249
+ }
250
+ if (filters.tags && filters.tags.length > 0) {
251
+ tasks = tasks.filter(t => filters.tags.some(tag => t.tags.includes(tag)));
252
+ }
253
+ }
254
+ // Sort tasks
255
+ tasks.sort((a, b) => {
256
+ const priorityOrder = { critical: 4, high: 3, medium: 2, low: 1 };
257
+ switch (validatedArgs.sortBy) {
258
+ case 'priority':
259
+ return validatedArgs.sortOrder === 'desc'
260
+ ? priorityOrder[b.priority] - priorityOrder[a.priority]
261
+ : priorityOrder[a.priority] - priorityOrder[b.priority];
262
+ case 'dueDate':
263
+ if (!a.dueDate && !b.dueDate)
264
+ return 0;
265
+ if (!a.dueDate)
266
+ return 1;
267
+ if (!b.dueDate)
268
+ return -1;
269
+ return validatedArgs.sortOrder === 'desc'
270
+ ? new Date(b.dueDate).getTime() - new Date(a.dueDate).getTime()
271
+ : new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime();
272
+ case 'createdAt':
273
+ return validatedArgs.sortOrder === 'desc'
274
+ ? new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
275
+ : new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
276
+ case 'updatedAt':
277
+ return validatedArgs.sortOrder === 'desc'
278
+ ? new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
279
+ : new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime();
280
+ default:
281
+ return 0;
282
+ }
283
+ });
284
+ // Apply limit
285
+ if (validatedArgs.limit) {
286
+ tasks = tasks.slice(0, validatedArgs.limit);
287
+ }
288
+ const taskList = tasks.map(task => {
289
+ const dueDateStr = task.dueDate ? new Date(task.dueDate).toLocaleDateString() : 'No due date';
290
+ const isOverdue = task.dueDate && new Date(task.dueDate) < new Date() && task.status !== 'completed';
291
+ return `**${task.title}** (${task.id.substring(0, 8)})\n` +
292
+ ` Status: ${task.status} | Priority: ${task.priority}\n` +
293
+ ` ${task.assignee ? `Assignee: ${task.assignee} | ` : ''}Due: ${dueDateStr} ${isOverdue ? '🔴 OVERDUE' : ''}\n` +
294
+ ` ${task.tags.length > 0 ? `Tags: ${task.tags.join(', ')} | ` : ''}Progress: ${task.progressPercentage}%\n` +
295
+ ` ${task.description || 'No description'}\n`;
296
+ }).join('\n');
297
+ return {
298
+ content: [{
299
+ type: 'text',
300
+ text: `# 📋 Task List (${tasks.length} tasks)\n\n${taskList || 'No tasks match the criteria.'}`
301
+ }]
302
+ };
303
+ }
304
+ case 'get_analytics': {
305
+ const analytics = await todoManager.getAnalytics();
306
+ const { metrics, trends, recommendations } = analytics;
307
+ let analyticsText = `# 📊 TODO Analytics\n\n`;
308
+ analyticsText += `## Task Metrics\n`;
309
+ analyticsText += `- **Total Tasks**: ${metrics.totalTasks}\n`;
310
+ analyticsText += `- **Completed**: ${metrics.completedTasks} (${metrics.completionPercentage.toFixed(1)}%)\n`;
311
+ analyticsText += `- **Priority Score**: ${metrics.priorityWeightedScore.toFixed(1)}%\n`;
312
+ analyticsText += `- **Critical Remaining**: ${metrics.criticalTasksRemaining}\n`;
313
+ analyticsText += `- **Blocked Tasks**: ${metrics.blockedTasksCount}\n`;
314
+ analyticsText += `- **Average Task Age**: ${metrics.averageTaskAge.toFixed(1)} days\n\n`;
315
+ if (validatedArgs.includeVelocity) {
316
+ analyticsText += `## Velocity Metrics\n`;
317
+ analyticsText += `- **Tasks/Week**: ${metrics.velocityMetrics.tasksCompletedLastWeek}\n`;
318
+ analyticsText += `- **Avg Completion Time**: ${metrics.velocityMetrics.averageCompletionTime.toFixed(1)} hours\n\n`;
319
+ }
320
+ if (recommendations.length > 0) {
321
+ analyticsText += `## Recommendations\n`;
322
+ analyticsText += recommendations.map(rec => `- ${rec}`).join('\n');
323
+ analyticsText += '\n\n';
324
+ }
325
+ if (trends.length > 0) {
326
+ analyticsText += `## Recent Score History\n`;
327
+ analyticsText += trends.slice(-5).map(trend => `- ${new Date(trend.timestamp).toLocaleDateString()}: ${trend.score.toFixed(1)}% (${trend.trigger})`).join('\n');
328
+ }
329
+ return {
330
+ content: [{
331
+ type: 'text',
332
+ text: analyticsText
333
+ }]
334
+ };
335
+ }
336
+ case 'import_adr_tasks': {
337
+ const adrDirectory = validatedArgs.adrDirectory;
338
+ const intentId = validatedArgs.intentId;
339
+ const preserveExisting = validatedArgs.preserveExisting;
340
+ const autoLinkDependencies = validatedArgs.autoLinkDependencies;
341
+ // Discover ADRs in the directory
342
+ const { discoverAdrsInDirectory } = await import('../utils/adr-discovery.js');
343
+ const discoveryResult = await discoverAdrsInDirectory(adrDirectory, true, // include content for task extraction
344
+ validatedArgs.projectPath);
345
+ if (discoveryResult.totalAdrs === 0) {
346
+ return {
347
+ content: [{
348
+ type: 'text',
349
+ text: `⚠️ No ADRs found in directory: ${adrDirectory}\n\n**Recommendations:**\n${discoveryResult.recommendations.join('\n')}`
350
+ }]
351
+ };
352
+ }
353
+ // Extract tasks from each ADR
354
+ const extractedTasks = await extractTasksFromAdrs(discoveryResult.adrs, adrDirectory, intentId, autoLinkDependencies);
355
+ // Import tasks into JSON backend
356
+ let importedCount = 0;
357
+ let skippedCount = 0;
358
+ const importResults = [];
359
+ for (const task of extractedTasks) {
360
+ try {
361
+ // Check if task already exists (by title and ADR reference)
362
+ const existingData = await todoManager.loadTodoData();
363
+ const existingTask = Object.values(existingData.tasks).find(t => t.title === task.title &&
364
+ t.linkedAdrs.some(adr => task.linkedAdrs.includes(adr)));
365
+ if (existingTask && preserveExisting) {
366
+ skippedCount++;
367
+ importResults.push(`⏭️ Skipped: "${task.title}" (already exists)`);
368
+ continue;
369
+ }
370
+ // Create the task
371
+ await todoManager.createTask({
372
+ title: task.title,
373
+ description: task.description,
374
+ priority: task.priority,
375
+ category: task.category,
376
+ tags: task.tags,
377
+ dependencies: task.dependencies,
378
+ linkedAdrs: task.linkedAdrs,
379
+ assignee: task.assignee,
380
+ ...(intentId && { intentId })
381
+ });
382
+ importedCount++;
383
+ importResults.push(`✅ Imported: "${task.title}" (${task.priority} priority)`);
384
+ }
385
+ catch (error) {
386
+ importResults.push(`❌ Failed: "${task.title}" - ${error instanceof Error ? error.message : String(error)}`);
387
+ }
388
+ }
389
+ return {
390
+ content: [{
391
+ type: 'text',
392
+ text: `# ✅ ADR Task Import Completed
393
+
394
+ ## Import Summary
395
+ - **ADRs Scanned**: ${discoveryResult.totalAdrs}
396
+ - **Tasks Extracted**: ${extractedTasks.length}
397
+ - **Tasks Imported**: ${importedCount}
398
+ - **Tasks Skipped**: ${skippedCount}
399
+ - **Preserve Existing**: ${preserveExisting}
400
+ - **Auto-Link Dependencies**: ${autoLinkDependencies}
401
+
402
+ ## ADR Status Overview
403
+ ${Object.entries(discoveryResult.summary.byStatus)
404
+ .map(([status, count]) => `- **${status}**: ${count} ADRs`)
405
+ .join('\n')}
406
+
407
+ ## Import Results
408
+ ${importResults.join('\n')}
409
+
410
+ ## Next Steps
411
+ 1. **View Tasks**: Use \`get_tasks\` operation to see imported tasks
412
+ 2. **Sync to Markdown**: Tasks will be automatically synced to TODO.md
413
+ 3. **Update Status**: Use \`update_task\` operation to track progress
414
+ 4. **Link Knowledge Graph**: Tasks are ${intentId ? `linked to intent ${intentId}` : 'ready for intent linking'}
415
+
416
+ *All tasks have been imported into the JSON backend and are ready for the complete TODO management workflow.*`
417
+ }]
418
+ };
419
+ }
420
+ case 'sync_knowledge_graph': {
421
+ const data = await todoManager.loadTodoData();
422
+ // Sync task completion status to knowledge graph
423
+ for (const task of Object.values(data.tasks)) {
424
+ if (task.intentId) {
425
+ await kgManager.updateTodoSnapshot(task.intentId, `Task ${task.status}: ${task.title} (${task.progressPercentage}%)`);
426
+ }
427
+ }
428
+ return {
429
+ content: [{
430
+ type: 'text',
431
+ text: `✅ Knowledge graph sync completed!\n\n**Direction**: ${validatedArgs.direction}\n**Tasks with Intent Links**: ${Object.values(data.tasks).filter(t => t.intentId).length}\n\n*Task progress has been synced to the knowledge graph.*`
432
+ }]
433
+ };
434
+ }
435
+ case 'sync_to_markdown': {
436
+ await todoManager.convertToMarkdown();
437
+ return {
438
+ content: [{
439
+ type: 'text',
440
+ text: `✅ Markdown sync completed!\n\n*TODO.md has been updated with the latest JSON data including health dashboard and formatted task lists.*`
441
+ }]
442
+ };
443
+ }
444
+ case 'import_from_markdown': {
445
+ if (validatedArgs.backupExisting) {
446
+ // Create backup of existing JSON
447
+ const data = await todoManager.loadTodoData();
448
+ const backupPath = `${validatedArgs.projectPath}/.mcp-adr-cache/todo-data-backup-${Date.now()}.json`;
449
+ const fs = await import('fs/promises');
450
+ await fs.writeFile(backupPath, JSON.stringify(data, null, 2));
451
+ }
452
+ await todoManager.importFromMarkdown();
453
+ return {
454
+ content: [{
455
+ type: 'text',
456
+ text: `✅ Markdown import completed!\n\n**Strategy**: ${validatedArgs.mergeStrategy}\n**Backup Created**: ${validatedArgs.backupExisting}\n\n*TODO.md has been parsed and imported into the JSON backend.*`
457
+ }]
458
+ };
459
+ }
460
+ default:
461
+ throw new McpAdrError(`Unknown operation: ${validatedArgs.operation}`, 'INVALID_INPUT');
462
+ }
463
+ }
464
+ catch (error) {
465
+ if (error instanceof z.ZodError) {
466
+ throw new McpAdrError(`Invalid input: ${error.errors.map(e => e.message).join(', ')}`, 'INVALID_INPUT');
467
+ }
468
+ throw new McpAdrError(`TODO management failed: ${error instanceof Error ? error.message : String(error)}`, 'TODO_MANAGEMENT_ERROR');
469
+ }
470
+ }
471
+ /**
472
+ * Extract implementation tasks from ADR content
473
+ */
474
+ async function extractTasksFromAdrs(adrs, _adrDirectory, _intentId, autoLinkDependencies = true) {
475
+ const extractedTasks = [];
476
+ for (const adr of adrs) {
477
+ if (!adr.content)
478
+ continue;
479
+ // Extract tasks from Decision and Consequences sections
480
+ const tasks = extractTasksFromAdrContent(adr);
481
+ // Add ADR metadata to each task
482
+ for (const task of tasks) {
483
+ task.linkedAdrs = [adr.path];
484
+ task.category = task.category || 'ADR Implementation';
485
+ task.tags = task.tags || [];
486
+ // Add ADR-specific tags
487
+ if (adr.metadata?.number) {
488
+ task.tags.push(`adr-${adr.metadata.number}`);
489
+ }
490
+ if (adr.status) {
491
+ task.tags.push(`status-${adr.status}`);
492
+ }
493
+ // Set priority based on ADR status and content
494
+ if (!task.priority) {
495
+ task.priority = determinePriorityFromAdr(adr);
496
+ }
497
+ extractedTasks.push(task);
498
+ }
499
+ }
500
+ // Link dependencies between ADRs if requested
501
+ if (autoLinkDependencies) {
502
+ linkAdrDependencies(extractedTasks, adrs);
503
+ }
504
+ return extractedTasks;
505
+ }
506
+ /**
507
+ * Extract tasks from ADR content sections
508
+ */
509
+ function extractTasksFromAdrContent(adr) {
510
+ const tasks = [];
511
+ const content = adr.content || '';
512
+ // Look for implementation tasks in Decision section
513
+ if (adr.decision && typeof adr.decision === 'string') {
514
+ const decisionTasks = extractTasksFromText(adr.decision, 'Implementation', 'high');
515
+ tasks.push(...decisionTasks);
516
+ }
517
+ // Look for tasks in Consequences section
518
+ if (adr.consequences && typeof adr.consequences === 'string') {
519
+ const consequenceTasks = extractTasksFromText(adr.consequences, 'Validation', 'medium');
520
+ tasks.push(...consequenceTasks);
521
+ }
522
+ // Look for explicit task lists in content
523
+ const taskListRegex = /(?:##?\s*(?:tasks?|todo|implementation|action items?)\s*\n)([\s\S]*?)(?=\n##|\n#|$)/gi;
524
+ let match;
525
+ while ((match = taskListRegex.exec(content)) !== null) {
526
+ const taskSection = match[1];
527
+ if (taskSection) {
528
+ const sectionTasks = extractTasksFromText(taskSection, 'Task', 'medium');
529
+ tasks.push(...sectionTasks);
530
+ }
531
+ }
532
+ // If no explicit tasks found, create a general implementation task
533
+ if (tasks.length === 0) {
534
+ tasks.push({
535
+ title: `Implement ${adr.title}`,
536
+ description: `Implement the architectural decision: ${adr.title}`,
537
+ priority: 'medium',
538
+ category: 'ADR Implementation',
539
+ tags: ['implementation'],
540
+ dependencies: [],
541
+ linkedAdrs: []
542
+ });
543
+ }
544
+ return tasks;
545
+ }
546
+ /**
547
+ * Extract tasks from text content using patterns
548
+ */
549
+ function extractTasksFromText(text, defaultCategory, defaultPriority) {
550
+ const tasks = [];
551
+ // Look for bullet points that look like tasks
552
+ const taskPatterns = [
553
+ /[-*+]\s+(.+?)(?:\n|$)/g,
554
+ /\d+\.\s+(.+?)(?:\n|$)/g,
555
+ /(?:must|should|will|need to|implement|create|build|develop|configure|setup|establish)\s+(.+?)(?:\.|$)/gi
556
+ ];
557
+ for (const pattern of taskPatterns) {
558
+ let match;
559
+ while ((match = pattern.exec(text)) !== null) {
560
+ const taskText = match[1]?.trim();
561
+ if (!taskText || taskText.length < 10 || taskText.length > 200)
562
+ continue; // Filter out very short/long matches
563
+ // Determine priority from keywords
564
+ let priority = defaultPriority;
565
+ if (/critical|urgent|must|security|breaking/.test(taskText.toLowerCase())) {
566
+ priority = 'critical';
567
+ }
568
+ else if (/important|should|key|major/.test(taskText.toLowerCase())) {
569
+ priority = 'high';
570
+ }
571
+ else if (/nice|could|minor|optional/.test(taskText.toLowerCase())) {
572
+ priority = 'low';
573
+ }
574
+ // Extract assignee if mentioned
575
+ let assignee;
576
+ const assigneeMatch = taskText.match(/@(\w+)|assigned to (\w+)|by (\w+)/i);
577
+ if (assigneeMatch) {
578
+ assignee = assigneeMatch[1] || assigneeMatch[2] || assigneeMatch[3];
579
+ }
580
+ tasks.push({
581
+ title: taskText.charAt(0).toUpperCase() + taskText.slice(1), // Capitalize first letter
582
+ description: `Task extracted from ADR: ${taskText}`,
583
+ priority,
584
+ category: defaultCategory,
585
+ tags: [],
586
+ dependencies: [],
587
+ linkedAdrs: [],
588
+ ...(assignee && { assignee })
589
+ });
590
+ }
591
+ }
592
+ return tasks;
593
+ }
594
+ /**
595
+ * Determine priority based on ADR status and content
596
+ */
597
+ function determinePriorityFromAdr(adr) {
598
+ const status = adr.status?.toLowerCase() || '';
599
+ const content = (adr.content || '').toLowerCase();
600
+ // High priority for accepted/active ADRs
601
+ if (['accepted', 'active', 'approved'].includes(status)) {
602
+ return 'high';
603
+ }
604
+ // Critical for security/breaking changes
605
+ if (/security|breaking|critical|urgent/.test(content)) {
606
+ return 'critical';
607
+ }
608
+ // Low priority for deprecated/superseded
609
+ if (['deprecated', 'superseded', 'rejected'].includes(status)) {
610
+ return 'low';
611
+ }
612
+ // Default to medium
613
+ return 'medium';
614
+ }
615
+ /**
616
+ * Link dependencies between ADR tasks
617
+ */
618
+ function linkAdrDependencies(tasks, adrs) {
619
+ // Simple dependency linking based on ADR numbers and references
620
+ for (const task of tasks) {
621
+ const taskAdr = adrs.find(adr => task.linkedAdrs.includes(adr.path));
622
+ if (!taskAdr)
623
+ continue;
624
+ // Look for references to other ADRs in the content
625
+ const content = taskAdr.content || '';
626
+ const adrReferences = content.match(/adr[-_]?(\d+)/gi) || [];
627
+ for (const ref of adrReferences) {
628
+ const refNumber = ref.match(/(\d+)/)?.[1];
629
+ if (!refNumber || refNumber === taskAdr.metadata?.number)
630
+ continue;
631
+ // Find tasks from the referenced ADR
632
+ const referencedAdr = adrs.find(adr => adr.metadata?.number === refNumber);
633
+ if (!referencedAdr)
634
+ continue;
635
+ const referencedTasks = tasks.filter(t => t.linkedAdrs.includes(referencedAdr.path));
636
+ for (const refTask of referencedTasks) {
637
+ if (!task.dependencies.includes(refTask.title)) {
638
+ task.dependencies.push(refTask.title);
639
+ }
640
+ }
641
+ }
642
+ }
643
+ }
644
+ //# sourceMappingURL=todo-management-tool-v2.js.map