project-mcp 3.2.1 → 3.2.2

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools/thoughts.js +119 -522
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-mcp",
3
- "version": "3.2.1",
3
+ "version": "3.2.2",
4
4
  "description": "Intent-based MCP server for project documentation search. Maps natural language queries to the right sources automatically—no configuration needed. The standard for AI agent documentation search.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -2,15 +2,14 @@
2
2
  * Thought processing tools.
3
3
  * Handles: process_thoughts, list_thoughts, get_thought
4
4
  *
5
- * This tool analyzes brain dump markdown files from .project/thoughts/todos/
6
- * and transforms them into properly structured YAML tasks.
5
+ * This tool reads brain dump files and provides context for the LLM to analyze.
6
+ * The LLM does the natural language understanding - the tool just gathers data.
7
7
  */
8
8
 
9
9
  import { readdir } from 'fs/promises';
10
- import { THOUGHTS_TODOS_DIR, PRIORITY_KEYWORDS, VALID_PRIORITIES } from '../lib/constants.js';
11
- import { readFile, writeFile, join, fileExists, ensureThoughtsTodosDir, matter } from '../lib/files.js';
12
- import { getCurrentDate, getISODate } from '../lib/dates.js';
13
- import { loadAllTasks, getNextTaskId, getExistingTaskIds } from '../lib/tasks.js';
10
+ import { THOUGHTS_TODOS_DIR, PROJECT_DIR } from '../lib/constants.js';
11
+ import { readFile, join, fileExists, ensureThoughtsTodosDir, matter } from '../lib/files.js';
12
+ import { loadAllTasks } from '../lib/tasks.js';
14
13
  import { loadAllFiles, getCachedFiles } from '../lib/search.js';
15
14
 
