mcp-adr-analysis-server 2.0.10 → 2.0.12

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 (73) 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 +340 -492
  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-v2.d.ts +55 -0
  14. package/dist/src/tools/smart-git-push-tool-v2.d.ts.map +1 -0
  15. package/dist/src/tools/smart-git-push-tool-v2.js +527 -0
  16. package/dist/src/tools/smart-git-push-tool-v2.js.map +1 -0
  17. package/dist/src/tools/smart-git-push-tool.d.ts +27 -3
  18. package/dist/src/tools/smart-git-push-tool.d.ts.map +1 -1
  19. package/dist/src/tools/smart-git-push-tool.js +490 -10
  20. package/dist/src/tools/smart-git-push-tool.js.map +1 -1
  21. package/dist/src/tools/smart-score-tool.d.ts +39 -4
  22. package/dist/src/tools/smart-score-tool.d.ts.map +1 -1
  23. package/dist/src/tools/smart-score-tool.js +94 -15
  24. package/dist/src/tools/smart-score-tool.js.map +1 -1
  25. package/dist/src/tools/todo-management-tool-v2.d.ts +329 -0
  26. package/dist/src/tools/todo-management-tool-v2.d.ts.map +1 -0
  27. package/dist/src/tools/todo-management-tool-v2.js +644 -0
  28. package/dist/src/tools/todo-management-tool-v2.js.map +1 -0
  29. package/dist/src/tools/tool-chain-orchestrator.d.ts +3 -41
  30. package/dist/src/tools/tool-chain-orchestrator.d.ts.map +1 -1
  31. package/dist/src/tools/tool-chain-orchestrator.js +10 -198
  32. package/dist/src/tools/tool-chain-orchestrator.js.map +1 -1
  33. package/dist/src/types/conversation-context.d.ts +6 -0
  34. package/dist/src/types/conversation-context.d.ts.map +1 -1
  35. package/dist/src/types/conversation-context.js +9 -1
  36. package/dist/src/types/conversation-context.js.map +1 -1
  37. package/dist/src/types/knowledge-graph-schemas.d.ts +298 -6
  38. package/dist/src/types/knowledge-graph-schemas.d.ts.map +1 -1
  39. package/dist/src/types/knowledge-graph-schemas.js +31 -3
  40. package/dist/src/types/knowledge-graph-schemas.js.map +1 -1
  41. package/dist/src/types/todo-json-schemas.d.ts +663 -0
  42. package/dist/src/types/todo-json-schemas.d.ts.map +1 -0
  43. package/dist/src/types/todo-json-schemas.js +113 -0
  44. package/dist/src/types/todo-json-schemas.js.map +1 -0
  45. package/dist/src/utils/chain-of-thought-template.d.ts.map +1 -1
  46. package/dist/src/utils/chain-of-thought-template.js.map +1 -1
  47. package/dist/src/utils/knowledge-graph-manager.d.ts +50 -0
  48. package/dist/src/utils/knowledge-graph-manager.d.ts.map +1 -1
  49. package/dist/src/utils/knowledge-graph-manager.js +209 -3
  50. package/dist/src/utils/knowledge-graph-manager.js.map +1 -1
  51. package/dist/src/utils/project-health-scoring.js +5 -5
  52. package/dist/src/utils/project-health-scoring.js.map +1 -1
  53. package/dist/src/utils/todo-json-manager.d.ts +71 -0
  54. package/dist/src/utils/todo-json-manager.d.ts.map +1 -0
  55. package/dist/src/utils/todo-json-manager.js +394 -0
  56. package/dist/src/utils/todo-json-manager.js.map +1 -0
  57. package/dist/src/utils/todo-markdown-converter.d.ts +16 -0
  58. package/dist/src/utils/todo-markdown-converter.d.ts.map +1 -0
  59. package/dist/src/utils/todo-markdown-converter.js +362 -0
  60. package/dist/src/utils/todo-markdown-converter.js.map +1 -0
  61. package/dist/src/utils/todo-migration.d.ts +47 -0
  62. package/dist/src/utils/todo-migration.d.ts.map +1 -0
  63. package/dist/src/utils/todo-migration.js +261 -0
  64. package/dist/src/utils/todo-migration.js.map +1 -0
  65. package/package.json +1 -1
  66. package/dist/src/tools/human-override-tool.d.ts +0 -42
  67. package/dist/src/tools/human-override-tool.d.ts.map +0 -1
  68. package/dist/src/tools/human-override-tool.js +0 -431
  69. package/dist/src/tools/human-override-tool.js.map +0 -1
  70. package/dist/src/tools/todo-management-tool.d.ts +0 -344
  71. package/dist/src/tools/todo-management-tool.d.ts.map +0 -1
  72. package/dist/src/tools/todo-management-tool.js +0 -1249
  73. package/dist/src/tools/todo-management-tool.js.map +0 -1
