feat(mcp): Add tagInfo to responses and integrate ContextGatherer

Enhances the MCP server to include 'tagInfo' (currentTag, availableTags) in all tool responses, providing better client-side context.

- Introduces a new 'ContextGatherer' utility to standardize the collection of file, task, and project context for AI-powered commands. This refactors several task-manager modules ('expand-task', 'research', 'update-task', etc.) to use the new utility.

- Fixes an issue in 'get-task' and 'get-tasks' MCP tools where the 'projectRoot' was not being passed correctly, preventing tag information from being included in their responses.

- Adds subtask '103.17' to track the implementation of the task template importing feature.

- Updates documentation ('.cursor/rules', 'docs/') to align with the new tagged task system and context gatherer logic.
This commit is contained in:
Eyal Toledano
2025-06-11 23:06:36 -04:00
parent bb775e3180
commit 83d6405b17
29 changed files with 34433 additions and 13239 deletions

View File

@@ -10,7 +10,9 @@ import {
readJSON,
writeJSON,
truncate,
isSilentMode
isSilentMode,
flattenTasksWithSubtasks,
findProjectRoot
} from '../utils.js';
import {
@@ -26,6 +28,8 @@ import {
isApiKeySet // Keep this check
} from '../config-manager.js';
import generateTaskFiles from './generate-task-files.js';
import { ContextGatherer } from '../utils/contextGatherer.js';
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
// Zod schema for post-parsing validation of the updated task object
const updatedTaskSchema = z
@@ -216,7 +220,7 @@ async function updateTaskById(
context = {},
outputFormat = 'text'
) {
const { session, mcpLog, projectRoot } = context;
const { session, mcpLog, projectRoot: providedProjectRoot } = context;
const logFn = mcpLog || consoleLog;
const isMCP = !!mcpLog;
@@ -255,8 +259,14 @@ async function updateTaskById(
throw new Error(`Tasks file not found: ${tasksPath}`);
// --- End Input Validations ---
// Determine project root
const projectRoot = providedProjectRoot || findProjectRoot();
if (!projectRoot) {
throw new Error('Could not determine project root directory');
}
// --- Task Loading and Status Check (Keep existing) ---
const data = readJSON(tasksPath);
const data = readJSON(tasksPath, projectRoot);
if (!data || !data.tasks)
throw new Error(`No valid tasks found in ${tasksPath}.`);
const taskIndex = data.tasks.findIndex((task) => task.id === taskId);
@@ -293,6 +303,35 @@ async function updateTaskById(
}
// --- End Task Loading ---
// --- Context Gathering ---
let gatheredContext = '';
try {
const contextGatherer = new ContextGatherer(projectRoot);
const allTasksFlat = flattenTasksWithSubtasks(data.tasks);
const fuzzySearch = new FuzzyTaskSearch(allTasksFlat, 'update-task');
const searchQuery = `${taskToUpdate.title} ${taskToUpdate.description} ${prompt}`;
const searchResults = fuzzySearch.findRelevantTasks(searchQuery, {
maxResults: 5,
includeSelf: true
});
const relevantTaskIds = fuzzySearch.getTaskIds(searchResults);
const finalTaskIds = [
...new Set([taskId.toString(), ...relevantTaskIds])
];
if (finalTaskIds.length > 0) {
const contextResult = await contextGatherer.gather({
tasks: finalTaskIds,
format: 'research'
});
gatheredContext = contextResult;
}
} catch (contextError) {
report('warn', `Could not gather context: ${contextError.message}`);
}
// --- End Context Gathering ---
// --- Display Task Info (CLI Only - Keep existing) ---
if (outputFormat === 'text') {
// Show the task that will be updated
@@ -370,7 +409,13 @@ Guidelines:
The changes described in the prompt should be thoughtfully applied to make the task more accurate and actionable.`;
const taskDataString = JSON.stringify(taskToUpdate, null, 2); // Use original task data
const userPrompt = `Here is the task to update:\n${taskDataString}\n\nPlease update this task based on the following new context:\n${prompt}\n\nIMPORTANT: In the task JSON above, any subtasks with "status": "done" or "status": "completed" should be preserved exactly as is. Build your changes around these completed items.\n\nReturn only the updated task as a valid JSON object.`;
let userPrompt = `Here is the task to update:\n${taskDataString}\n\nPlease update this task based on the following new context:\n${prompt}\n\nIMPORTANT: In the task JSON above, any subtasks with "status": "done" or "status": "completed" should be preserved exactly as is. Build your changes around these completed items.`;
if (gatheredContext) {
userPrompt += `\n\n# Project Context\n\n${gatheredContext}`;
}
userPrompt += `\n\nReturn only the updated task as a valid JSON object.`;
// --- End Build Prompts ---
let loadingIndicator = null;