chore: changeset + update rules.
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
import { addDependency } from '../../../../scripts/modules/dependency-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for addDependency with error handling.
|
||||
@@ -51,9 +52,15 @@ export async function addDependencyDirect(args, log) {
|
||||
|
||||
log.info(`Adding dependency: task ${taskId} will depend on ${dependencyId}`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
await addDependency(tasksPath, taskId, dependencyId);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -63,6 +70,9 @@ export async function addDependencyDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in addDependencyDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { addSubtask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Add a subtask to an existing task
|
||||
@@ -67,10 +68,17 @@ export async function addSubtaskDirect(args, log) {
|
||||
// Determine if we should generate files
|
||||
const generateFiles = !args.skipGenerate;
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Case 1: Convert existing task to subtask
|
||||
if (existingTaskId) {
|
||||
log.info(`Converting task ${existingTaskId} to a subtask of ${parentId}`);
|
||||
const result = await addSubtask(tasksPath, parentId, existingTaskId, null, generateFiles);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -92,6 +100,10 @@ export async function addSubtaskDirect(args, log) {
|
||||
};
|
||||
|
||||
const result = await addSubtask(tasksPath, parentId, null, newSubtaskData, generateFiles);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -101,6 +113,9 @@ export async function addSubtaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in addSubtaskDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { addTask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for adding a new task with error handling.
|
||||
@@ -20,6 +21,9 @@ import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
*/
|
||||
export async function addTaskDirect(args, log) {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Find the tasks.json path
|
||||
const tasksPath = findTasksJsonPath(args, log);
|
||||
|
||||
@@ -44,8 +48,18 @@ export async function addTaskDirect(args, log) {
|
||||
|
||||
log.info(`Adding new task with prompt: "${prompt}", dependencies: [${dependencies.join(', ')}], priority: ${priority}`);
|
||||
|
||||
// Call the addTask function
|
||||
const newTaskId = await addTask(tasksPath, prompt, dependencies, priority);
|
||||
// Call the addTask function with 'json' outputFormat to prevent console output when called via MCP
|
||||
const newTaskId = await addTask(
|
||||
tasksPath,
|
||||
prompt,
|
||||
dependencies,
|
||||
priority,
|
||||
{ mcpLog: log },
|
||||
'json'
|
||||
);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -55,6 +69,9 @@ export async function addTaskDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in addTaskDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { analyzeTaskComplexity } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
@@ -48,9 +49,15 @@ export async function analyzeTaskComplexityDirect(args, log) {
|
||||
log.info('Using Perplexity AI for research-backed complexity analysis');
|
||||
}
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
await analyzeTaskComplexity(options);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Verify the report file was created
|
||||
if (!fs.existsSync(outputPath)) {
|
||||
return {
|
||||
@@ -79,6 +86,9 @@ export async function analyzeTaskComplexityDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in analyzeTaskComplexityDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { clearSubtasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
@@ -68,9 +69,15 @@ export async function clearSubtasksDirect(args, log) {
|
||||
|
||||
log.info(`Clearing subtasks from tasks: ${taskIds}`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
clearSubtasks(tasksPath, taskIds);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Read the updated data to provide a summary
|
||||
const updatedData = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
|
||||
const taskIdArray = taskIds.split(',').map(id => parseInt(id.trim(), 10));
|
||||
@@ -90,6 +97,9 @@ export async function clearSubtasksDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in clearSubtasksDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Direct function implementation for displaying complexity analysis report
|
||||
*/
|
||||
|
||||
import { readComplexityReport } from '../../../../scripts/modules/utils.js';
|
||||
import { readComplexityReport, enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||
import path from 'path';
|
||||
@@ -39,8 +39,14 @@ export async function complexityReportDirect(args, log) {
|
||||
// Define the core action function to read the report
|
||||
const coreActionFn = async () => {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
const report = readComplexityReport(reportPath);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
if (!report) {
|
||||
log.warn(`No complexity report found at ${reportPath}`);
|
||||
return {
|
||||
@@ -60,6 +66,9 @@ export async function complexityReportDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error reading complexity report: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
@@ -82,6 +91,9 @@ export async function complexityReportDirect(args, log) {
|
||||
return result; // Returns { success, data/error, fromCache }
|
||||
} catch (error) {
|
||||
// Catch unexpected errors from getCachedOrExecute itself
|
||||
// Ensure silent mode is disabled
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Unexpected error during getCachedOrExecute for complexityReport: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
@@ -93,6 +105,9 @@ export async function complexityReportDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled if an outer error occurs
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in complexityReportDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import { expandAllTasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
|
||||
/**
|
||||
@@ -41,23 +42,38 @@ export async function expandAllTasksDirect(args, log) {
|
||||
log.info('Force regeneration of subtasks is enabled');
|
||||
}
|
||||
|
||||
// Call the core function
|
||||
await expandAllTasks(numSubtasks, useResearch, additionalContext, forceFlag);
|
||||
|
||||
// The expandAllTasks function doesn't have a return value, so we'll create our own success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: "Successfully expanded all pending tasks with subtasks",
|
||||
details: {
|
||||
numSubtasks: numSubtasks,
|
||||
research: useResearch,
|
||||
prompt: additionalContext,
|
||||
force: forceFlag
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
await expandAllTasks(numSubtasks, useResearch, additionalContext, forceFlag);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// The expandAllTasks function doesn't have a return value, so we'll create our own success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: "Successfully expanded all pending tasks with subtasks",
|
||||
details: {
|
||||
numSubtasks: numSubtasks,
|
||||
research: useResearch,
|
||||
prompt: additionalContext,
|
||||
force: forceFlag
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
throw error; // Rethrow to be caught by outer catch block
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in expandAllTasksDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { expandTask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { readJSON, writeJSON } from '../../../../scripts/modules/utils.js';
|
||||
import { readJSON, writeJSON, enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
@@ -116,28 +116,50 @@ export async function expandTaskDirect(args, log) {
|
||||
// Save tasks.json with potentially empty subtasks array
|
||||
writeJSON(tasksPath, data);
|
||||
|
||||
// Call the core expandTask function
|
||||
await expandTask(taskId, numSubtasks, useResearch, additionalContext);
|
||||
|
||||
// Read the updated data
|
||||
const updatedData = readJSON(tasksPath);
|
||||
const updatedTask = updatedData.tasks.find(t => t.id === taskId);
|
||||
|
||||
// Calculate how many subtasks were added
|
||||
const subtasksAdded = updatedTask.subtasks ?
|
||||
updatedTask.subtasks.length - subtasksCountBefore : 0;
|
||||
|
||||
// Return the result
|
||||
log.info(`Successfully expanded task ${taskId} with ${subtasksAdded} new subtasks`);
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
task: updatedTask,
|
||||
subtasksAdded,
|
||||
hasExistingSubtasks
|
||||
},
|
||||
fromCache: false
|
||||
};
|
||||
// Process the request
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call expandTask
|
||||
const result = await expandTask(taskId, numSubtasks, useResearch, additionalContext);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Read the updated data
|
||||
const updatedData = readJSON(tasksPath);
|
||||
const updatedTask = updatedData.tasks.find(t => t.id === taskId);
|
||||
|
||||
// Calculate how many subtasks were added
|
||||
const subtasksAdded = updatedTask.subtasks ?
|
||||
updatedTask.subtasks.length - subtasksCountBefore : 0;
|
||||
|
||||
// Return the result
|
||||
log.info(`Successfully expanded task ${taskId} with ${subtasksAdded} new subtasks`);
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
task: updatedTask,
|
||||
subtasksAdded,
|
||||
hasExistingSubtasks
|
||||
},
|
||||
fromCache: false
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error expanding task: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'CORE_FUNCTION_ERROR',
|
||||
message: error.message || 'Failed to expand task'
|
||||
},
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error expanding task: ${error.message}`);
|
||||
return {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { fixDependenciesCommand } from '../../../../scripts/modules/dependency-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
@@ -32,9 +33,15 @@ export async function fixDependenciesDirect(args, log) {
|
||||
};
|
||||
}
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the original command function
|
||||
await fixDependenciesCommand(tasksPath);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -43,6 +50,9 @@ export async function fixDependenciesDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error fixing dependencies: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { generateTaskFiles } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import path from 'path';
|
||||
|
||||
@@ -39,8 +40,27 @@ export async function generateTaskFilesDirect(args, log) {
|
||||
|
||||
log.info(`Generating task files from ${tasksPath} to ${outputDir}`);
|
||||
|
||||
// Execute core generateTaskFiles function
|
||||
generateTaskFiles(tasksPath, outputDir);
|
||||
// Execute core generateTaskFiles function in a separate try/catch
|
||||
try {
|
||||
// Enable silent mode to prevent logs from being written to stdout
|
||||
enableSilentMode();
|
||||
|
||||
// The function is synchronous despite being awaited elsewhere
|
||||
generateTaskFiles(tasksPath, outputDir);
|
||||
|
||||
// Restore normal logging after task generation
|
||||
disableSilentMode();
|
||||
} catch (genError) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in generateTaskFiles: ${genError.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: { code: 'GENERATE_FILES_ERROR', message: genError.message },
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
|
||||
// Return success with file paths
|
||||
return {
|
||||
@@ -54,6 +74,9 @@ export async function generateTaskFilesDirect(args, log) {
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging if an outer error occurs
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error generating task files: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { listTasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for listTasks with error handling and caching.
|
||||
@@ -38,6 +39,9 @@ export async function listTasksDirect(args, log) {
|
||||
// Define the action function to be executed on cache miss
|
||||
const coreListTasksAction = async () => {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
log.info(`Executing core listTasks function for path: ${tasksPath}, filter: ${statusFilter}, subtasks: ${withSubtasks}`);
|
||||
const resultData = listTasks(tasksPath, statusFilter, withSubtasks, 'json');
|
||||
|
||||
@@ -46,9 +50,16 @@ export async function listTasksDirect(args, log) {
|
||||
return { success: false, error: { code: 'INVALID_CORE_RESPONSE', message: 'Invalid or empty response from listTasks core function' } };
|
||||
}
|
||||
log.info(`Core listTasks function retrieved ${resultData.tasks.length} tasks`);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return { success: true, data: resultData };
|
||||
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Core listTasks function failed: ${error.message}`);
|
||||
return { success: false, error: { code: 'LIST_TASKS_CORE_ERROR', message: error.message || 'Failed to list tasks' } };
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { findNextTask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { readJSON } from '../../../../scripts/modules/utils.js';
|
||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for finding the next task to work on with error handling and caching.
|
||||
@@ -38,6 +39,9 @@ export async function nextTaskDirect(args, log) {
|
||||
// Define the action function to be executed on cache miss
|
||||
const coreNextTaskAction = async () => {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
log.info(`Finding next task from ${tasksPath}`);
|
||||
|
||||
// Read tasks data
|
||||
@@ -67,6 +71,9 @@ export async function nextTaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Return the next task data with the full tasks array for reference
|
||||
log.info(`Successfully found next task ${nextTask.id}: ${nextTask.title}`);
|
||||
return {
|
||||
@@ -77,6 +84,9 @@ export async function nextTaskDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error finding next task: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -7,6 +7,7 @@ import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { parsePRD } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for parsing PRD documents and generating tasks.
|
||||
@@ -66,9 +67,15 @@ export async function parsePRDDirect(args, log) {
|
||||
|
||||
log.info(`Preparing to parse PRD from ${inputPath} and output to ${outputPath} with ${numTasks} tasks`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core parsePRD function (which is not async but we'll await it to maintain consistency)
|
||||
await parsePRD(inputPath, outputPath, numTasks);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Since parsePRD doesn't return a value but writes to a file, we'll read the result
|
||||
// to return it to the caller
|
||||
if (fs.existsSync(outputPath)) {
|
||||
@@ -94,6 +101,9 @@ export async function parsePRDDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error parsing PRD: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { removeDependency } from '../../../../scripts/modules/dependency-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Remove a dependency from a task
|
||||
@@ -49,9 +50,15 @@ export async function removeDependencyDirect(args, log) {
|
||||
|
||||
log.info(`Removing dependency: task ${taskId} no longer depends on ${dependencyId}`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
await removeDependency(tasksPath, taskId, dependencyId);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -61,6 +68,9 @@ export async function removeDependencyDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in removeDependencyDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { removeSubtask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Remove a subtask from its parent task
|
||||
@@ -18,6 +19,9 @@ import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
*/
|
||||
export async function removeSubtaskDirect(args, log) {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
|
||||
|
||||
if (!args.id) {
|
||||
@@ -54,6 +58,9 @@ export async function removeSubtaskDirect(args, log) {
|
||||
|
||||
const result = await removeSubtask(tasksPath, args.id, convertToTask, generateFiles);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
if (convertToTask && result) {
|
||||
// Return info about the converted task
|
||||
return {
|
||||
@@ -73,6 +80,9 @@ export async function removeSubtaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled even if an outer error occurs
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in removeSubtaskDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { removeTask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
|
||||
/**
|
||||
@@ -49,9 +50,15 @@ export async function removeTaskDirect(args, log) {
|
||||
log.info(`Removing task with ID: ${taskId} from ${tasksPath}`);
|
||||
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core removeTask function
|
||||
const result = await removeTask(tasksPath, taskId);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
log.info(`Successfully removed task: ${taskId}`);
|
||||
|
||||
// Return the result
|
||||
@@ -66,6 +73,9 @@ export async function removeTaskDirect(args, log) {
|
||||
fromCache: false
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error removing task: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
@@ -77,6 +87,9 @@ export async function removeTaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled even if an outer error occurs
|
||||
disableSilentMode();
|
||||
|
||||
// Catch any unexpected errors
|
||||
log.error(`Unexpected error in removeTaskDirect: ${error.message}`);
|
||||
return {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { setTaskStatus } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for setTaskStatus with error handling.
|
||||
@@ -63,20 +64,40 @@ export async function setTaskStatusDirect(args, log) {
|
||||
|
||||
log.info(`Setting task ${taskId} status to "${newStatus}"`);
|
||||
|
||||
// Execute the setTaskStatus function with source=mcp to avoid console output
|
||||
await setTaskStatus(tasksPath, taskId, newStatus);
|
||||
|
||||
// Return success data
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated task ${taskId} status to "${newStatus}"`,
|
||||
taskId,
|
||||
status: newStatus,
|
||||
tasksPath
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
// Call the core function
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
await setTaskStatus(tasksPath, taskId, newStatus);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
log.info(`Successfully set task ${taskId} status to ${newStatus}`);
|
||||
|
||||
// Return success data
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated task ${taskId} status to "${newStatus}"`,
|
||||
taskId,
|
||||
status: newStatus,
|
||||
tasksPath
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error setting task status: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: { code: 'SET_STATUS_ERROR', message: error.message || 'Unknown error setting task status' },
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error setting task status: ${error.message}`);
|
||||
return {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { findTaskById } from '../../../../scripts/modules/utils.js';
|
||||
import { readJSON } from '../../../../scripts/modules/utils.js';
|
||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for showing task details with error handling and caching.
|
||||
@@ -52,6 +53,9 @@ export async function showTaskDirect(args, log) {
|
||||
// Define the action function to be executed on cache miss
|
||||
const coreShowTaskAction = async () => {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
log.info(`Retrieving task details for ID: ${taskId} from ${tasksPath}`);
|
||||
|
||||
// Read tasks data
|
||||
@@ -79,6 +83,9 @@ export async function showTaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Return the task data with the full tasks array for reference
|
||||
// (needed for formatDependenciesWithStatus function in UI)
|
||||
log.info(`Successfully found task ${taskId}`);
|
||||
@@ -90,6 +97,9 @@ export async function showTaskDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error showing task: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
@@ -112,6 +122,7 @@ export async function showTaskDirect(args, log) {
|
||||
return result; // Returns { success, data/error, fromCache }
|
||||
} catch (error) {
|
||||
// Catch unexpected errors from getCachedOrExecute itself
|
||||
disableSilentMode();
|
||||
log.error(`Unexpected error during getCachedOrExecute for showTask: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { updateSubtaskById } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
|
||||
/**
|
||||
@@ -68,35 +69,50 @@ export async function updateSubtaskByIdDirect(args, log) {
|
||||
|
||||
log.info(`Updating subtask with ID ${subtaskId} with prompt "${args.prompt}" and research: ${useResearch}`);
|
||||
|
||||
// Execute core updateSubtaskById function
|
||||
const updatedSubtask = await updateSubtaskById(tasksPath, subtaskId, args.prompt, useResearch);
|
||||
|
||||
// Handle the case where the subtask couldn't be updated (e.g., already marked as done)
|
||||
if (!updatedSubtask) {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core updateSubtaskById function
|
||||
const updatedSubtask = await updateSubtaskById(tasksPath, subtaskId, args.prompt, useResearch);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Handle the case where the subtask couldn't be updated (e.g., already marked as done)
|
||||
if (!updatedSubtask) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'SUBTASK_UPDATE_FAILED',
|
||||
message: 'Failed to update subtask. It may be marked as completed, or another error occurred.'
|
||||
},
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
|
||||
// Return the updated subtask information
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'SUBTASK_UPDATE_FAILED',
|
||||
message: 'Failed to update subtask. It may be marked as completed, or another error occurred.'
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated subtask with ID ${subtaskId}`,
|
||||
subtaskId,
|
||||
parentId: subtaskId.split('.')[0],
|
||||
subtask: updatedSubtask,
|
||||
tasksPath,
|
||||
useResearch
|
||||
},
|
||||
fromCache: false
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
throw error; // Rethrow to be caught by outer catch block
|
||||
}
|
||||
|
||||
// Return the updated subtask information
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated subtask with ID ${subtaskId}`,
|
||||
subtaskId,
|
||||
parentId: subtaskId.split('.')[0],
|
||||
subtask: updatedSubtask,
|
||||
tasksPath,
|
||||
useResearch
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error updating subtask by ID: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { updateTaskById } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for updateTaskById with error handling.
|
||||
@@ -79,9 +80,15 @@ export async function updateTaskByIdDirect(args, log) {
|
||||
|
||||
log.info(`Updating task with ID ${taskId} with prompt "${args.prompt}" and research: ${useResearch}`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core updateTaskById function
|
||||
await updateTaskById(tasksPath, taskId, args.prompt, useResearch);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Since updateTaskById doesn't return a value but modifies the tasks file,
|
||||
// we'll return a success message
|
||||
return {
|
||||
@@ -95,6 +102,9 @@ export async function updateTaskByIdDirect(args, log) {
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error updating task by ID: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { updateTasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
|
||||
/**
|
||||
@@ -73,22 +74,37 @@ export async function updateTasksDirect(args, log) {
|
||||
|
||||
log.info(`Updating tasks from ID ${fromId} with prompt "${args.prompt}" and research: ${useResearch}`);
|
||||
|
||||
// Execute core updateTasks function
|
||||
await updateTasks(tasksPath, fromId, args.prompt, useResearch);
|
||||
|
||||
// Since updateTasks doesn't return a value but modifies the tasks file,
|
||||
// we'll return a success message
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated tasks from ID ${fromId} based on the prompt`,
|
||||
fromId,
|
||||
tasksPath,
|
||||
useResearch
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core updateTasks function
|
||||
await updateTasks(tasksPath, fromId, args.prompt, useResearch);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Since updateTasks doesn't return a value but modifies the tasks file,
|
||||
// we'll return a success message
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated tasks from ID ${fromId} based on the prompt`,
|
||||
fromId,
|
||||
tasksPath,
|
||||
useResearch
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
throw error; // Rethrow to be caught by outer catch block
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error updating tasks: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { validateDependenciesCommand } from '../../../../scripts/modules/dependency-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
@@ -32,9 +33,15 @@ export async function validateDependenciesDirect(args, log) {
|
||||
};
|
||||
}
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the original command function
|
||||
await validateDependenciesCommand(tasksPath);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -43,6 +50,9 @@ export async function validateDependenciesDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error validating dependencies: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
217
mcp-server/src/core/utils/async-manager.js
Normal file
217
mcp-server/src/core/utils/async-manager.js
Normal file
@@ -0,0 +1,217 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
class AsyncOperationManager {
|
||||
constructor() {
|
||||
this.operations = new Map(); // Stores active operation state
|
||||
this.completedOperations = new Map(); // Stores completed operations
|
||||
this.maxCompletedOperations = 100; // Maximum number of completed operations to store
|
||||
this.listeners = new Map(); // For potential future notifications
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an operation to be executed asynchronously.
|
||||
* @param {Function} operationFn - The async function to execute (e.g., a Direct function).
|
||||
* @param {Object} args - Arguments to pass to the operationFn.
|
||||
* @param {Object} context - The MCP tool context { log, reportProgress, session }.
|
||||
* @returns {string} The unique ID assigned to this operation.
|
||||
*/
|
||||
addOperation(operationFn, args, context) {
|
||||
const operationId = `op-${uuidv4()}`;
|
||||
const operation = {
|
||||
id: operationId,
|
||||
status: 'pending',
|
||||
startTime: Date.now(),
|
||||
endTime: null,
|
||||
result: null,
|
||||
error: null,
|
||||
// Store necessary parts of context, especially log for background execution
|
||||
log: context.log,
|
||||
reportProgress: context.reportProgress, // Pass reportProgress through
|
||||
session: context.session // Pass session through if needed by the operationFn
|
||||
};
|
||||
this.operations.set(operationId, operation);
|
||||
this.log(operationId, 'info', `Operation added.`);
|
||||
|
||||
// Start execution in the background (don't await here)
|
||||
this._runOperation(operationId, operationFn, args, context).catch(err => {
|
||||
// Catch unexpected errors during the async execution setup itself
|
||||
this.log(operationId, 'error', `Critical error starting operation: ${err.message}`, { stack: err.stack });
|
||||
operation.status = 'failed';
|
||||
operation.error = { code: 'MANAGER_EXECUTION_ERROR', message: err.message };
|
||||
operation.endTime = Date.now();
|
||||
|
||||
// Move to completed operations
|
||||
this._moveToCompleted(operationId);
|
||||
});
|
||||
|
||||
return operationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to execute the operation.
|
||||
* @param {string} operationId - The ID of the operation.
|
||||
* @param {Function} operationFn - The async function to execute.
|
||||
* @param {Object} args - Arguments for the function.
|
||||
* @param {Object} context - The original MCP tool context.
|
||||
*/
|
||||
async _runOperation(operationId, operationFn, args, context) {
|
||||
const operation = this.operations.get(operationId);
|
||||
if (!operation) return; // Should not happen
|
||||
|
||||
operation.status = 'running';
|
||||
this.log(operationId, 'info', `Operation running.`);
|
||||
this.emit('statusChanged', { operationId, status: 'running' });
|
||||
|
||||
try {
|
||||
// Pass the necessary context parts to the direct function
|
||||
// The direct function needs to be adapted if it needs reportProgress
|
||||
// We pass the original context's log, plus our wrapped reportProgress
|
||||
const result = await operationFn(args, operation.log, {
|
||||
reportProgress: (progress) => this._handleProgress(operationId, progress),
|
||||
mcpLog: operation.log, // Pass log as mcpLog if direct fn expects it
|
||||
session: operation.session
|
||||
});
|
||||
|
||||
operation.status = result.success ? 'completed' : 'failed';
|
||||
operation.result = result.success ? result.data : null;
|
||||
operation.error = result.success ? null : result.error;
|
||||
this.log(operationId, 'info', `Operation finished with status: ${operation.status}`);
|
||||
|
||||
} catch (error) {
|
||||
this.log(operationId, 'error', `Operation failed with error: ${error.message}`, { stack: error.stack });
|
||||
operation.status = 'failed';
|
||||
operation.error = { code: 'OPERATION_EXECUTION_ERROR', message: error.message };
|
||||
} finally {
|
||||
operation.endTime = Date.now();
|
||||
this.emit('statusChanged', { operationId, status: operation.status, result: operation.result, error: operation.error });
|
||||
|
||||
// Move to completed operations if done or failed
|
||||
if (operation.status === 'completed' || operation.status === 'failed') {
|
||||
this._moveToCompleted(operationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an operation from active operations to completed operations history.
|
||||
* @param {string} operationId - The ID of the operation to move.
|
||||
* @private
|
||||
*/
|
||||
_moveToCompleted(operationId) {
|
||||
const operation = this.operations.get(operationId);
|
||||
if (!operation) return;
|
||||
|
||||
// Store only the necessary data in completed operations
|
||||
const completedData = {
|
||||
id: operation.id,
|
||||
status: operation.status,
|
||||
startTime: operation.startTime,
|
||||
endTime: operation.endTime,
|
||||
result: operation.result,
|
||||
error: operation.error,
|
||||
};
|
||||
|
||||
this.completedOperations.set(operationId, completedData);
|
||||
this.operations.delete(operationId);
|
||||
|
||||
// Trim completed operations if exceeding maximum
|
||||
if (this.completedOperations.size > this.maxCompletedOperations) {
|
||||
// Get the oldest operation (sorted by endTime)
|
||||
const oldest = [...this.completedOperations.entries()]
|
||||
.sort((a, b) => a[1].endTime - b[1].endTime)[0];
|
||||
|
||||
if (oldest) {
|
||||
this.completedOperations.delete(oldest[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles progress updates from the running operation and forwards them.
|
||||
* @param {string} operationId - The ID of the operation reporting progress.
|
||||
* @param {Object} progress - The progress object { progress, total? }.
|
||||
*/
|
||||
_handleProgress(operationId, progress) {
|
||||
const operation = this.operations.get(operationId);
|
||||
if (operation && operation.reportProgress) {
|
||||
try {
|
||||
// Use the reportProgress function captured from the original context
|
||||
operation.reportProgress(progress);
|
||||
this.log(operationId, 'debug', `Reported progress: ${JSON.stringify(progress)}`);
|
||||
} catch(err) {
|
||||
this.log(operationId, 'warn', `Failed to report progress: ${err.message}`);
|
||||
// Don't stop the operation, just log the reporting failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the status and result/error of an operation.
|
||||
* @param {string} operationId - The ID of the operation.
|
||||
* @returns {Object | null} The operation details or null if not found.
|
||||
*/
|
||||
getStatus(operationId) {
|
||||
// First check active operations
|
||||
const operation = this.operations.get(operationId);
|
||||
if (operation) {
|
||||
return {
|
||||
id: operation.id,
|
||||
status: operation.status,
|
||||
startTime: operation.startTime,
|
||||
endTime: operation.endTime,
|
||||
result: operation.result,
|
||||
error: operation.error,
|
||||
};
|
||||
}
|
||||
|
||||
// Then check completed operations
|
||||
const completedOperation = this.completedOperations.get(operationId);
|
||||
if (completedOperation) {
|
||||
return completedOperation;
|
||||
}
|
||||
|
||||
// Operation not found in either active or completed
|
||||
return {
|
||||
error: {
|
||||
code: 'OPERATION_NOT_FOUND',
|
||||
message: `Operation ID ${operationId} not found. It may have been completed and removed from history, or the ID may be invalid.`
|
||||
},
|
||||
status: 'not_found'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal logging helper to prefix logs with the operation ID.
|
||||
* @param {string} operationId - The ID of the operation.
|
||||
* @param {'info'|'warn'|'error'|'debug'} level - Log level.
|
||||
* @param {string} message - Log message.
|
||||
* @param {Object} [meta] - Additional metadata.
|
||||
*/
|
||||
log(operationId, level, message, meta = {}) {
|
||||
const operation = this.operations.get(operationId);
|
||||
// Use the logger instance associated with the operation if available, otherwise console
|
||||
const logger = operation?.log || console;
|
||||
const logFn = logger[level] || logger.log || console.log; // Fallback
|
||||
logFn(`[AsyncOp ${operationId}] ${message}`, meta);
|
||||
}
|
||||
|
||||
// --- Basic Event Emitter ---
|
||||
on(eventName, listener) {
|
||||
if (!this.listeners.has(eventName)) {
|
||||
this.listeners.set(eventName, []);
|
||||
}
|
||||
this.listeners.get(eventName).push(listener);
|
||||
}
|
||||
|
||||
emit(eventName, data) {
|
||||
if (this.listeners.has(eventName)) {
|
||||
this.listeners.get(eventName).forEach(listener => listener(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export a singleton instance
|
||||
const asyncOperationManager = new AsyncOperationManager();
|
||||
|
||||
// Export the manager and potentially the class if needed elsewhere
|
||||
export { asyncOperationManager, AsyncOperationManager };
|
||||
@@ -5,6 +5,7 @@ import { fileURLToPath } from "url";
|
||||
import fs from "fs";
|
||||
import logger from "./logger.js";
|
||||
import { registerTaskMasterTools } from "./tools/index.js";
|
||||
import { asyncOperationManager } from './core/utils/async-manager.js';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
@@ -34,6 +35,9 @@ class TaskMasterMCPServer {
|
||||
|
||||
this.server.addResourceTemplate({});
|
||||
|
||||
// Make the manager accessible (e.g., pass it to tool registration)
|
||||
this.asyncManager = asyncOperationManager;
|
||||
|
||||
// Bind methods
|
||||
this.init = this.init.bind(this);
|
||||
this.start = this.start.bind(this);
|
||||
@@ -49,8 +53,8 @@ class TaskMasterMCPServer {
|
||||
async init() {
|
||||
if (this.initialized) return;
|
||||
|
||||
// Register Task Master tools
|
||||
registerTaskMasterTools(this.server);
|
||||
// Pass the manager instance to the tool registration function
|
||||
registerTaskMasterTools(this.server, this.asyncManager);
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
@@ -83,4 +87,7 @@ class TaskMasterMCPServer {
|
||||
}
|
||||
}
|
||||
|
||||
// Export the manager from here as well, if needed elsewhere
|
||||
export { asyncOperationManager };
|
||||
|
||||
export default TaskMasterMCPServer;
|
||||
|
||||
@@ -11,7 +11,7 @@ const LOG_LEVELS = {
|
||||
|
||||
// Get log level from environment or default to info
|
||||
const LOG_LEVEL = process.env.LOG_LEVEL
|
||||
? LOG_LEVELS[process.env.LOG_LEVEL.toLowerCase()]
|
||||
? LOG_LEVELS[process.env.LOG_LEVEL.toLowerCase()] ?? LOG_LEVELS.info
|
||||
: LOG_LEVELS.info;
|
||||
|
||||
/**
|
||||
@@ -20,43 +20,66 @@ const LOG_LEVEL = process.env.LOG_LEVEL
|
||||
* @param {...any} args - Arguments to log
|
||||
*/
|
||||
function log(level, ...args) {
|
||||
const icons = {
|
||||
debug: chalk.gray("🔍"),
|
||||
info: chalk.blue("ℹ️"),
|
||||
warn: chalk.yellow("⚠️"),
|
||||
error: chalk.red("❌"),
|
||||
success: chalk.green("✅"),
|
||||
// Use text prefixes instead of emojis
|
||||
const prefixes = {
|
||||
debug: chalk.gray("[DEBUG]"),
|
||||
info: chalk.blue("[INFO]"),
|
||||
warn: chalk.yellow("[WARN]"),
|
||||
error: chalk.red("[ERROR]"),
|
||||
success: chalk.green("[SUCCESS]"),
|
||||
};
|
||||
|
||||
if (LOG_LEVELS[level] >= LOG_LEVEL) {
|
||||
const icon = icons[level] || "";
|
||||
if (LOG_LEVELS[level] !== undefined && LOG_LEVELS[level] >= LOG_LEVEL) {
|
||||
const prefix = prefixes[level] || "";
|
||||
let coloredArgs = args;
|
||||
|
||||
if (level === "error") {
|
||||
console.error(icon, chalk.red(...args));
|
||||
} else if (level === "warn") {
|
||||
console.warn(icon, chalk.yellow(...args));
|
||||
} else if (level === "success") {
|
||||
console.log(icon, chalk.green(...args));
|
||||
} else if (level === "info") {
|
||||
console.log(icon, chalk.blue(...args));
|
||||
} else {
|
||||
console.log(icon, ...args);
|
||||
try {
|
||||
switch(level) {
|
||||
case "error":
|
||||
coloredArgs = args.map(arg => typeof arg === 'string' ? chalk.red(arg) : arg);
|
||||
break;
|
||||
case "warn":
|
||||
coloredArgs = args.map(arg => typeof arg === 'string' ? chalk.yellow(arg) : arg);
|
||||
break;
|
||||
case "success":
|
||||
coloredArgs = args.map(arg => typeof arg === 'string' ? chalk.green(arg) : arg);
|
||||
break;
|
||||
case "info":
|
||||
coloredArgs = args.map(arg => typeof arg === 'string' ? chalk.blue(arg) : arg);
|
||||
break;
|
||||
case "debug":
|
||||
coloredArgs = args.map(arg => typeof arg === 'string' ? chalk.gray(arg) : arg);
|
||||
break;
|
||||
// default: use original args (no color)
|
||||
}
|
||||
} catch (colorError) {
|
||||
// Fallback if chalk fails on an argument
|
||||
// Use console.error here for internal logger errors, separate from normal logging
|
||||
console.error("Internal Logger Error applying chalk color:", colorError);
|
||||
coloredArgs = args;
|
||||
}
|
||||
|
||||
// Revert to console.log - FastMCP's context logger (context.log)
|
||||
// is responsible for directing logs correctly (e.g., to stderr)
|
||||
// during tool execution without upsetting the client connection.
|
||||
// Logs outside of tool execution (like startup) will go to stdout.
|
||||
console.log(prefix, ...coloredArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a logger object with methods for different log levels
|
||||
* Can be used as a drop-in replacement for existing logger initialization
|
||||
* @returns {Object} Logger object with info, error, debug, warn, and success methods
|
||||
*/
|
||||
export function createLogger() {
|
||||
const createLogMethod = (level) => (...args) => log(level, ...args);
|
||||
|
||||
return {
|
||||
debug: (message) => log("debug", message),
|
||||
info: (message) => log("info", message),
|
||||
warn: (message) => log("warn", message),
|
||||
error: (message) => log("error", message),
|
||||
success: (message) => log("success", message),
|
||||
debug: createLogMethod("debug"),
|
||||
info: createLogMethod("info"),
|
||||
warn: createLogMethod("warn"),
|
||||
error: createLogMethod("error"),
|
||||
success: createLogMethod("success"),
|
||||
log: log, // Also expose the raw log function
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { z } from "zod";
|
||||
import {
|
||||
handleApiResult,
|
||||
createErrorResponse,
|
||||
createContentResponse,
|
||||
getProjectRootFromSession
|
||||
} from "./utils.js";
|
||||
import { addTaskDirect } from "../core/task-master-core.js";
|
||||
@@ -14,11 +15,12 @@ import { addTaskDirect } from "../core/task-master-core.js";
|
||||
/**
|
||||
* Register the add-task tool with the MCP server
|
||||
* @param {Object} server - FastMCP server instance
|
||||
* @param {AsyncOperationManager} asyncManager - The async operation manager instance.
|
||||
*/
|
||||
export function registerAddTaskTool(server) {
|
||||
export function registerAddTaskTool(server, asyncManager) {
|
||||
server.addTool({
|
||||
name: "add_task",
|
||||
description: "Add a new task using AI",
|
||||
description: "Starts adding a new task using AI in the background.",
|
||||
parameters: z.object({
|
||||
prompt: z.string().describe("Description of the task to add"),
|
||||
dependencies: z.string().optional().describe("Comma-separated list of task IDs this task depends on"),
|
||||
@@ -26,29 +28,38 @@ export function registerAddTaskTool(server) {
|
||||
file: z.string().optional().describe("Path to the tasks file"),
|
||||
projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)")
|
||||
}),
|
||||
execute: async (args, { log, reportProgress, session }) => {
|
||||
execute: async (args, context) => {
|
||||
const { log, reportProgress, session } = context;
|
||||
try {
|
||||
log.info(`MCP add_task called with prompt: "${args.prompt}"`);
|
||||
log.info(`MCP add_task request received with prompt: \"${args.prompt}\"`);
|
||||
|
||||
// Get project root using the utility function
|
||||
if (!args.prompt) {
|
||||
return createErrorResponse("Prompt is required for add_task.", "VALIDATION_ERROR");
|
||||
}
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
// Fallback to args.projectRoot if session didn't provide one
|
||||
if (!rootFolder && args.projectRoot) {
|
||||
rootFolder = args.projectRoot;
|
||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
||||
}
|
||||
|
||||
// Call the direct function with the resolved rootFolder
|
||||
const result = await addTaskDirect({
|
||||
projectRoot: rootFolder, // Pass the resolved root
|
||||
|
||||
const directArgs = {
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
};
|
||||
|
||||
const operationId = asyncManager.addOperation(addTaskDirect, directArgs, context);
|
||||
|
||||
return handleApiResult(result, log);
|
||||
log.info(`Started background operation for add_task. Operation ID: ${operationId}`);
|
||||
|
||||
return createContentResponse({
|
||||
message: "Add task operation started successfully.",
|
||||
operationId: operationId
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
log.error(`Error in add_task MCP tool: ${error.message}`);
|
||||
return createErrorResponse(error.message, "ADD_TASK_ERROR");
|
||||
log.error(`Error initiating add_task operation: ${error.message}`, { stack: error.stack });
|
||||
return createErrorResponse(`Failed to start add task operation: ${error.message}`, "ADD_TASK_INIT_ERROR");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ export function registerAnalyzeTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Analyzing task complexity with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -42,9 +42,9 @@ export function registerAnalyzeTool(server) {
|
||||
const result = await analyzeTaskComplexityDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Task complexity analysis complete: ${result.data.message}`);
|
||||
|
||||
@@ -26,7 +26,7 @@ export function registerComplexityReportTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Getting complexity report with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -38,9 +38,9 @@ export function registerComplexityReportTool(server) {
|
||||
const result = await complexityReportDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully retrieved complexity report${result.fromCache ? ' (from cache)' : ''}`);
|
||||
|
||||
@@ -30,7 +30,7 @@ export function registerExpandAllTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Expanding all tasks with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -42,19 +42,19 @@ export function registerExpandAllTool(server) {
|
||||
const result = await expandAllTasksDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`All tasks expanded successfully: ${result.data.message}`);
|
||||
log.info(`Successfully expanded all tasks: ${result.data.message}`);
|
||||
} else {
|
||||
log.error(`Failed to expand tasks: ${result.error.message}`);
|
||||
log.error(`Failed to expand all tasks: ${result.error?.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
return handleApiResult(result, log, 'Error expanding tasks');
|
||||
return handleApiResult(result, log, 'Error expanding all tasks');
|
||||
} catch (error) {
|
||||
log.error(`Error in expandAll tool: ${error.message}`);
|
||||
log.error(`Error in expand-all tool: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@ export function registerExpandTaskTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Expanding task with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -48,20 +48,20 @@ export function registerExpandTaskTool(server) {
|
||||
const result = await expandTaskDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully expanded task ID: ${args.id} with ${result.data.subtasksAdded} new subtasks${result.data.hasExistingSubtasks ? ' (appended to existing subtasks)' : ''}`);
|
||||
log.info(`Successfully expanded task with ID ${args.id}`);
|
||||
} else {
|
||||
log.error(`Failed to expand task: ${result.error.message}`);
|
||||
log.error(`Failed to expand task: ${result.error?.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
return handleApiResult(result, log, 'Error expanding task');
|
||||
} catch (error) {
|
||||
log.error(`Error in expand-task tool: ${error.message}`);
|
||||
return createErrorResponse(`Failed to expand task: ${error.message}`);
|
||||
log.error(`Error in expand task tool: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ export function registerGenerateTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Generating task files with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -44,14 +44,14 @@ export function registerGenerateTool(server) {
|
||||
const result = await generateTaskFilesDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully generated task files: ${result.data.message}`);
|
||||
} else {
|
||||
log.error(`Failed to generate task files: ${result.error.message}`);
|
||||
log.error(`Failed to generate task files: ${result.error?.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
return handleApiResult(result, log, 'Error generating task files');
|
||||
|
||||
42
mcp-server/src/tools/get-operation-status.js
Normal file
42
mcp-server/src/tools/get-operation-status.js
Normal file
@@ -0,0 +1,42 @@
|
||||
// mcp-server/src/tools/get-operation-status.js
|
||||
import { z } from 'zod';
|
||||
import { createErrorResponse, createContentResponse } from './utils.js'; // Assuming these utils exist
|
||||
|
||||
/**
|
||||
* Register the get_operation_status tool.
|
||||
* @param {FastMCP} server - FastMCP server instance.
|
||||
* @param {AsyncOperationManager} asyncManager - The async operation manager.
|
||||
*/
|
||||
export function registerGetOperationStatusTool(server, asyncManager) {
|
||||
server.addTool({
|
||||
name: 'get_operation_status',
|
||||
description: 'Retrieves the status and result/error of a background operation.',
|
||||
parameters: z.object({
|
||||
operationId: z.string().describe('The ID of the operation to check.'),
|
||||
}),
|
||||
execute: async (args, { log }) => {
|
||||
try {
|
||||
const { operationId } = args;
|
||||
log.info(`Checking status for operation ID: ${operationId}`);
|
||||
|
||||
const status = asyncManager.getStatus(operationId);
|
||||
|
||||
// Status will now always return an object, but it might have status='not_found'
|
||||
if (status.status === 'not_found') {
|
||||
log.warn(`Operation ID not found: ${operationId}`);
|
||||
return createErrorResponse(
|
||||
status.error?.message || `Operation ID not found: ${operationId}`,
|
||||
status.error?.code || 'OPERATION_NOT_FOUND'
|
||||
);
|
||||
}
|
||||
|
||||
log.info(`Status for ${operationId}: ${status.status}`);
|
||||
return createContentResponse(status);
|
||||
|
||||
} catch (error) {
|
||||
log.error(`Error in get_operation_status tool: ${error.message}`, { stack: error.stack });
|
||||
return createErrorResponse(`Failed to get operation status: ${error.message}`, 'GET_STATUS_ERROR');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export function registerListTasksTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Getting tasks with filters: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -48,9 +48,9 @@ export function registerListTasksTool(server) {
|
||||
const result = await listTasksDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log);
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
log.info(`Retrieved ${result.success ? (result.data?.tasks?.length || 0) : 0} tasks${result.fromCache ? ' (from cache)' : ''}`);
|
||||
return handleApiResult(result, log, 'Error getting tasks');
|
||||
|
||||
@@ -27,12 +27,15 @@ import { registerComplexityReportTool } from "./complexity-report.js";
|
||||
import { registerAddDependencyTool } from "./add-dependency.js";
|
||||
import { registerRemoveTaskTool } from './remove-task.js';
|
||||
import { registerInitializeProjectTool } from './initialize-project.js';
|
||||
import { asyncOperationManager } from '../core/utils/async-manager.js';
|
||||
import { registerGetOperationStatusTool } from './get-operation-status.js';
|
||||
|
||||
/**
|
||||
* Register all Task Master tools with the MCP server
|
||||
* @param {Object} server - FastMCP server instance
|
||||
* @param {asyncOperationManager} asyncManager - The async operation manager instance
|
||||
*/
|
||||
export function registerTaskMasterTools(server) {
|
||||
export function registerTaskMasterTools(server, asyncManager) {
|
||||
try {
|
||||
// Register each tool
|
||||
registerListTasksTool(server);
|
||||
@@ -45,7 +48,7 @@ export function registerTaskMasterTools(server) {
|
||||
registerShowTaskTool(server);
|
||||
registerNextTaskTool(server);
|
||||
registerExpandTaskTool(server);
|
||||
registerAddTaskTool(server);
|
||||
registerAddTaskTool(server, asyncManager);
|
||||
registerAddSubtaskTool(server);
|
||||
registerRemoveSubtaskTool(server);
|
||||
registerAnalyzeTool(server);
|
||||
@@ -58,10 +61,13 @@ export function registerTaskMasterTools(server) {
|
||||
registerAddDependencyTool(server);
|
||||
registerRemoveTaskTool(server);
|
||||
registerInitializeProjectTool(server);
|
||||
registerGetOperationStatusTool(server, asyncManager);
|
||||
} catch (error) {
|
||||
logger.error(`Error registering Task Master tools: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
logger.info('Registered Task Master MCP tools');
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -31,7 +31,7 @@ export function registerNextTaskTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Finding next task with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -43,24 +43,20 @@ export function registerNextTaskTool(server) {
|
||||
const result = await nextTaskDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
if (result.data.nextTask) {
|
||||
log.info(`Successfully found next task ID: ${result.data.nextTask.id}${result.fromCache ? ' (from cache)' : ''}`);
|
||||
} else {
|
||||
log.info(`No eligible next task found${result.fromCache ? ' (from cache)' : ''}`);
|
||||
}
|
||||
log.info(`Successfully found next task: ${result.data?.task?.id || 'No available tasks'}`);
|
||||
} else {
|
||||
log.error(`Failed to find next task: ${result.error.message}`);
|
||||
log.error(`Failed to find next task: ${result.error?.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
return handleApiResult(result, log, 'Error finding next task');
|
||||
} catch (error) {
|
||||
log.error(`Error in next-task tool: ${error.message}`);
|
||||
return createErrorResponse(`Failed to find next task: ${error.message}`);
|
||||
log.error(`Error in nextTask tool: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ export function registerParsePRDTool(server) {
|
||||
}),
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Parsing PRD document with args: ${JSON.stringify(args)}`);
|
||||
log.info(`Parsing PRD with args: ${JSON.stringify(args)}`);
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -45,19 +45,19 @@ export function registerParsePRDTool(server) {
|
||||
const result = await parsePRDDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully generated ${result.data?.taskCount || 0} tasks from PRD at ${result.data?.outputPath}`);
|
||||
log.info(`Successfully parsed PRD: ${result.data.message}`);
|
||||
} else {
|
||||
log.error(`Failed to parse PRD: ${result.error?.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
return handleApiResult(result, log, 'Error parsing PRD document');
|
||||
return handleApiResult(result, log, 'Error parsing PRD');
|
||||
} catch (error) {
|
||||
log.error(`Error in parse_prd tool: ${error.message}`);
|
||||
log.error(`Error in parse-prd tool: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -28,7 +28,7 @@ export function registerRemoveDependencyTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Removing dependency for task ${args.id} from ${args.dependsOn} with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -40,9 +40,9 @@ export function registerRemoveDependencyTool(server) {
|
||||
const result = await removeDependencyDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully removed dependency: ${result.data.message}`);
|
||||
|
||||
@@ -29,7 +29,7 @@ export function registerRemoveSubtaskTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -41,9 +41,9 @@ export function registerRemoveSubtaskTool(server) {
|
||||
const result = await removeSubtaskDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Subtask removed successfully: ${result.data.message}`);
|
||||
|
||||
@@ -37,7 +37,7 @@ export function registerSetTaskStatusTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Setting status of task(s) ${args.id} to: ${args.status}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -49,9 +49,9 @@ export function registerSetTaskStatusTool(server) {
|
||||
const result = await setTaskStatusDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully updated status for task(s) ${args.id} to "${args.status}": ${result.data.message}`);
|
||||
|
||||
@@ -34,7 +34,7 @@ export function registerUpdateSubtaskTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Updating subtask with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -46,9 +46,9 @@ export function registerUpdateSubtaskTool(server) {
|
||||
const result = await updateSubtaskByIdDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully updated subtask with ID ${args.id}`);
|
||||
|
||||
@@ -34,7 +34,7 @@ export function registerUpdateTaskTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Updating task with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -46,9 +46,9 @@ export function registerUpdateTaskTool(server) {
|
||||
const result = await updateTaskByIdDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully updated task with ID ${args.id}`);
|
||||
|
||||
@@ -34,7 +34,7 @@ export function registerUpdateTool(server) {
|
||||
execute: async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
log.info(`Updating tasks with args: ${JSON.stringify(args)}`);
|
||||
await reportProgress({ progress: 0 });
|
||||
// await reportProgress({ progress: 0 });
|
||||
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
|
||||
@@ -46,9 +46,9 @@ export function registerUpdateTool(server) {
|
||||
const result = await updateTasksDirect({
|
||||
projectRoot: rootFolder,
|
||||
...args
|
||||
}, log, { reportProgress, mcpLog: log, session});
|
||||
}, log/*, { reportProgress, mcpLog: log, session}*/);
|
||||
|
||||
await reportProgress({ progress: 100 });
|
||||
// await reportProgress({ progress: 100 });
|
||||
|
||||
if (result.success) {
|
||||
log.info(`Successfully updated tasks from ID ${args.from}: ${result.data.message}`);
|
||||
|
||||
Reference in New Issue
Block a user