@@ -1,1249 +0,0 @@
1
- /**
2
- * MCP Tool for TODO.md management with incremental updates and task lifecycle
3
- * Provides smart merging, status tracking, and preservation of custom tasks
4
- */
5
- import { z } from 'zod';
6
- import { McpAdrError } from '../types/index.js';
7
- import { KnowledgeGraphManager } from '../utils/knowledge-graph-manager.js';
8
- // Task status enumeration
9
- const TaskStatus = z.enum(['pending', 'in_progress', 'completed', 'blocked', 'cancelled']);
10
- // Task priority enumeration
11
- const TaskPriority = z.enum(['low', 'medium', 'high', 'critical']);
12
- // Individual task schema
13
- const TaskSchema = z.object({
14
- id: z.string().describe('Unique task identifier'),
15
- title: z.string().describe('Task title/description'),
16
- status: TaskStatus.describe('Current task status'),
17
- priority: TaskPriority.optional().describe('Task priority level'),
18
- adrSource: z.string().optional().describe('Source ADR filename if task comes from ADR'),
19
- category: z.string().optional().describe('Task category (e.g., implementation, testing, docs)'),
20
- dependencies: z.array(z.string()).optional().describe('IDs of dependent tasks'),
21
- notes: z.string().optional().describe('Additional task notes'),
22
- createdAt: z.string().optional().describe('Task creation timestamp'),
23
- updatedAt: z.string().optional().describe('Last update timestamp'),
24
- estimatedHours: z.number().optional().describe('Estimated effort in hours'),
25
- assignee: z.string().optional().describe('Person assigned to task')
26
- });
27
- // TODO file structure schema
28
- const TodoStructure = z.object({
29
- title: z.string().describe('TODO file title'),
30
- lastUpdated: z.string().describe('Last update timestamp'),
31
- summary: z.object({
32
- total: z.number(),
33
- completed: z.number(),
34
- inProgress: z.number(),
35
- pending: z.number(),
36
- blocked: z.number()
37
- }).describe('Task count summary'),
38
- sections: z.array(z.object({
39
- name: z.string().describe('Section name'),
40
- tasks: z.array(TaskSchema).describe('Tasks in this section')
41
- })).describe('Organized task sections'),
42
- customContent: z.array(z.string()).optional().describe('Custom markdown content to preserve')
43
- });
44
- // Operation schemas
45
- const UpdateTaskStatusSchema = z.object({
46
- operation: z.literal('update_status'),
47
- todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
48
- updates: z.array(z.object({
49
- taskId: z.string().describe('Task ID to update'),
50
- status: TaskStatus.describe('New status'),
51
- notes: z.string().optional().describe('Update notes'),
52
- assignee: z.string().optional().describe('New assignee')
53
- })).describe('Status updates to apply')
54
- });
55
- const AddTasksSchema = z.object({
56
- operation: z.literal('add_tasks'),
57
- todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
58
- tasks: z.array(TaskSchema.omit({ id: true, createdAt: true, updatedAt: true })).describe('New tasks to add'),
59
- section: z.string().optional().describe('Section to add tasks to (will create if not exists)')
60
- });
61
- const MergeAdrSchema = z.object({
62
- operation: z.literal('merge_adr_updates'),
63
- todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
64
- adrDirectory: z.string().default('docs/adrs').describe('ADR directory path'),
65
- preserveCompleted: z.boolean().default(true).describe('Keep completed tasks'),
66
- preserveCustom: z.boolean().default(true).describe('Keep manually added tasks')
67
- });
68
- const SyncProgressSchema = z.object({
69
- operation: z.literal('sync_progress'),
70
- todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
71
- adrDirectory: z.string().default('docs/adrs').describe('ADR directory path'),
72
- updateAdrs: z.boolean().default(false).describe('Update ADR status based on TODO progress')
73
- });
74
- const AnalyzeProgressSchema = z.object({
75
- operation: z.literal('analyze_progress'),
76
- todoPath: z.string().default('TODO.md').describe('Path to TODO.md file'),
77
- timeframe: z.enum(['day', 'week', 'month', 'all']).default('week').describe('Analysis timeframe'),
78
- includeVelocity: z.boolean().default(true).describe('Include velocity metrics')
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
- });
136
- // Main operation schema
137
- const ManageTodoSchema = z.union([
138
- UpdateTaskStatusSchema,
139
- AddTasksSchema,
140
- MergeAdrSchema,
141
- SyncProgressSchema,
142
- AnalyzeProgressSchema,
143
- DeleteTaskSchema,
144
- EditTaskSchema,
145
- MoveTaskSchema,
146
- ManageSectionSchema,
147
- SearchTasksSchema
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
- }
189
- /**
190
- * Parse TODO.md file into structured format
191
- * Supports both TODO.md and todo.md for compatibility
192
- */
193
- async function parseTodoFile(todoPath) {
194
- try {
195
- const { readFile } = await import('fs/promises');
196
- const content = await readFile(todoPath, 'utf-8');
197
- const lines = content.split('\n');
198
- const structure = {
199
- title: 'TODO Management',
200
- lastUpdated: new Date().toISOString(),
201
- summary: { total: 0, completed: 0, inProgress: 0, pending: 0, blocked: 0 },
202
- sections: [],
203
- customContent: []
204
- };
205
- let currentSection = null;
206
- let taskIdCounter = 1;
207
- for (const line of lines) {
208
- const trimmed = line.trim();
209
- // Extract title
210
- if (trimmed.startsWith('# ') && !structure.title) {
211
- structure.title = trimmed.substring(2);
212
- continue;
213
- }
214
- // Extract sections
215
- if (trimmed.startsWith('## ')) {
216
- if (currentSection) {
217
- structure.sections.push(currentSection);
218
- }
219
- currentSection = {
220
- name: trimmed.substring(3),
221
- tasks: []
222
- };
223
- continue;
224
- }
225
- // Extract tasks (support various markdown formats)
226
- const taskMatch = trimmed.match(/^[-*+]\s*(\[([x\s])\])?\s*(.+)$/i);
227
- if (taskMatch && currentSection) {
228
- const [, , checkbox, taskText] = taskMatch;
229
- const isCompleted = checkbox?.toLowerCase() === 'x';
230
- // Parse task metadata from text
231
- const safeTaskText = taskText || '';
232
- const metadataMatch = safeTaskText.match(/^(.+?)\s*(?:\(([^)]+)\))?\s*(?:\[([^\]]+)\])?\s*(?:@(\w+))?\s*$/);
233
- const title = metadataMatch?.[1] || safeTaskText;
234
- const category = metadataMatch?.[2];
235
- const priority = metadataMatch?.[3];
236
- const assignee = metadataMatch?.[4];
237
- const task = {
238
- id: `task-${taskIdCounter++}`,
239
- title: (title || 'Untitled Task').trim(),
240
- status: isCompleted ? 'completed' : 'pending',
241
- ...(category && { category }),
242
- ...(priority && TaskPriority.safeParse(priority).success && { priority: priority }),
243
- ...(assignee && { assignee }),
244
- createdAt: new Date().toISOString(),
245
- updatedAt: new Date().toISOString()
246
- };
247
- currentSection.tasks.push(task);
248
- // Update summary
249
- structure.summary.total++;
250
- if (task.status === 'completed')
251
- structure.summary.completed++;
252
- else if (task.status === 'in_progress')
253
- structure.summary.inProgress++;
254
- else if (task.status === 'blocked')
255
- structure.summary.blocked++;
256
- else
257
- structure.summary.pending++;
258
- }
259
- }
260
- // Add final section
261
- if (currentSection) {
262
- structure.sections.push(currentSection);
263
- }
264
- return structure;
265
- }
266
- catch (error) {
267
- // Handle specific file not found vs other errors
268
- if (error?.code === 'ENOENT') {
269
- // If file doesn't exist, return empty structure
270
- return {
271
- title: 'TODO Management',
272
- lastUpdated: new Date().toISOString(),
273
- summary: { total: 0, completed: 0, inProgress: 0, pending: 0, blocked: 0 },
274
- sections: [{
275
- name: 'General Tasks',
276
- tasks: []
277
- }]
278
- };
279
- }
280
- // Re-throw other errors
281
- throw error;
282
- }
283
- }
284
- /**
285
- * Convert structured TODO back to markdown format with dynamic health scoring
286
- */
287
- async function formatTodoAsMarkdown(structure, projectPath) {
288
- const lines = [];
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
- }
324
- // Progress summary
325
- lines.push('## 📊 Progress Summary');
326
- lines.push('');
327
- lines.push(`- ✅ **Completed**: ${structure.summary.completed}`);
328
- lines.push(`- 🔄 **In Progress**: ${structure.summary.inProgress}`);
329
- lines.push(`- ⏳ **Pending**: ${structure.summary.pending}`);
330
- lines.push(`- 🚫 **Blocked**: ${structure.summary.blocked}`);
331
- lines.push('');
332
- // Sections and tasks
333
- for (const section of structure.sections) {
334
- lines.push(`## ${section.name}`);
335
- lines.push('');
336
- if (section.tasks.length === 0) {
337
- lines.push('*No tasks in this section*');
338
- lines.push('');
339
- continue;
340
- }
341
- for (const task of section.tasks) {
342
- const checkbox = task.status === 'completed' ? '[x]' : '[ ]';
343
- const statusIcon = {
344
- pending: '⏳',
345
- in_progress: '🔄',
346
- completed: '✅',
347
- blocked: '🚫',
348
- cancelled: '❌'
349
- }[task.status] || '⏳';
350
- let taskLine = `- ${checkbox} ${statusIcon} ${task.title}`;
351
- // Add metadata
352
- const metadata = [];
353
- if (task.category)
354
- metadata.push(task.category);
355
- if (task.priority)
356
- metadata.push(`${task.priority} priority`);
357
- if (task.assignee)
358
- metadata.push(`@${task.assignee}`);
359
- if (task.adrSource)
360
- metadata.push(`ADR: ${task.adrSource}`);
361
- if (metadata.length > 0) {
362
- taskLine += ` *(${metadata.join(', ')})*`;
363
- }
364
- if (task.notes) {
365
- taskLine += `\n > ${task.notes}`;
366
- }
367
- lines.push(taskLine);
368
- }
369
- lines.push('');
370
- }
371
- // Custom content
372
- if (structure.customContent && structure.customContent.length > 0) {
373
- lines.push('## Additional Notes');
374
- lines.push('');
375
- lines.push(...structure.customContent);
376
- lines.push('');
377
- }
378
- // Footer
379
- lines.push('---');
380
- lines.push('');
381
- lines.push(`*Generated by MCP ADR Analysis Server at ${new Date().toISOString()}*`);
382
- return lines.join('\n');
383
- }
384
- /**
385
- * Update task statuses in TODO structure
386
- */
387
- function updateTaskStatuses(structure, updates) {
388
- const updatedStructure = { ...structure };
389
- for (const update of updates) {
390
- for (const section of updatedStructure.sections) {
391
- const taskIndex = section.tasks.findIndex(task => task.id === update.taskId);
392
- if (taskIndex >= 0) {
393
- const task = { ...section.tasks[taskIndex] };
394
- const oldStatus = task.status;
395
- task.status = update.status;
396
- task.updatedAt = new Date().toISOString();
397
- if (update.notes !== undefined) {
398
- task.notes = update.notes;
399
- }
400
- if (update.assignee !== undefined) {
401
- task.assignee = update.assignee;
402
- }
403
- section.tasks[taskIndex] = task;
404
- // Update summary counts
405
- if (oldStatus !== update.status) {
406
- updatedStructure.summary[oldStatus]--;
407
- updatedStructure.summary[update.status]++;
408
- }
409
- break;
410
- }
411
- }
412
- }
413
- updatedStructure.lastUpdated = new Date().toISOString();
414
- return updatedStructure;
415
- }
416
- /**
417
- * Add new tasks to TODO structure
418
- */
419
- function addTasksToStructure(structure, newTasks, sectionName) {
420
- const updatedStructure = { ...structure };
421
- const targetSectionName = sectionName || 'General Tasks';
422
- // Find or create section
423
- let targetSection = updatedStructure.sections.find(s => s.name === targetSectionName);
424
- if (!targetSection) {
425
- targetSection = { name: targetSectionName, tasks: [] };
426
- updatedStructure.sections.push(targetSection);
427
- }
428
- // Add tasks with IDs
429
- const tasksWithIds = newTasks.map((task, index) => ({
430
- ...task,
431
- id: `task-${Date.now()}-${index}`,
432
- createdAt: new Date().toISOString(),
433
- updatedAt: new Date().toISOString()
434
- }));
435
- targetSection.tasks.push(...tasksWithIds);
436
- // Update summary
437
- updatedStructure.summary.total += tasksWithIds.length;
438
- for (const task of tasksWithIds) {
439
- updatedStructure.summary[task.status]++;
440
- }
441
- updatedStructure.lastUpdated = new Date().toISOString();
442
- return updatedStructure;
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
- }
603
- /**
604
- * Main TODO management function
605
- */
606
- export async function manageTodo(args, projectPath) {
607
- try {
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);
645
- switch (validatedArgs.operation) {
646
- case 'update_status': {
647
- const structure = await parseTodoFile(absoluteTodoPath);
648
- const updatedStructure = updateTaskStatuses(structure, validatedArgs.updates);
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
- }
662
- return {
663
- content: [{
664
- type: 'text',
665
- text: `# Task Status Updated
666
- ${needsScoreInitialization ? '\n🎯 **Health scoring header automatically added to TODO.md**\n' : ''}
667
- ## Updates Applied
668
- ${validatedArgs.updates.map(update => `- Task \`${update.taskId}\`: ${update.status}${update.notes ? ` (${update.notes})` : ''}`).join('\n')}
669
-
670
- ## Current Progress
671
- - ✅ Completed: ${updatedStructure.summary.completed}
672
- - 🔄 In Progress: ${updatedStructure.summary.inProgress}
673
- - ⏳ Pending: ${updatedStructure.summary.pending}
674
- - 🚫 Blocked: ${updatedStructure.summary.blocked}
675
-
676
- Updated TODO file: \`${absoluteTodoPath}\``
677
- }]
678
- };
679
- }
680
- case 'add_tasks': {
681
- const structure = await parseTodoFile(absoluteTodoPath);
682
- const updatedStructure = addTasksToStructure(structure, validatedArgs.tasks, validatedArgs.section);
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
- }
697
- return {
698
- content: [{
699
- type: 'text',
700
- text: `# Tasks Added Successfully
701
-
702
- ## Added ${validatedArgs.tasks.length} New Tasks
703
- ${validatedArgs.tasks.map((task) => `- ${task.title} (${task.status}${task.priority ? `, ${task.priority} priority` : ''})`).join('\n')}
704
-
705
- ## Section: ${validatedArgs.section || 'General Tasks'}
706
-
707
- ## Updated Progress
708
- - Total Tasks: ${updatedStructure.summary.total}
709
- - New Pending: ${validatedArgs.tasks.filter(t => t.status === 'pending').length}
710
-
711
- Updated TODO file: \`${absoluteTodoPath}\``
712
- }]
713
- };
714
- }
715
- case 'merge_adr_updates': {
716
- // Get current TODO structure
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');
749
- return {
750
- content: [{
751
- type: 'text',
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'}
758
-
759
- ## Current TODO Status
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
767
- - Preserve Completed: ${validatedArgs.preserveCompleted}
768
- - Preserve Custom: ${validatedArgs.preserveCustom}
769
-
770
- ## Next Steps
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
775
-
776
- *Note: Advanced ADR integration requires additional development*
777
- *Current implementation focuses on preserving existing content*`
778
- }]
779
- };
780
- }
781
- case 'sync_progress': {
782
- const structure = await parseTodoFile(absoluteTodoPath);
783
- return {
784
- content: [{
785
- type: 'text',
786
- text: `# TODO Progress Sync
787
-
788
- ## Current Status
789
- - Total Tasks: ${structure.summary.total}
790
- - Completed: ${structure.summary.completed}
791
- - In Progress: ${structure.summary.inProgress}
792
- - ADR Directory: ${validatedArgs.adrDirectory}
793
- - Update ADRs: ${validatedArgs.updateAdrs}
794
-
795
- ## Sync Results
796
- *This operation would sync TODO progress with ADR status*
797
- *Implementation requires integration with ADR status tracking*
798
-
799
- ## Next Steps
800
- 1. Review TODO completion status
801
- 2. Update corresponding ADR statuses if enabled
802
- 3. Generate sync report`
803
- }]
804
- };
805
- }
806
- case 'analyze_progress': {
807
- const structure = await parseTodoFile(absoluteTodoPath);
808
- // Calculate progress metrics
809
- const totalTasks = structure.summary.total;
810
- const completionRate = totalTasks > 0 ? (structure.summary.completed / totalTasks) * 100 : 0;
811
- // Calculate velocity (placeholder - would need historical data)
812
- const velocity = validatedArgs.includeVelocity ? {
813
- tasksPerDay: 2.5,
814
- estimatedCompletion: '2 weeks',
815
- trend: 'stable'
816
- } : null;
817
- return {
818
- content: [{
819
- type: 'text',
820
- text: `# TODO Progress Analysis
821
-
822
- ## Overall Progress
823
- - **Completion Rate**: ${completionRate.toFixed(1)}% (${structure.summary.completed}/${totalTasks})
824
- - **Active Tasks**: ${structure.summary.inProgress}
825
- - **Blocked Tasks**: ${structure.summary.blocked}
826
-
827
- ## Status Distribution
828
- - ✅ Completed: ${structure.summary.completed} (${((structure.summary.completed / totalTasks) * 100).toFixed(1)}%)
829
- - 🔄 In Progress: ${structure.summary.inProgress} (${((structure.summary.inProgress / totalTasks) * 100).toFixed(1)}%)
830
- - ⏳ Pending: ${structure.summary.pending} (${((structure.summary.pending / totalTasks) * 100).toFixed(1)}%)
831
- - 🚫 Blocked: ${structure.summary.blocked} (${((structure.summary.blocked / totalTasks) * 100).toFixed(1)}%)
832
-
833
- ${velocity ? `## Velocity Metrics
834
- - **Tasks per Day**: ${velocity.tasksPerDay}
835
- - **Estimated Completion**: ${velocity.estimatedCompletion}
836
- - **Trend**: ${velocity.trend}` : ''}
837
-
838
- ## Recommendations
839
- ${structure.summary.blocked > 0 ? '- 🚨 Address blocked tasks to improve flow' : ''}
840
- ${structure.summary.inProgress > 5 ? '- ⚠️ Consider reducing work in progress' : ''}
841
- ${completionRate < 50 ? '- 📈 Focus on completing existing tasks' : ''}
842
- ${completionRate > 80 ? '- 🎉 Great progress! Consider adding new tasks' : ''}
843
-
844
- *Analysis timeframe: ${validatedArgs.timeframe}*`
845
- }]
846
- };
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
- }
1238
- default:
1239
- throw new McpAdrError(`Unknown operation: ${validatedArgs.operation}`, 'INVALID_INPUT');
1240
- }
1241
- }
1242
- catch (error) {
1243
- if (error instanceof z.ZodError) {
1244
- throw new McpAdrError(`Invalid input: ${error.errors.map(e => e.message).join(', ')}`, 'INVALID_INPUT');
1245
- }
1246
- throw new McpAdrError(`TODO management failed: ${error instanceof Error ? error.message : String(error)}`, 'TODO_MANAGEMENT_ERROR');
1247
- }
1248
- }
1249
- //# sourceMappingURL=todo-management-tool.js.map