mcp-adr-analysis-server 2.0.9 → 2.0.10

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 (60) hide show
  1. package/dist/src/index.d.ts +8 -0
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +434 -5
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/resources/index.d.ts.map +1 -1
  6. package/dist/src/resources/index.js +71 -1
  7. package/dist/src/resources/index.js.map +1 -1
  8. package/dist/src/tools/human-override-tool.d.ts +42 -0
  9. package/dist/src/tools/human-override-tool.d.ts.map +1 -0
  10. package/dist/src/tools/human-override-tool.js +431 -0
  11. package/dist/src/tools/human-override-tool.js.map +1 -0
  12. package/dist/src/tools/smart-git-push-tool.d.ts +5 -1
  13. package/dist/src/tools/smart-git-push-tool.d.ts.map +1 -1
  14. package/dist/src/tools/smart-git-push-tool.js +70 -25
  15. package/dist/src/tools/smart-git-push-tool.js.map +1 -1
  16. package/dist/src/tools/smart-score-tool.d.ts +138 -0
  17. package/dist/src/tools/smart-score-tool.d.ts.map +1 -0
  18. package/dist/src/tools/smart-score-tool.js +602 -0
  19. package/dist/src/tools/smart-score-tool.js.map +1 -0
  20. package/dist/src/tools/todo-management-tool.d.ts +184 -6
  21. package/dist/src/tools/todo-management-tool.d.ts.map +1 -1
  22. package/dist/src/tools/todo-management-tool.js +819 -42
  23. package/dist/src/tools/todo-management-tool.js.map +1 -1
  24. package/dist/src/tools/tool-chain-orchestrator.d.ts +164 -0
  25. package/dist/src/tools/tool-chain-orchestrator.d.ts.map +1 -0
  26. package/dist/src/tools/tool-chain-orchestrator.js +738 -0
  27. package/dist/src/tools/tool-chain-orchestrator.js.map +1 -0
  28. package/dist/src/tools/troubleshoot-guided-workflow-tool.d.ts +150 -0
  29. package/dist/src/tools/troubleshoot-guided-workflow-tool.d.ts.map +1 -0
  30. package/dist/src/tools/troubleshoot-guided-workflow-tool.js +520 -0
  31. package/dist/src/tools/troubleshoot-guided-workflow-tool.js.map +1 -0
  32. package/dist/src/types/knowledge-graph-schemas.d.ts +481 -0
  33. package/dist/src/types/knowledge-graph-schemas.d.ts.map +1 -0
  34. package/dist/src/types/knowledge-graph-schemas.js +59 -0
  35. package/dist/src/types/knowledge-graph-schemas.js.map +1 -0
  36. package/dist/src/utils/json-safe.d.ts +38 -0
  37. package/dist/src/utils/json-safe.d.ts.map +1 -0
  38. package/dist/src/utils/json-safe.js +86 -0
  39. package/dist/src/utils/json-safe.js.map +1 -0
  40. package/dist/src/utils/knowledge-graph-manager.d.ts +28 -0
  41. package/dist/src/utils/knowledge-graph-manager.d.ts.map +1 -0
  42. package/dist/src/utils/knowledge-graph-manager.js +192 -0
  43. package/dist/src/utils/knowledge-graph-manager.js.map +1 -0
  44. package/dist/src/utils/mcp-response-validator.d.ts +30 -0
  45. package/dist/src/utils/mcp-response-validator.d.ts.map +1 -0
  46. package/dist/src/utils/mcp-response-validator.js +197 -0
  47. package/dist/src/utils/mcp-response-validator.js.map +1 -0
  48. package/dist/src/utils/project-health-scoring.d.ts +177 -0
  49. package/dist/src/utils/project-health-scoring.d.ts.map +1 -0
  50. package/dist/src/utils/project-health-scoring.js +370 -0
  51. package/dist/src/utils/project-health-scoring.js.map +1 -0
  52. package/dist/src/utils/release-readiness-detector.d.ts +2 -0
  53. package/dist/src/utils/release-readiness-detector.d.ts.map +1 -1
  54. package/dist/src/utils/release-readiness-detector.js +2 -0
  55. package/dist/src/utils/release-readiness-detector.js.map +1 -1
  56. package/dist/src/utils/todo-file-watcher.d.ts +26 -0
  57. package/dist/src/utils/todo-file-watcher.d.ts.map +1 -0
  58. package/dist/src/utils/todo-file-watcher.js +163 -0
  59. package/dist/src/utils/todo-file-watcher.js.map +1 -0
  60. package/package.json +1 -1
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { z } from 'zod';
6
6
  import { McpAdrError } from '../types/index.js';
7
+ import { KnowledgeGraphManager } from '../utils/knowledge-graph-manager.js';
7
8
  // Task status enumeration
8
9
  const TaskStatus = z.enum(['pending', 'in_progress', 'completed', 'blocked', 'cancelled']);
9
10
  // Task priority enumeration