16
15
  /**
@@ -19,20 +18,20 @@ import { loadAllFiles, getCachedFiles } from '../lib/search.js';
19
18
  export const definitions = [
20
19
  {
21
20
  name: 'process_thoughts',
22
- description: `Analyzes brain dump markdown files from .project/thoughts/todos/ and transforms them into structured YAML tasks. This tool is intelligent and understands:
21
+ description: `Reads brain dump markdown files from .project/thoughts/todos/ and returns the content along with project context for analysis.
23
22
 
24
- 1. **Explicit Intent** - What the user literally says they want to do
25
- 2. **Shadow Intent** - Implied goals, underlying motivations, hidden needs
26
- 3. **Practical Intent** - What actually needs to happen in concrete terms
23
+ This tool gathers:
24
+ 1. **Raw thought content** - The unstructured brain dump as written
25
+ 2. **Project context** - Existing tasks, roadmap milestones, decisions for reference
26
+ 3. **Task format guide** - The YAML structure for creating tasks
27
27
 
28
- The tool will:
29
- - Parse unstructured markdown brain dumps
30
- - Extract actionable tasks from free-form text
31
- - Infer priority from context, urgency words, and project alignment
32
- - Cross-reference with project docs (ROADMAP, existing tasks, decisions) for context
33
- - Output properly formatted YAML tasks ready for creation
28
+ YOU (the LLM) should then analyze the content to:
29
+ - Understand the user's intent (explicit, shadow/underlying, practical)
30
+ - Identify logical task groupings (consolidate related items)
31
+ - Determine appropriate priorities based on context
32
+ - Create well-structured tasks using create_task
34
33
 
35
- Use this when you have messy notes or brain dumps that need to be converted into actionable, tracked tasks.`,
34
+ The tool does NOT automatically create tasks - it provides you with everything needed to make intelligent decisions about task creation.`,
36
35
  inputSchema: {
37
36
  type: 'object',
38
37
  properties: {
@@ -43,26 +42,7 @@ Use this when you have messy notes or brain dumps that need to be converted into
43
42
  },
44
43
  project: {
45
44
  type: 'string',
46
- description:
47
- 'Project prefix for generated task IDs (e.g., "AUTH", "API"). Required for task creation.',
48
- },
49
- mode: {
50
- type: 'string',
51
- description:
52
- 'Processing mode: "analyze" (returns analysis without creating tasks), "create" (creates tasks directly), "preview" (shows what would be created). Default: "analyze".',
53
- enum: ['analyze', 'create', 'preview'],
54
- default: 'analyze',
55
- },
56
- default_owner: {
57
- type: 'string',
58
- description: 'Default owner for generated tasks. Default: "unassigned".',
59
- default: 'unassigned',
60
- },
61
- include_context: {
62
- type: 'boolean',
63
- description:
64
- 'Include project context analysis (searches project docs for relevant info). Default: true.',
65
- default: true,
45
+ description: 'Project prefix for task IDs when you create tasks (e.g., "AUTH", "API").',
66
46
  },
67
47
  },
68
48
  required: ['project'],
@@ -71,14 +51,13 @@ Use this when you have messy notes or brain dumps that need to be converted into
71
51
  {
72
52
  name: 'list_thoughts',
73
53
  description:
74
- 'Lists all thought files in the .project/thoughts/ directory structure. Shows available brain dump files organized by category (todos, etc.).',
54
+ 'Lists all thought files in the .project/thoughts/ directory structure. Shows available brain dump files organized by category.',
75
55
  inputSchema: {
76
56
  type: 'object',
77
57
  properties: {
78
58
  category: {
79
59
  type: 'string',
80
- description:
81
- 'Optional: Filter by thought category. Currently supported: "todos". More categories coming in the future.',
60
+ description: 'Optional: Filter by thought category. Currently supported: "todos".',
82
61
  enum: ['todos', ''],
83
62
  },
84
63
  },
@@ -86,14 +65,13 @@ Use this when you have messy notes or brain dumps that need to be converted into
86
65
  },
87
66
  {
88
67
  name: 'get_thought',
89
- description:
90
- 'Reads a specific thought file and returns its raw content. Use this to review a brain dump before processing.',
68
+ description: 'Reads a specific thought file and returns its raw content for review.',
91
69
  inputSchema: {
92
70
  type: 'object',
93
71
  properties: {
94
72
  file: {
95
73
  type: 'string',
96
- description: 'The thought file to read (e.g., "my-ideas.md"). Can be in any thoughts subdirectory.',
74
+ description: 'The thought file to read (e.g., "my-ideas.md").',
97
75
  },
98
76
  category: {
99
77
  type: 'string',
@@ -107,256 +85,14 @@ Use this when you have messy notes or brain dumps that need to be converted into
107
85
  ];
108
86
 
109
87
  /**
110
- * Intent analysis types
111
- */
112
- const INTENT_MARKERS = {
113
- // Explicit intent markers - direct statements of what to do
114
- explicit: [
115
- /\b(need to|have to|must|should|will|going to|want to|plan to)\b/i,
116
- /\b(implement|create|build|add|fix|update|change|remove|delete)\b/i,
117
- /\b(task|todo|action item|deliverable)\b/i,
118
- ],
119
- // Shadow intent markers - underlying motivations
120
- shadow: [
121
- /\b(because|since|so that|in order to|to enable|to allow|to prevent)\b/i,
122
- /\b(worried about|concerned|frustrated|annoying|painful|tedious)\b/i,
123
- /\b(would be nice|could|might|maybe|possibly|eventually)\b/i,
124
- /\b(users|customers|team|stakeholders)\s+(want|need|expect|complain)/i,
125
- ],
126
- // Practical intent markers - concrete actions
127
- practical: [
128
- /\b(step \d+|first|then|next|finally|after that)\b/i,
129
- /\b(file|function|class|module|component|api|endpoint|database)\b/i,
130
- /\b(test|deploy|configure|setup|install|migrate)\b/i,
131
- ],
132
- // Urgency markers
133
- urgency: {
134
- P0: [/\b(critical|blocker|urgent|asap|immediately|breaking|down|outage)\b/i],
135
- P1: [/\b(important|high priority|soon|this week|pressing|significant)\b/i],
136
- P2: [/\b(medium|normal|standard|regular|when possible)\b/i],
137
- P3: [/\b(low priority|nice to have|eventually|someday|minor|trivial)\b/i],
138
- },
139
- };
140
-
141
- /**
142
- * Extract todos from unstructured markdown content
143
- * @param {string} content - Raw markdown content
144
- * @returns {Array} Extracted todo items with metadata
145
- */
146
- function extractTodosFromContent(content) {
147
- const todos = [];
148
- const lines = content.split('\n');
149
-
150
- let currentContext = [];
151
- let currentSection = null;
152
-
153
- for (let i = 0; i < lines.length; i++) {
154
- const line = lines[i];
155
- const trimmed = line.trim();
156
-
157
- // Track section headers for context
158
- const headerMatch = trimmed.match(/^(#{1,6})\s+(.+)/);
159
- if (headerMatch) {
160
- currentSection = headerMatch[2];
161
- currentContext = [];
162
- continue;
163
- }
164
-
165
- // Skip empty lines but reset context after multiple empties
166
- if (!trimmed) {
167
- if (currentContext.length > 0 && lines[i - 1]?.trim() === '') {
168
- currentContext = [];
169
- }
170
- continue;
171
- }
172
-
173
- // Check for explicit todo markers
174
- const todoMatch =
175
- trimmed.match(/^[-*]\s*\[[ x]\]\s*(.+)/) ||
176
- trimmed.match(/^[-*]\s+(.+)/) ||
177
- trimmed.match(/^(\d+)\.\s+(.+)/);
178
-
179
- if (todoMatch) {
180
- const text = todoMatch[2] || todoMatch[1];
181
- if (text && text.length >= 5) {
182
- todos.push({
183
- raw: text.trim(),
184
- section: currentSection,
185
- context: [...currentContext],
186
- lineNumber: i + 1,
187
- isExplicitTodo: /^\[[ x]\]/.test(trimmed),
188
- });
189
- }
190
- } else if (hasActionableIntent(trimmed)) {
191
- // Lines with strong action intent even without list markers
192
- todos.push({
193
- raw: trimmed,
194
- section: currentSection,
195
- context: [...currentContext],
196
- lineNumber: i + 1,
197
- isExplicitTodo: false,
198
- });
199
- } else {
200
- // Add to context for following items
201
- currentContext.push(trimmed);
202
- if (currentContext.length > 3) {
203
- currentContext.shift();
204
- }
205
- }
206
- }
207
-
208
- return todos;
209
- }
210
-
211
- /**
212
- * Check if a line has actionable intent
213
- * @param {string} line - Line to check
214
- * @returns {boolean}
215
- */
216
- function hasActionableIntent(line) {
217
- // Must have at least one explicit intent marker
218
- const hasExplicit = INTENT_MARKERS.explicit.some(rx => rx.test(line));
219
- if (!hasExplicit) return false;
220
-
221
- // Must be long enough to be meaningful
222
- if (line.length < 15) return false;
223
-
224
- // Must not be a question or observation
225
- if (/^(what|how|why|when|where|who|is|are|was|were|do|does)\b/i.test(line)) return false;
226
- if (line.endsWith('?')) return false;
227
-
228
- return true;
229
- }
230
-
231
- /**
232
- * Analyze intent layers for a todo item
233
- * @param {object} todo - Todo item with raw text and context
234
- * @returns {object} Intent analysis
235
- */
236
- function analyzeIntent(todo) {
237
- const text = todo.raw;
238
- const context = todo.context.join(' ');
239
- const combined = `${text} ${context}`;
240
-
241
- const analysis = {
242
- explicit: null,
243
- shadow: null,
244
- practical: null,
245
- priority: 'P2',
246
- confidence: 0,
247
- tags: [],
248
- };
249
-
250
- // Extract explicit intent - what they say they want
251
- analysis.explicit = text;
252
-
253
- // Extract shadow intent - why they want it
254
- const shadowMatches = [];
255
- INTENT_MARKERS.shadow.forEach(rx => {
256
- const match = combined.match(rx);
257
- if (match) {
258
- // Get surrounding context
259
- const idx = combined.indexOf(match[0]);
260
- const start = Math.max(0, idx - 20);
261
- const end = Math.min(combined.length, idx + match[0].length + 50);
262
- shadowMatches.push(combined.substring(start, end).trim());
263
- }
264
- });
265
- if (shadowMatches.length > 0) {
266
- analysis.shadow = shadowMatches.join('; ');
267
- }
268
-
269
- // Extract practical intent - concrete actions
270
- const practicalMatches = [];
271
- INTENT_MARKERS.practical.forEach(rx => {
272
- if (rx.test(combined)) {
273
- practicalMatches.push(rx.source.replace(/\\b|\(|\)/g, ''));
274
- }
275
- });
276
- if (practicalMatches.length > 0) {
277
- analysis.practical = `Involves: ${practicalMatches.join(', ')}`;
278
- }
279
-
280
- // Determine priority from urgency markers
281
- for (const [priority, patterns] of Object.entries(INTENT_MARKERS.urgency)) {
282
- if (patterns.some(rx => rx.test(combined))) {
283
- analysis.priority = priority;
284
- break;
285
- }
286
- }
287
-
288
- // Also check keyword-based priority
289
- const textLower = combined.toLowerCase();
290
- for (const [keyword, pri] of Object.entries(PRIORITY_KEYWORDS)) {
291
- if (textLower.includes(keyword)) {
292
- // Only upgrade priority, don't downgrade
293
- const currentOrder = { P0: 0, P1: 1, P2: 2, P3: 3 };
294
- if (currentOrder[pri] < currentOrder[analysis.priority]) {
295
- analysis.priority = pri;
296
- }
297
- break;
298
- }
299
- }
300
-
301
- // Extract potential tags from brackets or hashtags
302
- const tagMatches = text.match(/\[([^\]]+)\]/g) || [];
303
- const hashTags = text.match(/#(\w+)/g) || [];
304
- analysis.tags = [
305
- ...tagMatches.map(t => t.slice(1, -1).toLowerCase()),
306
- ...hashTags.map(t => t.slice(1).toLowerCase()),
307
- ];
308
-
309
- // Calculate confidence based on markers found
310
- let confidence = 0;
311
- if (todo.isExplicitTodo) confidence += 40;
312
- if (INTENT_MARKERS.explicit.some(rx => rx.test(text))) confidence += 30;
313
- if (analysis.shadow) confidence += 15;
314
- if (analysis.practical) confidence += 15;
315
- analysis.confidence = Math.min(100, confidence);
316
-
317
- return analysis;
318
- }
319
-
320
- /**
321
- * Generate a clean title from raw todo text
322
- * @param {string} raw - Raw todo text
323
- * @returns {string} Clean title
324
- */
325
- function generateTitle(raw) {
326
- let title = raw
327
- // Remove checkbox markers
328
- .replace(/^\[[ x]\]\s*/, '')
329
- // Remove tag brackets
330
- .replace(/\[[^\]]+\]/g, '')
331
- // Remove hashtags
332
- .replace(/#\w+/g, '')
333
- // Remove leading action words that are too generic
334
- .replace(/^(need to|have to|must|should|will|want to)\s+/i, '')
335
- // Clean up whitespace
336
- .replace(/\s+/g, ' ')
337
- .trim();
338
-
339
- // Capitalize first letter
340
- title = title.charAt(0).toUpperCase() + title.slice(1);
341
-
342
- // Truncate if too long
343
- if (title.length > 80) {
344
- title = title.substring(0, 77) + '...';
345
- }
346
-
347
- return title;
348
- }
349
-
350
- /**
351
- * Get project context from existing docs and tasks
352
- * @returns {Promise<object>} Project context summary
88
+ * Get project context for the LLM
353
89
  */
354
90
  async function getProjectContext() {
355
91
  const context = {
356
92
  existingTasks: [],
357
- roadmapItems: [],
93
+ roadmap: null,
358
94
  decisions: [],
359
- summary: '',
95
+ status: null,
360
96
  };
361
97
 
362
98
  try {
@@ -367,83 +103,43 @@ async function getProjectContext() {
367
103
  title: t.title,
368
104
  status: t.status,
369
105
  priority: t.priority,
106
+ tags: t.tags || [],
370
107
  }));
371
108
 
372
- // Load project files for context
109
+ // Load project files
373
110
  await loadAllFiles();
374
111
  const files = getCachedFiles();
375
112
 
376
- // Find roadmap items
113
+ // Get roadmap content
377
114
  const roadmapFile = files.find(f => f.path.includes('ROADMAP'));
378
115
  if (roadmapFile) {
379
- const milestones = roadmapFile.content.match(/##\s+[^#\n]+/g) || [];
380
- context.roadmapItems = milestones.map(m => m.replace('##', '').trim());
116
+ context.roadmap = roadmapFile.content.substring(0, 2000); // First 2000 chars
381
117
  }
382
118
 
383
- // Find decisions
119
+ // Get decisions
384
120
  const decisionsFile = files.find(f => f.path.includes('DECISIONS'));
385
121
  if (decisionsFile) {
386
122
  const adrs = decisionsFile.content.match(/## ADR-\d+: [^\n]+/g) || [];
387
123
  context.decisions = adrs.map(a => a.replace('## ', ''));
388
124
  }
389
125
 
390
- // Build summary
391
- const parts = [];
392
- if (context.existingTasks.length > 0) {
393
- const inProgress = context.existingTasks.filter(t => t.status === 'in_progress');
394
- const todoCount = context.existingTasks.filter(t => t.status === 'todo').length;
395
- parts.push(
396
- `${context.existingTasks.length} existing tasks (${inProgress.length} in progress, ${todoCount} todo)`
397
- );
126
+ // Get current status
127
+ const statusFile = files.find(f => f.path.includes('STATUS'));
128
+ if (statusFile) {
129
+ context.status = statusFile.content.substring(0, 1000); // First 1000 chars
398
130
  }
399
- if (context.roadmapItems.length > 0) {
400
- parts.push(`${context.roadmapItems.length} roadmap milestones`);
401
- }
402
- if (context.decisions.length > 0) {
403
- parts.push(`${context.decisions.length} architecture decisions`);
404
- }
405
- context.summary = parts.join(', ') || 'No existing project context found';
406
- } catch (error) {
407
- context.summary = 'Unable to load project context';
131
+ } catch {
132
+ // Context loading failed, continue without it
408
133
  }
409
134
 
410
135
  return context;
411
136
  }
412
137
 
413
138
  /**
414
- * Check for potential duplicates or related tasks
415
- * @param {string} title - Task title to check
416
- * @param {Array} existingTasks - Existing tasks
417
- * @returns {Array} Related tasks
418
- */
419
- function findRelatedTasks(title, existingTasks) {
420
- const titleWords = title
421
- .toLowerCase()
422
- .split(/\s+/)
423
- .filter(w => w.length > 3);
424
- const related = [];
425
-
426
- for (const task of existingTasks) {
427
- const taskTitle = task.title.toLowerCase();
428
- const matchingWords = titleWords.filter(w => taskTitle.includes(w));
429
- if (matchingWords.length >= 2 || matchingWords.length / titleWords.length > 0.5) {
430
- related.push({
431
- id: task.id,
432
- title: task.title,
433
- status: task.status,
434
- similarity: matchingWords.length / titleWords.length,
435
- });
436
- }
437
- }
438
-
439
- return related.sort((a, b) => b.similarity - a.similarity).slice(0, 3);
440
- }
441
-
442
- /**
443
- * Process thoughts handler
139
+ * Process thoughts handler - returns data for LLM analysis
444
140
  */
445
141
  async function processThoughts(args) {
446
- const { file, project, mode = 'analyze', default_owner = 'unassigned', include_context = true } = args;
142
+ const { file, project } = args;
447
143
 
448
144
  await ensureThoughtsTodosDir();
449
145
 
@@ -479,222 +175,123 @@ async function processThoughts(args) {
479
175
  content: [
480
176
  {
481
177
  type: 'text',
482
- text: `⚠️ No thought files found in \`.project/thoughts/todos/\`\n\nCreate markdown files with your brain dumps, then run this tool to process them into structured tasks.`,
178
+ text: `⚠️ No thought files found in \`.project/thoughts/todos/\`\n\nCreate markdown files with your brain dumps, then run this tool to process them.`,
483
179
  },
484
180
  ],
485
181
  };
486
182
  }
487
183
 
488
- // Get project context if requested
489
- let projectContext = null;
490
- if (include_context) {
491
- projectContext = await getProjectContext();
492
- }
493
-
494
- // Process each file
495
- const allAnalyzedTodos = [];
496
- const processedFiles = [];
497
-
184
+ // Read all thought files
185
+ const thoughtContents = [];
498
186
  for (const thoughtFile of filesToProcess) {
499
187
  const content = await readFile(thoughtFile.path, 'utf-8');
500
188
  const parsed = matter(content);
501
- const rawContent = parsed.content;
502
-
503
- // Extract todos from content
504
- const extractedTodos = extractTodosFromContent(rawContent);
505
-
506
- // Analyze each todo
507
- for (const todo of extractedTodos) {
508
- const intent = analyzeIntent(todo);
509
- const title = generateTitle(todo.raw);
510
-
511
- // Skip if too low confidence
512
- if (intent.confidence < 30) continue;
513
-
514
- // Find related existing tasks
515
- const related = projectContext ? findRelatedTasks(title, projectContext.existingTasks) : [];
516
-
517
- allAnalyzedTodos.push({
518
- sourceFile: thoughtFile.name,
519
- lineNumber: todo.lineNumber,
520
- section: todo.section,
521
- raw: todo.raw,
522
- title,
523
- intent,
524
- related,
525
- taskData: {
526
- title,
527
- project: project.toUpperCase(),
528
- priority: intent.priority,
529
- status: 'todo',
530
- owner: default_owner,
531
- tags: intent.tags,
532
- description: buildDescription(todo, intent),
533
- },
534
- });
535
- }
536
-
537
- processedFiles.push({
538
- name: thoughtFile.name,
539
- todosFound: extractedTodos.length,
540
- todosKept: allAnalyzedTodos.filter(t => t.sourceFile === thoughtFile.name).length,
189
+ thoughtContents.push({
190
+ filename: thoughtFile.name,
191
+ content: parsed.content,
192
+ frontmatter: parsed.data,
541
193
  });
542
194
  }
543
195
 
544
- // Build result based on mode
545
- let result = `## Thought Processing Results\n\n`;
546
- result += `**Mode:** ${mode}\n`;
547
- result += `**Project:** ${project.toUpperCase()}\n`;
548
- result += `**Files Processed:** ${processedFiles.length}\n`;
549
- result += `**Todos Extracted:** ${allAnalyzedTodos.length}\n`;
196
+ // Get project context
197
+ const projectContext = await getProjectContext();
550
198
 
551
- if (projectContext) {
552
- result += `\n### Project Context\n\n${projectContext.summary}\n`;
553
- }
199
+ // Build the response for the LLM
200
+ let result = `# Thought Processing Data
554
201
 
555
- result += `\n### Processed Files\n\n`;
556
- for (const pf of processedFiles) {
557
- result += `- **${pf.name}**: ${pf.todosFound} items found, ${pf.todosKept} actionable\n`;
558
- }
202
+ ## Instructions for You (the LLM)
559
203
 
560
- if (allAnalyzedTodos.length === 0) {
561
- result += `\n⚠️ No actionable todos found. The content may not contain clear task items, or confidence was too low.\n`;
562
- result += `\n**Tips:**\n`;
563
- result += `- Use checkbox syntax: \`- [ ] Task description\`\n`;
564
- result += `- Include action verbs: implement, create, fix, add, update\n`;
565
- result += `- Add urgency markers: critical, urgent, important, soon\n`;
204
+ Analyze the thought content below and create appropriate tasks. Consider:
566
205
 
567
- return {
568
- content: [{ type: 'text', text: result }],
569
- };
570
- }
206
+ 1. **Intent Analysis**
207
+ - **Explicit intent**: What does the user literally say they want?
208
+ - **Shadow intent**: What's the underlying motivation? Why do they want this?
209
+ - **Practical intent**: What concrete actions are needed?
571
210
 
572
- result += `\n---\n\n## Extracted Todos\n\n`;
211
+ 2. **Task Consolidation**
212
+ - Group related items into single tasks with subtasks
213
+ - Don't create a separate task for every bullet point
214
+ - Section headers often indicate a logical task grouping
573
215
 
574
- const createdTasks = [];
216
+ 3. **Priority Assessment**
217
+ - P0: Critical/blocker/urgent - system down, security issue
218
+ - P1: High priority - important, needed soon
219
+ - P2: Medium (default) - normal work items
220
+ - P3: Low - nice to have, eventually
575
221
 
576
- for (let i = 0; i < allAnalyzedTodos.length; i++) {
577
- const todo = allAnalyzedTodos[i];
222
+ 4. **Use \`create_task\` to create tasks** with this structure:
223
+ - title: Clear, actionable task title
224
+ - project: "${project.toUpperCase()}"
225
+ - description: Include context and subtasks
226
+ - priority: P0-P3 based on your analysis
227
+ - tags: Relevant categorization
228
+ - subtasks: Array of subtask strings (for consolidated items)
578
229
 
579
- result += `### ${i + 1}. ${todo.title}\n\n`;
580
- result += `**Source:** \`${todo.sourceFile}\` (line ${todo.lineNumber})\n`;
581
- result += `**Priority:** ${todo.intent.priority} (confidence: ${todo.intent.confidence}%)\n`;
230
+ ---
582
231
 
583
- if (todo.section) {
584
- result += `**Section:** ${todo.section}\n`;
585
- }
232
+ ## Thought Files to Process
586
233
 
587
- result += `\n**Intent Analysis:**\n`;
588
- result += `- **Explicit:** ${todo.intent.explicit}\n`;
589
- if (todo.intent.shadow) {
590
- result += `- **Shadow (why):** ${todo.intent.shadow}\n`;
591
- }
592
- if (todo.intent.practical) {
593
- result += `- **Practical:** ${todo.intent.practical}\n`;
594
- }
234
+ `;
595
235
 
596
- if (todo.intent.tags.length > 0) {
597
- result += `- **Tags:** ${todo.intent.tags.join(', ')}\n`;
598
- }
236
+ for (const thought of thoughtContents) {
237
+ result += `### File: ${thought.filename}\n\n`;
238
+ result += '```markdown\n';
239
+ result += thought.content;
240
+ result += '\n```\n\n';
241
+ }
599
242
 
600
- if (todo.related.length > 0) {
601
- result += `\n**⚠️ Potentially Related Tasks:**\n`;
602
- for (const rel of todo.related) {
603
- result += `- ${rel.id}: ${rel.title} (${rel.status})\n`;
604
- }
605
- }
243
+ result += `---
606
244
 
607
- // Show YAML preview
608
- result += `\n**Generated YAML:**\n`;
609
- result += `\`\`\`yaml\n`;
610
- result += `title: "${todo.taskData.title}"\n`;
611
- result += `project: ${todo.taskData.project}\n`;
612
- result += `priority: ${todo.taskData.priority}\n`;
613
- result += `status: ${todo.taskData.status}\n`;
614
- result += `owner: ${todo.taskData.owner}\n`;
615
- if (todo.taskData.tags.length > 0) {
616
- result += `tags: [${todo.taskData.tags.join(', ')}]\n`;
617
- }
618
- result += `\`\`\`\n\n`;
245
+ ## Project Context
619
246
 
620
- // Create tasks in create mode
621
- if (mode === 'create') {
622
- try {
623
- // Import create_task handler dynamically to avoid circular deps
624
- const { handlers: taskHandlers } = await import('./tasks.js');
625
- const createResult = await taskHandlers.create_task({
626
- title: todo.taskData.title,
627
- project: todo.taskData.project,
628
- description: todo.taskData.description,
629
- owner: todo.taskData.owner,
630
- priority: todo.taskData.priority,
631
- tags: todo.taskData.tags,
632
- });
633
-
634
- // Extract task ID from result
635
- const idMatch = createResult.content[0].text.match(/\*\*([A-Z]+-\d+)\*\*/);
636
- if (idMatch) {
637
- createdTasks.push({
638
- id: idMatch[1],
639
- title: todo.taskData.title,
640
- });
641
- result += `✅ **Created:** ${idMatch[1]}\n\n`;
642
- }
643
- } catch (error) {
644
- result += `❌ **Failed to create:** ${error.message}\n\n`;
645
- }
646
- }
247
+ Use this to understand what already exists and align new tasks appropriately.
647
248
 
648
- result += `---\n\n`;
649
- }
249
+ ### Existing Tasks (${projectContext.existingTasks.length} total)
250
+
251
+ `;
650
252
 
651
- // Summary footer
652
- if (mode === 'analyze') {
653
- result += `\n## Next Steps\n\n`;
654
- result += `1. Review the extracted todos above\n`;
655
- result += `2. Run with \`mode: "preview"\` to see final task format\n`;
656
- result += `3. Run with \`mode: "create"\` to create the tasks\n`;
657
- result += `\nOr use \`create_task\` manually for more control over individual tasks.\n`;
658
- } else if (mode === 'create' && createdTasks.length > 0) {
659
- result += `\n## Created Tasks Summary\n\n`;
660
- result += `**${createdTasks.length} tasks created:**\n`;
661
- for (const task of createdTasks) {
662
- result += `- ${task.id}: ${task.title}\n`;
253
+ if (projectContext.existingTasks.length > 0) {
254
+ result += '| ID | Title | Status | Priority |\n';
255
+ result += '|----|-------|--------|----------|\n';
256
+ for (const task of projectContext.existingTasks.slice(0, 20)) {
257
+ result += `| ${task.id} | ${task.title.substring(0, 40)}${task.title.length > 40 ? '...' : ''} | ${task.status} | ${task.priority} |\n`;
663
258
  }
664
- result += `\nUse \`get_next_task\` to see the execution queue.\n`;
665
- } else if (mode === 'preview') {
666
- result += `\n## Preview Complete\n\n`;
667
- result += `Run with \`mode: "create"\` to create these ${allAnalyzedTodos.length} tasks.\n`;
259
+ if (projectContext.existingTasks.length > 20) {
260
+ result += `\n*...and ${projectContext.existingTasks.length - 20} more tasks*\n`;
261
+ }
262
+ } else {
263
+ result += '*No existing tasks*\n';
668
264
  }
669
265
 
670
- return {
671
- content: [{ type: 'text', text: result }],
672
- };
673
- }
674
-
675
- /**
676
- * Build task description from todo and intent analysis
677
- */
678
- function buildDescription(todo, intent) {
679
- let desc = '';
266
+ if (projectContext.roadmap) {
267
+ result += `\n### Roadmap Overview\n\n`;
268
+ result += '```\n' + projectContext.roadmap + '\n```\n';
269
+ }
680
270
 
681
- if (todo.section) {
682
- desc += `**From:** ${todo.section}\n\n`;
271
+ if (projectContext.decisions.length > 0) {
272
+ result += `\n### Architecture Decisions\n\n`;
273
+ for (const decision of projectContext.decisions) {
274
+ result += `- ${decision}\n`;
275
+ }
683
276
  }
684
277
 
685
- desc += `**Original thought:**\n> ${todo.raw}\n\n`;
278
+ result += `
279
+ ---
686
280
 
687
- if (intent.shadow) {
688
- desc += `**Context (why):**\n${intent.shadow}\n\n`;
689
- }
281
+ ## Your Task
690
282
 
691
- if (todo.context.length > 0) {
692
- desc += `**Surrounding context:**\n${todo.context.map(c => `> ${c}`).join('\n')}\n\n`;
693
- }
283
+ Now analyze the thought content above and:
284
+
285
+ 1. Identify the distinct tasks/initiatives (consolidate related items)
286
+ 2. For each task, determine title, priority, and relevant context
287
+ 3. Use \`create_task\` to create well-structured tasks
694
288
 
695
- desc += `---\n*Extracted from thought dump via process_thoughts*`;
289
+ Remember: Quality over quantity. Create fewer, well-scoped tasks rather than many granular ones.
290
+ `;
696
291
 
697
- return desc;
292
+ return {
293
+ content: [{ type: 'text', text: result }],
294
+ };
698
295
  }
699
296
 
700
297
  /**
@@ -758,7 +355,7 @@ async function listThoughts(args) {
758
355
 
759
356
  result += `---\n`;
760
357
  result += `**Total files:** ${totalFiles}\n\n`;
761
- result += `**Tools:** \`get_thought\` to read | \`process_thoughts\` to convert to tasks`;
358
+ result += `**Tools:** \`get_thought\` to read | \`process_thoughts\` to analyze`;
762
359
 
763
360
  return {
764
361
  content: [{ type: 'text', text: result }],
@@ -808,7 +405,7 @@ async function getThought(args) {
808
405
  result += parsed.content;
809
406
 
810
407
  result += `\n\n---\n`;
811
- result += `**Tools:** \`process_thoughts\` to convert to tasks`;
408
+ result += `**Tools:** \`process_thoughts\` to analyze and create tasks`;
812
409
 
813
410
  return {
814
411
  content: [{ type: 'text', text: result }],