@@ -43,7 +44,7 @@ const TodoStructure = z.object({
43
44
  // Operation schemas
44
45
  const UpdateTaskStatusSchema = z.object({
45
46
  operation: z.literal('update_status'),
46
- todoPath: z.string().default('todo.md').describe('Path to TODO.md file'),
47
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
47
48
  updates: z.array(z.object({
48
49
  taskId: z.string().describe('Task ID to update'),
49
50
  status: TaskStatus.describe('New status'),
@@ -53,44 +54,146 @@ const UpdateTaskStatusSchema = z.object({
53
54
  });
54
55
  const AddTasksSchema = z.object({
55
56
  operation: z.literal('add_tasks'),
56
- todoPath: z.string().default('todo.md').describe('Path to TODO.md file'),
57
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
57
58
  tasks: z.array(TaskSchema.omit({ id: true, createdAt: true, updatedAt: true })).describe('New tasks to add'),
58
59
  section: z.string().optional().describe('Section to add tasks to (will create if not exists)')
59
60
  });
60
61
  const MergeAdrSchema = z.object({
61
62
  operation: z.literal('merge_adr_updates'),
62
- todoPath: z.string().default('todo.md').describe('Path to TODO.md file'),
63
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
63
64
  adrDirectory: z.string().default('docs/adrs').describe('ADR directory path'),
64
65
  preserveCompleted: z.boolean().default(true).describe('Keep completed tasks'),
65
66
  preserveCustom: z.boolean().default(true).describe('Keep manually added tasks')
66
67
  });
67
68
  const SyncProgressSchema = z.object({
68
69
  operation: z.literal('sync_progress'),
69
- todoPath: z.string().default('todo.md').describe('Path to TODO.md file'),
70
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
70
71
  adrDirectory: z.string().default('docs/adrs').describe('ADR directory path'),
71
72
  updateAdrs: z.boolean().default(false).describe('Update ADR status based on TODO progress')
72
73
  });
73
74
  const AnalyzeProgressSchema = z.object({
74
75
  operation: z.literal('analyze_progress'),
75
- todoPath: z.string().default('todo.md').describe('Path to TODO.md file'),
76
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
76
77
  timeframe: z.enum(['day', 'week', 'month', 'all']).default('week').describe('Analysis timeframe'),
77
78
  includeVelocity: z.boolean().default(true).describe('Include velocity metrics')
78
79
  });
80
+ // New Interactive Operations
81
+ const DeleteTaskSchema = z.object({
82
+ operation: z.literal('delete_task'),
83
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
84
+ taskIds: z.array(z.string()).describe('Task IDs to delete'),
85
+ force: z.boolean().default(false).describe('Skip interactive confirmation if true'),
86
+ preserveComments: z.boolean().default(true).describe('Preserve task notes in deletion log')
87
+ });
88
+ const EditTaskSchema = z.object({
89
+ operation: z.literal('edit_task'),
90
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
91
+ taskId: z.string().describe('Task ID to edit'),
92
+ updates: z.object({
93
+ title: z.string().optional().describe('New task title'),
94
+ priority: TaskPriority.optional().describe('New priority level'),
95
+ category: z.string().optional().describe('New category'),
96
+ estimatedHours: z.number().optional().describe('New time estimate'),
97
+ dependencies: z.array(z.string()).optional().describe('New dependency list'),
98
+ notes: z.string().optional().describe('New notes (replaces existing)')
99
+ }).describe('Fields to update'),
100
+ interactive: z.boolean().default(true).describe('Enable interactive field selection'),
101
+ confirmChanges: z.boolean().default(true).describe('Require confirmation before applying')
102
+ });
103
+ const MoveTaskSchema = z.object({
104
+ operation: z.literal('move_task'),
105
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
106
+ taskId: z.string().describe('Task ID to move'),
107
+ targetSection: z.string().optional().describe('Target section name'),
108
+ position: z.number().optional().describe('Position in target section (0-based)'),
109
+ interactive: z.boolean().default(true).describe('Enable interactive section/position selection'),
110
+ createSection: z.boolean().default(false).describe('Create target section if it does not exist')
111
+ });
112
+ const ManageSectionSchema = z.object({
113
+ operation: z.literal('manage_section'),
114
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
115
+ action: z.enum(['create', 'delete', 'rename', 'reorder']).describe('Section management action'),
116
+ sectionName: z.string().describe('Section name to operate on'),
117
+ newName: z.string().optional().describe('New name for rename action'),
118
+ position: z.number().optional().describe('New position for reorder action'),
119
+ force: z.boolean().default(false).describe('Skip safety checks and confirmations'),
120
+ preserveTasks: z.boolean().default(true).describe('For delete: move tasks to another section instead of deleting')
121
+ });
122
+ const SearchTasksSchema = z.object({
123
+ operation: z.literal('search_tasks'),
124
+ todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
125
+ query: z.string().optional().describe('Text to search in task titles and notes'),
126
+ filters: z.object({
127
+ status: TaskStatus.optional(),
128
+ priority: TaskPriority.optional(),
129
+ assignee: z.string().optional(),
130
+ section: z.string().optional(),
131
+ hasNotes: z.boolean().optional(),
132
+ hasDependencies: z.boolean().optional()
133
+ }).optional().describe('Additional filtering criteria'),
134
+ includeMetadata: z.boolean().default(true).describe('Include full task metadata in results')
135
+ });
79
136
  // Main operation schema
80
137
  const ManageTodoSchema = z.union([
81
138
  UpdateTaskStatusSchema,
82
139
  AddTasksSchema,
83
140
  MergeAdrSchema,
84
141
  SyncProgressSchema,
85
- AnalyzeProgressSchema
142
+ AnalyzeProgressSchema,
143
+ DeleteTaskSchema,
144
+ EditTaskSchema,
145
+ MoveTaskSchema,
146
+ ManageSectionSchema,
147
+ SearchTasksSchema
86
148
  ]);
149
+ /**
150
+ * Count critical tasks (high priority or blocked)
151
+ */
152
+ function getCriticalTasksCount(structure) {
153
+ let criticalCount = 0;
154
+ for (const section of structure.sections) {
155
+ for (const task of section.tasks) {
156
+ if (task.status !== 'completed' &&
157
+ (task.priority === 'critical' || task.priority === 'high' || task.status === 'blocked')) {
158
+ criticalCount++;
159
+ }
160
+ }
161
+ }
162
+ return criticalCount;
163
+ }
164
+ /**
165
+ * Calculate priority weighted completion score
166
+ */
167
+ function calculatePriorityWeightedScore(structure) {
168
+ if (structure.summary.total === 0)
169
+ return 100;
170
+ const priorityWeights = {
171
+ critical: 4,
172
+ high: 3,
173
+ medium: 2,
174
+ low: 1
175
+ };
176
+ let totalWeightedTasks = 0;
177
+ let completedWeightedTasks = 0;
178
+ for (const section of structure.sections) {
179
+ for (const task of section.tasks) {
180
+ const weight = priorityWeights[task.priority] || 1;
181
+ totalWeightedTasks += weight;
182
+ if (task.status === 'completed') {
183
+ completedWeightedTasks += weight;
184
+ }
185
+ }
186
+ }
187
+ return totalWeightedTasks > 0 ? (completedWeightedTasks / totalWeightedTasks) * 100 : 100;
188
+ }
87
189
  /**
88
190
  * Parse TODO.md file into structured format
191
+ * Supports both TODO.md and todo.md for compatibility
89
192
  */
90
193
  async function parseTodoFile(todoPath) {
91
194
  try {
92
- const fs = await import('fs/promises');
93
- const content = await fs.readFile(todoPath, 'utf-8');
195
+ const { readFile } = await import('fs/promises');
196
+ const content = await readFile(todoPath, 'utf-8');
94
197
  const lines = content.split('\n');
95
198
  const structure = {
96
199
  title: 'TODO Management',
@@ -179,16 +282,45 @@ async function parseTodoFile(todoPath) {
179
282
  }
180
283
  }
181
284
  /**
182
- * Convert structured TODO back to markdown format
285
+ * Convert structured TODO back to markdown format with dynamic health scoring
183
286
  */
184
- function formatTodoAsMarkdown(structure) {
287
+ async function formatTodoAsMarkdown(structure, projectPath) {
185
288
  const lines = [];
186
- // Title and metadata
187
- lines.push(`# ${structure.title}`);
188
- lines.push('');
189
- lines.push(`**Last Updated**: ${new Date(structure.lastUpdated).toLocaleDateString()}`);
190
- lines.push(`**Progress**: ${structure.summary.completed}/${structure.summary.total} tasks completed (${Math.round((structure.summary.completed / structure.summary.total) * 100) || 0}%)`);
191
- lines.push('');
289
+ // Dynamic health scoring header if projectPath available
290
+ if (projectPath) {
291
+ try {
292
+ const { ProjectHealthScoring } = await import('../utils/project-health-scoring.js');
293
+ const healthScoring = new ProjectHealthScoring(projectPath);
294
+ // Update task completion score
295
+ const criticalTasks = getCriticalTasksCount(structure);
296
+ const priorityWeightedScore = calculatePriorityWeightedScore(structure);
297
+ await healthScoring.updateTaskCompletionScore({
298
+ completed: structure.summary.completed,
299
+ total: structure.summary.total,
300
+ criticalTasksRemaining: criticalTasks,
301
+ priorityWeightedScore: priorityWeightedScore
302
+ });
303
+ // Generate dynamic health header
304
+ const healthHeader = await healthScoring.generateScoreDisplay();
305
+ lines.push(healthHeader);
306
+ }
307
+ catch (error) {
308
+ // Fallback to traditional header if health scoring fails
309
+ lines.push(`# ${structure.title}`);
310
+ lines.push('');
311
+ lines.push(`**Last Updated**: ${new Date(structure.lastUpdated).toLocaleDateString()}`);
312
+ lines.push(`**Progress**: ${structure.summary.completed}/${structure.summary.total} tasks completed (${Math.round((structure.summary.completed / structure.summary.total) * 100) || 0}%)`);
313
+ lines.push('');
314
+ }
315
+ }
316
+ else {
317
+ // Traditional header when no project path
318
+ lines.push(`# ${structure.title}`);
319
+ lines.push('');
320
+ lines.push(`**Last Updated**: ${new Date(structure.lastUpdated).toLocaleDateString()}`);
321
+ lines.push(`**Progress**: ${structure.summary.completed}/${structure.summary.total} tasks completed (${Math.round((structure.summary.completed / structure.summary.total) * 100) || 0}%)`);
322
+ lines.push('');
323
+ }
192
324
  // Progress summary
193
325
  lines.push('## 📊 Progress Summary');
194
326
  lines.push('');
@@ -309,24 +441,229 @@ function addTasksToStructure(structure, newTasks, sectionName) {
309
441
  updatedStructure.lastUpdated = new Date().toISOString();
310
442
  return updatedStructure;
311
443
  }
444
+ /**
445
+ * Interactive confirmation helper for operations that need human approval
446
+ */
447
+ function generateInteractiveConfirmation(operation, details) {
448
+ switch (operation) {
449
+ case 'delete_task':
450
+ return `# ⚠️ Task Deletion Confirmation Required
451
+
452
+ ## Tasks to Delete:
453
+ ${details.tasks.map((task) => `- **${task.title}** (${task.status}${task.priority ? `, ${task.priority} priority` : ''})`).join('\n')}
454
+
455
+ ${details.hasNotes ? '\n## Notes that will be lost:\n' + details.tasks.filter((t) => t.notes).map((task) => `- **${task.title}**: ${task.notes}`).join('\n') : ''}
456
+
457
+ ## ⚠️ This action cannot be undone!
458
+
459
+ **To proceed with deletion, call the operation again with \`force: true\`**
460
+
461
+ **Alternatives:**
462
+ - Change status to 'cancelled' instead of deleting
463
+ - Archive tasks using the archive operation
464
+ - Move tasks to a "Completed" or "Archive" section`;
465
+ case 'edit_task':
466
+ return `# 📝 Task Edit Confirmation
467
+
468
+ ## Current Task:
469
+ - **Title**: ${details.current.title}
470
+ - **Status**: ${details.current.status}
471
+ - **Priority**: ${details.current.priority || 'none'}
472
+ - **Category**: ${details.current.category || 'none'}
473
+ - **Assignee**: ${details.current.assignee || 'unassigned'}
474
+ - **Notes**: ${details.current.notes || 'none'}
475
+
476
+ ## Proposed Changes:
477
+ ${Object.entries(details.changes).map(([key, value]) => `- **${key}**: "${details.current[key] || 'none'}" → "${value}"`).join('\n')}
478
+
479
+ **To proceed with these changes, call the operation again with \`confirmChanges: false\`**
480
+
481
+ **Note**: You can also call with \`interactive: false\` to skip this confirmation for future edits.`;
482
+ case 'move_task':
483
+ return `# 🔄 Task Move Confirmation
484
+
485
+ ## Task to Move:
486
+ **${details.task.title}** (${details.task.status})
487
+
488
+ ## Move Details:
489
+ - **From**: "${details.currentSection}"
490
+ - **To**: "${details.targetSection}"${details.position !== undefined ? `\n- **Position**: ${details.position}` : ''}
491
+
492
+ ${details.sectionExists ? '' : '\n⚠️ **Target section does not exist and will be created**'}
493
+
494
+ **Available Sections:**
495
+ ${details.availableSections.map((section, index) => `${index + 1}. ${section}`).join('\n')}
496
+
497
+ **To proceed, call the operation again with \`interactive: false\`**
498
+
499
+ **To choose a different section**, update the \`targetSection\` parameter and try again.`;
500
+ case 'manage_section':
501
+ return `# 🗂️ Section Management Confirmation
502
+
503
+ ## Action: ${details.action.toUpperCase()}
504
+ ## Section: "${details.sectionName}"
505
+
506
+ ${details.action === 'delete' ? `
507
+ ## ⚠️ Section Deletion Warning:
508
+ - **Tasks in section**: ${details.taskCount}
509
+ - **Preserve tasks**: ${details.preserveTasks}
510
+
511
+ ${details.preserveTasks ?
512
+ `Tasks will be moved to: "${details.targetSection || 'General Tasks'}"` :
513
+ '**ALL TASKS WILL BE DELETED!**'}
514
+
515
+ ## Available target sections:
516
+ ${details.availableSections.filter((s) => s !== details.sectionName).map((section, index) => `${index + 1}. ${section}`).join('\n')}
517
+ ` : ''}
518
+
519
+ ${details.action === 'rename' ? `
520
+ ## Rename Details:
521
+ - **Current name**: "${details.sectionName}"
522
+ - **New name**: "${details.newName}"
523
+ ` : ''}
524
+
525
+ **To proceed, call the operation again with \`force: true\`**`;
526
+ default:
527
+ return `# ❓ Unknown Operation: ${operation}\n\nPlease review the operation details and try again.`;
528
+ }
529
+ }
530
+ /**
531
+ * Find task by ID in TODO structure
532
+ */
533
+ function findTaskById(structure, taskId) {
534
+ for (const section of structure.sections) {
535
+ const task = section.tasks.find(t => t.id === taskId);
536
+ if (task) {
537
+ return { task, sectionName: section.name };
538
+ }
539
+ }
540
+ return null;
541
+ }
542
+ /**
543
+ * Search tasks by criteria
544
+ */
545
+ function searchTasks(structure, query, filters) {
546
+ const results = [];
547
+ for (const section of structure.sections) {
548
+ for (const task of section.tasks) {
549
+ let matches = true;
550
+ // Text query search
551
+ if (query) {
552
+ const searchText = `${task.title} ${task.notes || ''}`.toLowerCase();
553
+ matches = matches && searchText.includes(query.toLowerCase());
554
+ }
555
+ // Apply filters
556
+ if (filters) {
557
+ if (filters.status && task.status !== filters.status)
558
+ matches = false;
559
+ if (filters.priority && task.priority !== filters.priority)
560
+ matches = false;
561
+ if (filters.assignee && task.assignee !== filters.assignee)
562
+ matches = false;
563
+ if (filters.section && section.name !== filters.section)
564
+ matches = false;
565
+ if (filters.hasNotes !== undefined && Boolean(task.notes) !== filters.hasNotes)
566
+ matches = false;
567
+ if (filters.hasDependencies !== undefined && Boolean(task.dependencies?.length) !== filters.hasDependencies)
568
+ matches = false;
569
+ }
570
+ if (matches) {
571
+ results.push({ task, sectionName: section.name });
572
+ }
573
+ }
574
+ }
575
+ return results;
576
+ }
577
+ /**
578
+ * Check if TODO.md needs health scoring initialization
579
+ */
580
+ async function checkNeedsScoreInitialization(todoPath, projectPath) {
581
+ if (!projectPath) {
582
+ return false; // Can't initialize scoring without project path
583
+ }
584
+ try {
585
+ const { readFile } = await import('fs/promises');
586
+ const content = await readFile(todoPath, 'utf-8');
587
+ // Check if file has health scoring header
588
+ const lines = content.split('\n');
589
+ const hasHealthHeader = lines.some(line => line.includes('Project Health Dashboard') ||
590
+ line.includes('Overall Project Health') ||
591
+ line.includes('Health Metrics'));
592
+ return !hasHealthHeader;
593
+ }
594
+ catch (error) {
595
+ if (error.code === 'ENOENT') {
596
+ // File doesn't exist, will need scoring when created
597
+ return true;
598
+ }
599
+ // Other errors, assume no scoring needed to avoid failures
600
+ return false;
601
+ }
602
+ }
312
603
  /**
313
604
  * Main TODO management function
314
605
  */
315
- export async function manageTodo(args) {
606
+ export async function manageTodo(args, projectPath) {
316
607
  try {
317
608
  const validatedArgs = ManageTodoSchema.parse(args);
609
+ // Initialize knowledge graph manager
610
+ const kgManager = new KnowledgeGraphManager();
611
+ let intentId;
612
+ // Track operation intent in knowledge graph
613
+ if (['update_status', 'add_tasks', 'merge_adr_updates', 'sync_progress'].includes(validatedArgs.operation)) {
614
+ intentId = await kgManager.createIntent(`TODO Management: ${validatedArgs.operation}`, [`Execute ${validatedArgs.operation} operation`, 'Update TODO.md file'], 'medium');
615
+ }
616
+ // Resolve absolute TODO path consistently
617
+ const { resolve } = await import('path');
618
+ const { readFile, access, writeFile } = await import('fs/promises');
619
+ // Support both TODO.md and todo.md for backward compatibility
620
+ let todoPath = validatedArgs.todoPath;
621
+ const basePath = projectPath || process.cwd();
622
+ // If path is just the filename, check both cases
623
+ if (todoPath === 'TODO.md' || todoPath === 'todo.md') {
624
+ const upperPath = resolve(basePath, 'TODO.md');
625
+ const lowerPath = resolve(basePath, 'todo.md');
626
+ // Check which exists
627
+ try {
628
+ await access(upperPath);
629
+ todoPath = 'TODO.md';
630
+ }
631
+ catch {
632
+ try {
633
+ await access(lowerPath);
634
+ todoPath = 'todo.md';
635
+ }
636
+ catch {
637
+ // Neither exists, use standard TODO.md
638
+ todoPath = 'TODO.md';
639
+ }
640
+ }
641
+ }
642
+ const absoluteTodoPath = resolve(basePath, todoPath);
643
+ // Check if file exists and has health scoring header
644
+ const needsScoreInitialization = await checkNeedsScoreInitialization(absoluteTodoPath, projectPath);
318
645
  switch (validatedArgs.operation) {
319
646
  case 'update_status': {
320
- const structure = await parseTodoFile(validatedArgs.todoPath);
647
+ const structure = await parseTodoFile(absoluteTodoPath);
321
648
  const updatedStructure = updateTaskStatuses(structure, validatedArgs.updates);
322
- const markdown = formatTodoAsMarkdown(updatedStructure);
323
- const fs = await import('fs/promises');
324
- await fs.writeFile(validatedArgs.todoPath, markdown, 'utf-8');
649
+ const markdown = await formatTodoAsMarkdown(updatedStructure, projectPath);
650
+ await writeFile(absoluteTodoPath, markdown, 'utf-8');
651
+ // Track operation in knowledge graph
652
+ if (intentId) {
653
+ const tasksModified = validatedArgs.updates.map(u => u.taskId);
654
+ await kgManager.addToolExecution(intentId, 'manage_todo_update_status', validatedArgs, {
655
+ updatedTasks: validatedArgs.updates.length,
656
+ tasksModified,
657
+ structureSummary: updatedStructure.summary
658
+ }, true, [], tasksModified, undefined);
659
+ // Update TODO snapshot
660
+ await kgManager.updateTodoSnapshot(intentId, markdown);
661
+ }
325
662
  return {
326
663
  content: [{
327
664
  type: 'text',
328
665
  text: `# Task Status Updated
329
-
666
+ ${needsScoreInitialization ? '\n🎯 **Health scoring header automatically added to TODO.md**\n' : ''}
330
667
  ## Updates Applied
331
668
  ${validatedArgs.updates.map(update => `- Task \`${update.taskId}\`: ${update.status}${update.notes ? ` (${update.notes})` : ''}`).join('\n')}
332
669
 
@@ -336,16 +673,27 @@ ${validatedArgs.updates.map(update => `- Task \`${update.taskId}\`: ${update.sta
336
673
  - ⏳ Pending: ${updatedStructure.summary.pending}
337
674
  - 🚫 Blocked: ${updatedStructure.summary.blocked}
338
675
 
339
- Updated TODO file: \`${validatedArgs.todoPath}\``
676
+ Updated TODO file: \`${absoluteTodoPath}\``
340
677
  }]
341
678
  };
342
679
  }
343
680
  case 'add_tasks': {
344
- const structure = await parseTodoFile(validatedArgs.todoPath);
681
+ const structure = await parseTodoFile(absoluteTodoPath);
345
682
  const updatedStructure = addTasksToStructure(structure, validatedArgs.tasks, validatedArgs.section);
346
- const markdown = formatTodoAsMarkdown(updatedStructure);
347
- const fs = await import('fs/promises');
348
- await fs.writeFile(validatedArgs.todoPath, markdown, 'utf-8');
683
+ const markdown = await formatTodoAsMarkdown(updatedStructure, projectPath);
684
+ await writeFile(absoluteTodoPath, markdown, 'utf-8');
685
+ // Track operation in knowledge graph
686
+ if (intentId) {
687
+ const tasksCreated = validatedArgs.tasks.map(task => task.title);
688
+ await kgManager.addToolExecution(intentId, 'manage_todo_add_tasks', validatedArgs, {
689
+ addedTasks: validatedArgs.tasks.length,
690
+ section: validatedArgs.section || 'default',
691
+ tasksCreated,
692
+ structureSummary: updatedStructure.summary
693
+ }, true, tasksCreated, [], undefined);
694
+ // Update TODO snapshot
695
+ await kgManager.updateTodoSnapshot(intentId, markdown);
696
+ }
349
697
  return {
350
698
  content: [{
351
699
  type: 'text',
@@ -360,39 +708,78 @@ ${validatedArgs.tasks.map((task) => `- ${task.title} (${task.status}${task.prior
360
708
  - Total Tasks: ${updatedStructure.summary.total}
361
709
  - New Pending: ${validatedArgs.tasks.filter(t => t.status === 'pending').length}
362
710
 
363
- Updated TODO file: \`${validatedArgs.todoPath}\``
711
+ Updated TODO file: \`${absoluteTodoPath}\``
364
712
  }]
365
713
  };
366
714
  }
367
715
  case 'merge_adr_updates': {
368
716
  // Get current TODO structure
369
- const currentStructure = await parseTodoFile(validatedArgs.todoPath);
370
- // Get ADR-generated tasks using existing tool
371
- // Note: This would require integration with the existing generate_adr_todo tool
372
- // For now, we'll create a placeholder implementation
717
+ const currentStructure = await parseTodoFile(absoluteTodoPath);
718
+ // Create backup of current TODO
719
+ const backupPath = `${absoluteTodoPath}.backup.${Date.now()}`;
720
+ try {
721
+ const currentContent = await readFile(absoluteTodoPath, 'utf-8');
722
+ await writeFile(backupPath, currentContent, 'utf-8');
723
+ }
724
+ catch (error) {
725
+ // If backup fails, continue but warn
726
+ console.error(`Warning: Could not create backup at ${backupPath}:`, error);
727
+ }
728
+ // For now, return analysis with preservation logic
729
+ // This maintains existing structure while indicating merge capability
730
+ let mergedStructure = { ...currentStructure };
731
+ // Apply preservation logic
732
+ if (validatedArgs.preserveCompleted) {
733
+ // Keep completed tasks as-is
734
+ mergedStructure.sections.forEach(section => {
735
+ section.tasks = section.tasks.filter(task => task.status !== 'completed' || task.adrSource);
736
+ });
737
+ }
738
+ if (validatedArgs.preserveCustom) {
739
+ // Keep tasks without ADR source
740
+ mergedStructure.sections.forEach(section => {
741
+ section.tasks = section.tasks.filter(task => !task.adrSource || task.status === 'completed');
742
+ });
743
+ }
744
+ // Update last modified timestamp
745
+ mergedStructure.lastUpdated = new Date().toISOString();
746
+ // Write merged structure back
747
+ const markdown = await formatTodoAsMarkdown(mergedStructure, projectPath);
748
+ await writeFile(absoluteTodoPath, markdown, 'utf-8');
373
749
  return {
374
750
  content: [{
375
751
  type: 'text',
376
- text: `# ADR Merge Analysis
752
+ text: `# ADR Merge Completed
753
+ ${needsScoreInitialization ? '\n🎯 **Health scoring header automatically added to TODO.md**\n' : ''}
754
+ ## Merge Results
755
+ - **Source**: ${absoluteTodoPath}
756
+ - **Backup**: ${backupPath}
757
+ - **Preservation Applied**: ${validatedArgs.preserveCompleted ? 'Completed tasks preserved' : 'All tasks merged'}
377
758
 
378
759
  ## Current TODO Status
379
- - Total Tasks: ${currentStructure.summary.total}
380
- - Completed: ${currentStructure.summary.completed}
760
+ - Total Tasks: ${mergedStructure.summary.total}
761
+ - Completed: ${mergedStructure.summary.completed}
762
+ - In Progress: ${mergedStructure.summary.inProgress}
763
+ - Pending: ${mergedStructure.summary.pending}
764
+ - Blocked: ${mergedStructure.summary.blocked}
765
+
766
+ ## Preservation Settings
381
767
  - Preserve Completed: ${validatedArgs.preserveCompleted}
382
768
  - Preserve Custom: ${validatedArgs.preserveCustom}
383
769
 
384
770
  ## Next Steps
385
- 1. Review ADR-generated tasks from \`generate_adr_todo\`
386
- 2. Identify new tasks not in current TODO
387
- 3. Merge while preserving settings
388
- 4. Update TODO file
771
+ 1. Review merged TODO.md content
772
+ 2. Use \`manage_todo\` to add new ADR-derived tasks if needed
773
+ 3. Update task statuses using \`update_status\` operation
774
+ 4. Use \`compare_adr_progress\` to validate implementation
389
775
 
390
- *Full merge implementation requires integration with ADR generation results*`
776
+ *Note: Advanced ADR integration requires additional development*
777
+ *Current implementation focuses on preserving existing content*`
391
778
  }]
392
779
  };
393
780
  }
394
781
  case 'sync_progress': {
395
- const structure = await parseTodoFile(validatedArgs.todoPath);
782
+ const structure = await parseTodoFile(absoluteTodoPath);
396
783
  return {
397
784
  content: [{
398
785
  type: 'text',
@@ -417,7 +804,7 @@ Updated TODO file: \`${validatedArgs.todoPath}\``
417
804
  };
418
805
  }
419
806
  case 'analyze_progress': {
420
- const structure = await parseTodoFile(validatedArgs.todoPath);
807
+ const structure = await parseTodoFile(absoluteTodoPath);
421
808
  // Calculate progress metrics
422
809
  const totalTasks = structure.summary.total;
423
810
  const completionRate = totalTasks > 0 ? (structure.summary.completed / totalTasks) * 100 : 0;
@@ -458,6 +845,396 @@ ${completionRate > 80 ? '- 🎉 Great progress! Consider adding new tasks' : ''}
458
845
  }]
459
846
  };
460
847
  }
848
+ case 'delete_task': {
849
+ const structure = await parseTodoFile(absoluteTodoPath);
850
+ // Find tasks to delete
851
+ const tasksToDelete = validatedArgs.taskIds.map(id => findTaskById(structure, id)).filter(Boolean);
852
+ if (tasksToDelete.length === 0) {
853
+ return {
854
+ content: [{
855
+ type: 'text',
856
+ text: `# ❌ No Tasks Found\n\nNone of the specified task IDs were found:\n${validatedArgs.taskIds.map(id => `- ${id}`).join('\n')}\n\nUse \`search_tasks\` to find available task IDs.`
857
+ }]
858
+ };
859
+ }
860
+ if (tasksToDelete.length !== validatedArgs.taskIds.length) {
861
+ const foundIds = tasksToDelete.map(t => t.task.id);
862
+ const missingIds = validatedArgs.taskIds.filter(id => !foundIds.includes(id));
863
+ return {
864
+ content: [{
865
+ type: 'text',
866
+ text: `# ⚠️ Some Tasks Not Found\n\n**Found tasks:**\n${foundIds.map(id => `- ${id}`).join('\n')}\n\n**Missing tasks:**\n${missingIds.map(id => `- ${id}`).join('\n')}\n\nPlease verify task IDs and try again.`
867
+ }]
868
+ };
869
+ }
870
+ // Interactive confirmation unless force is true
871
+ if (!validatedArgs.force) {
872
+ const hasNotes = tasksToDelete.some(t => t.task.notes);
873
+ return {
874
+ content: [{
875
+ type: 'text',
876
+ text: generateInteractiveConfirmation('delete_task', {
877
+ tasks: tasksToDelete.map(t => t.task),
878
+ hasNotes
879
+ })
880
+ }]
881
+ };
882
+ }
883
+ // Perform deletion
884
+ const updatedStructure = { ...structure };
885
+ let deletedCount = 0;
886
+ for (const section of updatedStructure.sections) {
887
+ const initialLength = section.tasks.length;
888
+ section.tasks = section.tasks.filter(task => !validatedArgs.taskIds.includes(task.id));
889
+ deletedCount += initialLength - section.tasks.length;
890
+ }
891
+ // Update summary counts
892
+ for (const { task } of tasksToDelete) {
893
+ updatedStructure.summary.total--;
894
+ updatedStructure.summary[task.status]--;
895
+ }
896
+ updatedStructure.lastUpdated = new Date().toISOString();
897
+ // Write updated structure
898
+ const markdown = await formatTodoAsMarkdown(updatedStructure, projectPath);
899
+ await writeFile(absoluteTodoPath, markdown, 'utf-8');
900
+ return {
901
+ content: [{
902
+ type: 'text',
903
+ text: `# ✅ Tasks Deleted Successfully
904
+
905
+ ## Deleted ${deletedCount} Tasks:
906
+ ${tasksToDelete.map(({ task, sectionName }) => `- **${task.title}** from "${sectionName}" section`).join('\n')}
907
+
908
+ ## Updated Progress:
909
+ - Total Tasks: ${updatedStructure.summary.total}
910
+ - Completed: ${updatedStructure.summary.completed}
911
+ - In Progress: ${updatedStructure.summary.inProgress}
912
+ - Pending: ${updatedStructure.summary.pending}
913
+
914
+ Updated TODO file: \`${absoluteTodoPath}\``
915
+ }]
916
+ };
917
+ }
918
+ case 'edit_task': {
919
+ const structure = await parseTodoFile(absoluteTodoPath);
920
+ const taskResult = findTaskById(structure, validatedArgs.taskId);
921
+ if (!taskResult) {
922
+ return {
923
+ content: [{
924
+ type: 'text',
925
+ text: `# ❌ Task Not Found\n\nTask ID "${validatedArgs.taskId}" was not found.\n\nUse \`search_tasks\` to find available task IDs.`
926
+ }]
927
+ };
928
+ }
929
+ const { task: currentTask } = taskResult;
930
+ // Interactive confirmation if enabled
931
+ if (validatedArgs.interactive && validatedArgs.confirmChanges) {
932
+ return {
933
+ content: [{
934
+ type: 'text',
935
+ text: generateInteractiveConfirmation('edit_task', {
936
+ current: currentTask,
937
+ changes: validatedArgs.updates
938
+ })
939
+ }]
940
+ };
941
+ }
942
+ // Apply updates
943
+ const updatedStructure = { ...structure };
944
+ for (const section of updatedStructure.sections) {
945
+ const taskIndex = section.tasks.findIndex(t => t.id === validatedArgs.taskId);
946
+ if (taskIndex >= 0) {
947
+ const updatedTask = { ...section.tasks[taskIndex] };
948
+ // Apply each update
949
+ Object.entries(validatedArgs.updates).forEach(([key, value]) => {
950
+ if (value !== undefined) {
951
+ updatedTask[key] = value;
952
+ }
953
+ });
954
+ updatedTask.updatedAt = new Date().toISOString();
955
+ section.tasks[taskIndex] = updatedTask;
956
+ break;
957
+ }
958
+ }
959
+ updatedStructure.lastUpdated = new Date().toISOString();
960
+ // Write updated structure
961
+ const markdown = await formatTodoAsMarkdown(updatedStructure, projectPath);
962
+ await writeFile(absoluteTodoPath, markdown, 'utf-8');
963
+ return {
964
+ content: [{
965
+ type: 'text',
966
+ text: `# ✅ Task Updated Successfully
967
+
968
+ ## Updated Task: "${currentTask.title}"
969
+
970
+ ## Changes Applied:
971
+ ${Object.entries(validatedArgs.updates).filter(([, value]) => value !== undefined).map(([key, value]) => `- **${key}**: "${currentTask[key] || 'none'}" → "${value}"`).join('\n')}
972
+
973
+ Updated TODO file: \`${absoluteTodoPath}\``
974
+ }]
975
+ };
976
+ }
977
+ case 'move_task': {
978
+ const structure = await parseTodoFile(absoluteTodoPath);
979
+ const taskResult = findTaskById(structure, validatedArgs.taskId);
980
+ if (!taskResult) {
981
+ return {
982
+ content: [{
983
+ type: 'text',
984
+ text: `# ❌ Task Not Found\n\nTask ID "${validatedArgs.taskId}" was not found.\n\nUse \`search_tasks\` to find available task IDs.`
985
+ }]
986
+ };
987
+ }
988
+ const { task, sectionName: currentSection } = taskResult;
989
+ const availableSections = structure.sections.map(s => s.name);
990
+ // Interactive section selection if needed
991
+ if (validatedArgs.interactive && !validatedArgs.targetSection) {
992
+ return {
993
+ content: [{
994
+ type: 'text',
995
+ text: `# 🔄 Select Target Section
996
+
997
+ ## Task to Move:
998
+ **${task.title}** (currently in "${currentSection}")
999
+
1000
+ ## Available Sections:
1001
+ ${availableSections.map((section, index) => `${index + 1}. ${section}`).join('\n')}
1002
+
1003
+ **To proceed**, call the operation again with \`targetSection\` parameter set to your chosen section name.
1004
+
1005
+ **To skip interactive mode**, set \`interactive: false\`.`
1006
+ }]
1007
+ };
1008
+ }
1009
+ const targetSection = validatedArgs.targetSection || 'General Tasks';
1010
+ const sectionExists = availableSections.includes(targetSection);
1011
+ // Interactive confirmation if enabled
1012
+ if (validatedArgs.interactive) {
1013
+ return {
1014
+ content: [{
1015
+ type: 'text',
1016
+ text: generateInteractiveConfirmation('move_task', {
1017
+ task,
1018
+ currentSection,
1019
+ targetSection,
1020
+ position: validatedArgs.position,
1021
+ sectionExists,
1022
+ availableSections
1023
+ })
1024
+ }]
1025
+ };
1026
+ }
1027
+ // Perform the move
1028
+ const updatedStructure = { ...structure };
1029
+ // Remove from current section
1030
+ for (const section of updatedStructure.sections) {
1031
+ if (section.name === currentSection) {
1032
+ section.tasks = section.tasks.filter(t => t.id !== validatedArgs.taskId);
1033
+ break;
1034
+ }
1035
+ }
1036
+ // Add to target section
1037
+ let targetSectionObj = updatedStructure.sections.find(s => s.name === targetSection);
1038
+ if (!targetSectionObj) {
1039
+ if (validatedArgs.createSection) {
1040
+ targetSectionObj = { name: targetSection, tasks: [] };
1041
+ updatedStructure.sections.push(targetSectionObj);
1042
+ }
1043
+ else {
1044
+ return {
1045
+ content: [{
1046
+ type: 'text',
1047
+ text: `# ❌ Target Section Not Found\n\nSection "${targetSection}" does not exist.\n\n**Options:**\n- Choose an existing section\n- Set \`createSection: true\` to create the section\n\nAvailable sections:\n${availableSections.map(s => `- ${s}`).join('\n')}`
1048
+ }]
1049
+ };
1050
+ }
1051
+ }
1052
+ // Insert at specified position or at the end
1053
+ if (validatedArgs.position !== undefined && validatedArgs.position >= 0) {
1054
+ targetSectionObj.tasks.splice(validatedArgs.position, 0, task);
1055
+ }
1056
+ else {
1057
+ targetSectionObj.tasks.push(task);
1058
+ }
1059
+ updatedStructure.lastUpdated = new Date().toISOString();
1060
+ // Write updated structure
1061
+ const markdown = await formatTodoAsMarkdown(updatedStructure, projectPath);
1062
+ await writeFile(absoluteTodoPath, markdown, 'utf-8');
1063
+ return {
1064
+ content: [{
1065
+ type: 'text',
1066
+ text: `# ✅ Task Moved Successfully
1067
+
1068
+ ## Task: "${task.title}"
1069
+ - **From**: "${currentSection}"
1070
+ - **To**: "${targetSection}"${validatedArgs.position !== undefined ? `\n- **Position**: ${validatedArgs.position}` : ''}
1071
+
1072
+ ${!sectionExists ? '\n🆕 **Target section was created**' : ''}
1073
+
1074
+ Updated TODO file: \`${absoluteTodoPath}\``
1075
+ }]
1076
+ };
1077
+ }
1078
+ case 'manage_section': {
1079
+ const structure = await parseTodoFile(absoluteTodoPath);
1080
+ const sectionExists = structure.sections.some(s => s.name === validatedArgs.sectionName);
1081
+ const availableSections = structure.sections.map(s => s.name);
1082
+ if (validatedArgs.action === 'create') {
1083
+ if (sectionExists) {
1084
+ return {
1085
+ content: [{
1086
+ type: 'text',
1087
+ text: `# ❌ Section Already Exists\n\nSection "${validatedArgs.sectionName}" already exists.\n\nExisting sections:\n${availableSections.map(s => `- ${s}`).join('\n')}`
1088
+ }]
1089
+ };
1090
+ }
1091
+ // Create new section
1092
+ const updatedStructure = { ...structure };
1093
+ updatedStructure.sections.push({ name: validatedArgs.sectionName, tasks: [] });
1094
+ updatedStructure.lastUpdated = new Date().toISOString();
1095
+ const markdown = await formatTodoAsMarkdown(updatedStructure, projectPath);
1096
+ await writeFile(absoluteTodoPath, markdown, 'utf-8');
1097
+ return {
1098
+ content: [{
1099
+ type: 'text',
1100
+ text: `# ✅ Section Created Successfully\n\n**New section**: "${validatedArgs.sectionName}"\n\nUpdated TODO file: \`${absoluteTodoPath}\``
1101
+ }]
1102
+ };
1103
+ }
1104
+ if (!sectionExists) {
1105
+ return {
1106
+ content: [{
1107
+ type: 'text',
1108
+ text: `# ❌ Section Not Found\n\nSection "${validatedArgs.sectionName}" does not exist.\n\nAvailable sections:\n${availableSections.map(s => `- ${s}`).join('\n')}`
1109
+ }]
1110
+ };
1111
+ }
1112
+ const targetSection = structure.sections.find(s => s.name === validatedArgs.sectionName);
1113
+ // Interactive confirmation unless force is true
1114
+ if (!validatedArgs.force) {
1115
+ const details = {
1116
+ action: validatedArgs.action,
1117
+ sectionName: validatedArgs.sectionName,
1118
+ availableSections
1119
+ };
1120
+ if (validatedArgs.action === 'delete') {
1121
+ details.taskCount = targetSection.tasks.length;
1122
+ details.preserveTasks = validatedArgs.preserveTasks;
1123
+ details.targetSection = availableSections.find(s => s !== validatedArgs.sectionName) || 'General Tasks';
1124
+ }
1125
+ if (validatedArgs.action === 'rename') {
1126
+ details.newName = validatedArgs.newName;
1127
+ }
1128
+ return {
1129
+ content: [{
1130
+ type: 'text',
1131
+ text: generateInteractiveConfirmation('manage_section', details)
1132
+ }]
1133
+ };
1134
+ }
1135
+ // Perform the action
1136
+ const updatedStructure = { ...structure };
1137
+ if (validatedArgs.action === 'delete') {
1138
+ const sectionIndex = updatedStructure.sections.findIndex(s => s.name === validatedArgs.sectionName);
1139
+ const tasksToMove = targetSection.tasks;
1140
+ if (validatedArgs.preserveTasks && tasksToMove.length > 0) {
1141
+ // Move tasks to another section
1142
+ const fallbackSectionName = availableSections.find(s => s !== validatedArgs.sectionName) || 'General Tasks';
1143
+ let fallbackSection = updatedStructure.sections.find(s => s.name === fallbackSectionName);
1144
+ if (!fallbackSection && fallbackSectionName === 'General Tasks') {
1145
+ fallbackSection = { name: 'General Tasks', tasks: [] };
1146
+ updatedStructure.sections.unshift(fallbackSection);
1147
+ }
1148
+ if (fallbackSection) {
1149
+ fallbackSection.tasks.push(...tasksToMove);
1150
+ }
1151
+ }
1152
+ else {
1153
+ // Update summary counts for deleted tasks
1154
+ for (const task of tasksToMove) {
1155
+ updatedStructure.summary.total--;
1156
+ updatedStructure.summary[task.status]--;
1157
+ }
1158
+ }
1159
+ updatedStructure.sections.splice(sectionIndex, 1);
1160
+ }
1161
+ if (validatedArgs.action === 'rename') {
1162
+ const section = updatedStructure.sections.find(s => s.name === validatedArgs.sectionName);
1163
+ section.name = validatedArgs.newName;
1164
+ }
1165
+ updatedStructure.lastUpdated = new Date().toISOString();
1166
+ const markdown = await formatTodoAsMarkdown(updatedStructure, projectPath);
1167
+ await writeFile(absoluteTodoPath, markdown, 'utf-8');
1168
+ return {
1169
+ content: [{
1170
+ type: 'text',
1171
+ text: `# ✅ Section ${validatedArgs.action.charAt(0).toUpperCase() + validatedArgs.action.slice(1)} Successful
1172
+
1173
+ ${validatedArgs.action === 'delete' ? `
1174
+ ## Deleted Section: "${validatedArgs.sectionName}"
1175
+ - **Tasks**: ${targetSection.tasks.length}
1176
+ - **Preserved**: ${validatedArgs.preserveTasks}
1177
+ ${validatedArgs.preserveTasks && targetSection.tasks.length > 0 ? `- **Moved to**: "${availableSections.find(s => s !== validatedArgs.sectionName) || 'General Tasks'}"` : ''}
1178
+ ` : ''}
1179
+
1180
+ ${validatedArgs.action === 'rename' ? `
1181
+ ## Renamed Section:
1182
+ - **From**: "${validatedArgs.sectionName}"
1183
+ - **To**: "${validatedArgs.newName}"
1184
+ ` : ''}
1185
+
1186
+ Updated TODO file: \`${absoluteTodoPath}\``
1187
+ }]
1188
+ };
1189
+ }
1190
+ case 'search_tasks': {
1191
+ const structure = await parseTodoFile(absoluteTodoPath);
1192
+ const results = searchTasks(structure, validatedArgs.query, validatedArgs.filters);
1193
+ if (results.length === 0) {
1194
+ return {
1195
+ content: [{
1196
+ type: 'text',
1197
+ text: `# 🔍 No Tasks Found
1198
+
1199
+ **Search criteria:**
1200
+ ${validatedArgs.query ? `- **Query**: "${validatedArgs.query}"` : ''}
1201
+ ${validatedArgs.filters ? Object.entries(validatedArgs.filters).filter(([, value]) => value !== undefined).map(([key, value]) => `- **${key}**: ${value}`).join('\n') : ''}
1202
+
1203
+ **Total tasks in TODO.md**: ${structure.summary.total}
1204
+
1205
+ Try adjusting your search criteria or use \`analyze_progress\` to see all tasks.`
1206
+ }]
1207
+ };
1208
+ }
1209
+ return {
1210
+ content: [{
1211
+ type: 'text',
1212
+ text: `# 🔍 Search Results
1213
+
1214
+ Found **${results.length}** tasks:
1215
+
1216
+ ${results.map(({ task, sectionName }) => {
1217
+ let taskLine = `## ${task.title}`;
1218
+ if (validatedArgs.includeMetadata) {
1219
+ taskLine += `\n- **ID**: \`${task.id}\`\n- **Section**: ${sectionName}\n- **Status**: ${task.status}`;
1220
+ if (task.priority)
1221
+ taskLine += `\n- **Priority**: ${task.priority}`;
1222
+ if (task.assignee)
1223
+ taskLine += `\n- **Assignee**: ${task.assignee}`;
1224
+ if (task.category)
1225
+ taskLine += `\n- **Category**: ${task.category}`;
1226
+ if (task.notes)
1227
+ taskLine += `\n- **Notes**: ${task.notes}`;
1228
+ if (task.dependencies?.length)
1229
+ taskLine += `\n- **Dependencies**: ${task.dependencies.join(', ')}`;
1230
+ }
1231
+ return taskLine;
1232
+ }).join('\n\n')}
1233
+
1234
+ **Search completed on**: \`${absoluteTodoPath}\``
1235
+ }]
1236
+ };
1237
+ }
461
1238
  default:
462
1239
  throw new McpAdrError(`Unknown operation: ${validatedArgs.operation}`, 'INVALID_INPUT');
463
1240
  }